Address a bunch of review comments

This commit is contained in:
Bryan Weber 2023-04-23 10:47:34 -06:00 committed by Ray Speth
parent 1672898d73
commit ed4b3a910d
5 changed files with 43 additions and 92 deletions

View File

@ -84,48 +84,18 @@ def setup(app):
lines[i] = l.replace("*", r"\*") lines[i] = l.replace("*", r"\*")
app.connect('autodoc-process-docstring', escape_splats) app.connect('autodoc-process-docstring', escape_splats)
# NAMES = []
# DIRECTIVES = {}
# def get_rst(app, what, name, obj, options, signature,
# return_annotation):
# if "with_units" not in name:
# return
# doc_indent = ' '
# directive_indent = ''
# if what in ['method', 'attribute']:
# doc_indent += ' '
# directive_indent += ' '
# directive = '%s.. py:%s:: %s' % (directive_indent, what, name)
# if signature: # modules, attributes, ... don't have a signature
# directive += signature
# NAMES.append(name)
# rst = directive + '\n\n' + doc_indent + obj.__doc__ + '\n'
# DIRECTIVES[name] = rst
# def write_new_docs(app, exception):
# txt = ['My module documentation']
# txt.append('-----------------------\n')
# for name in NAMES:
# txt.append(DIRECTIVES[name])
# # print('\n'.join(txt))
# with open('../doc_new/generated.rst', 'w') as outfile:
# outfile.write('\n'.join(txt))
# app.connect('autodoc-process-signature', get_rst)
# app.connect('build-finished', write_new_docs)
autoclass_content = 'both' autoclass_content = 'both'
doxylink = { doxylink = {
'ct': (os.path.abspath('../../build/docs/Cantera.tag'), 'ct': (os.path.abspath('../../build/docs/Cantera.tag'),
'../../doxygen/html/') '../../doxygen/html/')
} }
intersphinx_mapping = { intersphinx_mapping = {
'python': ('https://docs.python.org/3', None), 'python': ('https://docs.python.org/3', None),
'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None),
'numpy': ('https://numpy.org/doc/stable/', None), 'numpy': ('https://numpy.org/doc/stable/', None),
'pint': ('https://pint.readthedocs.io/en/stable/', None),
} }
# Ensure that the primary domain is the Python domain, since we've added the # Ensure that the primary domain is the Python domain, since we've added the

View File

@ -3,10 +3,17 @@
Python Interface With Units Python Interface With Units
=========================== ===========================
This interface allows users to specify physical units associated with quantities.
To do so, this interface leverages the `pint <https://pint.readthedocs.io/en/stable/>`__
library to provide consistent unit conversion.
Solution with Units
-------------------
.. autoclass:: Solution .. autoclass:: Solution
PureFluid Phases With Units PureFluid Phases With Units
=========================== ---------------------------
The following convenience classes are available to create `PureFluid <PureFluid>` The following convenience classes are available to create `PureFluid <PureFluid>`
objects with the indicated equation of state: objects with the indicated equation of state:

View File

