diff --git a/include/cantera/oneD/Boundary1D.h b/include/cantera/oneD/Boundary1D.h index 3ad6175364..7b6d42586d 100644 --- a/include/cantera/oneD/Boundary1D.h +++ b/include/cantera/oneD/Boundary1D.h @@ -104,6 +104,8 @@ class Boundary1D : public Domain1D virtual void setupGrid(size_t n, const double* z) {} + virtual void fromArray(SolutionArray& arr, double* soln); + protected: void _init(size_t n); @@ -201,7 +203,6 @@ class Empty1D : public Boundary1D integer* diagg, double rdt); virtual shared_ptr asArray(const double* soln) const; - virtual void fromArray(SolutionArray& arr, double* soln) {} }; /** @@ -230,7 +231,6 @@ class Symm1D : public Boundary1D integer* diagg, double rdt); virtual shared_ptr asArray(const double* soln) const; - virtual void fromArray(SolutionArray& arr, double* soln) {} }; @@ -259,7 +259,6 @@ class Outlet1D : public Boundary1D integer* diagg, double rdt); virtual shared_ptr asArray(const double* soln) const; - virtual void fromArray(SolutionArray& arr, double* soln) {} }; diff --git a/src/oneD/Boundary1D.cpp b/src/oneD/Boundary1D.cpp index 83a29c3663..731ceb37b8 100644 --- a/src/oneD/Boundary1D.cpp +++ b/src/oneD/Boundary1D.cpp @@ -77,6 +77,11 @@ void Boundary1D::_init(size_t n) } } +void Boundary1D::fromArray(SolutionArray& arr, double* soln) +{ + setMeta(arr.meta()); +} + // ---------------- Inlet1D methods ---------------- Inlet1D::Inlet1D() @@ -574,9 +579,10 @@ shared_ptr Surf1D::asArray(const double* soln) const void Surf1D::fromArray(SolutionArray& arr, double* soln) { - Boundary1D::setMeta(arr.meta()); - arr.setLoc(0); - m_temp = arr.thermo()->temperature(); + auto meta = arr.meta(); + m_temp = meta["temperature"].asDouble(); + meta.erase("temperature"); + Boundary1D::setMeta(meta); } void Surf1D::show(std::ostream& s, const double* x) diff --git a/src/oneD/Sim1D.cpp b/src/oneD/Sim1D.cpp index 139c8f82e4..d87abc1174 100644 --- a/src/oneD/Sim1D.cpp +++ b/src/oneD/Sim1D.cpp @@ -308,7 +308,13 @@ AnyMap Sim1D::restore(const string& fname, const string& name) for (auto dom : m_dom) { auto arr = SolutionArray::create(dom->solution()); - arr->readEntry(fname, name, dom->id()); + try { + arr->readEntry(fname, name, dom->id()); + } catch (CanteraError& err) { + throw CanteraError("Sim1D::restore", + "Encountered exception when reading entry '{}' from '{}':\n{}", + name, fname, err.getMessage()); + } dom->resize(dom->nComponents(), arr->size()); if (!header.hasKey("generator")) { arr->meta() = legacyH5(arr, header); @@ -318,7 +324,13 @@ AnyMap Sim1D::restore(const string& fname, const string& name) resize(); m_xlast_ts.clear(); for (auto dom : m_dom) { - dom->fromArray(*arrs[dom->id()], m_state->data() + dom->loc()); + try { + dom->fromArray(*arrs[dom->id()], m_state->data() + dom->loc()); + } catch (CanteraError& err) { + throw CanteraError("Sim1D::restore", + "Encountered exception when restoring domain '{}' from HDF:\n{}", + dom->id(), err.getMessage()); + } } finalize(); } else if (extension == "yaml" || extension == "yml") { @@ -328,14 +340,26 @@ AnyMap Sim1D::restore(const string& fname, const string& name) for (auto dom : m_dom) { auto arr = SolutionArray::create(dom->solution()); - arr->readEntry(root, name, dom->id()); + try { + arr->readEntry(root, name, dom->id()); + } catch (CanteraError& err) { + throw CanteraError("Sim1D::restore", + "Encountered exception when reading entry '{}' from '{}':\n{}", + name, fname, err.getMessage()); + } dom->resize(dom->nComponents(), arr->size()); arrs[dom->id()] = arr; } resize(); m_xlast_ts.clear(); for (auto dom : m_dom) { - dom->fromArray(*arrs[dom->id()], m_state->data() + dom->loc()); + try { + dom->fromArray(*arrs[dom->id()], m_state->data() + dom->loc()); + } catch (CanteraError& err) { + throw CanteraError("Sim1D::restore", + "Encountered exception when restoring domain '{}' from YAML:\n{}", + dom->id(), err.getMessage()); + } } finalize(); } else { diff --git a/test/python/test_onedim.py b/test/python/test_onedim.py index 04ae737056..3a4595897e 100644 --- a/test/python/test_onedim.py +++ b/test/python/test_onedim.py @@ -1502,6 +1502,87 @@ def test_restart(self): for rhou_j in sim.density * sim.velocity: self.assertNear(rhou_j, rhou, 1e-4) + +class TestStagnationFlame(utilities.CanteraTest): + def setUp(self): + self.gas = ct.Solution("h2o2.yaml") + + def create_stagnation(self, comp, tsurf, tinlet, mdot, width): + p = 0.05 * ct.one_atm # pressure + self.gas.TPX = tinlet, p, comp + + sim = ct.ImpingingJet(gas=self.gas, width=width) + sim.inlet.mdot = mdot + sim.surface.T = tsurf + return sim + + def run_stagnation(self, xh2, mdot, width): + # Simplified version of the example 'stagnation_flame.py' + tburner = 373.0 # burner temperature + tsurf = 500.0 + comp = {'H2': xh2, 'O2': 1, 'AR': 7} + sim = self.create_stagnation(comp, tsurf, tburner, mdot, width) + + sim.set_grid_min(1e-4) + sim.set_refine_criteria(3., 0.1, 0.2, 0.06) + sim.set_initial_guess(products='equil') # assume adiabatic equilibrium products + + sim.solve(loglevel=0, auto=True) + + assert sim.T.max() > tburner + tsurf + self.sim = sim + + def test_stagnation_case1(self): + self.run_stagnation(xh2=1.8, mdot=0.06, width=0.2) + + @pytest.mark.skipif("native" not in ct.hdf_support(), + reason="Cantera compiled without HDF support") + def test_restore_hdf(self): + self.run_save_restore("h5") + + def test_restore_yaml(self): + self.run_save_restore("yaml") + + def run_save_restore(self, mode): + filename = self.test_work_path / f"stagnation.{mode}" + filename.unlink(missing_ok=True) + + self.run_stagnation(xh2=1.8, mdot=0.06, width=0.1) + self.sim.save(filename, "test") + + jet = ct.ImpingingJet(gas=self.gas) + jet.restore(filename, "test") + + self.check_save_restore(jet) + + def check_save_restore(self, jet): + # pytest.approx is used as equality for floats cannot be guaranteed for loaded + # HDF5 files if they were created on a different OS and/or architecture + assert list(jet.grid) == pytest.approx(list(self.sim.grid)) + assert list(jet.T) == pytest.approx(list(self.sim.T), 1e-3) + k = self.sim.gas.species_index('H2') + assert list(jet.X[k, :]) == pytest.approx(list(self.sim.X[k, :]), 1e-4) + + settings = self.sim.flame.get_settings3() + for k, v in jet.flame.get_settings3().items(): + assert k in settings + if k == "fixed_temperature": + # fixed temperature is NaN + continue + if isinstance(v, dict): + for kk, vv in v.items(): + if isinstance(vv, float): + assert settings[k][kk] == pytest.approx(vv) + else: + assert settings[k][kk] == vv + if isinstance(k, float): + assert settings[k] == pytest.approx(v) + else: + assert settings[k] == v + + jet.solve(loglevel=0) + + class TestImpingingJet(utilities.CanteraTest): def setUp(self): self.gas = ct.Solution("ptcombust-simple.yaml", "gas")