Skip to content

Expose C++ Units to Python#1076

Merged
speth merged 22 commits intoCantera:mainfrom
ischoegl:expose-units-to-python
Sep 16, 2021
Merged

Expose C++ Units to Python#1076
speth merged 22 commits intoCantera:mainfrom
ischoegl:expose-units-to-python

Conversation

@ischoegl
Copy link
Copy Markdown
Member

@ischoegl ischoegl commented Jul 17, 2021

Changes proposed in this pull request

  • Shorten Units::str() output
  • Expose CxxUnits to Cython interface
  • Implement Python API for Units
  • Expose Reaction.rate_coeff_units and ThermoPhase.standard_concentration_units
  • Expose UnitSystem to Python

If applicable, provide an example illustrating new features this pull request is introducing

The new feature can be used as follows:

In [1]: import cantera as ct
   ...: gas = ct.Solution("gri30.yaml")
   ...: gas.reaction(0)
   ...:
Out[1]: <ThreeBodyReaction: 2 O + M <=> O2 + M>

In [2]: gas.reaction(0).rate_coeff_units
Out[2]: <Units(1.0 m^6 / kmol^2 / s) at 7f928bee69f0>

In [3]: gas.standard_concentration_units
Out[3]: <Units(1.0 kmol / m^3) at 7f928bee6ab0>

In [4]: ct.Units("Pa")
Out[4]: <Units(1.0 kg / m / s^2) at 7f928bee6cf0>

In [5]: ct.UnitSystem().defaults
Out[5]: 
{'activation-energy': 'J/kmol',
 'current': 'A',
 'energy': 'J',
 'length': 'm',
 'mass': 'kg',
 'pressure': 'Pa',
 'quantity': 'kmol',
 'temperature': 'K',
 'time': 's'}

Checklist

  • The pull request includes a clear description of this code change
  • Commit messages have short titles and reference relevant issues
  • Build passes (scons build & scons test) and unit tests address code coverage
  • Style & formatting of contributed code follows contributing guidelines
  • The pull request is ready for review

Other thoughts

This PR seeks to expose units used in the C++ layer, rather than facilitating conversion of alternative units as proposed in #991.

@codecov
Copy link
Copy Markdown

codecov bot commented Jul 17, 2021

Codecov Report

Merging #1076 (6f8aed6) into main (e77148b) will increase coverage by 0.03%.
The diff coverage is 88.65%.

❗ Current head 6f8aed6 differs from pull request most recent head 1844332. Consider uploading reports for the commit 1844332 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1076      +/-   ##
==========================================
+ Coverage   73.45%   73.49%   +0.03%     
==========================================
  Files         364      364              
  Lines       47611    47735     +124     
==========================================
+ Hits        34972    35081     +109     
- Misses      12639    12654      +15     
Impacted Files Coverage Δ
include/cantera/base/Units.h 100.00% <ø> (ø)
include/cantera/base/YamlWriter.h 100.00% <ø> (ø)
include/cantera/kinetics/Falloff.h 67.64% <55.55%> (-6.43%) ⬇️
src/kinetics/Reaction.cpp 88.90% <60.00%> (-0.31%) ⬇️
src/base/Units.cpp 94.21% <91.89%> (-0.59%) ⬇️
src/kinetics/Falloff.cpp 86.79% <92.00%> (+1.60%) ⬆️
src/base/YamlWriter.cpp 100.00% <100.00%> (ø)
src/kinetics/FalloffFactory.cpp 100.00% <100.00%> (ø)
test/general/test_serialization.cpp 100.00% <100.00%> (ø)
test/general/test_units.cpp 100.00% <100.00%> (ø)
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e77148b...1844332. Read the comment docs.