@ -17,6 +17,12 @@ Q_ = units.Quantity
def copy_doc(method): def copy_doc(method):
"""Decorator to copy docstrings from related methods in upstream classes.
This decorator will copy the docstring from the same named method in the upstream
class, either `Solution` or `PureFluid`. The docstring in the method being
decorated is appended to the upstream documentation.
"""
doc = getattr(method, "__doc__", None) or "" doc = getattr(method, "__doc__", None) or ""
if isinstance(method, property): if isinstance(method, property):
method = method.fget method = method.fget
@ -32,10 +38,6 @@ def copy_doc(method):
class Solution: class Solution:
""" """
A class for chemically-reacting solutions. Instances can be created to
represent any type of solution -- a mixture of gases, a liquid solution, or
a solid solution, for example.
This implementation of `Solution <cantera.with_units.Solution>` operates with This implementation of `Solution <cantera.with_units.Solution>` operates with
units by using the `pint` library to convert between unit systems. All properties units by using the `pint` library to convert between unit systems. All properties
are assigned units in the standard MKS system that Cantera uses, substituting kmol are assigned units in the standard MKS system that Cantera uses, substituting kmol
@ -50,51 +52,19 @@ class Solution:
See the `pint documentation <https://pint.readthedocs.io>`__ for more information See the `pint documentation <https://pint.readthedocs.io>`__ for more information
about using pint's ``Quantity`` classes. about using pint's ``Quantity`` classes.
The most common way to instantiate `Solution` objects is by using a phase **Note:** This class is experimental. It only implements methods from `ThermoPhase`.
definition, species and reactions defined in an input file:: Methods from other classes are not yet supported. If you are interested in contributing
to this feature, please chime in on our enhancements issue:
gas = ct.Solution('gri30.yaml') `<https://github.com/Cantera/enhancements/issues/174>`__.
If an input file defines multiple phases, the corresponding key in the
``phases`` map can be used to specify the desired phase via the ``name`` keyword
argument of the constructor::
gas = ct.Solution('diamond.yaml', name='gas')
diamond = ct.Solution('diamond.yaml', name='diamond')
The name of the `Solution` object defaults to the *phase* identifier
specified in the input file. Upon initialization of a `Solution` object,
a custom name can assigned via::
gas.name = 'my_custom_name'
In addition, `Solution` objects can be constructed by passing the text of
the YAML phase definition in directly, using the ``yaml`` keyword
argument::
yaml_def = '''
phases:
- name: gas
thermo: ideal-gas
kinetics: gas
elements: [O, H, Ar]
species:
- gri30.yaml/species: all
reactions:
- gri30.yaml/reactions: declared-species
skip-undeclared-elements: true
skip-undeclared-third-bodies: true
state: {T: 300, P: 1 atm}
'''
gas = ct.Solution(yaml=yaml_def)
""" """
def __init__(self, infile="", name="", *, yaml=None): def __init__(self, infile="", name="", *, yaml=None):
self.__dict__["_phase"] = _Solution(infile, name, yaml=yaml) self.__dict__["_phase"] = _Solution(infile, name, yaml=yaml)
@common_properties@ @common_properties@
@thermophase_properties@ @thermophase_properties@
Solution.__doc__ = f"{Solution.__doc__}\n{_Solution.__doc__}"
class PureFluid: class PureFluid:
""" """
@ -113,9 +83,9 @@ class PureFluid:
about using pint's ``Quantity`` classes. about using pint's ``Quantity`` classes.
""" """
def __init__(self, infile, name="", *, yaml=None, **kwargs): def __init__(self, infile, name="", *, yaml=None, **kwargs):
self.__dict__["_phase"] = _PureFluid(infile, name, **kwargs) self.__dict__["_phase"] = _PureFluid(infile, name, yaml=yaml, **kwargs)
@common_properties@ @common_properties@
@property @property
@copy_doc @copy_doc
@ -135,12 +105,12 @@ class PureFluid:
f"Value {value!r} must be an instance of a pint.Quantity class" f"Value {value!r} must be an instance of a pint.Quantity class"
) from None ) from None
else: else:
raise # pragma: no cover raise
else: else:
Q = self.Q.magnitude Q = self.Q.magnitude
self._phase.Q = Q self._phase.Q = Q
@thermophase_properties@ @thermophase_properties@
@property @property
@copy_doc @copy_doc
@ -150,7 +120,6 @@ class PureFluid:
@TPQ.setter @TPQ.setter
def TPQ(self, value): def TPQ(self, value):
msg = "Value {value!r} must be an instance of a pint.Quantity class"
T = value[0] if value[0] is not None else self.T T = value[0] if value[0] is not None else self.T
P = value[1] if value[1] is not None else self.P P = value[1] if value[1] is not None else self.P
Q = value[2] if value[2] is not None else self.Q Q = value[2] if value[2] is not None else self.Q
@ -159,12 +128,14 @@ class PureFluid:
val.ito(unit) val.ito(unit)
except AttributeError as e: except AttributeError as e:
if "'ito'" in str(e): if "'ito'" in str(e):
raise CanteraError(msg.format(value=val)) from None raise CanteraError(
f"Value {val!r} must be an instance of a pint.Quantity class"
) from None
else: else:
raise # pragma: no cover raise
self._phase.TPQ = T.magnitude, P.magnitude, Q.magnitude self._phase.TPQ = T.magnitude, P.magnitude, Q.magnitude
@purefluid_properties@ @purefluid_properties@
PureFluid.__doc__ = f"{PureFluid.__doc__}\n{_PureFluid.__doc__}" PureFluid.__doc__ = f"{PureFluid.__doc__}\n{_PureFluid.__doc__}"

