Generic classes

A thermodynamic model in SigmaMu is composed of ThermoContribution objects stacked on top of a StateDefinition instance. The latter one interprets the numerical state vector as physical quantities, such as temperature, pressure and mole flows. Each contribution then builds on the already defined quantities to add new ones, until the model is complete.

The ThermoFactory administers the construction of this structure by allowing to first register the contribution classes, and then construct models based on a given structure. The model is then encapsulated into a ThermoFrame object.

ThermoContributionDict

simu.core.thermo.frame.ThermoContributionDict

A dictionary whose keys are the names of the contributions, and the values are tuples of the belonging classes and the options belonging to the definition. An example could be:

contribs = {
    "LinearHeatCapacity": (LinearHeatCapacity, {}),
    ...
    "MixingRule_A": (NonSymmetricMixingRule, {"target": "_ceos_a"}
} 

This kind of structure is used to define the sequence of contributions in a ThermoFrame object. Here, the class LinearHeatCapacity is defined straight forward with no options. For the mixing rule of the A contribution, called MixingRule_A, it uses the NonSymmetricMixingRule class, configured with ceos_a being the target.

It is up to each individual ThermoContribution implementation to support and document their set of options.

alias of Mapping[str, tuple[Type[ThermoContribution], Mapping[str, VT]]]

ThermoFactory

class simu.ThermoFactory

The ThermoFactory class hosts the definitions for the model contributions, enabling it to create instances of thermodynamic models of class ThermoFrame.

The class is largely meant to be a singleton, but to keep doors open, static attributes are avoided.

__init__()

Parameter-less constructor, initialising the data structure to host contribution definitions

register_state_definition(
definition_cls: Type[StateDefinition],
)

Register a new state definition with the name of its class.

Parameters:

definition_cls – The state definition to register

register(
*contributions: Type[ThermoContribution],
)

Registers contributions under the names of their classes.

The contributions must be concrete subclasses of ThermoContribution, and their names must be unique.

Parameters:

contributions – The contributions to register, being classes (not instances)

property contribution_names: Collection[str]

This property contains the full names of all registered contributions

create_frame(
species: Mapping[str, SpeciesDefinition],
configuration: Mapping,
) ThermoFrame

This factory method creates a ThermoFrame object from the given configuration, and is the recommended way to create ThermoFrame objects.

Parameters:
  • species – A dictionary mapping names to species definitions

  • configuration

    A nested dictionary with the following root entries:

    • state: A string identifier that represents the type of state as defined by register_state_definition().

    • contributions: A list of strings, representing the names of the contributions to stack. These identifiers must have been defined upfront by calls to register(). A contribution entry can also be a dictionary with the following keys:

      • cls: The contribution class (required). If this is the only key defined, its effect is as if the sole string of the class name was provided.

      • name: The name of the contribution in the created ThermoFrame object. This name must be unique within the frame definition. It will also be used to define the contribution parameter structure. If skipped, the name will be the same as cls. To be unique, this doesn’t work if one contribution class is used multiple times.

      • options: Any data structure that is accepted (and hopefully documented) for the particular contribution. If skipped, an empty dictionary is used.

Returns:

The thermodynamic model (ThermoFrame) object

ThermoFrame

class simu.ThermoFrame

This class represents the thermodynamic model, which defines a Function object QFunction of the state vector and the parameter vector, and calculates a set of thermodynamic properties.

The object should not be constructed via the class constructor by client code, but created by the create_frame() method that handles the house-keeping of contribution classes.

__init__(
species: Mapping[str, SpeciesDefinition],
state_definition: StateDefinition,
contributions: Mapping[str, tuple[Type[ThermoContribution], Mapping[str, VT]]],
)

This constructor establishes a thermo frame function object with given species and contributions.

__call__(
state: SX | Sequence[float],
parameters: NestedMap[Quantity],
squeeze_results: bool = True,
flow: bool = False,
)

Shortcut operator to call to the underlying function object.

The function call can be a stand-alone evaluation of the thermodynamic model, given floating point quantities for the state and the parameters. Alternatively, called with CasADi SX based quantities to become part of a larger functional.

Parameters:
  • state – A CasADi SX object or a sequence of floats, representing the thermodynamic state of the model. This is to be seen as a purely numerical object, as the physical interpretation, for instance as temperature, volume and mole flows, is first happening within the model.

  • parameters – A nested dictionary with string keys and Quantity leaves. Depending on the application, these quantities hold float or SX type magnitudes.

Returns:

A list of property collections, representing the thermodynamic properties, in the structure as defined by property_structure.

Depending on the flow parameter, extensive properties will be returned as flow quantities, such as W or mol/s or as stagnant state properties, such as J or mol.

property species: Sequence[str]

Returns a list of species names

property species_definitions: Mapping[str, SpeciesDefinition]

Return the map of species definitions

property vector_keys: Sequence[[<class 'str'>]]]

