tchoupy


Nametchoupy JSON
Version 1.0.0 PyPI version JSON
download
home_pageNone
SummaryTime-saving tool for unit conversions and dimensional quantity computations
upload_time2024-09-28 10:02:13
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT License Copyright (c) 2024 Martin Teuscher Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords units unit conversion conversion systems physics dimensional quantities
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # tchoupy
# Toolbox for Conversions (H ?) and Operations and Units with PYthon
# Time-saving tool for unit conversions and dimensional quantity computations

# ===INSTALLATION===

The project is written in **Python3** and makes use of various scientific
libraries which you have to install. To do so, we recommend using pip.
A good practice in python is to set up a virtual environment
```bash
python -m venv my_env
```
Now before any utilisation, please activate the virtual environment
```bash
source my_env/bin/activate
```
Install the requirements using pip.
Note: if pip is unable to find some librairies, it may be that they are already
builtins of your python installation. This often happens with e.g. the warnings
package.
```bash
pip install -r requirements.txt
```

# ===QUICKSTART===

The core of this package allows you to

1) create variables representing physical quantities: x = SI(3, 'km') represents
a length of 3 kilometers ('SI' is explained in the next section)

2) easily convert these variables into any compatible unit: x.m equals 3000
(m stands for meter), x.cm equals 100000, x.mile equals 1.864, etc

3) manipulate these quantities as numbers : x*x gives another physical quantity
equal to 9 (km)², 1/x**(1/2) can be expressed in meter^(-1/2) etc

4) perform more advanced functionalities described in what follows

## Different systems for unit conversions

A crucial thing to understand is that two units may or may not be convertible 
into one another, depending on what conversions you are allowing. For example,
In the International System of units (SI), 1 second cannot be expressed in
meters, while in High Energy Physics (HEP) it can, because the speed of light
is set to 1, implying that 1 second = 299792458 meters. You could also be a
meteorologist for whom height of water on the ground (in millimeters) is
equivalent to a volume of rainwater (in liters), thus wish to convert liters 
into millimeters and vice-versa; but obviously this conversion would not make
any sense outside of your field.

To account for this, you need to specify which set of rules ('system') you are 
using for conversions  when creating an object representing a physical quantity. 
Python-wise, this object will be an instance of the conversion system you want 
to use (which itself is a class, hence a callable). The syntax is very easy. 
Just call the system's name with a value and a unit as its two arguments:
```python
from tchoupy.systems import HEP, SI
>>> time1 = HEP(60, 's')   #60 seconds within High Energy Physics system
>>> time2 = SI(60, 's')   #60 seconds within International System of units
```
The value of the created object in a given unit is then retrieved by putting
the unit (its abbreviatied form) as an attribute of the object:
```python
>>> time1.min   # min stands for minute
1.0
>>> time2.min
1.0
>>> time1.m   # m stands for meter
17987547480.0
>>> time2.m
ValueError: Quantity 60.0[s1] cannot be expressed in [m] within SI system.
Dimensions do not match.
```
The latter error signals an incompability between seconds and meters in SI.


To provide as much versatility as possible, several systems are already
implemented in the package. As of version 1.0 you have access to :

- SI : conversion system according to the International System of units.
There are seven base units that are independent of each other (meaning, they
cannot be converted into one another) : second (s), meter (m), gram* (g),
kelvin (K), ampere (A), candela (cd), mole (mol). 
Additional units are: Hz, C, N, J, W, V, ohm, S, T, min, hr, day, yr, mpers,
mperh, Pa, bar, atm, Me, Mp, Mn, u, angstrom, AU, gpermol, earthgravity, 
optionally preceded by any of the following prefixes:
Y, Z, E, P, T, G, M, k, h, da, d, c, m, µ, mu, n, p, f, a, z, y. 

- HEP : conversion system for High Energy Physics, based on c = k_B = hbar = 1
and the Planck charge q_p = sqrt(4 pi epsilon_0 hbar c) = 1.
There is only one base unit : electronvolt (eV). Therefore, all units have a
dimension equal to some power of eV and any two units with the same dimension
can be converted into one another.
Additional units are J, g, MPl, K, V, Planck, one, mol, C, s, m, tPl, erg, 
Msun, reducedMpl, Me, Mp, Mn, u, Hz, A, gpermol, earthgravity, lightspeed,
mpers, mperh, ohm, lPl, min, hr, yr, pc, lyr, AU, angstrom, N, T, G, W, barn,
Jy, Pa, optionally preceded by any of the following prefixes: 
T, G, M, k, c, m, mu, n.

- Cosmo : TODO.

To import any of them, just look in the 'systems' submodule:
```python
>>> from tchoupy.systems import SI
```

## How to write a valid unit name

So far we have only dealt with quantities whose units are straightforward to
express (min, s, km). But what if the quantity's unit would be made of several
'subunits', e.g. meter per second, joule per kelvin, cubic meter and so forth?

Let us break this down with a few examples, then explain the rationale behind:

