Environments#

Definitions:#

  • “derived unit”: a unit in the SI system that is a compound of the SI base units (e.g. N, J, W, Hz)

  • “defined unit”: a non-SI unit whose definition comes from an SI unit, e.g. an inch is legally defined as 25.4 mm and a lb is approximately equal to 4.448222 N

  • “environment object”: describes the singleton Environment class (see below)

  • “environment” will be used to describe a Python namespace that is populated with variables of Physical instances

  • “environment file” describes a .json file where the unit definitions reside

forallpeople is an SI units library that allows other non-SI units to be defined and used. The definitions for the derived SI units (e.g. N, Pa, W, J, etc.) and the definitions for the defined non-SI units (e.g. lb, ft, psf) are all located in an environment file.

An environment object within forallpeople is a singleton instance of the forallpeople.environment.Environment class. Its purpose is to read the definitions of the units in the environment file and to populate the module namespace with those definitions as variables that are ready to be used for computation. It is not intended to be interacted with directly but instead through the forallpeople.environment function.

Loading Environments#

Use the environment function to load an environment JSON file. The JSON files are located in the package in the “environments” directory. i.e. path/to/your/python/environment/site-packages/forallpeople/environments when installed on your system.

Currently, this is the only location searched for environment files. This will likely change in a future update to allow environment files to be loaded from user-defined locations.

The location of your current forallpeople installation can generally be found by importing the module and then using the .__file__ attribute of the imported module’s variable name.

[1]:
import forallpeople as si
si.environment('default')

When the above code is run, the environment file is parsed and all of the units listed in that file are loaded in the forallpeople module namespace which, in this example, is under the prefix si.

Bundled environments#

forallpeople comes with the following bundled environments: * default: Defines the SI derived units * us_customary: Defines the most common units within the US Customary units system * structural: Defines a series of units in both SI and US Customary units systems that are commonly used in the field of structural engineering (my field of practice) in Canada. * electrical: My attempt at creating something useful for electrical engineers * thermal: My attempt at creating something useful for building envelope engineers * test_definitions: Used for the internal test suite. Not intended for daily use.

Inspecting environments#

To see what units have been loaded as variables, run the environment function with no arguments.

[2]:
si.environment()
{'Hz': 1.000 Hz, 'N': 1.000 N, 'Pa': 1.000 Pa, 'J': 1.000 J, 'W': 1.000 W, 'C': 1.000 C, 'V': 1.000 V, 'F': 1.000 F, 'Ohm': 1.000 Ω, 'S': 1.000 S, 'Wb': 1.000 Wb, 'T': 1.000 T, 'H': 1.000 H, 'Celsius': 1.000 °C, 'lux': 1.000 lux, 'Gy': 1.000 Gy, 'katal': 1.000 kat, 'minute': 1.000 minutes, 'hour': 1.000 hours, 'day': 1.000 days}
 {'kg': 1.000 kg, 'm': 1.000 m, 's': 1.000 s, 'A': 1.000 A, 'cd': 1.000 cd, 'K': 1.000 °C, 'mol': 1.000 mol}

This prints the contents of two dictionaries: 1. The dictionary of all the units that are defined in the environment file. 2. The dictionary of the SI base units which are always present when importing forallpeople

The keys of the dictionaries are the identifiers (variable names) used in namespace. So, si.Hz and si.N. The values of the dictionaries are the actual Physical instances.

Environment files#

An environemnt file is a JSON file which typically looks like this (excerpted from the included environment, “structural.json”):

{
    "mm": {
        "Dimension": [0,1,0,0,0,0,0],
        "Value": 0.001},
    "ft": {
        "Dimension": [0,1,0,0,0,0,0],
        "Symbol": "ft",
        "Factor": "1/0.3048"},
    "inch": {
        "Dimension": [0,1,0,0,0,0,0],
        "Symbol": "inch",
        "Factor": "12/0.3048"},
    "N": {
        "Dimension": [1,1,-2,0,0,0,0]},
    "kN": {
        "Dimension": [1,1,-2,0,0,0,0],
        "Value": 1000},
    "MN": {
        "Dimension": [1,1,-2,0,0,0,0],
        "Value": 1000000},

    // ...additional entries here
}

A single entry consists of the following schema:

Note, in this example // are used to indicate “comments” which are not valid in JSON so this example is not directly “copy-pastable” into an environment file.