Return the index keys for the registered vector properties. For a standard model, there should at least be entries for n and mu.

property property_structure: NestedMap[str]

Returns a recursive structure properties, defining the calculated properties, bounds, and residuals as follows:

   {"props": ...,
    "bounds": ...,
    "residuals": ...,
    "normed_residuals": ...
    }

The ``props`` sub-structure represents all calculated properties.
The ``bounds`` sub-structure contains all bounds, represented as
  variables that need to be truly positive.
The ``residuals`` structure contains the residuals in their given
  unit, while the ``normed_residuals`` structure contains the
  dimensionless ratios of residual values and tolerances.
property parameter_structure: NestedMap[str]

This property is to aid the process of parametrizing a model. It returns the structure of all required model parameters. Initially, the returned object contains units of measurements that must be replaced with actual quantities (symbolic or not) before the function (__call__()) or initial_state() can be called . For the latter, float quantities have to be provided to the parameter object.

create_symbol_state() SX

Create a symbol state that can be used to call the object functional symbolically.

initial_state(
state: InitialState,
parameters: NestedMap[Quantity],
) Sequence[float]

Return the state for given temperature, pressure and molar quantities - at given parameter set.

This method queries all contributions top-down for an implementation of the initialise method. If not overwritten by any (and thus returning a state), the model is expected to be in Gibbs coordinates already, and (T, p, n) is the initial state.

For non-Gibbs coordinates, the method refines the initial state estimate by the contribution by iteration. The given initial state is supposed to be sufficiently accurate to allow finding the solution.

ThermoContribution

class simu.ThermoContribution

This abstract class defines the interface of a contribution to a thermodynamic state function, as collected in ThermoFrame objects.

A contribution is a reusable part of a thermodynamic model that can be recombined meaningfully with other contributions. Examples are standard state, ideal mix, ideal gas, Gibbs excess contributions, and Helmholtz residual functions (equations of state).

The definition is based on the casadi library, and its definition is required to build a casadi evaluation structure as the implementation of the belonging equations.

The usage of this class is mainly indirect by instantiation via the ThermoFactory objects and parametrisation via the provided parameter structures.

A contribution can overwrite the class attribute provides, helping the user to identify feasible contributions if a downstream contribution does not find a symbol.

__init__(
species: Mapping[str, SpeciesDefinition],
options=None,
)
species_definitions: Mapping[str, SpeciesDefinition]

The map of species definition objects

options: Mapping[str, Any]

The map of species definition objects

reset()

Reset the object’s state by clearing defined residuals.

property species: Sequence[str]

Returns a list of species names

abstractmethod define(
res: MutableMapping[str, Quantity],
)

Abstract method to implement the casadi expressions that make up this contribution.

See Standard property names of thermodynamic model results for a guideline on how to name standard properties generated in the contribution implementations.

Parameters:
  • res – A dictionary with already calculated properties that is to be supplemented by the properties calculated in this contribution. The values of the dictionaries are of type casadi.SX.

  • bounds – A dictionary including properties of which the base_unit magnitude must stay positive. Solvers can use this information to stay within the mathematical domain of the model. By convention, if the property is also a result, the same name shall be used, and it is not a problem if prior entries are over-written. For instance multiple contributions will not allow negative T, and all of them shall declare this, as they cannot rely on the others being used in the same model.

  • par – A dictionary with parameters for this contribution. This dictionary can be nested. All values are scalar symbols of type casadi.SX.

Todo

  • refer to dedicated section the standard property names

initial_state(
state: InitialState,
properties: Mapping[str, Quantity],
) MutableSequence[float] | None

When the ThermoFrame object is queried for an initial state representation and deviates from Gibbs coordinates, The uppermost contribution that implements this method and does not return None takes the responsibility of calculating that state.

Hence, normally only Helmholtz models need to implement this method. The true model coordinates can however be entirely unconventionally, such that it is solely up to the contributions, how to obtain the initial state.

Parameters:
  • state – The default state

  • properties – The property structure, mapping strings to floats or list of floats.

Returns:

The initial state or None

add_residual(
name: str,
residual: Quantity,
tol_unit: str,
tol: float = 1e-07,
)

Define a residual that represents an implicit constraint in the thermodynamic model itself. Typical examples are equilibrium constraints on apparent species systems and any implicit thermodynamic models.

add_bound(
name: str,
bound: Quantity,
keys: Sequence[str] = None,
)

Add a domain bound to the contribution. This is a property that is required to be truly positive.

If bound is a vectorial property, its keys must first be registered under given name via declare_vector_keys().

