From c8b737035dad7eb94e17e56df2c268ba708e98a9 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Mon, 31 Dec 2018 14:41:19 -0500 Subject: [PATCH] [Thermo] Add method for setting the state using an AnyMap Analogous to ThermoPhase::setStateFromXML, but more flexible --- include/cantera/thermo/MolalityVPSSTP.h | 6 ++ include/cantera/thermo/SurfPhase.h | 6 ++ include/cantera/thermo/ThermoPhase.h | 25 +++++++ src/thermo/MolalityVPSSTP.cpp | 17 +++++ src/thermo/SurfPhase.cpp | 11 +++ src/thermo/ThermoPhase.cpp | 96 +++++++++++++++++++++++++ test/thermo/ThermoPhase_Test.cpp | 34 +++++++++ 7 files changed, 195 insertions(+) diff --git a/include/cantera/thermo/MolalityVPSSTP.h b/include/cantera/thermo/MolalityVPSSTP.h index 9d164575a..e3e34546d 100644 --- a/include/cantera/thermo/MolalityVPSSTP.h +++ b/include/cantera/thermo/MolalityVPSSTP.h @@ -500,6 +500,12 @@ public: */ void setState_TPM(doublereal t, doublereal p, const std::string& m); + //! @copydoc ThermoPhase::setState + /*! + * Additionally uses the keys `molalities` or `M` to set the molalities. + */ + virtual void setState(const AnyMap& state); + virtual void getdlnActCoeffdlnN(const size_t ld, doublereal* const dlnActCoeffdlnN) { getdlnActCoeffdlnN_numderiv(ld, dlnActCoeffdlnN); } diff --git a/include/cantera/thermo/SurfPhase.h b/include/cantera/thermo/SurfPhase.h index aecc5036e..3cfc1ffb8 100644 --- a/include/cantera/thermo/SurfPhase.h +++ b/include/cantera/thermo/SurfPhase.h @@ -394,6 +394,12 @@ public: */ void getCoverages(doublereal* theta) const; + //! @copydoc ThermoPhase::setState + /*! + * Additionally uses the key `coverages` to set the fractional coverages. + */ + virtual void setState(const AnyMap& state); + protected: //! Surface site density (kmol m-2) doublereal m_n0; diff --git a/include/cantera/thermo/ThermoPhase.h b/include/cantera/thermo/ThermoPhase.h index c49c53838..a6f06d8ff 100644 --- a/include/cantera/thermo/ThermoPhase.h +++ b/include/cantera/thermo/ThermoPhase.h @@ -1140,6 +1140,31 @@ public: */ virtual void setState_RPY(doublereal rho, doublereal p, const std::string& y); + //! Set the state using an AnyMap containing any combination of properties + //! supported by the thermodynamic model + /*! + * Accepted keys are: + * * `X` (mole fractions) + * * `Y` (mass fractions) + * * `T` or `temperature` + * * `P` or `pressure` [Pa] + * * `H` or `enthalpy` [J/kg] + * * `U` or `internal-energy` [J/kg] + * * `S` or `entropy` [J/kg/K] + * * `V` or `specific-volume` [m^3/kg] + * * `D` or `density` [kg/m^3] + * + * Composition can be specified as either an AnyMap of species names to + * values or as a composition string. All other values can be given as + * floating point values in Cantera's default units, or as strings with the + * units specified, which will be converted using the Units class. + * + * If no thermodynamic property pair is given, or only one of temperature or + * pressure is given, then 298.15 K and 101325 Pa will be used as necessary + * to fully set the state. + */ + virtual void setState(const AnyMap& state); + //@} private: diff --git a/src/thermo/MolalityVPSSTP.cpp b/src/thermo/MolalityVPSSTP.cpp index 4fded612f..c78c90478 100644 --- a/src/thermo/MolalityVPSSTP.cpp +++ b/src/thermo/MolalityVPSSTP.cpp @@ -271,6 +271,23 @@ void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, const std::string& setState_TP(t, p); } +void MolalityVPSSTP::setState(const AnyMap& state) { + AnyValue molalities; + if (state.hasKey("molalities")) { + molalities = state["molalities"]; + } else if (state.hasKey("M")) { + molalities = state["M"]; + } + + if (molalities.is()) { + setMolalitiesByName(molalities.asString()); + } else if (molalities.is()) { + setMolalitiesByName(molalities.asMap()); + } + + VPStandardStateTP::setState(state); +} + void MolalityVPSSTP::initThermo() { VPStandardStateTP::initThermo(); diff --git a/src/thermo/SurfPhase.cpp b/src/thermo/SurfPhase.cpp index 39118e107..6c0a466a2 100644 --- a/src/thermo/SurfPhase.cpp +++ b/src/thermo/SurfPhase.cpp @@ -289,6 +289,17 @@ void SurfPhase::setCoveragesByName(const compositionMap& cov) setCoverages(cv.data()); } +void SurfPhase::setState(const AnyMap& state) { + if (state.hasKey("coverages")) { + if (state["coverages"].is()) { + setCoveragesByName(state["coverages"].asString()); + } else { + setCoveragesByName(state["coverages"].asMap()); + } + } + ThermoPhase::setState(state); +} + void SurfPhase::_updateThermo(bool force) const { doublereal tnow = temperature(); diff --git a/src/thermo/ThermoPhase.cpp b/src/thermo/ThermoPhase.cpp index f88640e48..e59875332 100644 --- a/src/thermo/ThermoPhase.cpp +++ b/src/thermo/ThermoPhase.cpp @@ -191,6 +191,102 @@ void ThermoPhase::setState_UV(double u, double v, double rtol) setState_HPorUV(u, v, rtol, true); } +void ThermoPhase::setState(const AnyMap& input_state) +{ + AnyMap state = input_state; + + // Remap allowable synonyms + if (state.hasKey("mass-fractions")) { + state["Y"] = state["mass-fractions"]; + state.erase("mass-fractions"); + } + if (state.hasKey("mole-fractions")) { + state["X"] = state["mole-fractions"]; + state.erase("mole-fractions"); + } + if (state.hasKey("temperature")) { + state["T"] = state["temperature"]; + } + if (state.hasKey("pressure")) { + state["P"] = state["pressure"]; + } + if (state.hasKey("enthalpy")) { + state["H"] = state["enthalpy"]; + } + if (state.hasKey("int-energy")) { + state["U"] = state["int-energy"]; + } + if (state.hasKey("internal-energy")) { + state["U"] = state["internal-energy"]; + } + if (state.hasKey("specific-volume")) { + state["V"] = state["specific-volume"]; + } + if (state.hasKey("entropy")) { + state["S"] = state["entropy"]; + } + if (state.hasKey("density")) { + state["D"] = state["density"]; + } + + // Set composition + if (state.hasKey("X")) { + if (state["X"].is()) { + setMoleFractionsByName(state["X"].asString()); + } else { + setMoleFractionsByName(state["X"].asMap()); + } + state.erase("X"); + } else if (state.hasKey("Y")) { + if (state["Y"].is()) { + setMassFractionsByName(state["Y"].asString()); + } else { + setMassFractionsByName(state["Y"].asMap()); + } + state.erase("Y"); + } + // set thermodynamic state using whichever property pair is found + if (state.size() == 0) { + setState_TP(298.15, OneAtm); + } else if (state.hasKey("T") && state.hasKey("P")) { + setState_TP(state.convert("T", "K"), state.convert("P", "Pa")); + } else if (state.hasKey("T") && state.hasKey("D")) { + setState_TR(state.convert("T", "K"), state.convert("D", "kg/m^3")); + } else if (state.hasKey("T") && state.hasKey("V")) { + setState_TV(state.convert("T", "K"), state.convert("V", "m^3/kg")); + } else if (state.hasKey("H") && state.hasKey("P")) { + setState_HP(state.convert("H", "J/kg"), state.convert("P", "Pa")); + } else if (state.hasKey("U") && state.hasKey("V")) { + setState_UV(state.convert("U", "J/kg"), state.convert("V", "m^3/kg")); + } else if (state.hasKey("S") && state.hasKey("P")) { + setState_SP(state.convert("S", "J/kg/K"), state.convert("P", "Pa")); + } else if (state.hasKey("S") && state.hasKey("V")) { + setState_SV(state.convert("S", "J/kg/K"), state.convert("V", "m^3/kg")); + } else if (state.hasKey("S") && state.hasKey("T")) { + setState_ST(state.convert("S", "J/kg/K"), state.convert("T", "K")); + } else if (state.hasKey("P") && state.hasKey("V")) { + setState_PV(state.convert("P", "Pa"), state.convert("V", "m^3/kg")); + } else if (state.hasKey("U") && state.hasKey("P")) { + setState_UP(state.convert("U", "J/kg"), state.convert("P", "Pa")); + } else if (state.hasKey("V") && state.hasKey("H")) { + setState_VH(state.convert("V", "m^3/kg"), state.convert("H", "J/kg")); + } else if (state.hasKey("T") && state.hasKey("H")) { + setState_TH(state.convert("T", "K"), state.convert("H", "J/kg")); + } else if (state.hasKey("S") && state.hasKey("H")) { + setState_SH(state.convert("S", "J/kg/K"), state.convert("H", "J/kg")); + } else if (state.hasKey("D") && state.hasKey("P")) { + setState_RP(state.convert("D", "kg/m^3"), state.convert("P", "Pa")); + } else if (state.hasKey("T")) { + setState_TP(state.convert("T", "K"), OneAtm); + } else if (state.hasKey("P")) { + setState_TP(298.15, state.convert("P", "Pa")); + } else { + throw CanteraError("ThermoPhase::setState", + "'state' did not specify a recognized set of properties.\n" + "Keys provided were: {}", input_state.keys_str()); + } +} + void ThermoPhase::setState_conditional_TP(doublereal t, doublereal p, bool set_p) { setTemperature(t); diff --git a/test/thermo/ThermoPhase_Test.cpp b/test/thermo/ThermoPhase_Test.cpp index fd2673f02..21a64c86f 100644 --- a/test/thermo/ThermoPhase_Test.cpp +++ b/test/thermo/ThermoPhase_Test.cpp @@ -75,4 +75,38 @@ TEST_F(TestThermoMethods, setState_nan) EXPECT_THROW(thermo->setState_TR(555, nan), CanteraError); } +TEST_F(TestThermoMethods, setState_AnyMap) +{ + AnyMap state; + state["temperature"] = 321; + state["Y"] = "AR: 4, O2: 1.0"; + state["P"] = "5 bar"; + thermo->setState(state); + EXPECT_DOUBLE_EQ(thermo->temperature(), 321); + EXPECT_DOUBLE_EQ(thermo->pressure(), 5e5); + EXPECT_DOUBLE_EQ(thermo->massFraction("O2"), 0.2); + + AnyMap state2; + state2["P"] = OneAtm; + state2["enthalpy"] = 0; + state2["X"]["O2"] = 0.9; + state2["X"]["AR"] = 0.1; + thermo->setState(state2); + EXPECT_DOUBLE_EQ(thermo->pressure(), OneAtm); + EXPECT_NEAR(thermo->temperature(), 298.15, 1e-6); + EXPECT_DOUBLE_EQ(thermo->moleFraction("AR"), 0.1); + + AnyMap state3; + state3["density"] = 10; + state3["V"] = 0.1; + state3["mole-fractions"] = "O2: 1.0"; + EXPECT_THROW(thermo->setState(state3), CanteraError); + + AnyMap state4; + state4["mole-fractions"] = "O2: 1.0"; + thermo->setState(state4); + EXPECT_DOUBLE_EQ(thermo->pressure(), OneAtm); + EXPECT_NEAR(thermo->temperature(), 298.15, 1e-6); +} + }