"identifier": {
    "Dimension": [0, 0, 0, 0, 0, 0, 0], // req, an integer array of length 7
    "Symbol": "str", // opt, if not given, the identifier is used as the symbol
    "Factor": 1, // opt, int | float | str, if not given, a default value of 1.0 is assumed
    "Value": 1, // opt, int | float, if not given, a default value of 1.0 is assumed
    "Default": false, // opt, bool, if not given, a default value of false is assumed
}
  • Identifier: this is must be a valid Python identifier because it will be used as the variable name to which this quantity is assigned to when the environment file is loaded into the environment. The identifier will also be the display symbol if an alternate symbol string is not provided.

  • Dimension: describes a vector of SI base unit components in the following order: kg, m, s, A, cd, K, mol

  • Symbol: an alternate string to use instead of the identifier

  • Factor: a value that will be multiplied by to scale the SI base unit so to “convert” the SI base unit quantity into an equivalent amount of this new unit. The factor is commonly represented as a string representing an arithmetic expression (e.g. "1/0.45359237/9.80665/0.3048") but can also be an int or float. If an arithmetic string is provided, the string will be evaulated by converting each value first into a fractions.Fraction and then performing the operations described (eval is not used under-the-hood).

  • Default: if True (or true in JSON), then this unit will be the default displayed unit for any Physical instance that has matching Dimension and a Factor != 1.

Note: By design, default values (currently) ONLY apply to defined units (units that have a factor != 1). The motivation for enabling a default unit is to have a preferred fallback when a calculation using defined units results in a .factor attribute that does not have a match in the environment. In this case, having a representation suddenly switch to SI units is surprising.

To avoid this surprise, a unit definition can be specified as the default so a calculation using lbs can be multiplied/divided by many other values and, no matter how the .factor attribute gets mangled, the result can be specified in lbs (as long as the dimensions are consistent with the dimensions of lbs).

Customizing your environments (making new environment files)#

The recommended way of creating a new environment is to copy one of the existing environments, give it a new name, and begin customizing it by adding, removing, or modifying existing definitions. I recommend comparing and contrasting the “default.json” (which only defines derived units and has very few extra attributes in each definition) to the “structural.json” (which contains many defined units in the US Customary system…[because I practice engineering in Canada]).

Things to keep in mind:

  • The factor expressions used for US customary units (in “structural.json” and “us_customary.json”) are generally based off of the legal definitions of their components (meaning that these values are exact values). So the conversion from a kg to an international avoirdupois pound is exactly 0.45359237, which is a number you will see in the environment files that have any mass components being scaled into a US equivalent. You can find these legal definitions on Wikipedia (e.g. see above example in “Definitions”) but they are all based off of these two definitions: https://en.wikipedia.org/wiki/International_yard_and_pound

  • Adding a “Default” attribute will only be meaningful when applied to defined units

  • JSON does not allow you to have trailing commas (whereas Python does) - Watch out for that if you have invalid JSON as a result of copy-pasting definitions from the middle of the file to the end of the file.

  • JSON does not allow you to use comments so do not try to put any in

  • The attributes all have to be Title case. This was an early design decision that I would not make again but I am not willing to make a breaking change just for that (but I will update that if there are other breaking changes that need to be made).

Unit Resolution#

The representation of units (quantities) in a forallpeople environment are determined by three attributes: 1. Dimension: the vector of a quantity’s component SI base units (e.g. N has a dimension of (1, 1, -2, 0, 0, 0, 0) representing kg1 * m1 * s-2) 2. Factor: if the unit is a defined unit then it will have a .factor attribute != 1 3. Prefix: a prefix is only taken into consideration for SI base units and SI derived units (never for defined units)

An entry into an environment file describes the quantity name, it’s associated dimension, it’s factor, and its value (prefix is auto-generated based on the value).

The look-up order is as follows: 1. Dimension: are there one or more definitions with a dimension that matches the current state of an instance? 2. Factor: does one of the matching dimension definitions also have a matching .factor attribute?

  • If the factor and dimension are a match, it is a defined unit and the defined unit representation will be given priority.

  • If dimension matches but the factor does not match but the factor is != 1, then the derived definition will be used as a fallback unless a default unit is indicated (see below).

  • If dimension matches and the factor == 1, then the derived representation is given priority

  • If there is not a dimension match, the representation will fallback to a compound string of SI base units (e.g. kg1 * m1 * s-2)