Species

Species is a composite type (introduced by the keyword struct) and is defined by a human-readable name, a chemical symbol/notation, an underlying Formula object holding composition, charge, and string representations, a physical state aggregate_state, a species class, as well as an extensible map of custom properties (molar mass, thermodynamic data, etc.)

struct Species{T<:Number} <: AbstractSpecies
    name::String
    symbol::String
    formula::Formula{T}
    aggregate_state::AggregateState
    class::Class
    properties::OrderedDict{Symbol,PropertyType}
end
Advanced description
  • aggregate_state denotes the state of the species (solid, liquid, gas) for which the possible keywords are AS_AQUEOUS, AS_CRYSTAL, AS_GAS and AS_UNDEF
  • class defines the role played by the species in the solution. The possible keywords are SC_AQSOLVENT, SC_AQSOLUTE, SC_COMPONENT, SC_GASFLUID and SC_UNDEF
  • properties refers to the set of properties intrinsic to the species. These properties are detailed below.

Species construction

Species can be created from:

  • a Formula
fH2O = Formula("H2O")
H2O = Species(fH2O, aggregate_state=AS_AQUEOUS, class=SC_AQSOLVENT)
Species{Int64}
           name: H2O
         symbol: H2O
        formula: H2O ◆ H₂O
          atoms: H => 2, O => 1
         charge: 0
aggregate_state: AS_AQUEOUS
          class: SC_AQSOLVENT
     properties: M = 0.0180149999937744 kg mol⁻¹
  • a string
HSO4⁻ = Species("HSO₄⁻", aggregate_state=AS_AQUEOUS, class=SC_COMPONENT)
Species{Int64}
           name: HSO₄⁻
         symbol: HSO₄⁻
        formula: HSO₄⁻ ◆ HSO4-
          atoms: H => 1, S => 1, O => 4
         charge: -1
aggregate_state: AS_AQUEOUS
          class: SC_COMPONENT
     properties: M = 0.09706399996645676 kg mol⁻¹
  • a dictionary
CO2 = Species(Dict(:C => 1, :O => 2), aggregate_state=AS_GAS, class=SC_GASFLUID)
Species{Int64}
           name: CO₂
         symbol: CO₂
        formula: CO2 ◆ CO₂
          atoms: O => 2, C => 1
         charge: 0
aggregate_state: AS_GAS
          class: SC_GASFLUID
     properties: M = 0.04400899998479143 kg mol⁻¹
Adding charge

To add a charge when creating species with a dictionary, you must add, after the dictionary, the value of the charge (charge is considered an argument of the composite type).

SiO₃²⁻ = Species(Dict(:Si => 1, :O => 3), -2, aggregate_state=AS_AQUEOUS, class=SC_COMPONENT)
Species{Int64}
           name: SiO₃-2
         symbol: SiO₃-2
        formula: SiO3-2 ◆ SiO₃-2
          atoms: Si => 1, O => 3
         charge: -2
aggregate_state: AS_AQUEOUS
          class: SC_COMPONENT
     properties: M = 0.0760819999737077 kg mol⁻¹

Keyword arguments such as name, symbol, aggregate_state, class can be added during construction.

fH₂O = Formula("H2O")
H₂O = Species(fH₂O; name="Water", symbol="H₂O@", aggregate_state=AS_AQUEOUS, class=SC_AQSOLVENT)
Species{Int64}
           name: Water
         symbol: H₂O@
        formula: H2O ◆ H₂O
          atoms: H => 2, O => 1
         charge: 0
aggregate_state: AS_AQUEOUS
          class: SC_AQSOLVENT
     properties: M = 0.0180149999937744 kg mol⁻¹

And symbol accept unicode characters.

CO₂ = Species(Dict(:C=>1, :O=>2); name="Carbon dioxide", symbol="CO₂⤴", aggregate_state=AS_GAS, class=SC_GASFLUID)
Species{Int64}
           name: Carbon dioxide
         symbol: CO₂⤴
        formula: CO2 ◆ CO₂
          atoms: O => 2, C => 1
         charge: 0
aggregate_state: AS_GAS
          class: SC_GASFLUID
     properties: M = 0.04400899998479143 kg mol⁻¹
Comparison between species

Comparison between species (or cemspecies) are done by comparing atoms, aggregatestate and class. In the example below, vapour is not equal to H₂O since *aggregatestate* and class are different despite atoms are identical.

vapour = Species("H2O"; name="Vapour", symbol="H₂O⤴", aggregate_state=AS_GAS, class=SC_GASFLUID)
vapour == H₂O
Remark

You will also have noticed that a calculation of the molar mass of the species is systematically carried out.


Species properties

The molar mass is systematically calculated and integrated into the species properties. The heat capacity function is also integrated as a predefined function of the Species structure. This function is expressed as follows:

\[a_0 + a_1 * T + a_2 * T^{-2} + a_3 * T^{-0.5} + a_4 * T^2 + a_5 * T^3 + a_6 * T^4 + a_7 * T^{-3} + a_8 * T^{-1} + a_9 * T^{0.5} + a_{10} * log(T)\]

Other species properties are open and left to the discretion of users. We can of course imagine that these properties could contain thermodynamic properties such as the Gibbs energy of formation or even the entropy variation, these properties themselves being temperature dependent. These properties must nevertheless respect one of the following types: Number, AbstractVector{<:Number}, Function, AbstractString.

Imagine, for example, that we wanted to construct the $\text{CO}_2$ molecule in a gaseous state with some of its thermodynamic properties. The thermodynamic properties of the molecule, which can be found for example on the website thermoddem, are as follows at 298 K and 1 atm:

  • heat capacity ${\text{C}_p}^°$: 37.14 $\text{J}.\text{mol}^{-1}.\text{K}^{-1}$
  • molar volume $\text{V}^°$: 25.3 $\text{m}^3.\text{mol}^{-1}$
  • enthalpy of formation $\Delta_a {\text{H}^°}$: -393510 $\text{J}.\text{mol}^{-1}$
  • entropy $\text{S}^°$: 213.785 $\text{J}.\text{mol}^{-1}.\text{K}^{-1}$
  • Gibbs free energy of formation $\Delta_a {\text{G}^°}$: -394373 $\text{J}.\text{mol}^{-1}$

Furthermore, as explained above, heat capacity is a function of temperature. The parameters $a_0$, $a_1$, $a_2$, and $a_3$ can also be found on the same website. For $\text{CO}_2$, the values ​​are as follows: $a_0 = 33.98$, $a_1 = 23.88e-3$, $a_2 = 0$ et $a_3 = 0$.

using DynamicQuantities, ModelingToolkit
th_prop_0_CO2 = Dict(:Cp⁰ => 37.14u"J/K/mol", :ΔₐH⁰ => -393510u"J/mol", :S⁰ => 213.785u"J/K/mol", :ΔₐG⁰ => -394373u"J/mol", :V⁰ => 25.3e-3u"m^3/mol")
coeffs = Dict(:a₀ => 33.98u"J/K/mol", :a₁ => 23.88e-3u"J/mol/K^2", :a₂ => 0.0u"J*K/mol", :a₃ => 0.0u"J/mol/√K")
Heat capacity function

Although the function describing heat capacity has many parameters, it is of course possible to use only some of them. Here, only the parameters $a_0$ and $a_1$ are non-zero. The expression therefore becomes: $a_0 + a_1 * T$.

Reference temperature

It is important to define the reference temperature at which the thermodynamic properties are measured.

T_ref = Dict(:T => 298.15u"K")

Heat capacity, enthalpy and free energy as a function of temperature

Reference thermodynamical properties and temperature being defined, a simple call to build_thermo_functions allows the thermodynamic functions, such as heat capacity, entropy, enthalpy, and free enthalpy to be built as a function of temperature.

params_Cp_CO2 = merge(th_prop_0_CO2, coeffs, T_ref)
dtf_CO2 = build_thermo_functions(:cp_ft_equation, params_Cp_CO2)
OrderedCollections.OrderedDict{Symbol, SymbolicFunc{1, @NamedTuple{T::DynamicQuantities.Quantity{Float64, DynamicQuantities.Dimensions{DynamicQuantities.FRInt32}}}, Float64, DynamicQuantities.Dimensions{DynamicQuantities.FRInt32}}} with 4 entries:
  :Cp⁰  => 33.98 + 0.02388T [m² kg s⁻² K⁻¹ mol⁻¹] ◆ vars=(T) ◆ T=298.15 K
  :ΔₐH⁰ => -4.04703e5 + 33.98T + 0.01194(T^2) [m² kg s⁻² mol⁻¹] ◆ vars=(T) ◆ T=298.15 K
  :S⁰   => 13.0608 + 0.02388T + 33.98log(T) [m² kg s⁻² K⁻¹ mol⁻¹] ◆ vars=(T) ◆ T=298.15 K
  :ΔₐG⁰ => -3.41826e5 + 20.9192T - 0.01194(T^2) - 33.98T*log(T) [m² kg s⁻² mol⁻¹] ◆ vars=(T) ◆ T=298.15 K