Group and system enhancements
Thanks to #768 we have can improve the Group and Systems API. Please propose ideas as independent messages here. Please upvote/downvote or suggest modifications
Add Quantity.in_group(self, name: str) -> bool to test if a Quantity belongs to a given group. Equivalent to name in ureg.get_group(name, False).members
Add Quantity.to_system(self, name: str) -> Quantity to convert a Quantity to a given system.
UnitRegistry.get_compatible_units(self, unit,system_or_group: str) -> frozenset[Unit]
Get all compatible units within a system coherent with base units :
- base units
- named derived_units (from base_units)
- composition from based units and derived units
- Example :
UREG.get_compatible_units("lbf/in²","mks")- ("Pa","N/m²","kg/m/s²","bar")
- Example :
UnitRegistry.get_compatible_units(self, unit,system_or_group: str) -> frozenset[Unit]Get all compatible units within a system coherent with base units :
* base units * named derived_units (from base_units) * composition from based units and derived units * Example : `UREG.get_compatible_units("lbf/in²","mks")` * ("Pa","N/m²","kg/m/s²","bar")
Is there a difference with the current implementation here
Yes for now get_compatible_units use with system only filter with units added (through group inclusion) to the system not with (base units , derived units and composed units) as stated above.
I would request programmatic changes to unit systems to be added similar to #432 and #342. Since in 432, there was a request for how this would be useful, here is the use case:
The current default 'US' length unit is yards (in my opinion, why not miles or ft?). But if we had a programmatic unit system, I could simply add a one liner to the top of my package/script to override this undesireable, again in my opinion, unit selection. I don't want to have a separate file to maintain, and it makes the script immediately understandable without having to consult another file, which is more pythonic, again in my opinion.
import pint
ureg = pint.UnitRegistry(system='US')
ureg.define_defaultunit('mile')
original = 5280 * ureg('ft')
new = q.to_base_units() # new is now 1 mile
I would like systems to allow using non-base dimensions for base units. For example, atomic units are defined in terms of the constants e (charge), m_e (mass), hbar (action) and k_e (electric constant). It would be nice to be able to simply specify these as units, instead of m_e, a_0, "atomic unit of current" and "atomic unit of time".
It would be even nicer if one could re-define the base dimensions (maybe with explicit conversion rules), so that to_base_units would give the quantities in terms of e and hbar too.
For a couple years now I've been using a class I wrote that works with pint to create a portable system of units. It differs from the system definition currently in pint in that you access the base units in terms of their type (similar to but not the same as dimensionality) and, if not defined, it will automatically define derived units (acceleration, area, density, energy, force, power, pressure, speed, volume) as properties based on the required units (angle, length, mass, temperature, time).
It doesn't enforce dimensionality because sometimes modeling is intentionally performed with an inconsistent set of dimensions. An example described here is that in finite element modeling you may define mass and force to have the same units and density to be [force] / [length] ** 3 and use a correction factor to make the results consistent. (It's a dumb issue that doesn't really come up in the metric system, but it's pretty common unfortunately.)
The class I wrote accounts for this by having a check_consistency method which can warn if the system is inconsistent and return appropriate correction factors.
Also, it has class methods for:
-
meter_kilogram_second/mks -
centimeter_gram_second/cgs -
millimeter_tonne_second/`mmts -
meter_tonne_second/mts - `british_gravitational
- `english_engineering
- 'english_inch_force'
- 'english_inch_mass'
This works outside of the typical default/group/system definition, and maybe there's functionality I've overlooked that does similar things, so I'm not really sure of the best way to incorporate it. However, if there's interest I'm willing to provide the code/help.
I am intrigued by this. Can you provide more information? In particular, could you describe the type in a bit more depth?
A better term might've been the quantity, like the categories in default_en.txt, including both base and derived units. Currently that list is: acceleration, angle, area, density, energy, force, length, mass, power, pressure, speed, temperature, time, volume. That covers most of my use cases, but I know it's not exhaustive.
I write a lot of automation to parametrically generate models that aren't all in Python, so what I needed from a unit system is more from a systems engineering perspective. Things like:
- Define inputs or save defaults as quantities in whatever unit your most comfortable with, which pint does very well and is why I started using it in the first place
- Convert inputs to the model unit system and access things like standard gravity in the system's acceleration unit, which is similar to what to_base_units does even though it's fairly implicit
- Convert float model responses to system of units so that outputs can be returned as quantities
- Being explicit about what units were used for a given model
- Being able to write out all of the system quantities to a log for model documentation
- Explicitly defining in a function what type of quantity it is expecting/converting (acceleration, energy,...)
- Easily finding inconsistencies between the defined units in a system, which I talked about in the last comment
I think the main difference is that I needed it to be a bit more explicit than to_base_units or find_compatible_units. An example of how it works might help:
units = UnitSystem.mks()
units.check_consistency()
inp = Q_(1.0, 'psi')
input_value = inp.to(units.pressure).m # float with consistent unit
output_value = Q_(out, units.pressure) # out is a float post-processed from an external model
A derived unit could be defaulted (in which case the class property would find the derived unit), or explicitly defined, which is why checking consistency is important.
Hopefully that clears things up. Like I said, I hope I haven't duplicated functionality, and I'm not entirely sure how to integrate it since it currently bypasses the System definition in pint, but let me know what you think.
I want to use pint for unit conversions between different systems, and have not found a way to get base units by dimensionality for specified system. Something like ureg.sys.SI.get_base_units('[mass]'), that should return ureg.kilogram. Could you add something like this?