```python
>>> speed = SI(36, 'm1s_1')   # 36 meter per second
>>> speed.km1h_1   # x in kilometer per hour
10.0
>>> bar = SI(1e5, 'kg1s_2m_1')   # 1 bar = 1e5 pascal = 1e5 kg/m/s^2
>>> bar.Pa   # 1 bar in pascals
100000.0
>>> bar.kg1s_2m_1
```
So, to write a compound unit, concatenate the subunits, separated by the number
at which each subunit is powered. Note that whenever this number is negative,
an underscore (_) is used in place of a minus sign (-). The rationale is
visible in the examples: as an unit name shall always be a valid python
attribute name, it cannot contain anything else than letters, numbers or _.
A power of 1 cannot be omitted, because the program would wrongly decipher the
string: 'ms_1' would be read millisecond^(-1) and not meter * (second^(-1)).

You may also encounter the case when the power is a fractional number, but
in a similar fashion the character '/' cannot be part of an attribute name. You
must use 'o' instead (alias for 'over'), as in
```python
>>> length = SI(1, 'ha1o2')   # a length expressed in hectare^(1/2)
>>> length.m    
100.   # the side of a square with surface 1 hectare is 100 meter long
```
Finally, note that any prefix in the unit name is automatically included inside
the power, as naturally expected in the common language. For instance:
```python
>>> density = SI(1000, 'm_3')   # a number density of 1000 per cubic meter
>>> density.cm_3   # y per cubic centimeter
0.001   # and obviously not 10.
```

With all of the above you can express quantities in rather complex units. A last 
famous example from cosmology is the Hubble constant:
```python
>>> H0 = HEP(1.44e-42, 'GeV')
>>> H0.km1s_1Mpc_1   # H0 in kilometer per second per megaparsec
67.506760860605
```

Extra note: what if you ask for the object itself, without specifying any
argument? The object's builtin method __repr__ has been modified to return 
its value expressed in the primary units of the system, under a string
form:
```python
>>> bar = SI(1e5, 'kg1s_2m_1')   # 1 bar = 1e5 pascal = 1e5 kg/m/s^2
>>> bar
100000.0[s_2m_1kg1]   # 1e5 per squared second per meter times kg kilogram
```

## Operations on quantities

The last powerful feature of tchoupy is to help simplify your computations when
handling with physical quantities, so that you don't have to express all your
variables in the same unit or worry about unit conversion. It allows you
to add, substract, multiply, divide and take the power of the objects you define
(it overrides builtin methods like __add__ or __truediv__), then returns another
object whose unit is consistent with your computation. Consider the following:
```python
>>> length = SI(300,'m')   # 300 meters
>>> length2 = SI(5, 'km')   # 5 kilometers
>>> speed = SI(5, 'm1s_1')   # 5 meter per second
>>> volume = length**3
>>> type(volume)
conversionsystem_metaclass.SI
>>> volume.dam3   # volume in cubic decameter
27000.0
>>> length + length2
5300.0[m1]   # 5300 meters
>>> length / speed
60.0[s1]   # it takes 60 seconds to travel 300m at 5m/s
>>> time = length / speed
>>> time.min   # time in minutes
1.0
>>> length + time
ValueError: The space of dimensional numbers is a graded algebra: you can add
two numbers of the same dimension, or multiply a number by a dimensionless
scalar or another number, but adding two numbers of different dimensions
is not allowed.
```
Note that the last error message would not have been triggered if HEP system
had been chosen over SI system, as time and length have the same dimension
from the HEP point of view.

To maintain consistency, it is only possible to mix quantities defined from
the same system.

Remark: floating powers of physical quantities (e.g. length**3.45) are
approximated by the closest rational number (with sufficiently low denominator),
in order to only keep track of rational dimensions. This is motivated by 
physics considerations, as there is to our knowledge no example of irrational
dimensions (e.g. meter**sqrt(2)). See advanced features for more details.

# ===ADVANCED FEATURES===

## Create one's own conversion system

To increase the versatility of possible conversions, the user can define their
own conversion systems. They can create any unit and any conversion rules that
are suitable for their purpose. Let's see how.

To create a new system of units, you must call the ConversionSystem class (the
returned object will itself be a class, as HEP or SI above, hence
ConversionSystem - CoSy henceforth - is a so-called "metaclass").
Suppose for instance that we are working on a hiking website, and want to
convert between lengths and durations of hiking paths. We could define a
conversion system as follows:
```python
from tchoupy import ConversionSystem
Hikes = ConversionSystem('Hikes', 

                     'hundredmeter',
                     [('minute', 0.7), ('mile', 16.09), ('step', 1/140)],
                     
                     'gram',
                     [('ounce', 28.3)],
                     
                     'hundredmeter2',
                     [('hectare', 1.)],
                     
					 SI_prefixes = ['k', 'c', 'm'],
                     custom_prefixes = [('big', 2.)],
                     MAX_DENOMINATOR = 100,
					 
                     meter = (0.01, 'hundredmeter'),
                     hour = (60., 'minute'),
                     second = (1/60, 'minute'),
                     heartbeat = (0.8, 'second'),
                     mileph = 'mile1hour_1',
                     kilostepph = 'kstep1hour_1',
                     imperialhectare = 'mile2',
                     
                     )
```
1) 1st argument to CoSy is the name of your unit conversion system. In principle
it can be different from the name of the variable to which it is assigned,
but there is no reason to complicate things. So, Hikes is a system as SI
or HEP before.

