Standard behaviours#
forallpeople
has chosen a “convention over configuration” approach. While units environments are intended to be customizable for each individual person’s preference, certain behaviours have been designed as “standard” and are described below.
[2]:
import forallpeople as si
si.environment('default')
1. Conventional Arithmetic#
forallpeople
defines the methods required for conventional arithmetic between Physical
instances.
Addition and subtraction are possible between physical quantities of the same dimension.
Multiplication and division are possible between any physical quantities and result in new dimensions.
Exponentiation is possible with integers and floats
Floor division and modulo are currently not defined because of the ambiguity created with the other standard behaviours (see: Using Floor and Modulo, below)
[3]:
a = 500 * si.kg
b = 23 * si.kg
c = 300 * si.kg
display(a + b - c)
[4]:
d = 9.81 * si.m / si.s**2
display(d)
2. Auto-scaling of base units and derived units#
[5]:
display(5000 * si.kg) # Auto-scaling to Mg
[6]:
display(4_000_000_000 / si.s) # Auto-scaling to GHz
[7]:
display(0.000000112 * si.m) # Auto-scaling to nm
Undefined products of base units are not scaled
[8]:
display(40000 * si.A * si.kg) # No auto-scaling because kg*A is not in the environment
Instances of defined units (i.e. .factor
attribute != 1) are not scaled.
[9]:
si.environment('us_customary')
display(40000 * si.lbf)
3. Three-decimals precision#
Due to auto-scaling, Physical
instances only show three units of precision. However, this can be changed at the instance-level by using the built-in round()
function and at the module-level by setting the precision in the environment (see Environments).
[10]:
si.environment('default')
[11]:
a = 4230.2349329 * si.kg
display(a)
b = round(a, 6)
display(b)
If an instance is not eligible for auto-scaling (see above) then the three decimal places of precision can lead to unexpected apparent results.
[12]:
c = 1 / (4000000 * si.kg * si.A)
display(c) # 0.000???
display(round(c, 9))
display("{:.3e}".format(c))
'2.500e-07 kg⁻¹·A⁻¹'
4. Unit cancellation#
When an instance has its units “cancelled” out by multiplication or division, then the resulting value is a float
.
[13]:
length = 4.2 * si.m
spacing = 0.25 * si.m
num_of_spaces = length / spacing
display(num_of_spaces)
16.8
[14]:
a = 40 * si.Hz
b = 3 * si.s
cycles = a * b
display(cycles)
120.0
5. Conversion to float
and int
#
Due to auto-scaling the display value is often different than the underlying .value
attribute, which represents the instance’s value in SI base units.
Using float()
will return the value of the instance in its scaled value.
Similarily, when using an environment where US customary units are defined, using float()
will return the factored value.
In other words, the intention of the behaviour of float()
is WYSIWYG (what you see is what you get). This enables other customized behaviours which may be desirable even if they are mathematically inconsistent (see Dimensionsally-inconsistent Calculations).
To get the unscaled and unfactored value, use .value
instead.
[15]:
a = 5523400 * si.N
display(a)
display(a.value)
5523400
[16]:
b = float(a) # The value will retain its scaling in MN
display(b)
c = int(b)
display(c)
5.5234
5
6. Using math
functions#
The functions in the built-in math
module, generally, accept a float
and return a float
. When Physical
instances are passed to the math functions, float()
is first called on the Physical
which, naturally, converts it to a float (see above). This obliterates any unit information about the physical instance.
[17]:
import math
[18]:
length = 4200 * si.m # Auto-scales to 4.2 km
display(math.sqrt(length)) # Returns sqrt of 4.2
2.04939015319192
To add unit information back to the resulting quantity, use the .split()
method to perform the calculation in a mathematically consistent way.
[19]:
value, unit = length.split()
display(math.sqrt(value) * unit)
See Dimensionally-inconsistent Calculations for additional information on managing similar situations.
7. Using Floor and Modulo#
Currently, //
and %
are not implemented on Physical
objects because a decision has not been made on how they should behave. i.e. Should the //
and %
operators work on the underlying SI .value
attribute or on the displayed and scaled unit?
If you have an opinion on this, please voice it on a GitHub issue.