Parameters:
  • name – Name of the bounded variable

  • bound – A Scalar or vectorial quantity to remain truly positive

  • keys – If the quantity is vectorial or keys are given, they will be used to identify the individual element(s). If keys are not given, the vector quantity will use any previously defined declare_vector_keys() definition instead. If that entry also does not exist, species names are assumed to be keys.

property par_scalar

Shortcut method for self.parameters.register_scalar

property par_vector

Shortcut method for self.parameters.register_vector

property par_sparse_matrix

Shortcut method for self.parameters.register_sparse_matrix

property par_sparse_3d

Shortcut method for self.parameters.register_sparse_3d

property residuals: Mapping[str, Residual]

Return the defined residuals of this contribution

declare_vector_keys(
name: str,
keys: Sequence[str] = None,
)

Register a property as a vector property with given keys. This way it can be handled correctly by the numeric handler.

Parameters:
  • name – The name of the property

  • keys – The names of the keys. If None (default), the species names are used.

StateDefinition

class simu.StateDefinition

This class defines the interpretation of the state vector in terms of physical properties. This interpretation is then consumed by the contributions as input for their calculations towards the complete thermodynamic model.

abstractmethod prepare(result: dict, flow: bool = False)

This method can assume to find the state vector x in the result dictionary, and is expected to add the physical interpretation of its elements to the same dictionary. It is entirely up to the contributions that rely on this state.

For the Gibbs state, the new elements would be T, p, and n, denoting temperature, pressure and quantities respectively.

The parameter flow impacts the definition of units of measurement. When True, all extensive variables are divided by time.

declare_vector_keys(
species: Mapping[str, SpeciesDefinition],
) Mapping[str, Sequence[str]]

Declare the keys of vectorial state properties. In most cases, this will be the species names for the mole vector.

abstractmethod reverse(
state: InitialState,
) Sequence[float]

Return the state vector as complete as possible with given temperature, pressure and quantities. The task of the contributions’ ThermoContribution.initial_state() method is it then to complete it. Missing elements shall be filled with None.

SpeciesDefinition

class simu.SpeciesDefinition

This class holds a definition of a species and provides, based on the formula, the molecular weight, charge, and a dictionary of element composition.

>>> a = SpeciesDefinition("H3PO4")
>>> print(f"{a.molecular_weight:~.3f}")
97.993 g / mol
>>> a.elements
{'H': 3, 'P': 1, 'O': 4}
>>> a = SpeciesDefinition("PO4:3-")
>>> print(f"{a.charge:~}")
-3 e / mol
formula: str

The formula as it was given in the constructor. The admitted formula syntax is described with examples for the FormulaParser class.

molecular_weight: Quantity

The molecular weight determined by summing up the atomic weights of the contained atoms; quantum effects and electron masses are neglected.

charge: Quantity

The electronic charge of the species’ molecule

elements: Mapping[str, int]

A dictionary, mapping the occurring atoms to their amount in the species’ molecule

__init__(formula: str) None

SpeciesDB

class simu.SpeciesDB

Based on a dictionary of species names to formulae, this class represents a dictionary of the species names to species definitions.

Note

For now, this class is quite primitive, but might be extended to handle more meta-data, such as CAS registry numbers and species aliases.

__init__(formulae: Mapping[str, str])

Create a species collection based on a mapping of species names to their formulae.

get_sub_db(keys: Sequence[str]) Self

Get a subset of species definitions as a new SpeciesDB object.

InitialState

class simu.InitialState

Dataclass describing an initial state, which is always defined in terms of temperature, pressure, and molar quantities.

Temperature and pressure are scalar quantities of respective physical dimensions. the mol_vector quantity is vectorial and can arbitrarily be defined in units compatible with mol or mol/s. The stored value will however be as a state, not a flow, i.e. compatible to mol.

classmethod from_si(
temperature: float,
pressure: float,
mol_vector: Sequence[float],
) Self

Construct an initial state based on SI units, i.e. K, Pa and mol.

classmethod from_cbar(
temperature: float,
pressure: float,
mol_vector: Sequence[float],
) Self

Construct an initial state based on degC, bar and mol as units.

classmethod from_std(num_species: int)

Construct an initial state at 25 degC, 1 bar and one mol for each species.

classmethod from_dict(
struct: NestedMap[Quantity],
species: Sequence[str],
)

Convert a nested Quantity map as obtained by to_dict() to an initial state. The map must contain the top-level keys T, p and n, and a mapping from species to molar quantities as value of the n key. The correct sequence of species is provided separately, to be consistent with the targeted thermodynamic model.

The method raises a KeyError, if the elements of struct are not as expected, in particular considering the given species argument.

to_dict(
species: Sequence[str],
) NestedMap[Quantity]

Convert initial state into dictionary of quantities. The mole vector is described as a sub-dictionary, mapping species to partial molar quantities

__init__(
temperature: Quantity,
pressure: Quantity,
mol_vector: Quantity,
) None