2) Following the name come (any even number of) positional arguments, working
by pair. The first argument of a pair is a unit. The second is a list of other
units that can be converted into the primary unit you just specified, together
with the conversion ratio. 

2a) Here, we have defined our first fundamental unit, 'hundredmeter'.
We then defined a minute, equal to 0.7 hundredmeter (based on
the assumption that someone walks 70 meters in one minute), a mile 
(1609 meters) and a step (a 140th of 100meter or 71cm). We emphasize that
'meter' or 'cm' do not exist in the conversion system, we simply
mention them for the sake of clarity. Any two of these units can then be 
converted into one another:
```python
>>> short_hike = Hikes(3, 'mile')
>>> short_hike.minute
68.95714285714286   # It takes arounds 69 minutes to walk 3 miles
```

2b) The primary unit of the second pair is 'gram', and we define what an ounce
is (28.3 grams). Each individual primary unit is treated as independent from 
the others, so conversion between hundredmeter and gram are supposed to be
impossible.

2c) The third pair refers to a previously defined primary unit ('hundredmeter')
that has been raised to some power. Contrary to 2b), no new primary unit is 
defined here, but additional unit "hectare" is created. As a consequence,
units equivalent to hundredmeter, when raised to the 
appropriate power, will automatically be compatible with hectare:
```python
>>> other_hike = Hikes(30, 'minute')   # A 30-minute long hike
>>> other_hike.hundredmeter
21.0
>>> (other_hike**2).hectare   # squaring a quantity equivalent to length 
441.0
```
All in all, the number of independant dimensions here is 2 (hundredmeter and
grams).

3) Keyword arguments:

3a) In SI_prefixes, list the common prefixes you want to make available in your
system. They will be attached to unit names. Here, since we included 'k'
(kilo), units khundredmeter, kminute, kstep, kgram, kounce and khectare are
thus available. Same is true for 'c' (0.01) and 'm' (0.001):
```python
>>> afewsteps = Hikes(2000.0, 'step')
>>> afewsteps.kstep
2.0
>>> surface = Hikes(1.0, 'khundredmeter2')
>>> surface.hundredmeter2
1000000.0
```
IMPORTANT: in the second example, note that the power following the unit ALSO
applies to the prefix value (1000^2 = 1e6). This will always be the case:
cm3 (cubic centimeter) = (cm)^3 = 1e-6 (m^3).

IMPORTANT: if you want ALL common prefixes from yotta to yocto to be included,
you can set `SI_prefixes = "_ALL"` instead of having to write the whole list.


3b) In custom_prefixes you can generate new (alphabetical characters only)
prefix abbreviations that are not part of the classical list of prefixes. 
The rest is similar to 3a).

3c) MAX_DENOMINATOR: see below.

4) Extra keywords arguments:
This feature makes it practical to define a lot of extra units, as soon as they
can be defined by a composition of other previously defined units, with a 
optional prefactor. As you can see, the syntax is:
    `new_unit_name = (prefactor, composite_unit_string)`
where a valid string for composite_unit_string value has been defined in the 
Quickstart section above. A few important remarks:
- the syntax `new_unit_name = composite_unit_string` is also valid and is a 
    shortcut for `new_unit_name = (1.0, composite_unit_string)`
- as dictionaries have a canonical order in python, you can use a new unit 
    defined this way inside the composite_unit_string of another, as long as 
    the latter is defined after the former was. See the definition of heartbeat
    or mileph above: they are based on units second and hour, themselves defined
    as a keyword argument.
- new_unit_name must only be made of alphabetical characters
- Prefixes will also be attached to the units defined this way.
 
### MAX_DENOMINATOR

Best use: do not touch this argument.

The possibility of powering physical dimensions (cubic meter, second squared,...)
has already been mentioned. However, to our knowledge, physics always deals 
with powers being rational numbers (for an example of a non-integer power, 
hertz^(1/2) is a unit sometimes used in signaling theory. But more simply, we 
would like (x^3)^(1/3) to return x, so allowing for integer powers immediately 
calls for the need of fractional powers anyway). Mathematically speaking, the 
space of physical dimensions is a Q-vector space (or even, a Q-graded algebra). 

Ensuring compatibility between units and quantities therefore implies to keep 
track of these rational dimensions: if x can be expressed in cm, x**3 must be 
expressible in cm3, but not in cm2. As we chose to represent unit names as
strings, yhis requirement is naturally in conflict with python floating point
errors, since e.g. x**(0.1+0.2) should a priori be expressed in 
`cm0.30000000000000004` !
To bypass this problem, we need to round up floating point errors to determine 
what power was "intented" by the user" (here 0.3 was intended although python 
returned 0.30000000000000004). The rounding precision is then an arbitrary 
parameter, left to the user. The following rule has been retained:
When a physical quantity is raised to some float power, the program will 
replace the float power by the closest rational with denominator (in 
irreducible form) lower than or equal to MAX_DENOMINATOR. This way, the 
dimension remains a rational fraction of the original dimension.

The default is MAX_DENOMINATOR=100, as it is assumed that fractional powers 
with denominator bigger than 100 very rarely occurs in physics.

All of this is achieved thanks to the Fractions module, which is reputably slow.
Future versions might leverage this dependency.

## Add custom units to a preexisting conversion system

You can add a unit to an already existing system this way:
```python
>>> Masses = ConversionSystem('Masses',
                                  'gram',
                                  [('ton', 1e6), ('ounce', 28.3)]
                                  )
>>> Masses['kilo'] = (1000.0, 'gram')
```
The syntax is similar to item 4) in the last subsection:
    `System[new_unit_name] = (prefactor, composite_unit_string)`
and the types & restrictions are the same. Prefixes included in the system 
(see 3a) of the last subsection) will also be attached to this new unit.

This can be very handy when you need a system similar to a builtin one,
except for a few units, but don't want to rebuild it entirely from scratch:
```python
>>> from tchoupy.systems import HEP as HEP_but_with_speed_of_sound
>>> HEP_but_with_speed_of_sound['soundspeed'] = (343.0, 'm1s_1')
>>> HEP_but_with_speed_of_sound(1., 'soundspeed').lightspeed   
1.1441248465296617e-06   # speed of sound in speed of light unit
```

## Change the fundamental unit of a (preexisting) system

TODO. This is already available through the change_repr(old, new) method, but
is not safe as of the first version.

# ===COMMON PITFALLS===

Below we list a few common mistakes that are worth knowing about.

1) Be careful not to use an unappropriate primary units for your system
(e.g. electronvolt while all your computations are about miles and kilometers).
As the program internally converts every value into primary units, very
large conversion factors may quickly appear, and even be enhanced by squaring 
or cubing your units. This can potentially lead to large floating point errors
or even overflows. To avoid this behaviour, create your own system with an
appropriate primary unit, or use the change_repr method of a builtin dict
to change one of its fundamental units (FEATURE TO COME).

2) Because of prefixes, overlaps in unit definitions may happen when defining
your own systems - or adding new units to an existing one. For instance,
 "giga-al" (Gal) and gallons (Gal) cannot be present in the same system. 
