Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converting nanoseconds to picoseconds? #19

Closed
sdbbs opened this issue Oct 23, 2020 · 2 comments
Closed

Converting nanoseconds to picoseconds? #19

sdbbs opened this issue Oct 23, 2020 · 2 comments

Comments

@sdbbs
Copy link

sdbbs commented Oct 23, 2020

I tried reading a bit through the docs, but cannot figure how to do this...

Let's say I have a '1 ns', and I would like to convert this to picoseconds; a-priori I know that 1 ns = 1000 ps. So:

>>> from quantiphy import Quantity
>>> mytime=Quantity('1 ns')
>>> mytime
Quantity('1 ns')
>>> mytime.real
1e-09
>>> mytime.units
's'
>>> mytime.render(scale='ps')
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/quantiphy.py", line 235, in convert_units
    return _unit_conversions[(to_units, from_units)](value)
KeyError: ('ps', 's')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/site-packages/quantiphy.py", line 2207, in render
    number, units = _scale(scale, number, self.units)
  File "/usr/lib/python3.8/site-packages/quantiphy.py", line 55, in _scale
    number = convert_units(scale, units, number)
  File "/usr/lib/python3.8/site-packages/quantiphy.py", line 237, in convert_units
    raise UnknownConversion(to_units, from_units)
quantiphy.UnknownConversion: unable to convert between 'ps' and 's'.

>>> mytime.render(1e-12, scale='s')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/site-packages/quantiphy.py", line 2250, in render
    assert form in ['eng', False], '{}: unknown form.'.format(form)
AssertionError: 1e-12: unknown form.

How can I correctly convert from one to another prefix (e.g. n to p), without changing the (physical) unit?


Edit: Read through https://quantiphy.readthedocs.io/en/stable/user.html#scaling-when-creating-a-quantity - so I tried using a tuple for scale:

>>> mytime
Quantity('1 ns')
>>> mytime.render(scale=(1e-12, 's'))
'1e-21 s'

.... and this passes code-wise, but gives me the wrong result.


Edit2: found https://stackoverflow.com/questions/10969759/python-library-to-convert-between-si-unit-prefixes/45122861#45122861 - and thought this would help:

>>> mytime.render(show_si=False, scale=(1e-12, 'ps'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: render() got an unexpected keyword argument 'show_si'

Thankfully, it was easy to find in the repo that: "... change show_si to form ..."; so I tried this:

>>> mytime.render(form=False, scale=(1e-12, 'ps'))
'1e-21 ps'

Wrong result again ... however, the opposite seems to work:

>>> mytime.render(form=False, scale=(1e12, 'ps'))
'1e3 ps'

... yes, indeed 1ns is 1e3 = 1000 ps, great! Except, now I'm not sure, what is the meaning of the first number in scale ...

However, the .render function apparently just prints - what I'd like is to convert to a new Quantity, so that when I specify '1 ns' as input time, and specify 'ps' as the "output" unit, then I'd like to be able to read the 1e3 == 1000 as a plain number somehow (without the units) ... - Ah, that probably works like this:

>>> mytime.scale(scale=(1e12, 'ps'))
Quantity('1 kps')
>>> mytime.scale(scale=(1e12, 'ps')).real
1000.0000000000001

So, this kind of conversion is possible - but how are these kinds of conversions supposed to work?

@KenKundert
Copy link
Owner

The primary intended workflow with QuantiPhy is:

  1. Use QuantiPhy to convert numbers with units and scale factors given as strings into Quantity objects where any scale factor provided has been evaluated with the resulting number being stored in its base units. So, Quantity('1 ns') is stored as 1e-9 's'.
  2. Perform any needed computation that involves these numbers. These computation generally result in the units being lost. For example:
    >>> tstart = Quantity('25 ps')
    >>> tstop = Quantity('1.025 ns')
    >>> tdelta = tstop - tstart
    >>> tdelta
    1e-09
  1. Convert quantities to strings for output.
    >>> print(Quantity(tdelta, 's'))
    1 ns

So a fundamental assumption is that the except when reading and writing numbers, the value is always represented in its base units, in this case, seconds.

I don't really understand your motivation for wanting the value in picoseconds, so let me give two possible answers.

First I will assume that internally you need picoseconds in order to perform a calculation. In that case you can simply do the following:

    >>> mytime = Quantity('1 ns')
    >>> mytime_in_ps = 1e12 * mytime

Or, if you want the code to be more explicit, you can use the following:

    >>> mytime_in_ps = mytime / Quantity('1 ps')

Notice that mytime_in_ps is represented as a float. You generally only want to use a QuantiPhy's quantity when representing numbers in their base units, otherwise you can end up with weird results like:

    >>> print(Quantity(1000, units='ps'))
    1 kps

That explains why I did not use the scale feature of QuantiPhy, as it would have ended up with a quantity with base units of 'ps'. You can do this if you want, but then you should avoid outputting numbers with scale factors. So for example, you can use:

    >>> mytime = Quantity('1 ns', scale=(1e12, 'ps'))
    >>> print(mytime)
    1 kps
    >>> mytime.real
    1000
    >>> mytime.units
    'ps'

    >>> print(mytime.fixed())
    1000 ps

The scale factor is a scale by value rather than a scale to value, so it is simply multiplied by the specified number to get the final result. So in this case '1 ns' is converted to 1e-9 's', which is then multiplied by 1e12 to get the final result in 'ps'.

The other possibility that I can think of is that the internal computations are performed in seconds and you simply want to convert the results into picoseconds upon output. This is the more common scenario but it does not appear to be what you want. Anyway, just in case, here it is:

    >>> mytime = Quantity('1 ns')
    >>> print(mytime.render(scale=(1e12, 'ps')))
    1 kps

In this case scale converts the result to picoseconds, but you should also suppress the use of SI scale factors:

    >>> print(mytime.fixed(scale=(1e12, 'ps')))
    1000 ps

    >>> print(mytime.render(scale=(1e12, 'ps'), form='eng'))
    1e3 ps

@sdbbs
Copy link
Author

sdbbs commented Oct 26, 2020

Hi @KenKundert ,

Many thanks for the excellent feedback!

I don't really understand your motivation for wanting the value in picoseconds,

Ah yes, sorry about that.

Basically, I started experting with some libraries for VCD files; one of the key parts of these files is the timescale statement:

 $timescale 1ps $end

So, the timescale is specified as a string. Now, some libraries default to "ps" timescale, some to "ns" timescale - so if I want to convert from one to the other, and avoid resampling, I want to find out how many picoseconds is, say, "100 ns". So I throught - instead of me trying to write my own converter, I might as well try to see is there is some units library - and I found Quantiphy. And it works great for me in this case - I was just unclear on how things are supposed to work.

The scale factor is a scale by value rather than a scale to value, so it is simply multiplied by the specified number to get the final result

Excellent - thanks for this, this is the critical piece of information that I was missing. And thanks also for the great examples. I guess I can close this issue now ...

@sdbbs sdbbs closed this as completed Oct 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants