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,
false) !=
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,
false);
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,
false);
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(
822 span<const double>(m_data->data() + m_loc * m_stride, nState));
826void SolutionArray::updateState(
int loc)
829 size_t nState = m_sol->thermo()->stateSize();
830 m_sol->thermo()->saveState(
831 span<double>(m_data->data() + m_loc * m_stride, nState));
834vector<double> SolutionArray::getState(
int loc)
837 size_t nState = m_sol->thermo()->stateSize();
838 vector<double> out(nState);
839 m_sol->thermo()->saveState(out);
843void SolutionArray::setState(
int loc,
const vector<double>& state)
845 size_t nState = m_sol->thermo()->stateSize();
846 if (state.size() != nState) {
848 "Expected array to have length {}, but received an array of length {}.",
849 nState, state.size());
852 m_sol->thermo()->restoreState(state);
853 m_sol->thermo()->saveState(
854 span<double>(m_data->data() + m_loc * m_stride, nState));
857void SolutionArray::normalize() {
858 auto phase = m_sol->thermo();
859 auto nativeState = phase->nativeState();
860 if (nativeState.size() < 3) {
863 size_t nState = phase->stateSize();
864 vector<double> out(nState);
865 if (nativeState.count(
"Y")) {
866 size_t offset = nativeState[
"Y"];
867 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
869 auto* data = m_data->data() + m_loc * m_stride +
offset;
870 phase->setMassFractions(span<const double>(data, phase->nSpecies()));
871 m_sol->thermo()->saveState(out);
874 }
else if (nativeState.count(
"X")) {
875 size_t offset = nativeState[
"X"];
876 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
878 auto* data = m_data->data() + m_loc * m_stride +
offset;
879 phase->setMoleFractions(span<const double>(data, phase->nSpecies()));
880 m_sol->thermo()->saveState(out);
885 "Not implemented for mode '{}'.", phase->nativeMode());
889AnyMap SolutionArray::getAuxiliary(
int loc)
893 for (
const auto& [key, extra] : *m_extra) {
894 if (extra.is<
void>()) {
896 }
else if (extra.isVector<
long int>()) {
897 out[key] = extra.asVector<
long int>()[m_loc];
898 }
else if (extra.isVector<
double>()) {
899 out[key] = extra.asVector<
double>()[m_loc];
900 }
else if (extra.isVector<
string>()) {
901 out[key] = extra.asVector<
string>()[m_loc];
902 }
else if (extra.isVector<vector<long int>>()) {
903 out[key] = extra.asVector<vector<long int>>()[m_loc];
904 }
else if (extra.isVector<vector<double>>()) {
905 out[key] = extra.asVector<vector<double>>()[m_loc];
906 }
else if (extra.isVector<vector<string>>()) {
907 out[key] = extra.asVector<vector<string>>()[m_loc];
910 "Unable to retrieve data for component '{}' with type '{}'.",
911 key, extra.type_str());
917void SolutionArray::setAuxiliary(
int loc,
const AnyMap& data)
920 for (
const auto& [name, value] : data) {
921 if (!m_extra->count(name)) {
923 "Unknown auxiliary component '{}'.", name);
925 auto& extra = m_extra->at(name);
926 if (extra.is<
void>()) {
927 if (m_dataSize > 1) {
929 "Unable to set location for type '{}': "
930 "component is not initialized.", name);
932 _initExtra(name, value);
936 if (extra.isVector<
long int>()) {
937 setAuxiliarySingle<long int>(m_loc, extra, value);
938 }
else if (extra.isVector<
double>()) {
939 setAuxiliarySingle<double>(m_loc, extra, value);
940 }
else if (extra.isVector<
string>()) {
941 setAuxiliarySingle<string>(m_loc, extra, value);
942 }
else if (extra.isVector<vector<long int>>()) {
943 setAuxiliaryMulti<long int>(m_loc, extra, value);
944 }
else if (extra.isVector<vector<double>>()) {
945 setAuxiliaryMulti<double>(m_loc, extra, value);
946 }
else if (extra.isVector<vector<string>>()) {
947 setAuxiliaryMulti<string>(m_loc, extra, value);
950 "Unable to set entry for type '{}'.", extra.type_str());
955 "Encountered incompatible value for component '{}':\n{}",
961AnyMap preamble(
const string& desc)
965 data[
"description"] = desc;
967 data[
"generator"] =
"Cantera SolutionArray";
968 data[
"cantera-version"] = CANTERA_VERSION;
969 data[
"git-commit"] = gitCommit();
974 struct tm* newtime = localtime(&aclock);
975 data[
"date"] = stripnonprint(asctime(newtime));
980AnyMap& openField(AnyMap& root,
const string& name)
990 for (
auto& field : tokens) {
993 if (sub.hasKey(field) && !sub[field].is<AnyMap>()) {
994 throw CanteraError(
"openField",
995 "Encountered invalid existing field '{}'.", path);
996 }
else if (!sub.hasKey(field)) {
997 sub[field] = AnyMap();
999 ptr = &sub[field].as<AnyMap>();
1004void SolutionArray::writeHeader(
const string& fname,
const string& name,
1005 const string& desc,
bool overwrite)
1011 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1019void SolutionArray::writeHeader(
AnyMap& root,
const string& name,
1020 const string& desc,
bool overwrite)
1022 AnyMap& data = openField(root, name);
1023 if (!data.
empty()) {
1026 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1030 data.
update(preamble(desc));
1033void SolutionArray::writeEntry(
const string& fname,
bool overwrite,
const string& basis)
1035 if (apiNdim() != 1) {
1037 "Tabular output of CSV data only works for 1D SolutionArray objects.");
1039 set<string> speciesNames;
1040 for (
const auto& species : m_sol->thermo()->speciesNames()) {
1041 speciesNames.insert(species);
1045 const auto& nativeState = m_sol->thermo()->nativeState();
1046 mole = nativeState.find(
"X") != nativeState.end();
1047 }
else if (basis ==
"X" || basis ==
"mole") {
1049 }
else if (basis ==
"Y" || basis ==
"mass") {
1053 "Invalid species basis '{}'.", basis);
1056 auto names = componentNames();
1057 size_t last = names.size() - 1;
1058 vector<AnyValue> components;
1059 vector<bool> isSpecies;
1060 fmt::memory_buffer header;
1061 for (
const auto& key : names) {
1064 if (speciesNames.find(key) == speciesNames.end()) {
1066 isSpecies.push_back(
false);
1067 components.emplace_back(getComponent(key));
1068 col = components.size() - 1;
1069 if (!components[col].isVector<double>() &&
1070 !components[col].isVector<
long int>() &&
1071 !components[col].isVector<string>())
1074 "Multi-dimensional column '{}' is not supported for CSV output.",
1079 isSpecies.push_back(
true);
1080 components.emplace_back(
AnyValue());
1081 col = components.size() - 1;
1083 label =
"X_" + label;
1085 label =
"Y_" + label;
1088 if (label.find(
"\"") != string::npos || label.find(
"\n") != string::npos) {
1090 "Detected column name containing double quotes or line feeds: '{}'.",
1093 string sep = (col == last) ?
"" :
",";
1094 if (label.find(
",") != string::npos) {
1095 fmt_append(header,
"\"{}\"{}", label, sep);
1097 fmt_append(header,
"{}{}", label, sep);
1102 if (std::ifstream(fname).good()) {
1105 "File '{}' already exists; use option 'overwrite' to replace CSV file.",
1108 std::remove(fname.c_str());
1110 std::ofstream output(fname);
1111 output << to_string(header) << std::endl;
1113 size_t maxLen =
npos;
1114 vector<double> buf(speciesNames.size(), 0.);
1115 for (
int row = 0; row < static_cast<int>(m_size); row++) {
1116 fmt::memory_buffer line;
1117 if (maxLen !=
npos) {
1118 line.reserve(maxLen);
1122 m_sol->thermo()->getMoleFractions(buf);
1124 m_sol->thermo()->getMassFractions(buf);
1128 for (
size_t col = 0; col < components.size(); col++) {
1129 string sep = (col == last) ?
"" :
",";
1130 if (isSpecies[col]) {
1131 fmt_append(line,
"{:.9g}{}", buf[idx++], sep);
1133 auto& data = components[col];
1134 if (data.isVector<
double>()) {
1135 fmt_append(line,
"{:.9g}{}", data.asVector<
double>()[row], sep);
1136 }
else if (data.isVector<
long int>()) {
1137 fmt_append(line,
"{}{}", data.asVector<
long int>()[row], sep);
1138 }
else if (data.isVector<
bool>()) {
1139 fmt_append(line,
"{}{}",
1140 static_cast<bool>(data.asVector<
bool>()[row]), sep);
1142 auto value = data.asVector<
string>()[row];
1143 if (value.find(
"\"") != string::npos ||
1144 value.find(
"\n") != string::npos)
1147 "Detected value containing double quotes or line feeds: "
1150 if (value.find(
",") != string::npos) {
1151 fmt_append(line,
"\"{}\"{}", value, sep);
1153 fmt_append(line,
"{}{}", value, sep);
1158 output << to_string(line) << std::endl;
1159 maxLen = std::max(maxLen, line.size());
1163void SolutionArray::writeEntry(
const string& fname,
const string& name,
1164 const string& sub,
bool overwrite,
int compression)
1168 "Group name specifying root location must not be empty.");
1170 if (m_size < m_dataSize) {
1172 "Unable to save sliced data.");
1187 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1194 if (apiNdim() == 1) {
1195 more[
"size"] = int(m_dataSize);
1197 more[
"api-shape"] = m_apiShape;
1199 if (!m_meta.hasKey(
"transport-model") && m_sol->transport()) {
1200 more[
"transport-model"] = m_sol->transportModel();
1202 more[
"components"] = componentNames();
1208 const auto& nativeState = m_sol->thermo()->nativeState();
1209 size_t nSpecies = m_sol->thermo()->nSpecies();
1210 for (
auto& [key,
offset] : nativeState) {
1211 if (key ==
"X" || key ==
"Y") {
1212 vector<vector<double>> prop;
1213 for (
size_t i = 0; i < m_size; i++) {
1214 size_t first =
offset + i * m_stride;
1215 prop.emplace_back(m_data->begin() + first,
1216 m_data->begin() + first + nSpecies);
1222 auto data = getComponent(key);
1227 for (
const auto& [key, value] : *m_extra) {
1228 if (isSimpleVector(value)) {
1230 }
else if (value.is<
void>()) {
1234 "Unable to save component '{}' with data type {}.",
1235 key, value.type_str());
1240void SolutionArray::writeEntry(
AnyMap& root,
const string& name,
const string& sub,
1245 "Field name specifying root location must not be empty.");
1247 if (m_size < m_dataSize) {
1249 "Unable to save sliced data.");
1257 AnyMap& data = openField(root, path);
1258 bool preexisting = !data.
empty();
1259 if (preexisting && !overwrite) {
1261 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1263 if (apiNdim() == 1) {
1264 data[
"size"] = int(m_dataSize);
1266 data[
"api-shape"] = m_apiShape;
1268 if (m_sol->transport() && m_sol->transportModel() !=
"none") {
1269 data[
"transport-model"] = m_sol->transportModel();
1274 data[
"components"] = componentNames();
1277 for (
auto& [_, key] : *m_order) {
1278 data[key] = m_extra->
at(key);
1281 auto phase = m_sol->thermo();
1284 data[
"temperature"] = phase->temperature();
1285 data[
"pressure"] = phase->pressure();
1286 auto surf = std::dynamic_pointer_cast<SurfPhase>(phase);
1287 auto nSpecies = phase->nSpecies();
1288 vector<double> values(nSpecies);
1290 surf->getCoverages(values);
1292 phase->getMassFractions(values);
1295 for (
size_t k = 0; k < nSpecies; k++) {
1296 if (values[k] != 0.0) {
1297 items[phase->speciesName(k)] = values[k];
1301 data[
"coverages"] = std::move(items);
1303 data[
"mass-fractions"] = std::move(items);
1305 }
else if (m_size > 1) {
1306 for (
auto& code : phase->nativeMode()) {
1307 string name(1, code);
1308 if (name ==
"X" || name ==
"Y") {
1309 for (
auto& spc : phase->speciesNames()) {
1310 data[spc] = getComponent(spc);
1312 data[
"basis"] = name ==
"X" ?
"mole" :
"mass";
1314 data[name] = getComponent(name);
1319 static bool reg = AnyMap::addOrderingRules(
"SolutionArray",
1320 {{
"head",
"type"}, {
"head",
"size"}, {
"head",
"basis"}});
1322 data[
"__type__"] =
"SolutionArray";
1331void SolutionArray::append(
const vector<double>& state,
const AnyMap& extra)
1333 if (apiNdim() > 1) {
1335 "Unable to append multi-dimensional arrays.");
1341 setState(pos, state);
1342 setAuxiliary(pos, extra);
1350void SolutionArray::save(
const string& fname,
const string& name,
const string& sub,
1351 const string& desc,
bool overwrite,
int compression,
1352 const string& basis)
1354 if (m_size < m_dataSize) {
1356 "Unable to save sliced data.");
1358 size_t dot = fname.find_last_of(
".");
1360 if (extension ==
"csv") {
1363 "Parameter 'name' not used for CSV output.");
1365 writeEntry(fname, overwrite, basis);
1370 "Argument 'basis' is not used for HDF or YAML output.", basis);
1372 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1373 writeHeader(fname, name, desc, overwrite);
1374 writeEntry(fname, name, sub,
true, compression);
1377 if (extension ==
"yaml" || extension ==
"yml") {
1380 std::ifstream file(fname);
1381 if (file.good() && file.peek() != std::ifstream::traits_type::eof()) {
1382 data = AnyMap::fromYamlFile(fname);
1384 writeHeader(data, name, desc, overwrite);
1385 writeEntry(data, name, sub,
true);
1388 std::ofstream out(fname);
1389 out << data.toYamlString();
1390 AnyMap::clearCachedFile(fname);
1394 "Unknown file extension '{}'.", extension);
1397AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1404const AnyMap& locateField(
const AnyMap& root,
const string& name)
1411 vector<string> tokens = tokenizePath(name);
1412 const AnyMap* ptr = &root;
1414 for (
auto& field : tokens) {
1415 path +=
"/" + field;
1416 const AnyMap& sub = *ptr;
1417 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1418 throw CanteraError(
"SolutionArray::locateField",
1419 "No field or solution with name '{}'.", path);
1421 ptr = &sub[field].as<AnyMap>();
1428 auto sub = locateField(root, name);
1430 for (
const auto& [key, value] : sub) {
1431 if (!sub[key].is<AnyMap>()) {
1432 header[key] = value;
1438AnyMap SolutionArray::restore(
const string& fname,
1439 const string& name,
const string& sub)
1441 size_t dot = fname.find_last_of(
".");
1444 if (extension ==
"csv") {
1446 "CSV import not implemented; if using Python, data can be imported via "
1447 "'read_csv' instead.");
1449 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1450 readEntry(fname, name, sub);
1451 header = readHeader(fname, name);
1452 }
else if (extension ==
"yaml" || extension ==
"yml") {
1453 const AnyMap& root = AnyMap::fromYamlFile(fname);
1454 readEntry(root, name, sub);
1455 header = readHeader(root, name);
1458 "Unknown file extension '{}'; supported extensions include "
1459 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1464void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1466 if (!m_extra->count(name)) {
1468 "Component '{}' does not exist.", name);
1470 auto& extra = (*m_extra)[name];
1471 if (!extra.is<
void>()) {
1473 "Component '{}' is already initialized.", name);
1476 if (value.
is<
long int>()) {
1477 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1478 }
else if (value.
is<
double>()) {
1479 extra = vector<double>(m_dataSize, value.
as<
double>());
1480 }
else if (value.
is<
string>()) {
1481 extra = vector<string>(m_dataSize, value.
as<
string>());
1482 }
else if (value.
isVector<
long int>()) {
1483 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1484 }
else if (value.
isVector<
double>()) {
1485 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1486 }
else if (value.
isVector<
string>()) {
1487 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1488 }
else if (value.
is<
void>()) {
1493 "Unable to initialize component '{}' with type '{}'.",
1499 "Encountered incompatible value for initializing component '{}':\n{}",
1504void SolutionArray::_resizeExtra(
const string& name,
const AnyValue& value)
1506 if (!m_extra->count(name)) {
1508 "Component '{}' does not exist.", name);
1510 auto& extra = (*m_extra)[name];
1511 if (extra.is<
void>()) {
1517 if (extra.isVector<
long int>()) {
1518 resizeSingle<long int>(extra, m_dataSize, value);
1519 }
else if (extra.isVector<
double>()) {
1520 resizeSingle<double>(extra, m_dataSize, value);
1521 }
else if (extra.isVector<
string>()) {
1522 resizeSingle<string>(extra, m_dataSize, value);
1523 }
else if (extra.isVector<vector<double>>()) {
1524 resizeMulti<double>(extra, m_dataSize, value);
1525 }
else if (extra.isVector<vector<long int>>()) {
1526 resizeMulti<long int>(extra, m_dataSize, value);
1527 }
else if (extra.isVector<vector<string>>()) {
1528 resizeMulti<string>(extra, m_dataSize, value);
1531 "Unable to resize using type '{}'.", extra.type_str());
1536 "Encountered incompatible value for resizing component '{}':\n{}",
1541void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1543 if (!m_extra->count(name)) {
1545 "Extra component '{}' does not exist.", name);
1548 auto& extra = m_extra->at(name);
1549 if (data.
is<
void>() && m_size == m_dataSize) {
1556 if (extra.is<
void>()) {
1557 if (m_size != m_dataSize) {
1559 "Unable to replace '{}' for sliced data.", name);
1569 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1574 "Unable to initialize '{}' with empty array when SolutionArray is not "
1577 _initExtra(name, data);
1578 _resizeExtra(name, data);
1582 if (data.
is<
long int>()) {
1583 setScalar<long int>(extra, data, m_active);
1584 }
else if (data.
is<
double>()) {
1585 setScalar<double>(extra, data, m_active);
1586 }
else if (data.
is<
string>()) {
1587 setScalar<string>(extra, data, m_active);
1588 }
else if (data.
isVector<
long int>()) {
1589 setSingle<long int>(extra, data, m_active);
1590 }
else if (data.
isVector<
double>()) {
1591 setSingle<double>(extra, data, m_active);
1592 }
else if (data.
isVector<
string>()) {
1593 setSingle<string>(extra, data, m_active);
1594 }
else if (data.
isVector<vector<long int>>()) {
1595 setMulti<long int>(extra, data, m_active);
1596 }
else if (data.
isVector<vector<double>>()) {
1597 setMulti<double>(extra, data, m_active);
1598 }
else if (data.
isVector<vector<string>>()) {
1599 setMulti<string>(extra, data, m_active);
1602 "Unable to set sliced data for component '{}' with type '{}'.",
1607string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1611 const auto& nativeState = m_sol->thermo()->nativeState();
1612 bool usesNativeState =
false;
1613 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1615 for (
const auto& item : m_sol->thermo()->fullStates()) {
1618 usesNativeState =
true;
1619 for (
size_t i = 0; i < item.size(); i++) {
1621 name = string(1, item[i]);
1622 if (surf && (name ==
"X" || name ==
"Y")) {
1625 usesNativeState =
false;
1628 if (names.count(name)) {
1630 usesNativeState &= nativeState.count(name) > 0;
1631 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1633 usesNativeState &= nativeState.count(name) > 0;
1640 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1645 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1647 return "legacySurf";
1649 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1651 return "legacyInlet";
1654 "Detected incomplete thermodynamic information. Full states for a '{}' "
1655 "phase\nmay be defined by the following modes:\n'{}'\n"
1656 "Available data are: '{}'", m_sol->thermo()->type(),
1657 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1659 if (usesNativeState && native) {
1665set<string> SolutionArray::_stateProperties(
1666 const string& mode,
bool alias)
1669 if (mode ==
"native") {
1670 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1671 states.insert(alias ? aliasMap.at(name) : name);
1674 for (
const auto& m : mode) {
1675 const string name = string(1, m);
1676 states.insert(alias ? aliasMap.at(name) : name);
1683string getName(
const set<string>& names,
const string& name)
1685 if (names.count(name)) {
1688 const auto& alias = aliasMap.at(name);
1689 if (names.count(alias)) {
1695void SolutionArray::readEntry(
const string& fname,
const string& name,
1701 "Group name specifying root location must not be empty.");
1704 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1706 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1712 "Group name specifying data entry is empty.");
1715 auto [size, names] = file.
contents(path);
1717 if (m_meta.hasKey(
"size")) {
1719 resize(m_meta[
"size"].as<long int>());
1720 m_meta.erase(
"size");
1721 }
else if (m_meta.hasKey(
"api-shape")) {
1723 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1724 m_meta.erase(
"api-shape");
1727 resize(
static_cast<int>(size));
1735 string mode = _detectMode(names);
1736 set<string> states = _stateProperties(mode);
1737 if (states.count(
"C")) {
1738 if (names.count(
"X")) {
1741 mode = mode.substr(0, 2) +
"X";
1742 }
else if (names.count(
"Y")) {
1745 mode = mode.substr(0, 2) +
"Y";
1750 size_t nSpecies = m_sol->thermo()->nSpecies();
1751 size_t nState = m_sol->thermo()->stateSize();
1752 const auto& nativeStates = m_sol->thermo()->nativeState();
1753 if (mode ==
"native") {
1755 for (
const auto& [name,
offset] : nativeStates) {
1756 if (name ==
"X" || name ==
"Y") {
1758 data = file.
readData(path, name, m_size, nSpecies);
1759 auto prop = data.
asVector<vector<double>>();
1760 for (
size_t i = 0; i < m_dataSize; i++) {
1761 std::copy(prop[i].begin(), prop[i].end(),
1762 m_data->data() +
offset + i * m_stride);
1766 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1767 setComponent(name, data);
1770 }
else if (mode ==
"TPX") {
1772 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1773 vector<double> T = std::move(data.
asVector<
double>());
1774 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1775 vector<double> P = std::move(data.
asVector<
double>());
1776 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1777 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1778 for (
size_t i = 0; i < m_dataSize; i++) {
1779 m_sol->thermo()->setMoleFractions_NoNorm(X[i]);
1780 m_sol->thermo()->setState_TP(T[i], P[i]);
1781 m_sol->thermo()->saveState(
1782 span<double>(m_data->data() + i * m_stride, nState));
1784 }
else if (mode ==
"TDX") {
1786 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1787 vector<double> T = std::move(data.
asVector<
double>());
1788 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1789 vector<double> D = std::move(data.
asVector<
double>());
1790 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1791 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1792 for (
size_t i = 0; i < m_dataSize; i++) {
1793 m_sol->thermo()->setMoleFractions_NoNorm(X[i]);
1794 m_sol->thermo()->setState_TD(T[i], D[i]);
1795 m_sol->thermo()->saveState(
1796 span<double>(m_data->data() + i * m_stride, nState));
1798 }
else if (mode ==
"TPY") {
1800 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1801 vector<double> T = std::move(data.
asVector<
double>());
1802 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1803 vector<double> P = std::move(data.
asVector<
double>());
1804 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1805 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1806 for (
size_t i = 0; i < m_dataSize; i++) {
1807 m_sol->thermo()->setMassFractions_NoNorm(Y[i]);
1808 m_sol->thermo()->setState_TP(T[i], P[i]);
1809 m_sol->thermo()->saveState(
1810 span<double>(m_data->data() + i * m_stride, nState));
1812 }
else if (mode ==
"legacySurf") {
1815 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1816 vector<double> T = std::move(data.
asVector<
double>());
1817 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1818 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1819 for (
size_t i = 0; i < m_dataSize; i++) {
1820 m_sol->thermo()->setMoleFractions_NoNorm(X[i]);
1821 m_sol->thermo()->setTemperature(T[i]);
1822 m_sol->thermo()->saveState(
1823 span<double>(m_data->data() + i * m_stride, nState));
1826 "Detected legacy HDF format with incomplete state information\nfor name "
1827 "'{}' (pressure missing).", path);
1828 }
else if (mode ==
"") {
1830 "Data are not consistent with full state modes.");
1833 "Import of '{}' data is not supported.", mode);
1837 if (m_meta.hasKey(
"components")) {
1838 const auto& components = m_meta[
"components"].asVector<
string>();
1840 for (
const auto& name : components) {
1841 if (hasComponent(name,
false) || name ==
"X" || name ==
"Y") {
1845 if (reverseAliasMap.count(name)) {
1846 _name = reverseAliasMap.at(name);
1848 addExtra(_name, back);
1850 data = file.
readData(path, name, m_dataSize);
1851 setComponent(_name, data);
1854 m_meta.erase(
"components");
1857 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1858 for (
const auto& name : names) {
1859 if (!hasComponent(name,
false) && name !=
"X" && name !=
"Y") {
1861 if (reverseAliasMap.count(name)) {
1862 _name = reverseAliasMap.at(name);
1866 data = file.
readData(path, name, m_dataSize);
1867 setComponent(_name, data);
1872 if (m_meta.hasKey(
"transport-model")) {
1873 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1877void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1881 "Field name specifying root location must not be empty.");
1883 auto path = locateField(root, name);
1884 if (path.hasKey(
"generator") && sub !=
"") {
1886 path = locateField(root, name +
"/" + sub);
1887 }
else if (sub ==
"" && path.hasKey(
"data")) {
1889 path = locateField(root, name +
"/data");
1894 if (path.hasKey(
"size")) {
1896 resize(path[
"size"].asInt());
1897 }
else if (path.hasKey(
"api-shape")) {
1899 auto& shape = path[
"api-shape"].asVector<
long int>();
1903 size = path.getInt(
"points", 0);
1904 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1908 resize(
static_cast<int>(size));
1913 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1914 set<string> names = path.keys();
1915 size_t nState = m_sol->thermo()->stateSize();
1916 if (m_dataSize == 0) {
1918 }
else if (m_dataSize == 1) {
1920 string mode = _detectMode(names,
false);
1921 if (mode ==
"TPY") {
1922 double T = path[getName(names,
"T")].asDouble();
1923 double P = path[getName(names,
"P")].asDouble();
1924 auto Y = path[
"mass-fractions"].asMap<
double>();
1925 m_sol->thermo()->setState_TPY(T, P, Y);
1926 }
else if (mode ==
"TPC") {
1927 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1928 double T = path[getName(names,
"T")].asDouble();
1929 double P = path[
"pressure"].asDouble();
1930 m_sol->thermo()->setState_TP(T, P);
1931 auto cov = path[
"coverages"].asMap<
double>();
1932 surf->setCoveragesByName(cov);
1933 }
else if (mode ==
"legacyInlet") {
1936 double T = path[getName(names,
"T")].asDouble();
1937 auto Y = path[
"mass-fractions"].asMap<
double>();
1938 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1940 "Detected legacy YAML format with incomplete state information\n"
1941 "for name '{}' (pressure missing).", name +
"/" + sub);
1942 }
else if (mode ==
"") {
1944 "Data are not consistent with full state modes.");
1947 "Import of '{}' data is not supported.", mode);
1949 m_sol->thermo()->saveState(span<double>(m_data->data(), nState));
1950 auto props = _stateProperties(mode,
true);
1951 exclude.insert(props.begin(), props.end());
1954 if (path.hasKey(
"components")) {
1955 const auto& components = path[
"components"].asVector<
string>();
1957 for (
const auto& name : components) {
1959 if (hasComponent(name,
false)) {
1962 if (reverseAliasMap.count(name)) {
1963 _name = reverseAliasMap.at(name);
1965 addExtra(_name, back);
1967 setComponent(_name, path[name]);
1968 exclude.insert(name);
1972 for (
const auto& [name, value] : path) {
1973 if (value.isVector<
double>()) {
1974 const vector<double>& data = value.asVector<
double>();
1975 if (data.size() == m_dataSize) {
1977 if (!hasComponent(name,
false)) {
1978 if (reverseAliasMap.count(name)) {
1979 _name = reverseAliasMap.
at(name);
1983 setComponent(_name, value);
1984 exclude.insert(name);
1991 const auto& nativeState = m_sol->thermo()->nativeState();
1993 set<string> missingProps;
1994 for (
const auto& [name,
offset] : nativeState) {
1995 if (exclude.count(name)) {
1998 missingProps.insert(name);
2002 set<string> TY = {
"T",
"Y"};
2003 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
2005 double P = path[
"pressure"].asDouble();
2006 const size_t offset_T = nativeState.find(
"T")->second;
2007 const size_t offset_D = nativeState.find(
"D")->second;
2008 const size_t offset_Y = nativeState.find(
"Y")->second;
2009 for (
size_t i = 0; i < m_dataSize; i++) {
2010 double T = (*m_data)[offset_T + i * m_stride];
2011 span<double> Y(m_data->data() + offset_Y + i * m_stride,
2012 m_sol->thermo()->nSpecies());
2013 m_sol->thermo()->setState_TPY(T, P, Y);
2014 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
2016 }
else if (missingProps.size()) {
2018 "Incomplete state information: missing '{}'.",
2019 ba::join(missingProps,
"', '"));
2024 for (
const auto& [name, value] : path) {
2025 if (!exclude.count(name)) {
2026 m_meta[name] = value;
2030 if (m_meta.hasKey(
"transport-model")) {
2031 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
2040 vector<T> data(slice.size());
2041 const auto& vec = extra.
asVector<T>();
2042 for (
size_t k = 0; k < slice.size(); ++k) {
2043 data[k] = vec[slice[k]];
2051AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
2053 vector<vector<T>> data(slice.size());
2054 const auto& vec = extra.asVector<vector<T>>();
2055 for (
size_t k = 0; k < slice.size(); ++k) {
2056 data[k] = vec[slice[k]];
2064void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2066 T value = data.as<T>();
2067 if (extra.isVector<T>()) {
2068 auto& vec = extra.asVector<T>();
2069 for (
size_t k = 0; k < slice.size(); ++k) {
2070 vec[slice[k]] = value;
2073 throw CanteraError(
"SolutionArray::setScalar",
2074 "Incompatible input data: unable to assign '{}' data to '{}'.",
2075 data.type_str(), extra.type_str());
2080void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2082 size_t size = slice.size();
2083 if (extra.vectorSize() == size && data.vectorSize() == size) {
2087 if (extra.matrixShape().first == size && data.vectorSize() == size) {
2091 if (extra.type_str() != data.type_str()) {
2093 throw CanteraError(
"SolutionArray::setSingle",
2094 "Incompatible types: expected '{}' but received '{}'.",
2095 extra.type_str(), data.type_str());
2097 const auto& vData = data.asVector<T>();
2098 if (vData.size() != size) {
2099 throw CanteraError(
"SolutionArray::setSingle",
2100 "Invalid input data size: expected {} entries but received {}.",
2101 size, vData.size());
2103 auto& vec = extra.asVector<T>();
2104 for (
size_t k = 0; k < size; ++k) {
2105 vec[slice[k]] = vData[k];
2110void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2112 if (!data.isMatrix<T>()) {
2113 throw CanteraError(
"SolutionArray::setMulti",
2114 "Invalid input data shape: inconsistent number of columns.");
2116 size_t size = slice.size();
2117 auto [rows, cols] = data.matrixShape();
2118 if (extra.matrixShape().first == size && rows == size) {
2122 if (extra.vectorSize() == size && rows == size) {
2126 if (extra.type_str() != data.type_str()) {
2128 throw CanteraError(
"SolutionArray::setMulti",
2129 "Incompatible types: expected '{}' but received '{}'.",
2130 extra.type_str(), data.type_str());
2133 throw CanteraError(
"SolutionArray::setMulti",
2134 "Invalid input data shape: expected {} rows but received {}.",
2137 if (extra.matrixShape().second != cols) {
2138 throw CanteraError(
"SolutionArray::setMulti",
2139 "Invalid input data shape: expected {} columns but received {}.",
2140 extra.matrixShape().second, cols);
2142 const auto& vData = data.asVector<vector<T>>();
2143 auto& vec = extra.asVector<vector<T>>();
2144 for (
size_t k = 0; k < slice.size(); ++k) {
2145 vec[slice[k]] = vData[k];
2150void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2153 if (value.is<
void>()) {
2154 defaultValue = vector<T>(1)[0];
2156 defaultValue = value.as<T>();
2158 extra.asVector<T>().resize(size, defaultValue);
2162void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2164 vector<T> defaultValue;
2165 if (value.is<
void>()) {
2166 defaultValue = vector<T>(extra.matrixShape().second);
2168 defaultValue = value.as<vector<T>>();
2170 extra.asVector<vector<T>>().resize(size, defaultValue);
2174void resetSingle(AnyValue& extra,
const vector<int>& slice)
2176 T defaultValue = vector<T>(1)[0];
2177 vector<T>& data = extra.asVector<T>();
2178 for (
size_t k = 0; k < slice.size(); ++k) {
2179 data[slice[k]] = defaultValue;
2184void resetMulti(AnyValue& extra,
const vector<int>& slice)
2186 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2187 vector<vector<T>>& data = extra.asVector<vector<T>>();
2188 for (
size_t k = 0; k < slice.size(); ++k) {
2189 data[slice[k]] = defaultValue;
2194void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2196 extra.asVector<T>()[loc] = value.as<T>();
2200void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2202 const auto& value = data.asVector<T>();
2203 auto& vec = extra.asVector<vector<T>>();
2204 if (value.size() != vec[loc].size()) {
2205 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2206 "New element size {} does not match existing column size {}.",
2207 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.
vector< string > tokenizePath(const string &in_val)
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...