This is checked by the program and it will raise an error if that happens.  


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "tchoupy",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "units, unit conversion, conversion systems, physics, dimensional quantities",
    "author": null,
    "author_email": "Martin Teuscher <teuscher.edu@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/6f/e6/208119d0390483f12e4a64175ef8992e67e97ebf56372cf8706b4ff4453d/tchoupy-1.0.0.tar.gz",
    "platform": null,
    "description": "# tchoupy\r\n# Toolbox for Conversions (H ?) and Operations and Units with PYthon\r\n# Time-saving tool for unit conversions and dimensional quantity computations\r\n\r\n# ===INSTALLATION===\r\n\r\nThe project is written in **Python3** and makes use of various scientific\r\nlibraries which you have to install. To do so, we recommend using pip.\r\nA good practice in python is to set up a virtual environment\r\n```bash\r\npython -m venv my_env\r\n```\r\nNow before any utilisation, please activate the virtual environment\r\n```bash\r\nsource my_env/bin/activate\r\n```\r\nInstall the requirements using pip.\r\nNote: if pip is unable to find some librairies, it may be that they are already\r\nbuiltins of your python installation. This often happens with e.g. the warnings\r\npackage.\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n# ===QUICKSTART===\r\n\r\nThe core of this package allows you to\r\n\r\n1) create variables representing physical quantities: x = SI(3, 'km') represents\r\na length of 3 kilometers ('SI' is explained in the next section)\r\n\r\n2) easily convert these variables into any compatible unit: x.m equals 3000\r\n(m stands for meter), x.cm equals 100000, x.mile equals 1.864, etc\r\n\r\n3) manipulate these quantities as numbers : x*x gives another physical quantity\r\nequal to 9 (km)\u00b2, 1/x**(1/2) can be expressed in meter^(-1/2) etc\r\n\r\n4) perform more advanced functionalities described in what follows\r\n\r\n## Different systems for unit conversions\r\n\r\nA crucial thing to understand is that two units may or may not be convertible \r\ninto one another, depending on what conversions you are allowing. For example,\r\nIn the International System of units (SI), 1 second cannot be expressed in\r\nmeters, while in High Energy Physics (HEP) it can, because the speed of light\r\nis set to 1, implying that 1 second = 299792458 meters. You could also be a\r\nmeteorologist for whom height of water on the ground (in millimeters) is\r\nequivalent to a volume of rainwater (in liters), thus wish to convert liters \r\ninto millimeters and vice-versa; but obviously this conversion would not make\r\nany sense outside of your field.\r\n\r\nTo account for this, you need to specify which set of rules ('system') you are \r\nusing for conversions  when creating an object representing a physical quantity. \r\nPython-wise, this object will be an instance of the conversion system you want \r\nto use (which itself is a class, hence a callable). The syntax is very easy. \r\nJust call the system's name with a value and a unit as its two arguments:\r\n```python\r\nfrom tchoupy.systems import HEP, SI\r\n>>> time1 = HEP(60, 's')   #60 seconds within High Energy Physics system\r\n>>> time2 = SI(60, 's')   #60 seconds within International System of units\r\n```\r\nThe value of the created object in a given unit is then retrieved by putting\r\nthe unit (its abbreviatied form) as an attribute of the object:\r\n```python\r\n>>> time1.min   # min stands for minute\r\n1.0\r\n>>> time2.min\r\n1.0\r\n>>> time1.m   # m stands for meter\r\n17987547480.0\r\n>>> time2.m\r\nValueError: Quantity 60.0[s1] cannot be expressed in [m] within SI system.\r\nDimensions do not match.\r\n```\r\nThe latter error signals an incompability between seconds and meters in SI.\r\n\r\n\r\nTo provide as much versatility as possible, several systems are already\r\nimplemented in the package. As of version 1.0 you have access to :\r\n\r\n- SI : conversion system according to the International System of units.\r\nThere are seven base units that are independent of each other (meaning, they\r\ncannot be converted into one another) : second (s), meter (m), gram* (g),\r\nkelvin (K), ampere (A), candela (cd), mole (mol). \r\nAdditional units are: Hz, C, N, J, W, V, ohm, S, T, min, hr, day, yr, mpers,\r\nmperh, Pa, bar, atm, Me, Mp, Mn, u, angstrom, AU, gpermol, earthgravity, \r\noptionally preceded by any of the following prefixes:\r\nY, Z, E, P, T, G, M, k, h, da, d, c, m, \u00b5, mu, n, p, f, a, z, y. \r\n\r\n- HEP : conversion system for High Energy Physics, based on c = k_B = hbar = 1\r\nand the Planck charge q_p = sqrt(4 pi epsilon_0 hbar c) = 1.\r\nThere is only one base unit : electronvolt (eV). Therefore, all units have a\r\ndimension equal to some power of eV and any two units with the same dimension\r\ncan be converted into one another.\r\nAdditional units are J, g, MPl, K, V, Planck, one, mol, C, s, m, tPl, erg, \r\nMsun, reducedMpl, Me, Mp, Mn, u, Hz, A, gpermol, earthgravity, lightspeed,\r\nmpers, mperh, ohm, lPl, min, hr, yr, pc, lyr, AU, angstrom, N, T, G, W, barn,\r\nJy, Pa, optionally preceded by any of the following prefixes: \r\nT, G, M, k, c, m, mu, n.\r\n\r\n- Cosmo : TODO.\r\n\r\nTo import any of them, just look in the 'systems' submodule:\r\n```python\r\n>>> from tchoupy.systems import SI\r\n```\r\n\r\n## How to write a valid unit name\r\n\r\nSo far we have only dealt with quantities whose units are straightforward to\r\nexpress (min, s, km). But what if the quantity's unit would be made of several\r\n'subunits', e.g. meter per second, joule per kelvin, cubic meter and so forth?\r\n\r\nLet us break this down with a few examples, then explain the rationale behind:\r\n\r\n```python\r\n>>> speed = SI(36, 'm1s_1')   # 36 meter per second\r\n>>> speed.km1h_1   # x in kilometer per hour\r\n10.0\r\n>>> bar = SI(1e5, 'kg1s_2m_1')   # 1 bar = 1e5 pascal = 1e5 kg/m/s^2\r\n>>> bar.Pa   # 1 bar in pascals\r\n100000.0\r\n>>> bar.kg1s_2m_1\r\n```\r\nSo, to write a compound unit, concatenate the subunits, separated by the number\r\nat which each subunit is powered. Note that whenever this number is negative,\r\nan underscore (_) is used in place of a minus sign (-). The rationale is\r\nvisible in the examples: as an unit name shall always be a valid python\r\nattribute name, it cannot contain anything else than letters, numbers or _.\r\nA power of 1 cannot be omitted, because the program would wrongly decipher the\r\nstring: 'ms_1' would be read millisecond^(-1) and not meter * (second^(-1)).\r\n\r\nYou may also encounter the case when the power is a fractional number, but\r\nin a similar fashion the character '/' cannot be part of an attribute name. You\r\nmust use 'o' instead (alias for 'over'), as in\r\n```python\r\n>>> length = SI(1, 'ha1o2')   # a length expressed in hectare^(1/2)\r\n>>> length.m    \r\n100.   # the side of a square with surface 1 hectare is 100 meter long\r\n```\r\nFinally, note that any prefix in the unit name is automatically included inside\r\nthe power, as naturally expected in the common language. For instance:\r\n```python\r\n>>> density = SI(1000, 'm_3')   # a number density of 1000 per cubic meter\r\n>>> density.cm_3   # y per cubic centimeter\r\n0.001   # and obviously not 10.\r\n```\r\n\r\nWith all of the above you can express quantities in rather complex units. A last \r\nfamous example from cosmology is the Hubble constant:\r\n```python\r\n>>> H0 = HEP(1.44e-42, 'GeV')\r\n>>> H0.km1s_1Mpc_1   # H0 in kilometer per second per megaparsec\r\n67.506760860605\r\n```\r\n\r\nExtra note: what if you ask for the object itself, without specifying any\r\nargument? The object's builtin method __repr__ has been modified to return \r\nits value expressed in the primary units of the system, under a string\r\nform:\r\n```python\r\n>>> bar = SI(1e5, 'kg1s_2m_1')   # 1 bar = 1e5 pascal = 1e5 kg/m/s^2\r\n>>> bar\r\n100000.0[s_2m_1kg1]   # 1e5 per squared second per meter times kg kilogram\r\n```\r\n\r\n## Operations on quantities\r\n\r\nThe last powerful feature of tchoupy is to help simplify your computations when\r\nhandling with physical quantities, so that you don't have to express all your\r\nvariables in the same unit or worry about unit conversion. It allows you\r\nto add, substract, multiply, divide and take the power of the objects you define\r\n(it overrides builtin methods like __add__ or __truediv__), then returns another\r\nobject whose unit is consistent with your computation. Consider the following:\r\n```python\r\n>>> length = SI(300,'m')   # 300 meters\r\n>>> length2 = SI(5, 'km')   # 5 kilometers\r\n>>> speed = SI(5, 'm1s_1')   # 5 meter per second\r\n>>> volume = length**3\r\n>>> type(volume)\r\nconversionsystem_metaclass.SI\r\n>>> volume.dam3   # volume in cubic decameter\r\n27000.0\r\n>>> length + length2\r\n5300.0[m1]   # 5300 meters\r\n>>> length / speed\r\n60.0[s1]   # it takes 60 seconds to travel 300m at 5m/s\r\n>>> time = length / speed\r\n>>> time.min   # time in minutes\r\n1.0\r\n>>> length + time\r\nValueError: The space of dimensional numbers is a graded algebra: you can add\r\ntwo numbers of the same dimension, or multiply a number by a dimensionless\r\nscalar or another number, but adding two numbers of different dimensions\r\nis not allowed.\r\n```\r\nNote that the last error message would not have been triggered if HEP system\r\nhad been chosen over SI system, as time and length have the same dimension\r\nfrom the HEP point of view.\r\n\r\nTo maintain consistency, it is only possible to mix quantities defined from\r\nthe same system.\r\n\r\nRemark: floating powers of physical quantities (e.g. length**3.45) are\r\napproximated by the closest rational number (with sufficiently low denominator),\r\nin order to only keep track of rational dimensions. This is motivated by \r\nphysics considerations, as there is to our knowledge no example of irrational\r\ndimensions (e.g. meter**sqrt(2)). See advanced features for more details.\r\n\r\n# ===ADVANCED FEATURES===\r\n\r\n## Create one's own conversion system\r\n\r\nTo increase the versatility of possible conversions, the user can define their\r\nown conversion systems. They can create any unit and any conversion rules that\r\nare suitable for their purpose. Let's see how.\r\n\r\nTo create a new system of units, you must call the ConversionSystem class (the\r\nreturned object will itself be a class, as HEP or SI above, hence\r\nConversionSystem - CoSy henceforth - is a so-called \"metaclass\").\r\nSuppose for instance that we are working on a hiking website, and want to\r\nconvert between lengths and durations of hiking paths. We could define a\r\nconversion system as follows:\r\n```python\r\nfrom tchoupy import ConversionSystem\r\nHikes = ConversionSystem('Hikes', \r\n\r\n                     'hundredmeter',\r\n                     [('minute', 0.7), ('mile', 16.09), ('step', 1/140)],\r\n                     \r\n                     'gram',\r\n                     [('ounce', 28.3)],\r\n                     \r\n                     'hundredmeter2',\r\n                     [('hectare', 1.)],\r\n                     \r\n\t\t\t\t\t SI_prefixes = ['k', 'c', 'm'],\r\n                     custom_prefixes = [('big', 2.)],\r\n                     MAX_DENOMINATOR = 100,\r\n\t\t\t\t\t \r\n                     meter = (0.01, 'hundredmeter'),\r\n                     hour = (60., 'minute'),\r\n                     second = (1/60, 'minute'),\r\n                     heartbeat = (0.8, 'second'),\r\n                     mileph = 'mile1hour_1',\r\n                     kilostepph = 'kstep1hour_1',\r\n                     imperialhectare = 'mile2',\r\n                     \r\n                     )\r\n```\r\n1) 1st argument to CoSy is the name of your unit conversion system. In principle\r\nit can be different from the name of the variable to which it is assigned,\r\nbut there is no reason to complicate things. So, Hikes is a system as SI\r\nor HEP before.\r\n\r\n2) Following the name come (any even number of) positional arguments, working\r\nby pair. The first argument of a pair is a unit. The second is a list of other\r\nunits that can be converted into the primary unit you just specified, together\r\nwith the conversion ratio. \r\n\r\n2a) Here, we have defined our first fundamental unit, 'hundredmeter'.\r\nWe then defined a minute, equal to 0.7 hundredmeter (based on\r\nthe assumption that someone walks 70 meters in one minute), a mile \r\n(1609 meters) and a step (a 140th of 100meter or 71cm). We emphasize that\r\n'meter' or 'cm' do not exist in the conversion system, we simply\r\nmention them for the sake of clarity. Any two of these units can then be \r\nconverted into one another:\r\n```python\r\n>>> short_hike = Hikes(3, 'mile')\r\n>>> short_hike.minute\r\n68.95714285714286   # It takes arounds 69 minutes to walk 3 miles\r\n```\r\n\r\n2b) The primary unit of the second pair is 'gram', and we define what an ounce\r\nis (28.3 grams). Each individual primary unit is treated as independent from \r\nthe others, so conversion between hundredmeter and gram are supposed to be\r\nimpossible.\r\n\r\n2c) The third pair refers to a previously defined primary unit ('hundredmeter')\r\nthat has been raised to some power. Contrary to 2b), no new primary unit is \r\ndefined here, but additional unit \"hectare\" is created. As a consequence,\r\nunits equivalent to hundredmeter, when raised to the \r\nappropriate power, will automatically be compatible with hectare:\r\n```python\r\n>>> other_hike = Hikes(30, 'minute')   # A 30-minute long hike\r\n>>> other_hike.hundredmeter\r\n21.0\r\n>>> (other_hike**2).hectare   # squaring a quantity equivalent to length \r\n441.0\r\n```\r\nAll in all, the number of independant dimensions here is 2 (hundredmeter and\r\ngrams).\r\n\r\n3) Keyword arguments:\r\n\r\n3a) In SI_prefixes, list the common prefixes you want to make available in your\r\nsystem. They will be attached to unit names. Here, since we included 'k'\r\n(kilo), units khundredmeter, kminute, kstep, kgram, kounce and khectare are\r\nthus available. Same is true for 'c' (0.01) and 'm' (0.001):\r\n```python\r\n>>> afewsteps = Hikes(2000.0, 'step')\r\n>>> afewsteps.kstep\r\n2.0\r\n>>> surface = Hikes(1.0, 'khundredmeter2')\r\n>>> surface.hundredmeter2\r\n1000000.0\r\n```\r\nIMPORTANT: in the second example, note that the power following the unit ALSO\r\napplies to the prefix value (1000^2 = 1e6). This will always be the case:\r\ncm3 (cubic centimeter) = (cm)^3 = 1e-6 (m^3).\r\n\r\nIMPORTANT: if you want ALL common prefixes from yotta to yocto to be included,\r\nyou can set `SI_prefixes = \"_ALL\"` instead of having to write the whole list.\r\n\r\n\r\n3b) In custom_prefixes you can generate new (alphabetical characters only)\r\nprefix abbreviations that are not part of the classical list of prefixes. \r\nThe rest is similar to 3a).\r\n\r\n3c) MAX_DENOMINATOR: see below.\r\n\r\n4) Extra keywords arguments:\r\nThis feature makes it practical to define a lot of extra units, as soon as they\r\ncan be defined by a composition of other previously defined units, with a \r\noptional prefactor. As you can see, the syntax is:\r\n    `new_unit_name = (prefactor, composite_unit_string)`\r\nwhere a valid string for composite_unit_string value has been defined in the \r\nQuickstart section above. A few important remarks:\r\n- the syntax `new_unit_name = composite_unit_string` is also valid and is a \r\n    shortcut for `new_unit_name = (1.0, composite_unit_string)`\r\n- as dictionaries have a canonical order in python, you can use a new unit \r\n    defined this way inside the composite_unit_string of another, as long as \r\n    the latter is defined after the former was. See the definition of heartbeat\r\n    or mileph above: they are based on units second and hour, themselves defined\r\n    as a keyword argument.\r\n- new_unit_name must only be made of alphabetical characters\r\n- Prefixes will also be attached to the units defined this way.\r\n \r\n### MAX_DENOMINATOR\r\n\r\nBest use: do not touch this argument.\r\n\r\nThe possibility of powering physical dimensions (cubic meter, second squared,...)\r\nhas already been mentioned. However, to our knowledge, physics always deals \r\nwith powers being rational numbers (for an example of a non-integer power, \r\nhertz^(1/2) is a unit sometimes used in signaling theory. But more simply, we \r\nwould like (x^3)^(1/3) to return x, so allowing for integer powers immediately \r\ncalls for the need of fractional powers anyway). Mathematically speaking, the \r\nspace of physical dimensions is a Q-vector space (or even, a Q-graded algebra). \r\n\r\nEnsuring compatibility between units and quantities therefore implies to keep \r\ntrack of these rational dimensions: if x can be expressed in cm, x**3 must be \r\nexpressible in cm3, but not in cm2. As we chose to represent unit names as\r\nstrings, yhis requirement is naturally in conflict with python floating point\r\nerrors, since e.g. x**(0.1+0.2) should a priori be expressed in \r\n`cm0.30000000000000004` !\r\nTo bypass this problem, we need to round up floating point errors to determine \r\nwhat power was \"intented\" by the user\" (here 0.3 was intended although python \r\nreturned 0.30000000000000004). The rounding precision is then an arbitrary \r\nparameter, left to the user. The following rule has been retained:\r\nWhen a physical quantity is raised to some float power, the program will \r\nreplace the float power by the closest rational with denominator (in \r\nirreducible form) lower than or equal to MAX_DENOMINATOR. This way, the \r\ndimension remains a rational fraction of the original dimension.\r\n\r\nThe default is MAX_DENOMINATOR=100, as it is assumed that fractional powers \r\nwith denominator bigger than 100 very rarely occurs in physics.\r\n\r\nAll of this is achieved thanks to the Fractions module, which is reputably slow.\r\nFuture versions might leverage this dependency.\r\n\r\n## Add custom units to a preexisting conversion system\r\n\r\nYou can add a unit to an already existing system this way:\r\n```python\r\n>>> Masses = ConversionSystem('Masses',\r\n                                  'gram',\r\n                                  [('ton', 1e6), ('ounce', 28.3)]\r\n                                  )\r\n>>> Masses['kilo'] = (1000.0, 'gram')\r\n```\r\nThe syntax is similar to item 4) in the last subsection:\r\n    `System[new_unit_name] = (prefactor, composite_unit_string)`\r\nand the types & restrictions are the same. Prefixes included in the system \r\n(see 3a) of the last subsection) will also be attached to this new unit.\r\n\r\nThis can be very handy when you need a system similar to a builtin one,\r\nexcept for a few units, but don't want to rebuild it entirely from scratch:\r\n```python\r\n>>> from tchoupy.systems import HEP as HEP_but_with_speed_of_sound\r\n>>> HEP_but_with_speed_of_sound['soundspeed'] = (343.0, 'm1s_1')\r\n>>> HEP_but_with_speed_of_sound(1., 'soundspeed').lightspeed   \r\n1.1441248465296617e-06   # speed of sound in speed of light unit\r\n```\r\n\r\n## Change the fundamental unit of a (preexisting) system\r\n\r\nTODO. This is already available through the change_repr(old, new) method, but\r\nis not safe as of the first version.\r\n\r\n# ===COMMON PITFALLS===\r\n\r\nBelow we list a few common mistakes that are worth knowing about.\r\n\r\n1) Be careful not to use an unappropriate primary units for your system\r\n(e.g. electronvolt while all your computations are about miles and kilometers).\r\nAs the program internally converts every value into primary units, very\r\nlarge conversion factors may quickly appear, and even be enhanced by squaring \r\nor cubing your units. This can potentially lead to large floating point errors\r\nor even overflows. To avoid this behaviour, create your own system with an\r\nappropriate primary unit, or use the change_repr method of a builtin dict\r\nto change one of its fundamental units (FEATURE TO COME).\r\n\r\n2) Because of prefixes, overlaps in unit definitions may happen when defining\r\nyour own systems - or adding new units to an existing one. For instance,\r\n \"giga-al\" (Gal) and gallons (Gal) cannot be present in the same system. \r\nThis is checked by the program and it will raise an error if that happens.  \r\n\r\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2024 Martin Teuscher  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
    "summary": "Time-saving tool for unit conversions and dimensional quantity computations",
    "version": "1.0.0",
    "project_urls": {
        "Homepage": "https://github.com/Martinuche/tchoupy"
    },
    "split_keywords": [
        "units",
        " unit conversion",
        " conversion systems",
        " physics",
        " dimensional quantities"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d569e385c07a7350cf097b1172ae2822b0015456aad347f2f3f5f3b1e27de140",
                "md5": "c3bd1e1a9829fbe86ad1a982a628a9b1",
                "sha256": "af7dd84659f362421aa895035fb3099d64a264314b36e99b1546b2f31fd4a6fd"
            },
            "downloads": -1,
            "filename": "tchoupy-1.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c3bd1e1a9829fbe86ad1a982a628a9b1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 39669,
            "upload_time": "2024-09-28T10:02:11",
            "upload_time_iso_8601": "2024-09-28T10:02:11.766889Z",
            "url": "https://files.pythonhosted.org/packages/d5/69/e385c07a7350cf097b1172ae2822b0015456aad347f2f3f5f3b1e27de140/tchoupy-1.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6fe6208119d0390483f12e4a64175ef8992e67e97ebf56372cf8706b4ff4453d",
                "md5": "251906aad6c05de2a638e3fc2ea42ae2",
                "sha256": "f205790f1ab294354fa9350b96d77e64a06b7c45c3101c0085e1c053b9a77d77"
            },
            "downloads": -1,
            "filename": "tchoupy-1.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "251906aad6c05de2a638e3fc2ea42ae2",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 44398,
            "upload_time": "2024-09-28T10:02:13",
            "upload_time_iso_8601": "2024-09-28T10:02:13.549067Z",
            "url": "https://files.pythonhosted.org/packages/6f/e6/208119d0390483f12e4a64175ef8992e67e97ebf56372cf8706b4ff4453d/tchoupy-1.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-28 10:02:13",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Martinuche",
    "github_project": "tchoupy",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "tchoupy"
}
        
Elapsed time: 0.52030s