16#include <boost/algorithm/string.hpp>
17#include <boost/range/adaptor/reversed.hpp>
22namespace ba = boost::algorithm;
29const map<string, string> aliasMap = {
33 {
"Y",
"mass-fractions"},
34 {
"X",
"mole-fractions"},
36 {
"U",
"specific-internal-energy"},
37 {
"V",
"specific-volume"},
38 {
"H",
"specific-enthalpy"},
39 {
"S",
"specific-entropy"},
40 {
"Q",
"vapor-fraction"},
43const map<string, string> reverseAliasMap = {
49 {
"spread-rate",
"spreadRate"},
50 {
"spread_rate",
"spreadRate"},
52 {
"radial-pressure-gradient",
"Lambda"},
55 {
"electric-field",
"eField"},
56 {
"oxidizer-velocity",
"Uo"},
63 return reverseAliasMap;
66SolutionArray::SolutionArray(
const shared_ptr<Solution>& sol,
67 int size,
const AnyMap& meta)
73 if (!m_sol || !m_sol->thermo()) {
74 throw CanteraError(
"SolutionArray::SolutionArray",
75 "Unable to create SolutionArray from invalid Solution object.");
77 m_stride = m_sol->thermo()->stateSize();
78 m_sol->thermo()->addSpeciesLock();
79 m_data = make_shared<vector<double>>(m_dataSize * m_stride, 0.);
80 m_extra = make_shared<map<string, AnyValue>>();
81 m_order = make_shared<map<int, string>>();
82 for (
size_t i = 0; i < m_dataSize; ++i) {
83 m_active.push_back(
static_cast<int>(i));
87 m_apiShape[0] =
static_cast<long>(m_dataSize);
90SolutionArray::SolutionArray(
const SolutionArray& other,
91 const vector<int>& selected)
93 , m_size(selected.size())
94 , m_dataSize(other.m_data->size())
95 , m_stride(other.m_stride)
96 , m_data(other.m_data)
97 , m_extra(other.m_extra)
98 , m_order(other.m_order)
101 m_sol->thermo()->addSpeciesLock();
102 if (!other.m_shared) {
108 m_active.reserve(selected.size());
109 for (
auto loc : selected) {
110 m_active.push_back(other.m_active.at(loc));
113 for (
auto loc : m_active) {
114 if (loc < 0 || loc >= (
int)m_dataSize) {
115 IndexError(
"SolutionArray::SolutionArray",
"indices", loc, m_dataSize);
118 set<int> unique(selected.begin(), selected.end());
119 if (unique.size() < selected.size()) {
120 throw CanteraError(
"SolutionArray::SolutionArray",
"Indices must be unique.");
124SolutionArray::~SolutionArray()
126 m_sol->thermo()->removeSpeciesLock();
132void resetSingle(AnyValue& extra,
const vector<int>& slice);
135AnyValue getSingle(
const AnyValue& extra,
const vector<int>& slice);
138void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
141void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value);
144void resetMulti(AnyValue& extra,
const vector<int>& slice);
147AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice);
150void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
153void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value);
156void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value);
159void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data);
162void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
166void SolutionArray::reset()
168 size_t nState = m_sol->thermo()->stateSize();
169 vector<double> state(nState);
170 m_sol->thermo()->saveState(state);
171 for (
size_t k = 0; k < m_size; ++k) {
172 std::copy(state.begin(), state.end(), m_data->data() + m_active[k] * m_stride);
174 for (
auto& [key, extra] : *m_extra) {
175 if (extra.is<
void>()) {
177 }
else if (extra.isVector<
double>()) {
178 resetSingle<double>(extra, m_active);
179 }
else if (extra.isVector<
long int>()) {
180 resetSingle<long int>(extra, m_active);
181 }
else if (extra.isVector<
string>()) {
182 resetSingle<string>(extra, m_active);
183 }
else if (extra.isVector<vector<double>>()) {
184 resetMulti<double>(extra, m_active);
185 }
else if (extra.isVector<vector<long int>>()) {
186 resetMulti<long int>(extra, m_active);
187 }
else if (extra.isVector<vector<string>>()) {
188 resetMulti<string>(extra, m_active);
191 "Unable to reset component '{}' with type '{}'.",
192 key, extra.type_str());
197void SolutionArray::resize(
int size)
201 "Resize is ambiguous for multi-dimensional arrays; use setApiShape "
204 if (m_data.use_count() > 1) {
206 "Unable to resize as data are shared by multiple objects.");
208 _resize(
static_cast<size_t>(size));
209 m_apiShape[0] =
static_cast<long>(size);
212void SolutionArray::setApiShape(
const vector<long int>& shape)
215 for (
auto dim : shape) {
218 if (m_shared && size != m_size) {
220 "Unable to set shape of shared data as sizes are inconsistent:\n"
221 "active size is {} but shape implies {}.", m_size, size);
223 if (!m_shared && size != m_dataSize) {
224 if (m_data.use_count() > 1) {
226 "Unable to set shape as data are shared by multiple objects.");
233void SolutionArray::_resize(
size_t size)
237 m_data->resize(m_dataSize * m_stride, 0.);
238 for (
auto& [key, data] : *m_extra) {
242 for (
size_t i = 0; i < m_dataSize; ++i) {
243 m_active.push_back(
static_cast<int>(i));
249vector<string> doubleColumn(
string name,
const vector<double>& comp,
255 string notation = fmt::format(
"{{:{}.{}g}}", width, (width - 1) / 2);
256 int csize =
static_cast<int>(comp.size());
257 int dots = csize + 1;
259 for (
const auto& val : comp) {
261 raw.push_back(boost::trim_copy(fmt::format(fmt::runtime(notation), val)));
264 dots = (rows + 1) / 2;
265 for (
int row = 0; row < dots; row++) {
266 data.push_back(comp[row]);
267 raw.push_back(boost::trim_copy(
268 fmt::format(fmt::runtime(notation), comp[row])));
270 for (
int row = csize - rows / 2; row < csize; row++) {
271 data.push_back(comp[row]);
272 raw.push_back(boost::trim_copy(
273 fmt::format(fmt::runtime(notation), comp[row])));
278 bool isFloat =
false;
279 bool isScientific =
false;
283 for (
const auto& repr : raw) {
285 if (name[0] ==
'-') {
287 name = name.substr(1);
289 if (name.find(
'e') != string::npos) {
293 tail = name.find(
'e') - name.find(
'.');
296 tail = std::max(tail, name.find(
'e') - name.find(
'.'));
300 }
else if (name.find(
'.') != string::npos) {
304 head = std::max(head, name.find(
'.'));
305 tail = std::max(tail, name.size() - name.find(
'.'));
308 head = std::max(head, name.size());
311 size_t maxLen = std::max((
size_t)4, head + tail + exp + isFloat + 1);
312 size_t over = std::max(0, (
int)name.size() - (
int)maxLen);
315 notation = fmt::format(
" {{:>{}.{}e}}", over + maxLen, tail);
316 }
else if (isFloat) {
318 notation = fmt::format(
" {{:>{}.{}f}}", over + maxLen, tail);
321 notation = fmt::format(
" {{:>{}.0f}}", over + maxLen);
323 maxLen = fmt::format(fmt::runtime(notation), 0.).size();
326 string section = fmt::format(
"{{:>{}}}", maxLen);
327 vector<string> col = {fmt::format(fmt::runtime(section), name)};
329 for (
const auto& val : data) {
330 col.push_back(fmt::format(fmt::runtime(notation), val));
333 col.push_back(fmt::format(fmt::runtime(section),
"..."));
339vector<string> integerColumn(
string name,
const vector<long int>& comp,
343 vector<long int> data;
344 string notation = fmt::format(
"{{:{}}}", width);
346 int csize =
static_cast<int>(comp.size());
347 int dots = csize + 1;
349 for (
const auto& val : comp) {
351 string formatted = boost::trim_copy(
352 fmt::format(fmt::runtime(notation), val));
353 if (formatted[0] ==
'-') {
354 formatted = formatted.substr(1);
356 maxLen = std::max(maxLen, formatted.size());
359 dots = (rows + 1) / 2;
360 for (
int row = 0; row < dots; row++) {
361 data.push_back(comp[row]);
362 string formatted = boost::trim_copy(
363 fmt::format(fmt::runtime(notation), comp[row]));
364 if (formatted[0] ==
'-') {
365 formatted = formatted.substr(1);
367 maxLen = std::max(maxLen, formatted.size());
369 for (
int row = csize - rows / 2; row < csize; row++) {
370 data.push_back(comp[row]);
371 string formatted = boost::trim_copy(
372 fmt::format(fmt::runtime(notation), comp[row]));
373 if (formatted[0] ==
'-') {
374 formatted = formatted.substr(1);
376 maxLen = std::max(maxLen, formatted.size());
382 notation = fmt::format(
"{{:<{}}}", maxLen);
385 maxLen = std::max(maxLen, name.size());
386 notation = fmt::format(
" {{:>{}}}", maxLen + 1);
390 vector<string> col = {fmt::format(fmt::runtime(notation), name)};
392 for (
const auto& val : data) {
393 col.push_back(fmt::format(fmt::runtime(notation), val));
396 col.push_back(fmt::format(fmt::runtime(notation),
".."));
402vector<string> stringColumn(
string name,
const vector<string>& comp,
407 string notation = fmt::format(
"{{:{}}}", width);
409 int csize =
static_cast<int>(comp.size());
410 int dots = csize + 1;
412 for (
const auto& val : comp) {
414 maxLen = std::max(maxLen,
415 boost::trim_copy(fmt::format(fmt::runtime(notation), val)).size());
418 dots = (rows + 1) / 2;
419 for (
int row = 0; row < dots; row++) {
420 data.push_back(comp[row]);
421 maxLen = std::max(maxLen,
423 fmt::format(fmt::runtime(notation), comp[row])).size());
425 for (
int row = csize - rows / 2; row < csize; row++) {
426 data.push_back(comp[row]);
427 maxLen = std::max(maxLen,
429 fmt::format(fmt::runtime(notation), comp[row])).size());
434 notation = fmt::format(
" {{:>{}}}", maxLen);
435 vector<string> col = {fmt::format(fmt::runtime(notation), name)};
437 for (
const auto& val : data) {
438 col.push_back(fmt::format(fmt::runtime(notation), val));
441 col.push_back(fmt::format(fmt::runtime(notation),
"..."));
447vector<string> formatColumn(
string name,
const AnyValue& comp,
int rows,
int width)
449 if (comp.isVector<
double>()) {
450 return doubleColumn(name, comp.asVector<
double>(), rows, width);
452 if (comp.isVector<
long int>()) {
453 return integerColumn(name, comp.asVector<
long int>(), rows, width);
455 if (comp.isVector<
string>()) {
456 return stringColumn(name, comp.asVector<
string>(), rows, width);
462 if (comp.isVector<vector<double>>()) {
463 repr =
"[ <double> ]";
464 size =
len(comp.asVector<vector<double>>());
465 }
else if (comp.isVector<vector<long int>>()) {
466 repr =
"[ <long int> ]";
467 size =
len(comp.asVector<vector<long int>>());
468 }
else if (comp.isVector<vector<string>>()) {
469 repr =
"[ <string> ]";
470 size =
len(comp.asVector<vector<string>>());
473 "formatColumn",
"Encountered invalid data for component '{}'.", name);
475 size_t maxLen = std::max(repr.size(), name.size());
478 string notation = fmt::format(
" {{:>{}}}", maxLen);
479 repr = fmt::format(fmt::runtime(notation), repr);
480 vector<string> col = {fmt::format(fmt::runtime(notation), name)};
482 for (
int row = 0; row < size; row++) {
486 int dots = (rows + 1) / 2;
487 for (
int row = 0; row < dots; row++) {
490 col.push_back(fmt::format(fmt::runtime(notation),
"..."));
491 for (
int row = size - rows / 2; row < size; row++) {
500string SolutionArray::info(
const vector<string>& keys,
int rows,
int width)
502 fmt::memory_buffer b;
504 vector<string> components;
508 components = componentNames();
512 vector<long int> index;
513 for (
const auto ix : m_active) {
516 vector<vector<string>> cols = {integerColumn(
"", index, rows, col_width)};
517 vector<vector<string>> tail;
518 size_t size = cols.back().size();
524 int back =
len(components) - 1;
525 int fLen =
len(cols.back()[0]);
529 while (!done && front <= back) {
531 while (bLen + sep <= fLen && front <= back) {
533 key = components[back];
534 auto col = formatColumn(key, getComponent(key), rows, col_width);
535 if (
len(col[0]) + fLen + bLen + sep > width) {
540 bLen +=
len(tail.back()[0]);
543 if (done || front > back) {
546 while (fLen <= bLen + sep && front <= back) {
548 key = components[front];
549 auto col = formatColumn(key, getComponent(key), rows, col_width);
550 if (
len(col[0]) + fLen + bLen + sep > width) {
555 fLen +=
len(cols.back()[0]);
559 if (cols.size() + tail.size() < components.size() + 1) {
561 cols.push_back(vector<string>(size + 1,
" ..."));
564 cols.insert(cols.end(), tail.rbegin(), tail.rend());
567 for (
size_t row = 0; row < size; row++) {
568 for (
const auto& col : cols) {
569 fmt_append(b, col[row]);
575 fmt_append(b,
"\n[{} rows x {} components; state='{}']",
576 m_size, components.size(), m_sol->thermo()->nativeMode());
579 return to_string(b) + err.
what();
584shared_ptr<ThermoPhase> SolutionArray::thermo()
586 return m_sol->thermo();
589string SolutionArray::transportModel()
591 return m_sol->transportModel();
594vector<string> SolutionArray::componentNames()
const
596 vector<string> components;
599 while (m_order->count(pos)) {
600 components.push_back(m_order->at(pos));
605 auto phase = m_sol->thermo();
606 for (
auto code : phase->nativeMode()) {
607 string name = string(1, code);
608 if (name ==
"X" || name ==
"Y") {
609 for (
auto& spc : phase->speciesNames()) {
610 components.push_back(spc);
613 components.push_back(name);
619 while (m_order->count(pos)) {
620 components.push_back(m_order->at(pos));
627void SolutionArray::addExtra(
const string& name,
bool back)
629 if (m_extra->count(name)) {
631 "Component '{}' already exists.", name);
635 if (m_order->size()) {
637 m_order->emplace(m_order->begin()->first - 1, name);
640 m_order->emplace(-1, name);
643 if (m_order->size()) {
645 m_order->emplace(m_order->rbegin()->first + 1, name);
648 m_order->emplace(0, name);
653vector<string> SolutionArray::listExtra(
bool all)
const
655 vector<string> names;
657 while (m_order->count(pos)) {
658 const auto& name = m_order->at(pos);
659 if (all || !m_extra->at(name).is<
void>()) {
660 names.push_back(name);
667 while (m_order->count(pos)) {
668 const auto& name = m_order->at(pos);
669 if (all || !m_extra->at(name).is<
void>()) {
670 names.push_back(name);
677bool SolutionArray::hasComponent(
const string& name,
bool checkAlias)
const
679 if (m_extra->count(name)) {
683 if (m_sol->thermo()->speciesIndex(name) !=
npos) {
687 if (name ==
"X" || name ==
"Y") {
691 if (checkAlias && reverseAliasMap.count(name)) {
696 return (m_sol->thermo()->nativeState().count(name));
699AnyValue SolutionArray::getComponent(
const string& name)
const
702 if (!hasComponent(name,
false) && reverseAliasMap.count(name)) {
703 _name = reverseAliasMap.at(name);
704 }
else if (!hasComponent(name)) {
706 "Unknown component '{}'.", name);
710 if (m_extra->count(_name)) {
712 const auto& extra = m_extra->at(_name);
713 if (extra.is<
void>()) {
716 if (m_size == m_dataSize) {
719 if (extra.isVector<
long int>()) {
720 return getSingle<long int>(extra, m_active);
722 if (extra.isVector<
double>()) {
723 return getSingle<double>(extra, m_active);
725 if (extra.isVector<
string>()) {
726 return getSingle<string>(extra, m_active);
728 if (extra.isVector<vector<double>>()) {
729 return getMulti<double>(extra, m_active);
731 if (extra.isVector<vector<long int>>()) {
732 return getMulti<long int>(extra, m_active);
734 if (extra.isVector<vector<string>>()) {
735 return getMulti<string>(extra, m_active);
738 "Unable to get sliced data for component '{}' with type '{}'.",
739 name, extra.type_str());
743 vector<double> data(m_size);
744 size_t ix = m_sol->thermo()->speciesIndex(_name);
747 ix = m_sol->thermo()->nativeState()[_name];
750 ix += m_stride - m_sol->thermo()->nSpecies();
752 for (
size_t k = 0; k < m_size; ++k) {
753 data[k] = (*m_data)[m_active[k] * m_stride + ix];
759bool isSimpleVector(
const AnyValue& any) {
766void SolutionArray::setComponent(
const string& name,
const AnyValue& data)
768 if (!hasComponent(name)) {
770 "Unknown component '{}'.", name);
772 if (m_extra->count(name)) {
773 _setExtra(name, data);
780 "Invalid type of component '{}': expected simple array type, "
781 "but received '{}'.", name, data.
type_str());
783 if (size != m_size) {
785 "Invalid size of component '{}': expected size {} but received {}.",
789 auto& vec = data.
asVector<
double>();
790 size_t ix = m_sol->thermo()->speciesIndex(name);
792 ix = m_sol->thermo()->nativeState()[name];
794 ix += m_stride - m_sol->thermo()->nSpecies();
796 for (
size_t k = 0; k < m_size; ++k) {
797 (*m_data)[m_active[k] * m_stride + ix] = vec[k];
801void SolutionArray::setLoc(
int loc,
bool restore)
803 size_t loc_ =
static_cast<size_t>(loc);
806 "Unable to set location in empty SolutionArray.");
807 }
else if (loc < 0) {
810 "Both current and buffered indices are invalid.");
813 }
else if (
static_cast<size_t>(m_active[loc_]) == m_loc) {
815 }
else if (loc_ >= m_size) {
816 throw IndexError(
"SolutionArray::setLoc",
"indices", loc_, m_size);
818 m_loc =
static_cast<size_t>(m_active[loc_]);
820 size_t nState = m_sol->thermo()->stateSize();
821 m_sol->thermo()->restoreState(nState, m_data->data() + m_loc * m_stride);
825void SolutionArray::updateState(
int loc)
828 size_t nState = m_sol->thermo()->stateSize();
829 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
832vector<double> SolutionArray::getState(
int loc)
835 size_t nState = m_sol->thermo()->stateSize();
836 vector<double> out(nState);
837 m_sol->thermo()->saveState(out);
841void SolutionArray::setState(
int loc,
const vector<double>& state)
843 size_t nState = m_sol->thermo()->stateSize();
844 if (state.size() != nState) {
846 "Expected array to have length {}, but received an array of length {}.",
847 nState, state.size());
850 m_sol->thermo()->restoreState(state);
851 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
854void SolutionArray::normalize() {
855 auto phase = m_sol->thermo();
856 auto nativeState = phase->nativeState();
857 if (nativeState.size() < 3) {
860 size_t nState = phase->stateSize();
861 vector<double> out(nState);
862 if (nativeState.count(
"Y")) {
863 size_t offset = nativeState[
"Y"];
864 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
866 phase->setMassFractions(m_data->data() + m_loc * m_stride +
offset);
867 m_sol->thermo()->saveState(out);
870 }
else if (nativeState.count(
"X")) {
871 size_t offset = nativeState[
"X"];
872 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
874 phase->setMoleFractions(m_data->data() + m_loc * m_stride +
offset);
875 m_sol->thermo()->saveState(out);
880 "Not implemented for mode '{}'.", phase->nativeMode());
884AnyMap SolutionArray::getAuxiliary(
int loc)
888 for (
const auto& [key, extra] : *m_extra) {
889 if (extra.is<
void>()) {
891 }
else if (extra.isVector<
long int>()) {
892 out[key] = extra.asVector<
long int>()[m_loc];
893 }
else if (extra.isVector<
double>()) {
894 out[key] = extra.asVector<
double>()[m_loc];
895 }
else if (extra.isVector<
string>()) {
896 out[key] = extra.asVector<
string>()[m_loc];
897 }
else if (extra.isVector<vector<long int>>()) {
898 out[key] = extra.asVector<vector<long int>>()[m_loc];
899 }
else if (extra.isVector<vector<double>>()) {
900 out[key] = extra.asVector<vector<double>>()[m_loc];
901 }
else if (extra.isVector<vector<string>>()) {
902 out[key] = extra.asVector<vector<string>>()[m_loc];
905 "Unable to retrieve data for component '{}' with type '{}'.",
906 key, extra.type_str());
912void SolutionArray::setAuxiliary(
int loc,
const AnyMap& data)
915 for (
const auto& [name, value] : data) {
916 if (!m_extra->count(name)) {
918 "Unknown auxiliary component '{}'.", name);
920 auto& extra = m_extra->at(name);
921 if (extra.is<
void>()) {
922 if (m_dataSize > 1) {
924 "Unable to set location for type '{}': "
925 "component is not initialized.", name);
927 _initExtra(name, value);
931 if (extra.isVector<
long int>()) {
932 setAuxiliarySingle<long int>(m_loc, extra, value);
933 }
else if (extra.isVector<
double>()) {
934 setAuxiliarySingle<double>(m_loc, extra, value);
935 }
else if (extra.isVector<
string>()) {
936 setAuxiliarySingle<string>(m_loc, extra, value);
937 }
else if (extra.isVector<vector<long int>>()) {
938 setAuxiliaryMulti<long int>(m_loc, extra, value);
939 }
else if (extra.isVector<vector<double>>()) {
940 setAuxiliaryMulti<double>(m_loc, extra, value);
941 }
else if (extra.isVector<vector<string>>()) {
942 setAuxiliaryMulti<string>(m_loc, extra, value);
945 "Unable to set entry for type '{}'.", extra.type_str());
950 "Encountered incompatible value for component '{}':\n{}",
956AnyMap preamble(
const string& desc)
960 data[
"description"] = desc;
962 data[
"generator"] =
"Cantera SolutionArray";
963 data[
"cantera-version"] = CANTERA_VERSION;
964 data[
"git-commit"] = gitCommit();
969 struct tm* newtime = localtime(&aclock);
970 data[
"date"] = stripnonprint(asctime(newtime));
975AnyMap& openField(AnyMap& root,
const string& name)
982 vector<string> tokens;
986 for (
auto& field : tokens) {
989 if (sub.hasKey(field) && !sub[field].is<AnyMap>()) {
990 throw CanteraError(
"openField",
991 "Encountered invalid existing field '{}'.", path);
992 }
else if (!sub.hasKey(field)) {
993 sub[field] = AnyMap();
995 ptr = &sub[field].as<AnyMap>();
1000void SolutionArray::writeHeader(
const string& fname,
const string& name,
1001 const string& desc,
bool overwrite)
1007 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1015void SolutionArray::writeHeader(
AnyMap& root,
const string& name,
1016 const string& desc,
bool overwrite)
1018 AnyMap& data = openField(root, name);
1019 if (!data.
empty()) {
1022 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1026 data.
update(preamble(desc));
1029void SolutionArray::writeEntry(
const string& fname,
bool overwrite,
const string& basis)
1031 if (apiNdim() != 1) {
1033 "Tabular output of CSV data only works for 1D SolutionArray objects.");
1035 set<string> speciesNames;
1036 for (
const auto& species : m_sol->thermo()->speciesNames()) {
1037 speciesNames.insert(species);
1041 const auto& nativeState = m_sol->thermo()->nativeState();
1042 mole = nativeState.find(
"X") != nativeState.end();
1043 }
else if (basis ==
"X" || basis ==
"mole") {
1045 }
else if (basis ==
"Y" || basis ==
"mass") {
1049 "Invalid species basis '{}'.", basis);
1052 auto names = componentNames();
1053 size_t last = names.size() - 1;
1054 vector<AnyValue> components;
1055 vector<bool> isSpecies;
1056 fmt::memory_buffer header;
1057 for (
const auto& key : names) {
1060 if (speciesNames.find(key) == speciesNames.end()) {
1062 isSpecies.push_back(
false);
1063 components.emplace_back(getComponent(key));
1064 col = components.size() - 1;
1065 if (!components[col].isVector<double>() &&
1066 !components[col].isVector<
long int>() &&
1067 !components[col].isVector<string>())
1070 "Multi-dimensional column '{}' is not supported for CSV output.",
1075 isSpecies.push_back(
true);
1076 components.emplace_back(
AnyValue());
1077 col = components.size() - 1;
1079 label =
"X_" + label;
1081 label =
"Y_" + label;
1084 if (label.find(
"\"") != string::npos || label.find(
"\n") != string::npos) {
1086 "Detected column name containing double quotes or line feeds: '{}'.",
1089 string sep = (col == last) ?
"" :
",";
1090 if (label.find(
",") != string::npos) {
1091 fmt_append(header,
"\"{}\"{}", label, sep);
1093 fmt_append(header,
"{}{}", label, sep);
1098 if (std::ifstream(fname).good()) {
1101 "File '{}' already exists; use option 'overwrite' to replace CSV file.",
1104 std::remove(fname.c_str());
1106 std::ofstream output(fname);
1107 output << to_string(header) << std::endl;
1109 size_t maxLen =
npos;
1110 vector<double> buf(speciesNames.size(), 0.);
1111 for (
int row = 0; row < static_cast<int>(m_size); row++) {
1112 fmt::memory_buffer line;
1113 if (maxLen !=
npos) {
1114 line.reserve(maxLen);
1118 m_sol->thermo()->getMoleFractions(buf.data());
1120 m_sol->thermo()->getMassFractions(buf.data());
1124 for (
size_t col = 0; col < components.size(); col++) {
1125 string sep = (col == last) ?
"" :
",";
1126 if (isSpecies[col]) {
1127 fmt_append(line,
"{:.9g}{}", buf[idx++], sep);
1129 auto& data = components[col];
1130 if (data.isVector<
double>()) {
1131 fmt_append(line,
"{:.9g}{}", data.asVector<
double>()[row], sep);
1132 }
else if (data.isVector<
long int>()) {
1133 fmt_append(line,
"{}{}", data.asVector<
long int>()[row], sep);
1134 }
else if (data.isVector<
bool>()) {
1135 fmt_append(line,
"{}{}",
1136 static_cast<bool>(data.asVector<
bool>()[row]), sep);
1138 auto value = data.asVector<
string>()[row];
1139 if (value.find(
"\"") != string::npos ||
1140 value.find(
"\n") != string::npos)
1143 "Detected value containing double quotes or line feeds: "
1146 if (value.find(
",") != string::npos) {
1147 fmt_append(line,
"\"{}\"{}", value, sep);
1149 fmt_append(line,
"{}{}", value, sep);
1154 output << to_string(line) << std::endl;
1155 maxLen = std::max(maxLen, line.size());
1159void SolutionArray::writeEntry(
const string& fname,
const string& name,
1160 const string& sub,
bool overwrite,
int compression)
1164 "Group name specifying root location must not be empty.");
1166 if (m_size < m_dataSize) {
1168 "Unable to save sliced data.");
1183 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1190 if (apiNdim() == 1) {
1191 more[
"size"] = int(m_dataSize);
1193 more[
"api-shape"] = m_apiShape;
1195 if (!m_meta.hasKey(
"transport-model") && m_sol->transport()) {
1196 more[
"transport-model"] = m_sol->transportModel();
1198 more[
"components"] = componentNames();
1204 const auto& nativeState = m_sol->thermo()->nativeState();
1205 size_t nSpecies = m_sol->thermo()->nSpecies();
1206 for (
auto& [key,
offset] : nativeState) {
1207 if (key ==
"X" || key ==
"Y") {
1208 vector<vector<double>> prop;
1209 for (
size_t i = 0; i < m_size; i++) {
1210 size_t first =
offset + i * m_stride;
1211 prop.emplace_back(m_data->begin() + first,
1212 m_data->begin() + first + nSpecies);
1218 auto data = getComponent(key);
1223 for (
const auto& [key, value] : *m_extra) {
1224 if (isSimpleVector(value)) {
1226 }
else if (value.is<
void>()) {
1230 "Unable to save component '{}' with data type {}.",
1231 key, value.type_str());
1236void SolutionArray::writeEntry(
AnyMap& root,
const string& name,
const string& sub,
1241 "Field name specifying root location must not be empty.");
1243 if (m_size < m_dataSize) {
1245 "Unable to save sliced data.");
1253 AnyMap& data = openField(root, path);
1254 bool preexisting = !data.
empty();
1255 if (preexisting && !overwrite) {
1257 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1259 if (apiNdim() == 1) {
1260 data[
"size"] = int(m_dataSize);
1262 data[
"api-shape"] = m_apiShape;
1264 if (m_sol->transport() && m_sol->transportModel() !=
"none") {
1265 data[
"transport-model"] = m_sol->transportModel();
1270 data[
"components"] = componentNames();
1273 for (
auto& [_, key] : *m_order) {
1274 data[key] = m_extra->
at(key);
1277 auto phase = m_sol->thermo();
1280 data[
"temperature"] = phase->temperature();
1281 data[
"pressure"] = phase->pressure();
1282 auto surf = std::dynamic_pointer_cast<SurfPhase>(phase);
1283 auto nSpecies = phase->nSpecies();
1284 vector<double> values(nSpecies);
1286 surf->getCoverages(&values[0]);
1288 phase->getMassFractions(&values[0]);
1291 for (
size_t k = 0; k < nSpecies; k++) {
1292 if (values[k] != 0.0) {
1293 items[phase->speciesName(k)] = values[k];
1297 data[
"coverages"] = std::move(items);
1299 data[
"mass-fractions"] = std::move(items);
1301 }
else if (m_size > 1) {
1302 for (
auto& code : phase->nativeMode()) {
1303 string name(1, code);
1304 if (name ==
"X" || name ==
"Y") {
1305 for (
auto& spc : phase->speciesNames()) {
1306 data[spc] = getComponent(spc);
1308 data[
"basis"] = name ==
"X" ?
"mole" :
"mass";
1310 data[name] = getComponent(name);
1315 static bool reg = AnyMap::addOrderingRules(
"SolutionArray",
1316 {{
"head",
"type"}, {
"head",
"size"}, {
"head",
"basis"}});
1318 data[
"__type__"] =
"SolutionArray";
1327void SolutionArray::append(
const vector<double>& state,
const AnyMap& extra)
1329 if (apiNdim() > 1) {
1331 "Unable to append multi-dimensional arrays.");
1337 setState(pos, state);
1338 setAuxiliary(pos, extra);
1346void SolutionArray::save(
const string& fname,
const string& name,
const string& sub,
1347 const string& desc,
bool overwrite,
int compression,
1348 const string& basis)
1350 if (m_size < m_dataSize) {
1352 "Unable to save sliced data.");
1354 size_t dot = fname.find_last_of(
".");
1356 if (extension ==
"csv") {
1359 "Parameter 'name' not used for CSV output.");
1361 writeEntry(fname, overwrite, basis);
1366 "Argument 'basis' is not used for HDF or YAML output.", basis);
1368 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1369 writeHeader(fname, name, desc, overwrite);
1370 writeEntry(fname, name, sub,
true, compression);
1373 if (extension ==
"yaml" || extension ==
"yml") {
1376 std::ifstream file(fname);
1377 if (file.good() && file.peek() != std::ifstream::traits_type::eof()) {
1378 data = AnyMap::fromYamlFile(fname);
1380 writeHeader(data, name, desc, overwrite);
1381 writeEntry(data, name, sub,
true);
1384 std::ofstream out(fname);
1385 out << data.toYamlString();
1386 AnyMap::clearCachedFile(fname);
1390 "Unknown file extension '{}'.", extension);
1393AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1400const AnyMap& locateField(
const AnyMap& root,
const string& name)
1407 vector<string> tokens;
1408 tokenizePath(name, tokens);
1409 const AnyMap* ptr = &root;
1411 for (
auto& field : tokens) {
1412 path +=
"/" + field;
1413 const AnyMap& sub = *ptr;
1414 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1415 throw CanteraError(
"SolutionArray::locateField",
1416 "No field or solution with name '{}'.", path);
1418 ptr = &sub[field].as<AnyMap>();
1425 auto sub = locateField(root, name);
1427 for (
const auto& [key, value] : sub) {
1428 if (!sub[key].is<AnyMap>()) {
1429 header[key] = value;
1435AnyMap SolutionArray::restore(
const string& fname,
1436 const string& name,
const string& sub)
1438 size_t dot = fname.find_last_of(
".");
1441 if (extension ==
"csv") {
1443 "CSV import not implemented; if using Python, data can be imported via "
1444 "'read_csv' instead.");
1446 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1447 readEntry(fname, name, sub);
1448 header = readHeader(fname, name);
1449 }
else if (extension ==
"yaml" || extension ==
"yml") {
1450 const AnyMap& root = AnyMap::fromYamlFile(fname);
1451 readEntry(root, name, sub);
1452 header = readHeader(root, name);
1455 "Unknown file extension '{}'; supported extensions include "
1456 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1461void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1463 if (!m_extra->count(name)) {
1465 "Component '{}' does not exist.", name);
1467 auto& extra = (*m_extra)[name];
1468 if (!extra.is<
void>()) {
1470 "Component '{}' is already initialized.", name);
1473 if (value.
is<
long int>()) {
1474 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1475 }
else if (value.
is<
double>()) {
1476 extra = vector<double>(m_dataSize, value.
as<
double>());
1477 }
else if (value.
is<
string>()) {
1478 extra = vector<string>(m_dataSize, value.
as<
string>());
1479 }
else if (value.
isVector<
long int>()) {
1480 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1481 }
else if (value.
isVector<
double>()) {
1482 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1483 }
else if (value.
isVector<
string>()) {
1484 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1485 }
else if (value.
is<
void>()) {
1490 "Unable to initialize component '{}' with type '{}'.",
1496 "Encountered incompatible value for initializing component '{}':\n{}",
1501void SolutionArray::_resizeExtra(
const string& name,
const AnyValue& value)
1503 if (!m_extra->count(name)) {
1505 "Component '{}' does not exist.", name);
1507 auto& extra = (*m_extra)[name];
1508 if (extra.is<
void>()) {
1514 if (extra.isVector<
long int>()) {
1515 resizeSingle<long int>(extra, m_dataSize, value);
1516 }
else if (extra.isVector<
double>()) {
1517 resizeSingle<double>(extra, m_dataSize, value);
1518 }
else if (extra.isVector<
string>()) {
1519 resizeSingle<string>(extra, m_dataSize, value);
1520 }
else if (extra.isVector<vector<double>>()) {
1521 resizeMulti<double>(extra, m_dataSize, value);
1522 }
else if (extra.isVector<vector<long int>>()) {
1523 resizeMulti<long int>(extra, m_dataSize, value);
1524 }
else if (extra.isVector<vector<string>>()) {
1525 resizeMulti<string>(extra, m_dataSize, value);
1528 "Unable to resize using type '{}'.", extra.type_str());
1533 "Encountered incompatible value for resizing component '{}':\n{}",
1538void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1540 if (!m_extra->count(name)) {
1542 "Extra component '{}' does not exist.", name);
1545 auto& extra = m_extra->at(name);
1546 if (data.
is<
void>() && m_size == m_dataSize) {
1553 if (extra.is<
void>()) {
1554 if (m_size != m_dataSize) {
1556 "Unable to replace '{}' for sliced data.", name);
1566 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1571 "Unable to initialize '{}' with empty array when SolutionArray is not "
1574 _initExtra(name, data);
1575 _resizeExtra(name, data);
1579 if (data.
is<
long int>()) {
1580 setScalar<long int>(extra, data, m_active);
1581 }
else if (data.
is<
double>()) {
1582 setScalar<double>(extra, data, m_active);
1583 }
else if (data.
is<
string>()) {
1584 setScalar<string>(extra, data, m_active);
1585 }
else if (data.
isVector<
long int>()) {
1586 setSingle<long int>(extra, data, m_active);
1587 }
else if (data.
isVector<
double>()) {
1588 setSingle<double>(extra, data, m_active);
1589 }
else if (data.
isVector<
string>()) {
1590 setSingle<string>(extra, data, m_active);
1591 }
else if (data.
isVector<vector<long int>>()) {
1592 setMulti<long int>(extra, data, m_active);
1593 }
else if (data.
isVector<vector<double>>()) {
1594 setMulti<double>(extra, data, m_active);
1595 }
else if (data.
isVector<vector<string>>()) {
1596 setMulti<string>(extra, data, m_active);
1599 "Unable to set sliced data for component '{}' with type '{}'.",
1604string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1608 const auto& nativeState = m_sol->thermo()->nativeState();
1609 bool usesNativeState =
false;
1610 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1612 for (
const auto& item : m_sol->thermo()->fullStates()) {
1615 usesNativeState =
true;
1616 for (
size_t i = 0; i < item.size(); i++) {
1618 name = string(1, item[i]);
1619 if (surf && (name ==
"X" || name ==
"Y")) {
1622 usesNativeState =
false;
1625 if (names.count(name)) {
1627 usesNativeState &= nativeState.count(name) > 0;
1628 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1630 usesNativeState &= nativeState.count(name) > 0;
1637 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1642 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1644 return "legacySurf";
1646 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1648 return "legacyInlet";
1651 "Detected incomplete thermodynamic information. Full states for a '{}' "
1652 "phase\nmay be defined by the following modes:\n'{}'\n"
1653 "Available data are: '{}'", m_sol->thermo()->type(),
1654 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1656 if (usesNativeState && native) {
1662set<string> SolutionArray::_stateProperties(
1663 const string& mode,
bool alias)
1666 if (mode ==
"native") {
1667 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1668 states.insert(alias ? aliasMap.at(name) : name);
1671 for (
const auto& m : mode) {
1672 const string name = string(1, m);
1673 states.insert(alias ? aliasMap.at(name) : name);
1680string getName(
const set<string>& names,
const string& name)
1682 if (names.count(name)) {
1685 const auto& alias = aliasMap.at(name);
1686 if (names.count(alias)) {
1692void SolutionArray::readEntry(
const string& fname,
const string& name,
1698 "Group name specifying root location must not be empty.");
1701 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1703 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1709 "Group name specifying data entry is empty.");
1712 auto [size, names] = file.
contents(path);
1714 if (m_meta.hasKey(
"size")) {
1716 resize(m_meta[
"size"].as<long int>());
1717 m_meta.erase(
"size");
1718 }
else if (m_meta.hasKey(
"api-shape")) {
1720 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1721 m_meta.erase(
"api-shape");
1724 resize(
static_cast<int>(size));
1732 string mode = _detectMode(names);
1733 set<string> states = _stateProperties(mode);
1734 if (states.count(
"C")) {
1735 if (names.count(
"X")) {
1738 mode = mode.substr(0, 2) +
"X";
1739 }
else if (names.count(
"Y")) {
1742 mode = mode.substr(0, 2) +
"Y";
1747 size_t nSpecies = m_sol->thermo()->nSpecies();
1748 size_t nState = m_sol->thermo()->stateSize();
1749 const auto& nativeStates = m_sol->thermo()->nativeState();
1750 if (mode ==
"native") {
1752 for (
const auto& [name,
offset] : nativeStates) {
1753 if (name ==
"X" || name ==
"Y") {
1755 data = file.
readData(path, name, m_size, nSpecies);
1756 auto prop = data.
asVector<vector<double>>();
1757 for (
size_t i = 0; i < m_dataSize; i++) {
1758 std::copy(prop[i].begin(), prop[i].end(),
1759 m_data->data() +
offset + i * m_stride);
1763 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1764 setComponent(name, data);
1767 }
else if (mode ==
"TPX") {
1769 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1770 vector<double> T = std::move(data.
asVector<
double>());
1771 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1772 vector<double> P = std::move(data.
asVector<
double>());
1773 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1774 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1775 for (
size_t i = 0; i < m_dataSize; i++) {
1776 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1777 m_sol->thermo()->setState_TP(T[i], P[i]);
1778 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1780 }
else if (mode ==
"TDX") {
1782 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1783 vector<double> T = std::move(data.
asVector<
double>());
1784 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1785 vector<double> D = std::move(data.
asVector<
double>());
1786 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1787 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1788 for (
size_t i = 0; i < m_dataSize; i++) {
1789 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1790 m_sol->thermo()->setState_TD(T[i], D[i]);
1791 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1793 }
else if (mode ==
"TPY") {
1795 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1796 vector<double> T = std::move(data.
asVector<
double>());
1797 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1798 vector<double> P = std::move(data.
asVector<
double>());
1799 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1800 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1801 for (
size_t i = 0; i < m_dataSize; i++) {
1802 m_sol->thermo()->setMassFractions_NoNorm(Y[i].data());
1803 m_sol->thermo()->setState_TP(T[i], P[i]);
1804 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1806 }
else if (mode ==
"legacySurf") {
1809 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1810 vector<double> T = std::move(data.
asVector<
double>());
1811 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1812 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1813 for (
size_t i = 0; i < m_dataSize; i++) {
1814 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1815 m_sol->thermo()->setTemperature(T[i]);
1816 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1819 "Detected legacy HDF format with incomplete state information\nfor name "
1820 "'{}' (pressure missing).", path);
1821 }
else if (mode ==
"") {
1823 "Data are not consistent with full state modes.");
1826 "Import of '{}' data is not supported.", mode);
1830 if (m_meta.hasKey(
"components")) {
1831 const auto& components = m_meta[
"components"].asVector<
string>();
1833 for (
const auto& name : components) {
1834 if (hasComponent(name,
false) || name ==
"X" || name ==
"Y") {
1838 if (reverseAliasMap.count(name)) {
1839 _name = reverseAliasMap.at(name);
1841 addExtra(_name, back);
1843 data = file.
readData(path, name, m_dataSize);
1844 setComponent(_name, data);
1847 m_meta.erase(
"components");
1850 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1851 for (
const auto& name : names) {
1852 if (!hasComponent(name,
false) && name !=
"X" && name !=
"Y") {
1854 if (reverseAliasMap.count(name)) {
1855 _name = reverseAliasMap.at(name);
1859 data = file.
readData(path, name, m_dataSize);
1860 setComponent(_name, data);
1865 if (m_meta.hasKey(
"transport-model")) {
1866 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1870void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1874 "Field name specifying root location must not be empty.");
1876 auto path = locateField(root, name);
1877 if (path.hasKey(
"generator") && sub !=
"") {
1879 path = locateField(root, name +
"/" + sub);
1880 }
else if (sub ==
"" && path.hasKey(
"data")) {
1882 path = locateField(root, name +
"/data");
1887 if (path.hasKey(
"size")) {
1889 resize(path[
"size"].asInt());
1890 }
else if (path.hasKey(
"api-shape")) {
1892 auto& shape = path[
"api-shape"].asVector<
long int>();
1896 size = path.getInt(
"points", 0);
1897 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1901 resize(
static_cast<int>(size));
1906 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1907 set<string> names = path.keys();
1908 size_t nState = m_sol->thermo()->stateSize();
1909 if (m_dataSize == 0) {
1911 }
else if (m_dataSize == 1) {
1913 string mode = _detectMode(names,
false);
1914 if (mode ==
"TPY") {
1915 double T = path[getName(names,
"T")].asDouble();
1916 double P = path[getName(names,
"P")].asDouble();
1917 auto Y = path[
"mass-fractions"].asMap<
double>();
1918 m_sol->thermo()->setState_TPY(T, P, Y);
1919 }
else if (mode ==
"TPC") {
1920 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1921 double T = path[getName(names,
"T")].asDouble();
1922 double P = path[
"pressure"].asDouble();
1923 m_sol->thermo()->setState_TP(T, P);
1924 auto cov = path[
"coverages"].asMap<
double>();
1925 surf->setCoveragesByName(cov);
1926 }
else if (mode ==
"legacyInlet") {
1929 double T = path[getName(names,
"T")].asDouble();
1930 auto Y = path[
"mass-fractions"].asMap<
double>();
1931 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1933 "Detected legacy YAML format with incomplete state information\n"
1934 "for name '{}' (pressure missing).", name +
"/" + sub);
1935 }
else if (mode ==
"") {
1937 "Data are not consistent with full state modes.");
1940 "Import of '{}' data is not supported.", mode);
1942 m_sol->thermo()->saveState(nState, m_data->data());
1943 auto props = _stateProperties(mode,
true);
1944 exclude.insert(props.begin(), props.end());
1947 if (path.hasKey(
"components")) {
1948 const auto& components = path[
"components"].asVector<
string>();
1950 for (
const auto& name : components) {
1952 if (hasComponent(name,
false)) {
1955 if (reverseAliasMap.count(name)) {
1956 _name = reverseAliasMap.at(name);
1958 addExtra(_name, back);
1960 setComponent(_name, path[name]);
1961 exclude.insert(name);
1965 for (
const auto& [name, value] : path) {
1966 if (value.isVector<
double>()) {
1967 const vector<double>& data = value.asVector<
double>();
1968 if (data.size() == m_dataSize) {
1970 if (!hasComponent(name,
false)) {
1971 if (reverseAliasMap.count(name)) {
1972 _name = reverseAliasMap.
at(name);
1976 setComponent(_name, value);
1977 exclude.insert(name);
1984 const auto& nativeState = m_sol->thermo()->nativeState();
1986 set<string> missingProps;
1987 for (
const auto& [name,
offset] : nativeState) {
1988 if (exclude.count(name)) {
1991 missingProps.insert(name);
1995 set<string> TY = {
"T",
"Y"};
1996 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
1998 double P = path[
"pressure"].asDouble();
1999 const size_t offset_T = nativeState.find(
"T")->second;
2000 const size_t offset_D = nativeState.find(
"D")->second;
2001 const size_t offset_Y = nativeState.find(
"Y")->second;
2002 for (
size_t i = 0; i < m_dataSize; i++) {
2003 double T = (*m_data)[offset_T + i * m_stride];
2004 m_sol->thermo()->setState_TPY(
2005 T, P, m_data->data() + offset_Y + i * m_stride);
2006 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
2008 }
else if (missingProps.size()) {
2010 "Incomplete state information: missing '{}'.",
2011 ba::join(missingProps,
"', '"));
2016 for (
const auto& [name, value] : path) {
2017 if (!exclude.count(name)) {
2018 m_meta[name] = value;
2022 if (m_meta.hasKey(
"transport-model")) {
2023 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
2032 vector<T> data(slice.size());
2033 const auto& vec = extra.
asVector<T>();
2034 for (
size_t k = 0; k < slice.size(); ++k) {
2035 data[k] = vec[slice[k]];
2043AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
2045 vector<vector<T>> data(slice.size());
2046 const auto& vec = extra.asVector<vector<T>>();
2047 for (
size_t k = 0; k < slice.size(); ++k) {
2048 data[k] = vec[slice[k]];
2056void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2058 T value = data.as<T>();
2059 if (extra.isVector<T>()) {
2060 auto& vec = extra.asVector<T>();
2061 for (
size_t k = 0; k < slice.size(); ++k) {
2062 vec[slice[k]] = value;
2065 throw CanteraError(
"SolutionArray::setScalar",
2066 "Incompatible input data: unable to assign '{}' data to '{}'.",
2067 data.type_str(), extra.type_str());
2072void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2074 size_t size = slice.size();
2075 if (extra.vectorSize() == size && data.vectorSize() == size) {
2079 if (extra.matrixShape().first == size && data.vectorSize() == size) {
2083 if (extra.type_str() != data.type_str()) {
2085 throw CanteraError(
"SolutionArray::setSingle",
2086 "Incompatible types: expected '{}' but received '{}'.",
2087 extra.type_str(), data.type_str());
2089 const auto& vData = data.asVector<T>();
2090 if (vData.size() != size) {
2091 throw CanteraError(
"SolutionArray::setSingle",
2092 "Invalid input data size: expected {} entries but received {}.",
2093 size, vData.size());
2095 auto& vec = extra.asVector<T>();
2096 for (
size_t k = 0; k < size; ++k) {
2097 vec[slice[k]] = vData[k];
2102void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2104 if (!data.isMatrix<T>()) {
2105 throw CanteraError(
"SolutionArray::setMulti",
2106 "Invalid input data shape: inconsistent number of columns.");
2108 size_t size = slice.size();
2109 auto [rows, cols] = data.matrixShape();
2110 if (extra.matrixShape().first == size && rows == size) {
2114 if (extra.vectorSize() == size && rows == size) {
2118 if (extra.type_str() != data.type_str()) {
2120 throw CanteraError(
"SolutionArray::setMulti",
2121 "Incompatible types: expected '{}' but received '{}'.",
2122 extra.type_str(), data.type_str());
2125 throw CanteraError(
"SolutionArray::setMulti",
2126 "Invalid input data shape: expected {} rows but received {}.",
2129 if (extra.matrixShape().second != cols) {
2130 throw CanteraError(
"SolutionArray::setMulti",
2131 "Invalid input data shape: expected {} columns but received {}.",
2132 extra.matrixShape().second, cols);
2134 const auto& vData = data.asVector<vector<T>>();
2135 auto& vec = extra.asVector<vector<T>>();
2136 for (
size_t k = 0; k < slice.size(); ++k) {
2137 vec[slice[k]] = vData[k];
2142void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2145 if (value.is<
void>()) {
2146 defaultValue = vector<T>(1)[0];
2148 defaultValue = value.as<T>();
2150 extra.asVector<T>().resize(size, defaultValue);
2154void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2156 vector<T> defaultValue;
2157 if (value.is<
void>()) {
2158 defaultValue = vector<T>(extra.matrixShape().second);
2160 defaultValue = value.as<vector<T>>();
2162 extra.asVector<vector<T>>().resize(size, defaultValue);
2166void resetSingle(AnyValue& extra,
const vector<int>& slice)
2168 T defaultValue = vector<T>(1)[0];
2169 vector<T>& data = extra.asVector<T>();
2170 for (
size_t k = 0; k < slice.size(); ++k) {
2171 data[slice[k]] = defaultValue;
2176void resetMulti(AnyValue& extra,
const vector<int>& slice)
2178 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2179 vector<vector<T>>& data = extra.asVector<vector<T>>();
2180 for (
size_t k = 0; k < slice.size(); ++k) {
2181 data[slice[k]] = defaultValue;
2186void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2188 extra.asVector<T>()[loc] = value.as<T>();
2192void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2194 const auto& value = data.asVector<T>();
2195 auto& vec = extra.asVector<vector<T>>();
2196 if (value.size() != vec[loc].size()) {
2197 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2198 "New element size {} does not match existing column size {}.",
2199 value.size(), vec[loc].size());
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
void setLoc(int line, int column)
For values which are derived from an input file, set the line and column of this value in that file.
A map of string keys to values whose type can vary at runtime.
bool empty() const
Return boolean indicating whether AnyMap is empty.
void clear()
Erase all items in the mapping.
const AnyValue & at(const string &key) const
Get the value of the item stored in key.
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
A wrapper for a variable whose type is determined at runtime.
bool isVector() const
Returns true if the held value is a vector of the specified type, such as vector<double>.
pair< size_t, size_t > matrixShape() const
Returns rows and columns of a matrix.
size_t vectorSize() const
Returns size of the held vector.
bool isScalar() const
Returns true if the held value is a scalar type (such as double, long int, string,...
bool is() const
Returns true if the held value is of the specified type.
const vector< T > & asVector(size_t nMin=npos, size_t nMax=npos) const
Return the held value, if it is a vector of type T.
const T & as() const
Get the value of this key as the specified type.
string type_str() const
Returns a string specifying the type of the held value.
Base class for exceptions thrown by Cantera classes.
const char * what() const override
Get a description of the error.
virtual string getMessage() const
Method overridden by derived classes to format the error message.
An array index is out of range.
An error indicating that an unimplemented function has been called.
A wrapper class handling storage to HDF.
pair< size_t, set< string > > contents(const string &id) const
Retrieve contents of file from a specified location.
void writeAttributes(const string &id, const AnyMap &meta)
Write attributes to a specified location.
void deleteGroup(const string &id)
Delete group.
AnyMap readAttributes(const string &id, bool recursive) const
Read attributes from a specified location.
AnyValue readData(const string &id, const string &name, size_t rows, size_t cols=npos) const
Read dataset from a specified location.
bool checkGroup(const string &id, bool permissive=false)
Check whether path location exists.
void setCompressionLevel(int level)
Set compression level (0..9)
void writeData(const string &id, const string &name, const AnyValue &data)
Write dataset to a specified location.
void tokenizePath(const string &in_val, vector< string > &v)
This function separates a string up into tokens according to the location of path separators.
string toLowerCopy(const string &input)
Convert to lower case.
U len(const T &container)
Get the size of a container, cast to a signed integer type.
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
const map< string, string > & _componentAliasMap()
Return mapping of component alias names to standardized component names.
offset
Offsets of solution components in the 1D solution array.
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...