View File

@ -1,5 +1,7 @@
""" """
Isentropic, adiabatic flow example - calculate area ratio vs. Mach number curve Isentropic, adiabatic flow example - calculate area ratio vs. Mach number curve.
Uses the pint library to include customized units in the calculation.
Requires: Cantera >= 3.0.0, pint Requires: Cantera >= 3.0.0, pint
Keywords: thermodynamics, compressible flow, units Keywords: thermodynamics, compressible flow, units
@ -10,7 +12,7 @@ import numpy as np
# This sets the default output format of the units to have 2 significant digits # This sets the default output format of the units to have 2 significant digits
# and the units are printed with a Unicode font. See: # and the units are printed with a Unicode font. See:
# https://pint.readthedocs.io/en/stable/formatting.html#unit-format-types # https://pint.readthedocs.io/en/stable/user/formatting.html
ctu.units.default_format = ".2F~P" ctu.units.default_format = ".2F~P"
@ -18,8 +20,8 @@ def soundspeed(gas):
"""The speed of sound. Assumes an ideal gas.""" """The speed of sound. Assumes an ideal gas."""
gamma = gas.cp / gas.cv gamma = gas.cp / gas.cv
return np.sqrt(gamma * ctu.units.molar_gas_constant specific_gas_constant = ctu.units.molar_gas_constant / gas.mean_molecular_weight
* gas.T / gas.mean_molecular_weight).to("m/s") return np.sqrt(gamma * specific_gas_constant * gas.T).to("m/s")
def isentropic(gas=None): def isentropic(gas=None):
@ -45,7 +47,7 @@ def isentropic(gas=None):
data = [] data = []
# compute values for a range of pressure ratios # compute values for a range of pressure ratios
p_range = np.logspace(-3, 0, 200) * p0 p_range = np.logspace(-3, 0, 10) * p0
for p in p_range: for p in p_range:
# set the state using (p,s0) # set the state using (p,s0)

View File

@ -1,5 +1,6 @@
""" """
Compute the "equilibrium" and "frozen" sound speeds for a gas Compute the "equilibrium" and "frozen" sound speeds for a gas. Uses the pint library to
include customized units in the calculation.
Requires: Cantera >= 3.0.0, pint Requires: Cantera >= 3.0.0, pint
Keywords: thermodynamics, equilibrium, units Keywords: thermodynamics, equilibrium, units
@ -57,7 +58,7 @@ def equilibrium_sound_speeds(gas, rtol=1.0e-6, max_iter=5000):
if __name__ == "__main__": if __name__ == "__main__":
gas = ctu.Solution('gri30.yaml') gas = ctu.Solution('gri30.yaml')
gas.X = 'CH4:1.00, O2:2.0, N2:7.52' gas.X = 'CH4:1.00, O2:2.0, N2:7.52'
T_range = np.linspace(80.33, 4760.33, 50) * ctu.units.degF T_range = np.linspace(80, 4880, 25) * ctu.units.degF
print("Temperature Equilibrium Sound Speed Frozen Sound Speed Frozen Sound Speed Check") print("Temperature Equilibrium Sound Speed Frozen Sound Speed Frozen Sound Speed Check")
for T in T_range: for T in T_range:
gas.TP = T, 1.0 * ctu.units.atm gas.TP = T, 1.0 * ctu.units.atm