@ischoegl ischoegl force-pushed the expose-units-to-python branch 2 times, most recently from 08b8e16 to 735990b Compare July 17, 2021 22:27
@ischoegl ischoegl changed the title Expose reaction rate coefficient units to python Expose C++ Units to Python Jul 17, 2021
@ischoegl ischoegl force-pushed the expose-units-to-python branch from 735990b to 65f1e58 Compare July 18, 2021 03:19
@ischoegl ischoegl marked this pull request as ready for review July 18, 2021 03:36
@ischoegl ischoegl requested a review from a team July 18, 2021 03:41
@ischoegl ischoegl force-pushed the expose-units-to-python branch 6 times, most recently from f25bca8 to 5abd858 Compare July 19, 2021 02:23
@ischoegl ischoegl force-pushed the expose-units-to-python branch 3 times, most recently from f034688 to 01c2ba1 Compare July 27, 2021 11:59
@ischoegl ischoegl force-pushed the expose-units-to-python branch from 01c2ba1 to 6345889 Compare August 19, 2021 02:29
Copy link
Copy Markdown
Member

@speth speth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making information about the units of reaction rate coefficients and phase standard concentrations available is a valuable addition to the Python interface, and a light interface to the Units class seems to serve this purpose, though I think there are currently a few quirks.

While I understand that the string representation of Units objects draws from the existing implementation (with some cleanup), I think that this output is kind of confusing when the units aren't in Cantera's base system, e.g. in the case of

>>> ct.Units("mol/cm^3")
<Units(999.9999999999999 kmol / m^3) at 7f6ee85c93f0>

it's not really clear anymore that what this is intended to mean is that 1000 is the conversion factor between mol/cm^3 and kmol/m^3, rather than representing a particular quantity of 1000 kmol/m^3. This distinction gets even muddier with newly-allowed expressions like Units("4 atm").

