From f6f52c30da4bbc55d42693635032fd0b2841ce21 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Mon, 16 Dec 2019 14:56:31 -0500 Subject: [PATCH] [Input] Improve behavior when kinetics / reactions are unspecified Including a 'reactions' field without a 'kinetics' field in the phase definition is an error. Including a 'kinetics' field without a 'reactions' field is allowed only if a 'reactions' section exists. To specify a kinetics model with no reactions, the 'reactions' field must be set to 'none'. --- src/kinetics/KineticsFactory.cpp | 40 ++++++++++++++++------ test/data/phase-reaction-spec1.yaml | 24 +++++++++++++ test/data/phase-reaction-spec2.yaml | 21 ++++++++++++ test/kinetics/kineticsFromYaml.cpp | 52 +++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 test/data/phase-reaction-spec1.yaml create mode 100644 test/data/phase-reaction-spec2.yaml diff --git a/src/kinetics/KineticsFactory.cpp b/src/kinetics/KineticsFactory.cpp index 07988e6508..180f2e792d 100644 --- a/src/kinetics/KineticsFactory.cpp +++ b/src/kinetics/KineticsFactory.cpp @@ -62,9 +62,7 @@ unique_ptr newKinetics(vector& phases, kin->addPhase(*phase); } kin->init(); - if (kin->kineticsType() != "Kinetics") { - addReactions(*kin, phaseNode, rootNode); - } + addReactions(*kin, phaseNode, rootNode); return kin; } @@ -103,12 +101,23 @@ void addReactions(Kinetics& kin, const AnyMap& phaseNode, const AnyMap& rootNode vector sections, rules; if (phaseNode.hasKey("reactions")) { + if (kin.kineticsType() == "Kinetics") { + throw InputFileError("addReactions", phaseNode["reactions"], + "Phase entry includes a 'reactions' field but does not " + "specify a kinetics model."); + } const auto& reactionsNode = phaseNode.at("reactions"); if (reactionsNode.is()) { - // Specification of the rule for adding species from the default - // 'reactions' section - sections.push_back("reactions"); - rules.push_back(reactionsNode.asString()); + if (rootNode.hasKey("reactions")) { + // Specification of the rule for adding species from the default + // 'reactions' section, if it exists + sections.push_back("reactions"); + rules.push_back(reactionsNode.asString()); + } else { + throw InputFileError("addReactions", reactionsNode, + "Phase entry implies existence of 'reactions' section " + "which does not exist in the current input file."); + } } else if (reactionsNode.is>()) { // List of sections from which all species should be added for (const auto& item : reactionsNode.as>()) { @@ -123,10 +132,19 @@ void addReactions(Kinetics& kin, const AnyMap& phaseNode, const AnyMap& rootNode rules.push_back(item.begin()->second.asString()); } } - } else { - // Default behavior is to add all reactions from the 'reactions' section - sections.push_back("reactions"); - rules.push_back("all"); + } else if (kin.kineticsType() != "Kinetics") { + if (rootNode.hasKey("reactions")) { + // Default behavior is to add all reactions from the 'reactions' + // section, if a 'kinetics' model has been specified + sections.push_back("reactions"); + rules.push_back("all"); + } else { + throw InputFileError("addReactions", phaseNode, + "Phase entry implies existence of 'reactions' section which " + "does not exist in the current input file. Add the field " + "'reactions: none' to the phase entry to specify a kinetics " + "model with no reactions."); + } } // Add reactions from each section diff --git a/test/data/phase-reaction-spec1.yaml b/test/data/phase-reaction-spec1.yaml new file mode 100644 index 0000000000..d8621474cc --- /dev/null +++ b/test/data/phase-reaction-spec1.yaml @@ -0,0 +1,24 @@ +phases: + # should work fine +- name: nokinetics-noreactions + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + + # should be an error +- name: kinetics-no-reaction-section1 + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + kinetics: gas + + # should be an error +- name: kinetics-no-reaction-section2 + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + kinetics: gas + reactions: all + + # should be an error +- name: nokinetics-reactions + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + reactions: all diff --git a/test/data/phase-reaction-spec2.yaml b/test/data/phase-reaction-spec2.yaml new file mode 100644 index 0000000000..7d17ccd01b --- /dev/null +++ b/test/data/phase-reaction-spec2.yaml @@ -0,0 +1,21 @@ +phases: + # should work fine (no reactions) +- name: nokinetics-noreactions + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + + # should also work fine (all reactions) +- name: kinetics-noreactions + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + kinetics: gas + + # should be an error +- name: nokinetics-reactions + species: [{gri30.yaml/species: [O2, H2, H2O]}] + thermo: ideal-gas + reactions: all + +reactions: +- equation: 2 H2 + O2 => 2 H2O + rate-constant: {A: 1.4e11, b: 0.5, Ea: 2400 cal/mol} diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 7cb3792b56..84d1af35af 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -251,3 +251,55 @@ TEST(Kinetics, ElectrochemFromYaml) EXPECT_NEAR(ropf[0], 0.279762338, 1e-8); EXPECT_NEAR(ropr[0], 0.045559670, 1e-8); } + +TEST(KineticsFromYaml, NoKineticsModelOrReactionsField1) +{ + auto soln = newSolution("phase-reaction-spec1.yaml", + "nokinetics-noreactions"); + EXPECT_EQ(soln->kinetics()->kineticsType(), "Kinetics"); + EXPECT_EQ(soln->kinetics()->nReactions(), (size_t) 0); +} + +TEST(KineticsFromYaml, NoKineticsModelOrReactionsField2) +{ + auto soln = newSolution("phase-reaction-spec2.yaml", + "nokinetics-noreactions"); + EXPECT_EQ(soln->kinetics()->kineticsType(), "Kinetics"); + EXPECT_EQ(soln->kinetics()->nReactions(), (size_t) 0); +} + +TEST(KineticsFromYaml, KineticsModelWithoutReactionsSection1) +{ + EXPECT_THROW(newSolution("phase-reaction-spec1.yaml", + "kinetics-no-reaction-section1"), + InputFileError); +} + +TEST(KineticsFromYaml, KineticsModelWithoutReactionsSection2) +{ + EXPECT_THROW(newSolution("phase-reaction-spec1.yaml", + "kinetics-no-reaction-section2"), + InputFileError); +} + +TEST(KineticsFromYaml, KineticsModelWithoutReactionsField) +{ + auto soln = newSolution("phase-reaction-spec2.yaml", + "kinetics-noreactions"); + EXPECT_EQ(soln->kinetics()->kineticsType(), "Gas"); + EXPECT_EQ(soln->kinetics()->nReactions(), (size_t) 1); +} + +TEST(KineticsFromYaml, ReactionsFieldWithoutKineticsModel1) +{ + EXPECT_THROW(newSolution("phase-reaction-spec1.yaml", + "nokinetics-reactions"), + InputFileError); +} + +TEST(KineticsFromYaml, ReactionsFieldWithoutKineticsModel2) +{ + EXPECT_THROW(newSolution("phase-reaction-spec2.yaml", + "nokinetics-reactions"), + InputFileError); +}