From 703fabded195d9dcfc84146992d98bef90227549 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 4 Aug 2024 07:33:07 -0500 Subject: [PATCH] [clib] Disambiguate objects in Cabinet::index Add object parents to Cabinet, which are used to disambiguate objects that are created automatically, for example ThermoPhase / Kinetics / Transport that are instantiated together with new Solution objects. --- include/cantera/clib/ct.h | 4 ++ src/clib/Cabinet.h | 50 ++++++++++++++++++++--- src/clib/ct.cpp | 83 ++++++++++++++++++++++++++++----------- test/clib/test_clib.cpp | 65 ++++++++++++++++++++++-------- 4 files changed, 157 insertions(+), 45 deletions(-) diff --git a/include/cantera/clib/ct.h b/include/cantera/clib/ct.h index b6410bd3ff..9fe65092bc 100644 --- a/include/cantera/clib/ct.h +++ b/include/cantera/clib/ct.h @@ -35,6 +35,8 @@ extern "C" { CANTERA_CAPI int soln_adjacentName(int n, int a, int lennm, char* nm); CANTERA_CAPI int thermo_newFromFile(const char* filename, const char* phasename); //!< @todo remove from .NET and Fortran interfaces + CANTERA_CAPI int thermo_parent(int n); + CANTERA_CAPI int thermo_size(); CANTERA_CAPI int thermo_del(int n); CANTERA_CAPI size_t thermo_nElements(int n); CANTERA_CAPI size_t thermo_nSpecies(int n); @@ -125,6 +127,7 @@ extern "C" { CANTERA_CAPI int kin_newFromFile(const char* filename, const char* phasename, int reactingPhase, int neighbor1, int neighbor2, int neighbor3, int neighbor4); //!< @todo remove from .NET and Fortran interfaces + CANTERA_CAPI int kin_parent(int n); CANTERA_CAPI int kin_del(int n); CANTERA_CAPI size_t kin_nSpecies(int n); CANTERA_CAPI size_t kin_nReactions(int n); @@ -159,6 +162,7 @@ extern "C" { CANTERA_CAPI int trans_newDefault(int th, int loglevel); //!< @todo remove from .NET and Fortran interfaces CANTERA_CAPI int trans_new(const char* model, int th, int loglevel); //!< @todo remove from .NET and Fortran interfaces + CANTERA_CAPI int trans_parent(int n); CANTERA_CAPI int trans_del(int n); CANTERA_CAPI int trans_transportModel(int n, int lennm, char* nm); CANTERA_CAPI double trans_viscosity(int n); diff --git a/src/clib/Cabinet.h b/src/clib/Cabinet.h index 107032cfc8..40dbfc3260 100644 --- a/src/clib/Cabinet.h +++ b/src/clib/Cabinet.h @@ -11,6 +11,7 @@ #include "cantera/base/ctexceptions.h" #include "cantera/base/utilities.h" #include +#include namespace Cantera { @@ -61,12 +62,14 @@ class SharedCabinet /** * Add a new object. The index of the object is returned. */ - static int add(shared_ptr obj) { + static int add(shared_ptr obj, int parent=-1) { dataRef data = getData(); data.push_back(obj); + auto& parents = getParents(); + parents.push_back(parent); int idx = static_cast(data.size()) - 1; lookupRef lookup = getLookup(); - if (index(*obj) >= 0) { + if (lookup.count(obj.get())) { lookup[obj.get()].insert(idx); } else { lookup[obj.get()] = {idx}; @@ -97,6 +100,7 @@ class SharedCabinet */ static int reset() { getData().clear(); + getParents().clear(); getLookup().clear(); return 0; } @@ -107,7 +111,7 @@ class SharedCabinet static int copy(int n) { dataRef data = getData(); try { - return add(*data[n]); + return add(*data[n]); // do not copy parent to avoid ambiguous data } catch (std::exception& err) { throw CanteraError("SharedCabinet::newCopy", err.what()); } @@ -138,6 +142,17 @@ class SharedCabinet } } + /** + * Return handle of parent to object n. + */ + static int parent(int n) { + auto& parents = getParents(); + if (n < 0 || n >= len(parents)) { + throw CanteraError("SharedCabinet::parent", "Index {} out of range.", n); + } + return parents[n]; + } + /** * Return a shared pointer to object n. */ @@ -189,13 +204,19 @@ class SharedCabinet * object is not in the SharedCabinet. If multiple indices reference the same * object, the index of the last one added is returned. */ - static int index(const M& obj) { + static int index(const M& obj, int parent=-1) { lookupRef lookup = getLookup(); if (!lookup.count(&obj)) { return -1; } set& entry = lookup.at(&obj); - return *entry.rbegin(); + auto& parents = getParents(); + for (const auto e : boost::adaptors::reverse(entry)) { + if (parents[e] == parent) { + return e; + } + } + return -2; // not found } private: @@ -211,6 +232,18 @@ class SharedCabinet return s_storage->m_table; } + /** + * Static function that returns a pointer to the list of parent object handles of + * the singleton SharedCabinet instance. All member functions should + * access the data through this function. + */ + static vector& getParents() { + if (s_storage == nullptr) { + s_storage = new SharedCabinet(); + } + return s_storage->m_parents; + } + /** * Static function that returns a pointer to the reverse lookup table of * the singleton SharedCabinet instance. All member functions should @@ -234,7 +267,12 @@ class SharedCabinet std::unordered_map> m_lookup; /** - * list to hold pointers to objects. + * List to hold handles of parent objects. + */ + vector m_parents; + + /** + * List to hold pointers to objects. */ vector> m_table; }; diff --git a/src/clib/ct.cpp b/src/clib/ct.cpp index 194eb3a296..6d38f003d4 100644 --- a/src/clib/ct.cpp +++ b/src/clib/ct.cpp @@ -61,15 +61,16 @@ extern "C" { { try { auto soln = newSolution(infile, name, transport); + int id = SolutionCabinet::add(soln); // add associated objects - ThermoCabinet::add(soln->thermo()); + ThermoCabinet::add(soln->thermo(), id); if (soln->kinetics()) { - KineticsCabinet::add(soln->kinetics()); + KineticsCabinet::add(soln->kinetics(), id); } if (soln->transport()) { - TransportCabinet::add(soln->transport()); + TransportCabinet::add(soln->transport(), id); } - return SolutionCabinet::add(soln); + return id; } catch (...) { return handleAllExceptions(-1, ERR); } @@ -93,14 +94,15 @@ extern "C" { // adjacent phases can be retrieved via soln_adjacent } // add associated objects - ThermoCabinet::add(soln->thermo()); + int id = SolutionCabinet::add(soln); + ThermoCabinet::add(soln->thermo(), id); if (soln->kinetics()) { - KineticsCabinet::add(soln->kinetics()); + KineticsCabinet::add(soln->kinetics(), id); } if (soln->transport()) { - TransportCabinet::add(soln->transport()); + TransportCabinet::add(soln->transport(), id); } - return SolutionCabinet::add(soln); + return id; } catch (...) { return handleAllExceptions(-1, ERR); } @@ -112,18 +114,18 @@ extern "C" { if (n >= 0 && n < SolutionCabinet::size()) { // remove all associated objects auto soln = SolutionCabinet::at(n); - int index = ThermoCabinet::index(*(soln->thermo())); + int index = ThermoCabinet::index(*(soln->thermo()), n); if (index >= 0) { ThermoCabinet::del(index); } if (soln->kinetics()) { - index = KineticsCabinet::index(*(soln->kinetics())); + index = KineticsCabinet::index(*(soln->kinetics()), n); if (index >= 0) { KineticsCabinet::del(index); } } if (soln->transport()) { - index = TransportCabinet::index(*(soln->transport())); + index = TransportCabinet::index(*(soln->transport()), n); if (index >= 0) { TransportCabinet::del(index); } @@ -151,7 +153,7 @@ extern "C" { { try { auto soln = SolutionCabinet::at(n); - return ThermoCabinet::index(*soln->thermo()); + return ThermoCabinet::index(*soln->thermo(), n); } catch (...) { return handleAllExceptions(-2, ERR); } @@ -164,7 +166,7 @@ extern "C" { if (!soln->kinetics()) { return -1; } - return KineticsCabinet::index(*(soln->kinetics())); + return KineticsCabinet::index(*(soln->kinetics()), n); } catch (...) { return handleAllExceptions(-2, ERR); } @@ -177,7 +179,7 @@ extern "C" { if (!soln->transport()) { return -1; } - return TransportCabinet::index(*(soln->transport())); + return TransportCabinet::index(*(soln->transport()), n); } catch (...) { return handleAllExceptions(-2, ERR); } @@ -188,9 +190,9 @@ extern "C" { try { auto soln = SolutionCabinet::at(n); TransportCabinet::del( - TransportCabinet::index(*(soln->transport()))); + TransportCabinet::index(*(soln->transport()), n)); soln->setTransportModel(model); - return TransportCabinet::add(soln->transport()); + return TransportCabinet::add(soln->transport(), n); } catch (...) { return handleAllExceptions(-1, ERR); } @@ -209,19 +211,20 @@ extern "C" { { try { auto soln = SolutionCabinet::at(n); - if (a < 0 || a >= (int)soln->nAdjacent()) { + if (a < 0 || a >= static_cast(soln->nAdjacent())) { return -1; } auto adj = soln->adjacent(a); // add associated objects - ThermoCabinet::add(adj->thermo()); + int id = SolutionCabinet::add(adj); + ThermoCabinet::add(adj->thermo(), id); if (adj->kinetics()) { - KineticsCabinet::add(adj->kinetics()); + KineticsCabinet::add(adj->kinetics(), id); } if (adj->transport()) { - TransportCabinet::add(adj->transport()); + TransportCabinet::add(adj->transport(), id); } - return SolutionCabinet::add(adj); + return id; } catch (...) { return handleAllExceptions(-2, ERR); } @@ -231,7 +234,7 @@ extern "C" { { try { auto soln = SolutionCabinet::at(n); - if (a < 0 || a >= (int)soln->nAdjacent()) { + if (a < 0 || a >= static_cast(soln->nAdjacent())) { throw CanteraError("soln_adjacentName", "Invalid index {}.", a); } return static_cast(copyString(soln->adjacent(a)->name(), nm, lennm)); @@ -1726,6 +1729,42 @@ extern "C" { } } + int thermo_parent(int n) + { + try { + return ThermoCabinet::parent(n); + } catch (...) { + return handleAllExceptions(-2, ERR); + } + } + + int thermo_size() + { + try { + return ThermoCabinet::size(); + } catch (...) { + return handleAllExceptions(-1, ERR); + } + } + + int kin_parent(int n) + { + try { + return KineticsCabinet::parent(n); + } catch (...) { + return handleAllExceptions(-2, ERR); + } + } + + int trans_parent(int n) + { + try { + return TransportCabinet::parent(n); + } catch (...) { + return handleAllExceptions(-2, ERR); + } + } + int thermo_del(int n) { try { diff --git a/test/clib/test_clib.cpp b/test/clib/test_clib.cpp index a3e5591532..511e63e968 100644 --- a/test/clib/test_clib.cpp +++ b/test/clib/test_clib.cpp @@ -28,11 +28,22 @@ TEST(ct, cabinet_exceptions) string err = reportError(); EXPECT_THAT(err, HasSubstr("Index 999 out of range.")); + soln_thermo(998); + err = reportError(); + EXPECT_THAT(err, HasSubstr("Index 998 out of range.")); + int ret = soln_del(999); ASSERT_EQ(ret, -1); err = reportError(); EXPECT_THAT(err, HasSubstr("delete a non-existing object.")); + int ref = soln_newSolution("h2o2.yaml", "ohmech", "default"); + soln_del(ref); + int thermo = soln_thermo(ref); + EXPECT_EQ(thermo, -2); + err = reportError(); + EXPECT_THAT(err, HasSubstr("has been deleted.")); + ct_resetStorage(); ret = soln_del(0); ASSERT_EQ(ret, -1); @@ -48,6 +59,9 @@ TEST(ct, new_solution) int buflen = soln_name(ref, 0, 0) + 1; // include \0 ASSERT_EQ(buflen, int(name.size() + 1)); + int thermo = soln_thermo(ref); + ASSERT_EQ(thermo_parent(thermo), ref); + char* buf = new char[buflen]; soln_name(ref, buflen, buf); string solName = buf; @@ -59,45 +73,62 @@ TEST(ct, soln_objects) { ct_resetStorage(); - thermo_newFromFile("gri30.yaml", "gri30"); + int thermo0 = thermo_newFromFile("gri30.yaml", "gri30"); + ASSERT_EQ(thermo_size(), 1); int ref = soln_newSolution("gri30.yaml", "gri30", "none"); ASSERT_EQ(ref, 0); + ASSERT_EQ(thermo_size(), 2); int ref2 = soln_newSolution("h2o2.yaml", "ohmech", "default"); ASSERT_EQ(ref2, 1); + ASSERT_EQ(thermo_size(), 3); - int thermo = soln_thermo(ref2); - ASSERT_EQ(thermo, 2); - ASSERT_EQ(thermo_nSpecies(thermo), 10u); + ASSERT_EQ(thermo_parent(thermo0), -1); - int kin = soln_kinetics(ref2); - ASSERT_EQ(kin, 1); - ASSERT_EQ(kin_nReactions(kin), 29u); + int thermo = soln_thermo(ref); + ASSERT_EQ(thermo_parent(thermo), ref); - int trans = soln_kinetics(ref2); - ASSERT_EQ(trans, 1); - int buflen = trans_transportModel(trans, 0, 0); + int thermo2 = soln_thermo(ref2); + ASSERT_EQ(thermo2, 2); + ASSERT_EQ(thermo_nSpecies(thermo2), 10u); + ASSERT_EQ(thermo_parent(thermo2), ref2); + + int kin = soln_kinetics(ref); + + int kin2 = soln_kinetics(ref2); + ASSERT_EQ(kin2, 1); + ASSERT_EQ(kin_nReactions(kin2), 29u); + ASSERT_EQ(kin_parent(kin2), ref2); + ASSERT_EQ(kin_parent(kin), ref); + + int trans = soln_transport(ref); + ASSERT_EQ(trans_parent(trans), ref); + + int trans2 = soln_transport(ref2); + ASSERT_EQ(trans2, 1); + int buflen = trans_transportModel(trans2, 0, 0); vector buf(buflen); - trans_transportModel(trans, buflen, buf.data()); + trans_transportModel(trans2, buflen, buf.data()); string trName(buf.data()); ASSERT_EQ(trName, "mixture-averaged"); + ASSERT_EQ(trans_parent(trans2), ref2); soln_del(ref2); - size_t nsp = thermo_nSpecies(thermo); + size_t nsp = thermo_nSpecies(thermo2); ASSERT_EQ(nsp, npos); string err = reportError(); EXPECT_THAT(err, HasSubstr("has been deleted.")); - size_t nr = thermo_nSpecies(thermo); + size_t nr = thermo_nSpecies(thermo2); ASSERT_EQ(nr, npos); err = reportError(); EXPECT_THAT(err, HasSubstr("has been deleted.")); - trans = soln_setTransportModel(ref, "mixture-averaged"); - ASSERT_EQ(trans, 2); - buflen = trans_transportModel(trans, 0, 0); + trans2 = soln_setTransportModel(ref, "mixture-averaged"); + ASSERT_EQ(trans2, 2); + buflen = trans_transportModel(trans2, 0, 0); buf.resize(buflen); - trans_transportModel(trans, buflen, buf.data()); + trans_transportModel(trans2, buflen, buf.data()); trName = buf.data(); ASSERT_EQ(trName, "mixture-averaged"); }