Assuming the goal here is just to provide unit information for these special cases where the dimensions vary in different instances, rather than presenting a full unit conversion capability (which I don't think we really want or need) I'd suggest that maybe Python Units objects should only be constructible in the default unit system, and that they could be represented without the leading 1.0 factor, e.g.

>>> ct.Units("J/kmol")
<Units(kg * m^2 / kmol / s^2) at 7f6ef085c030>

and noting that practically, there's really no purpose for a user-constructed Units object.

I'm not clear on what the benefit of exposing the UnitSystem class here is. Is the purpose just to make information about the (completely fixed) default unit system available?

@ischoegl
Copy link
Copy Markdown
Member Author

ischoegl commented Sep 2, 2021

@speth ... thanks for your comments!

I think making information about the units of reaction rate coefficients and phase standard concentrations available is a valuable addition to the Python interface, and a light interface to the Units class seems to serve this purpose, though I think there are currently a few quirks. [...]

This distinction gets even muddier with newly-allowed expressions like Units("4 atm").

Interesting observation. I definitely didn't have that in mind when I created the constructor! While I did code this up to finally wrap my head around the internal unit conversions (it did help), my main concern was to display what's done internally.

I'm not clear on what the benefit of exposing the UnitSystem class here is. Is the purpose just to make information about the (completely fixed) default unit system available?

Displaying the unit system was certainly one of my main concerns here (it would clear up things once and for all for any user). But at the same time, it would serve a purpose to have it as a stand-alone object that is passed to YamlWriter (see 5751633), where Unitswith conversion factors do make some sense.

I agree that making the Python Units constructor less 'useful' makes sense. This should be a relatively easy tweak to this PR.

@ischoegl ischoegl force-pushed the expose-units-to-python branch from 6345889 to 65dfd08 Compare September 3, 2021 16:24
@ischoegl
Copy link
Copy Markdown
Member Author

ischoegl commented Sep 3, 2021

@speth ... I reworked some of the interfaces to make things more intuitive. For unit output, the factor is suppressed:

In [1]: import cantera as ct
   ...:    ...: gas = ct.Solution("gri30.yaml")
   ...:    ...: gas.reaction(0)
   ...: 
Out[1]: <ThreeBodyReaction: 2 O + M <=> O2 + M>

In [2]: gas.reaction(0).rate_coeff_units
Out[2]: <Units(m^6 / kmol^2 / s) at 7fd73eb86f30>

In [3]: gas.standard_concentration_units
Out[3]: <Units(kmol / m^3) at 7fd73eb86c90>

The UnitSystem now displays units automatically, and has a more intuitive units property (instead of the internally used C++ defaults, which originates in the pre-existing Units::setDefaults):

In [4]: ct.UnitSystem().units # default unit system
Out[4]: 
{'activation-energy': 'J / kmol',
 'current': 'A',
 'energy': 'J',
 'length': 'm',
 'mass': 'kg',
 'pressure': 'Pa',
 'quantity': 'kmol',
 'temperature': 'K',
 'time': 's'}

In [5]: units = ct.UnitSystem({"quantity": "mol", "length": "cm"}) 

In [6]: units.units # alternate unit system (e.g. input to YamlWriter)
Out[6]:
{'activation-energy': 'J / mol',
 'current': 'A',
 'energy': 'J',
 'length': 'cm',
 'mass': 'kg',
 'pressure': 'Pa',
 'quantity': 'mol',
 'temperature': 'K',
 'time': 's'}

Finally, unit conversions are no longer enabled for unit strings with non-unity conversion factors:

In [6]: ct.Units('Pa')
Out[6]: <Units(kg / m / s^2) at 7f0275f42c90>

In [7]: ct.Units('3 Pa')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-f54d3ab3f8e5> in <module>
----> 1 ct.Units('3 Pa')

/src/interfaces/cython/cantera/units.pyx in cantera._cantera.Units.__cinit__()
     19             self.units = CxxUnits(stringify(name))
     20             if abs(self.units.factor() - 1.) > np.nextafter(0, 1):
---> 21                 raise ValueError(
     22                     "The creation of 'Units' objects that require a conversion "
     23                     "factor\nwith respect to Cantera's default unit system is not "

ValueError: The creation of 'Units' objects that require a conversion factor
with respect to Cantera's default unit system is not supported:
input '3 Pa' is equivalent to '3.0 kg / m / s^2' where the conversion factor is '3.0'

PS: I added a direct UnitSystem based setter YamlWriter::setUnitSystem to clarify my intent.

In [8]: writer = ct.YamlWriter()
   ...: writer.add_solution(ct.Solution("h2o2.yaml"))
   ...: writer.output_units = ct.UnitSystem({"quantity": "mol", "length": "cm"})

(this was possible even before the latest commits, but now there's a direct path for this)

@ischoegl ischoegl requested a review from speth September 3, 2021 16:40
@ischoegl ischoegl force-pushed the expose-units-to-python branch from 65dfd08 to 9950057 Compare September 3, 2021 18:10
@ischoegl ischoegl force-pushed the expose-units-to-python branch from 589fc5d to 7a703e7 Compare September 5, 2021 17:42
@ischoegl ischoegl force-pushed the expose-units-to-python branch from 7a703e7 to 4a448e8 Compare September 5, 2021 18:07
Copy link
Copy Markdown
Member

@speth speth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, @ischoegl, I think this is fairly close.

@ischoegl ischoegl requested a review from speth September 10, 2021 19:27
@ischoegl ischoegl force-pushed the expose-units-to-python branch 2 times, most recently from 23c7b25 to fd56254 Compare September 10, 2021 21:21
@ischoegl
Copy link
Copy Markdown
Member Author

@speth ... thank you for the additional comments, which should be taken care of. Per suggestion, all the formatting flags are now implemented in C++ rather than just for Python. The unit tests were easily taken care of and the documentation is updated. Regarding Unittest::default the direction I am taking requires the steps, as I am starting from the factors and work my way from there.

@ischoegl ischoegl force-pushed the expose-units-to-python branch from fd56254 to d45482b Compare September 10, 2021 21:49
@ischoegl
Copy link
Copy Markdown
Member Author

@speth ... let me know if there's anything else. From my side, I believe this is ready to be merged.

@ischoegl
Copy link
Copy Markdown
Member Author

@speth ... thanks for clarifying! I simply cherry-picked your suggestion as it's indeed simpler (I was apparently too focused on the numeric factors ...).

Copy link
Copy Markdown
Member

@speth speth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @ischoegl, I think that wraps this all up.

@speth speth merged commit 8266605 into Cantera:main Sep 16, 2021
@ischoegl ischoegl deleted the expose-units-to-python branch September 16, 2021 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants