-
Notifications
You must be signed in to change notification settings - Fork 478
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
Support quantities whose units are ratios of the same unit (m^3/m^3
), without reducing
#551
Comments
In a different thread I have suggested to label units not only by their dimensions but also their role. (I do not have a better name). It's purpose is to provide a way to distinguish between quantities with the same same units but different "extended dimensionality". (See #505) I think something like this could help also in your case if we allow roles to be provided on the fly
Nobody has opened yet a discussion about the API and how it should be implemented, but I think it is something worth pursuing. |
I am also interested in this. I like the If I can find the time learn the As for the name, "role" seems ok,.. some other options might be: guise, mien, or mode... I like mode, but for abbreviated attribute access the letter "m" conflicts with the m in "magnitude". I do think I like role better than guise. |
A commit for this will be most welcomed. I can guide you through. |
Is the early API above, eg. # building up to ratio g/m^3 K+ to g/m^3 Na+ eg. relative concetrations in seawater
>>> q1 = ureg.Quantity(1.0, "role.K : g / role.water : m^3") # in-str annotation to be escaped
>>> q2 = ureg.Quantity(10.0, "g{} / m^3{}").role("Na", "water") # method, curly brace escape
>>> q_return = q1 / q2
# parse role directly into unit string for __repr__
<Quantity(0.1, "g K / m^3 water / g Na / m^3 water")>
>>> q3 = ureg.Quantity(1.0, "role:K: g / role:water: m^3") # different escape
>>> q4 = ureg.Quantity(10.0, "g / m^3").role("Na", "water") # method, inferred escape/insertion
>>> q_return = q3 / q4
# separate role into an extra return string in Quantity
<Quantity(0.1, "g/m^3 / g/m^3", "K/water, Na/water")> I am agnostic on the return types shown - whatever makes more sense with pint's internals q2 I think is most intuitive - in essence, all we are doing is annotating our units
>>> q5 = ureg.Quantity(1.0, "inch")
>>> q6 = ureg.Quantity(1.0, "inch")
>>> q7 = q5.role() / q6.role()
<Quantity(1.0, "inch / inch")> There is an important point to consider, however - a lot of people will only want to give a role to one of their units, eg. (again, @Ricyteach 's SO question) >>> q8 = ureg.Quantity(1.0, "kip * ft / ft{}").role("member length")
<Quantity(1.0, "kip * ft / ft member length")> Should This has a lot of usefulness, especially for plotting, reporting, and once the pandas integration is stable. I would be willing to contribute as well - I have only a little time looking at the codebase, but I believe a |
Thanks for the insight and the great ideas. The API is totally open for discussions, and we do not have PR yet. The aspect that worries about your proposal is the ordering need. When Pint parses and operates on units, they are reordered. So |
OH is that so? Is there a defined pattern to it, eg. precedence of certain dimensions over others? Where should I look for this reordering - registry.py? util.py? |
Multiplication of units are stored in a dict and therefore the order is nos guaranteed in all supported Python versions. |
FWIW: I have made no progress on this idea, and would be very happy to see someone else take a crack at it. I'd be willing to try it out when finished though. |
Ok, but where exactly is this done? One place, many places? It seems that most of the parsing occurs in util.py, but I am unsure.
|
Ok, as an initial point, replacing At this point, my crude understanding of pint's internals is that the markers for a I'm in the process of writing a test for this at the |
The topic of corporate sustainability is heating up (due to Global Climate Change). Many companies report production intensity in terms of tonnes of CO2 emitted per unit of production. When the unit of production is tonnes of Steel, they expect an intensity metric of |
What's the status of this? |
Have another great example also from civil engineering world - Air Infiltration parameter for windows, measured in I am new to pint (and it looks amazing!) so I don't know if I could contribute to this topic yet, but would appreciate the option for that for sure! |
I'm interested in this feature as well. It's useful in chemical engineering, where mass and molar yields are used but are not equal (mole of product / mole of input != mass of product / mass of input), so preserving the starting units is important. While there's been some discussion around the api of assigning and displaying "roles", I'm curious about some of the expected behavior when there are roles within units (I'm going to stick with the name "roles" since I don't have a better one). Q: are units that have a role "isolated" from all other units? i.e. they can only be simplified with units of the same role? And only added with quantities that match all units and roles? Basic usage, where same units with different roles do not simplify: >>> u1 = ureg.Unit("g Na")
<Unit("gram Na")>
>>> u2 = ureg.Unit("g water")
<Unit("gram water")>
>>> u1 / u2
<Unit('gram Na / gram water')> With that framework, it seem like units without roles should get treated as if they have their own role (a >>> u1 = ureg.Unit("g Na")
<Unit("gram Na")>
>>> u2 = ureg.Unit("g")
<Unit('gram')>
>>> u3 = u1 / u2
<Unit('gram Na/ gram')>
>>> u3 / ureg.Unit("g")
<Unit('gram Na / gram ** 2')> If I add a quantity with a role to one without a role, should it take the role of the first quantity or throw an error? >>> q1 = ureg.Quantity("3.0 gram Na")
3.0 <Unit("gram Na")>
>>> u2 = ureg.Quantity("5.0 gram")
5.0 <Unit('gram')>
>>> u1 + u2
DimensionalityError: Cannot convert role "None" (gram) to role "Na" (gram) Q: How are conversions handled when roles are present? Should it be required to specify the role for any conversion, so it will only look at that subset of the units? >>> q1 = ureg.Quantity("1.0 g Na / g water")
1.0 <Unit("gram Na / gram water")>
# gram Na -> ounce Na, water units ignored because it's a different role
>>> q1.to("ounce", role="Na")
0.035274 <Unit("ounce Na / gram water")> Could it be possible to not specify a role? In that case, does it try to convert any roles it can? (couldn't think of a realistic situation, so using contrived units) >>> q1 = ureg.Quantity("1.0 g Na * g water")
1.0 <Unit("gram Na * gram water")>
# Both Na and water roles have a dimensionality match to "ounce", so both are converted
>>> q1.to("ounce")
0.0012 <Unit("ounce Na * ounce water")>
>>> q2 = ureg.Quantity("1.0 g Na * m**3 water")
1.0 <Unit("gram Na * meter ** 3 water")>
# Na role has dimensionality match so converted - water role doesn't, so ignored
>>> q2.to("ounce")
0.035274 <Unit("ounce Na * meter ** 3 water")> Or does not passing a role mean it defaults to >>> q1 = ureg.Quantity("1.0 g Na * g water")
1.0 <Unit("gram Na * gram water")>
>>> q1.to("ounce")
DimensionalityError: No units with role of "None" (ounce) found
>>> q2 = ureg.Quantity("1.0 g Na * g")
1.0 <Unit("gram Na * gram")>
# Only "gram" has role of None
>>> q2.to("ounce")
0.035274 <Unit("gram Na * ounce")> This will result in behavior that some might find unexpected (again, there's probably a more realistic example...) >>> car_weight = ureg.Quantity("3000 lb car")
3000 <Unit("pound car")>
>>> acceleration = ureg.Quantity("15.0 miles per hour per sec")
15.0 <Unit('mile / hour / second')>
>>> force = car_weight * acceleration
45000.0 <Unit('pound car * mile / hour / second')>
# role of None has units of mile / hour/ second
>>> force.to("newton")
DimensionalityError: Cannot convert from 'mile / hour / second' ([length] / [time] ** 2) to 'newton' ([length] * [mass] / [time] ** 2) Q: Can unit definitions have roles? How would that work with conversions? The conversion examples above take units within a role, covert them, and gives the resulting units the same role. The conversion itself does not consider roles, since it needs to account for any incoming role (e.g. water, salt, etc.) and none of the definitions currently have roles. Issue #505 mentions adding roles to definitions themselves, to distinguish dimensionless quantities that should be treated differently. I think this could work if roles in definitions were only allowed on base units because, from what I can tell, you can't create a ureg.Unit with base units. Thus, during a conversion the "outer" roles are removed, conversion happens that can take into account the base-unit roles, and the resulting units (without a role) get the "outer" role re-applied. With @hgrecco's example:
>>> q1 = ureg.Quantity("1.0 bit / second")
1.0 <Unit('bit / second')>
>>> q1.to('count / second')
DimensionalityError: Cannot convert role "information" (bit) to role None (count) Hopefully my explanations are reasonably clear. Looking forward to some feedback on these ideas. Thanks! |
One issue I see here is >>> q= ureg.Quantity(10.0, 'm^3/m^3')
>>> q will reduce to so For the same reason this example won't work We'd need a non-reducting UnitsContainer for this to work. ie the role discussion in this issue is indedepent of the original issue. q= ureg.Quantity(10.0, 'm^3/m^3')
q.units
<NonReducingUnitsContainer [{'meter' : 3}, {'meter'}: -3] > I think the |
This use case is an example for why "quantity kinds" are considered in unit-of-measurement models. Work has been started in #1967 to add quantity kinds to pint. It seems that the concept of "quantity kind" is called "role" here? |
Yes, this issue is several years old. Newer issues have use kind as the term instead. |
First of all thanks for the excellent library!
In engineering it is common to have measurement units like
m^3/m^3
which represents a volume fraction or volume ratio, such as "water volume/total volume". Although it is the same as dimensionless, it is important to keep this representation as to distinguish it from other dimensionless ratios such askg/kg
or(mg/l)/(mg/l)
.What happens today is:
What is the desired behaviour:
In other words, we don't want to "reduce" the unit automatically.
Note that this already works for units such as
cm^3/m^3
. In this case, the unit is preserved even though it could be reduced to only an1e-6
factor:The text was updated successfully, but these errors were encountered: