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_dataSize)
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 throw 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>()) {
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 || loc_ >= m_size) {
808 throw IndexError(
"SolutionArray::setLoc",
"indices", loc_, m_size);
809 }
else if (
static_cast<size_t>(m_active[loc_]) == m_loc) {
812 m_loc =
static_cast<size_t>(m_active[loc_]);
814 size_t nState = m_sol->thermo()->stateSize();
815 m_sol->thermo()->restoreState(
816 span<const double>(m_data->data() + m_loc * m_stride, nState));
820void SolutionArray::updateState(
int loc)
823 size_t nState = m_sol->thermo()->stateSize();
824 m_sol->thermo()->saveState(
825 span<double>(m_data->data() + m_loc * m_stride, nState));
828vector<double> SolutionArray::getState(
int loc)
const
832 if (loc < 0 ||
static_cast<size_t>(loc) >= m_size) {
833 throw IndexError(
"SolutionArray::getState",
"indices",
834 static_cast<size_t>(loc), m_size);
836 size_t index =
static_cast<size_t>(m_active[loc]);
837 size_t nState = m_sol->thermo()->stateSize();
838 const double* data = m_data->data() + index * m_stride;
839 return vector<double>(data, data + nState);
842void SolutionArray::setState(
int loc,
const vector<double>& state)
844 size_t nState = m_sol->thermo()->stateSize();
845 if (state.size() != nState) {
847 "Expected array to have length {}, but received an array of length {}.",
848 nState, state.size());
851 m_sol->thermo()->restoreState(state);
852 m_sol->thermo()->saveState(
853 span<double>(m_data->data() + m_loc * m_stride, nState));
856void SolutionArray::normalize() {
857 auto phase = m_sol->thermo();
858 auto nativeState = phase->nativeState();
859 if (nativeState.size() < 3) {
862 size_t nState = phase->stateSize();
863 vector<double> out(nState);
864 if (nativeState.count(
"Y")) {
865 size_t offset = nativeState[
"Y"];
866 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
868 auto* data = m_data->data() + m_loc * m_stride +
offset;
869 phase->setMassFractions(span<const double>(data, phase->nSpecies()));
870 m_sol->thermo()->saveState(out);
873 }
else if (nativeState.count(
"X")) {
874 size_t offset = nativeState[
"X"];
875 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
877 auto* data = m_data->data() + m_loc * m_stride +
offset;
878 phase->setMoleFractions(span<const double>(data, phase->nSpecies()));
879 m_sol->thermo()->saveState(out);
884 "Not implemented for mode '{}'.", phase->nativeMode());
888AnyMap SolutionArray::getAuxiliary(
int loc)
const
892 if (loc < 0 ||
static_cast<size_t>(loc) >= m_size) {
893 throw IndexError(
"SolutionArray::getAuxiliary",
"indices",
894 static_cast<size_t>(loc), m_size);
896 size_t index =
static_cast<size_t>(m_active[loc]);
898 for (
const auto& [key, extra] : *m_extra) {
899 if (extra.is<
void>()) {
901 }
else if (extra.isVector<
long int>()) {
902 out[key] = extra.asVector<
long int>()[index];
903 }
else if (extra.isVector<
double>()) {
904 out[key] = extra.asVector<
double>()[index];
905 }
else if (extra.isVector<
string>()) {
906 out[key] = extra.asVector<
string>()[index];
907 }
else if (extra.isVector<vector<long int>>()) {
908 out[key] = extra.asVector<vector<long int>>()[index];
909 }
else if (extra.isVector<vector<double>>()) {
910 out[key] = extra.asVector<vector<double>>()[index];
911 }
else if (extra.isVector<vector<string>>()) {
912 out[key] = extra.asVector<vector<string>>()[index];
915 "Unable to retrieve data for component '{}' with type '{}'.",
916 key, extra.type_str());
922void SolutionArray::setAuxiliary(
int loc,
const AnyMap& data)
925 for (
const auto& [name, value] : data) {
926 if (!m_extra->count(name)) {
928 "Unknown auxiliary component '{}'.", name);
930 auto& extra = m_extra->at(name);
931 if (extra.is<
void>()) {
932 if (m_dataSize > 1) {
934 "Unable to set location for type '{}': "
935 "component is not initialized.", name);
937 _initExtra(name, value);
941 if (extra.isVector<
long int>()) {
942 setAuxiliarySingle<long int>(m_loc, extra, value);
943 }
else if (extra.isVector<
double>()) {
944 setAuxiliarySingle<double>(m_loc, extra, value);
945 }
else if (extra.isVector<
string>()) {
946 setAuxiliarySingle<string>(m_loc, extra, value);
947 }
else if (extra.isVector<vector<long int>>()) {
948 setAuxiliaryMulti<long int>(m_loc, extra, value);
949 }
else if (extra.isVector<vector<double>>()) {
950 setAuxiliaryMulti<double>(m_loc, extra, value);
951 }
else if (extra.isVector<vector<string>>()) {
952 setAuxiliaryMulti<string>(m_loc, extra, value);
955 "Unable to set entry for type '{}'.", extra.type_str());
960 "Encountered incompatible value for component '{}':\n{}",
966AnyMap preamble(
const string& desc)
970 data[
"description"] = desc;
972 data[
"generator"] =
"Cantera SolutionArray";
973 data[
"cantera-version"] = CANTERA_VERSION;
974 data[
"git-commit"] = gitCommit();
979 struct tm* newtime = localtime(&aclock);
980 data[
"date"] = stripnonprint(asctime(newtime));
985AnyMap& openField(AnyMap& root,
const string& name)
995 for (
auto& field : tokens) {
998 if (sub.hasKey(field) && !sub[field].is<AnyMap>()) {
999 throw CanteraError(
"openField",
1000 "Encountered invalid existing field '{}'.", path);
1001 }
else if (!sub.hasKey(field)) {
1002 sub[field] = AnyMap();
1004 ptr = &sub[field].as<AnyMap>();
1009void SolutionArray::writeHeader(
const string& fname,
const string& name,
1010 const string& desc,
bool overwrite)
1016 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1024void SolutionArray::writeHeader(
AnyMap& root,
const string& name,
1025 const string& desc,
bool overwrite)
1027 AnyMap& data = openField(root, name);
1028 if (!data.
empty()) {
1031 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1035 data.
update(preamble(desc));
1038void SolutionArray::writeEntry(
const string& fname,
bool overwrite,
const string& basis)
1040 if (apiNdim() != 1) {
1042 "Tabular output of CSV data only works for 1D SolutionArray objects.");
1044 set<string> speciesNames;
1045 for (
const auto& species : m_sol->thermo()->speciesNames()) {
1046 speciesNames.insert(species);
1050 const auto& nativeState = m_sol->thermo()->nativeState();
1051 mole = nativeState.find(
"X") != nativeState.end();
1052 }
else if (basis ==
"X" || basis ==
"mole") {
1054 }
else if (basis ==
"Y" || basis ==
"mass") {
1058 "Invalid species basis '{}'.", basis);
1061 auto names = componentNames();
1062 size_t last = names.size() - 1;
1063 vector<AnyValue> components;
1064 vector<bool> isSpecies;
1065 fmt::memory_buffer header;
1066 for (
const auto& key : names) {
1069 if (speciesNames.find(key) == speciesNames.end()) {
1071 isSpecies.push_back(
false);
1072 components.emplace_back(getComponent(key));
1073 col = components.size() - 1;
1074 if (!components[col].isVector<double>() &&
1075 !components[col].isVector<
long int>() &&
1076 !components[col].isVector<string>())
1079 "Multi-dimensional column '{}' is not supported for CSV output.",
1084 isSpecies.push_back(
true);
1085 components.emplace_back(
AnyValue());
1086 col = components.size() - 1;
1088 label =
"X_" + label;
1090 label =
"Y_" + label;
1093 if (label.find(
"\"") != string::npos || label.find(
"\n") != string::npos) {
1095 "Detected column name containing double quotes or line feeds: '{}'.",
1098 string sep = (col == last) ?
"" :
",";
1099 if (label.find(
",") != string::npos) {
1100 fmt_append(header,
"\"{}\"{}", label, sep);
1102 fmt_append(header,
"{}{}", label, sep);
1107 if (std::ifstream(fname).good()) {
1110 "File '{}' already exists; use option 'overwrite' to replace CSV file.",
1113 std::remove(fname.c_str());
1115 std::ofstream output(fname);
1116 output << to_string(header) << std::endl;
1118 size_t maxLen =
npos;
1119 vector<double> buf(speciesNames.size(), 0.);
1120 for (
int row = 0; row < static_cast<int>(m_size); row++) {
1121 fmt::memory_buffer line;
1122 if (maxLen !=
npos) {
1123 line.reserve(maxLen);
1127 m_sol->thermo()->getMoleFractions(buf);
1129 m_sol->thermo()->getMassFractions(buf);
1133 for (
size_t col = 0; col < components.size(); col++) {
1134 string sep = (col == last) ?
"" :
",";
1135 if (isSpecies[col]) {
1136 fmt_append(line,
"{:.9g}{}", buf[idx++], sep);
1138 auto& data = components[col];
1139 if (data.isVector<
double>()) {
1140 fmt_append(line,
"{:.9g}{}", data.asVector<
double>()[row], sep);
1141 }
else if (data.isVector<
long int>()) {
1142 fmt_append(line,
"{}{}", data.asVector<
long int>()[row], sep);
1143 }
else if (data.isVector<
bool>()) {
1144 fmt_append(line,
"{}{}",
1145 static_cast<bool>(data.asVector<
bool>()[row]), sep);
1147 auto value = data.asVector<
string>()[row];
1148 if (value.find(
"\"") != string::npos ||
1149 value.find(
"\n") != string::npos)
1152 "Detected value containing double quotes or line feeds: "
1155 if (value.find(
",") != string::npos) {
1156 fmt_append(line,
"\"{}\"{}", value, sep);
1158 fmt_append(line,
"{}{}", value, sep);
1163 output << to_string(line) << std::endl;
1164 maxLen = std::max(maxLen, line.size());
1168void SolutionArray::writeEntry(
const string& fname,
const string& name,
1169 const string& sub,
bool overwrite,
int compression)
1173 "Group name specifying root location must not be empty.");
1177 "Unable to save sliced data.");
1192 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1199 if (apiNdim() == 1) {
1200 more[
"size"] = int(m_dataSize);
1202 more[
"api-shape"] = m_apiShape;
1204 if (!m_meta.hasKey(
"transport-model") && m_sol->transport()) {
1205 more[
"transport-model"] = m_sol->transportModel();
1207 more[
"components"] = componentNames();
1213 const auto& nativeState = m_sol->thermo()->nativeState();
1214 size_t nSpecies = m_sol->thermo()->nSpecies();
1215 for (
auto& [key,
offset] : nativeState) {
1216 if (key ==
"X" || key ==
"Y") {
1217 vector<vector<double>> prop;
1218 for (
size_t i = 0; i < m_size; i++) {
1219 size_t first =
offset + i * m_stride;
1220 prop.emplace_back(m_data->begin() + first,
1221 m_data->begin() + first + nSpecies);
1227 auto data = getComponent(key);
1232 for (
const auto& [key, value] : *m_extra) {
1233 if (isSimpleVector(value)) {
1235 }
else if (value.is<
void>()) {
1239 "Unable to save component '{}' with data type {}.",
1240 key, value.type_str());
1245void SolutionArray::writeEntry(
AnyMap& root,
const string& name,
const string& sub,
1250 "Field name specifying root location must not be empty.");
1254 "Unable to save sliced data.");
1262 AnyMap& data = openField(root, path);
1263 bool preexisting = !data.
empty();
1264 if (preexisting && !overwrite) {
1266 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1268 if (apiNdim() == 1) {
1269 data[
"size"] = int(m_dataSize);
1271 data[
"api-shape"] = m_apiShape;
1273 if (m_sol->transport() && m_sol->transportModel() !=
"none") {
1274 data[
"transport-model"] = m_sol->transportModel();
1279 data[
"components"] = componentNames();
1282 for (
auto& [_, key] : *m_order) {
1283 data[key] = m_extra->
at(key);
1286 auto phase = m_sol->thermo();
1289 data[
"temperature"] = phase->temperature();
1290 data[
"pressure"] = phase->pressure();
1291 auto surf = std::dynamic_pointer_cast<SurfPhase>(phase);
1292 auto nSpecies = phase->nSpecies();
1293 vector<double> values(nSpecies);
1295 surf->getCoverages(values);
1297 phase->getMassFractions(values);
1300 for (
size_t k = 0; k < nSpecies; k++) {
1301 if (values[k] != 0.0) {
1302 items[phase->speciesName(k)] = values[k];
1306 data[
"coverages"] = std::move(items);
1308 data[
"mass-fractions"] = std::move(items);
1310 }
else if (m_size > 1) {
1311 for (
auto& code : phase->nativeMode()) {
1312 string name(1, code);
1313 if (name ==
"X" || name ==
"Y") {
1314 for (
auto& spc : phase->speciesNames()) {
1315 data[spc] = getComponent(spc);
1317 data[
"basis"] = name ==
"X" ?
"mole" :
"mass";
1319 data[name] = getComponent(name);
1324 static bool reg = AnyMap::addOrderingRules(
"SolutionArray",
1325 {{
"head",
"type"}, {
"head",
"size"}, {
"head",
"basis"}});
1327 data[
"__type__"] =
"SolutionArray";
1336void SolutionArray::append(
const vector<double>& state,
const AnyMap& extra)
1338 if (apiNdim() > 1) {
1340 "Unable to append multi-dimensional arrays.");
1346 setState(pos, state);
1347 setAuxiliary(pos, extra);
1355void SolutionArray::save(
const string& fname,
const string& name,
const string& sub,
1356 const string& desc,
bool overwrite,
int compression,
1357 const string& basis)
1361 "Unable to save sliced data.");
1363 size_t dot = fname.find_last_of(
".");
1365 if (extension ==
"csv") {
1368 "Parameter 'name' not used for CSV output.");
1370 writeEntry(fname, overwrite, basis);
1375 "Argument 'basis' is not used for HDF or YAML output.", basis);
1377 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1378 writeHeader(fname, name, desc, overwrite);
1379 writeEntry(fname, name, sub,
true, compression);
1382 if (extension ==
"yaml" || extension ==
"yml") {
1385 std::ifstream file(fname);
1386 if (file.good() && file.peek() != std::ifstream::traits_type::eof()) {
1387 data = AnyMap::fromYamlFile(fname);
1389 writeHeader(data, name, desc, overwrite);
1390 writeEntry(data, name, sub,
true);
1393 std::ofstream out(fname);
1394 out << data.toYamlString();
1395 AnyMap::clearCachedFile(fname);
1399 "Unknown file extension '{}'.", extension);
1402AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1409const AnyMap& locateField(
const AnyMap& root,
const string& name)
1416 vector<string> tokens = tokenizePath(name);
1417 const AnyMap* ptr = &root;
1419 for (
auto& field : tokens) {
1420 path +=
"/" + field;
1421 const AnyMap& sub = *ptr;
1422 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1423 throw CanteraError(
"SolutionArray::locateField",
1424 "No field or solution with name '{}'.", path);
1426 ptr = &sub[field].as<AnyMap>();
1433 auto sub = locateField(root, name);
1435 for (
const auto& [key, value] : sub) {
1436 if (!sub[key].is<AnyMap>()) {
1437 header[key] = value;
1443AnyMap SolutionArray::restore(
const string& fname,
1444 const string& name,
const string& sub)
1446 size_t dot = fname.find_last_of(
".");
1449 if (extension ==
"csv") {
1451 "CSV import not implemented; if using Python, data can be imported via "
1452 "'read_csv' instead.");
1454 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1455 readEntry(fname, name, sub);
1456 header = readHeader(fname, name);
1457 }
else if (extension ==
"yaml" || extension ==
"yml") {
1458 const AnyMap& root = AnyMap::fromYamlFile(fname);
1459 readEntry(root, name, sub);
1460 header = readHeader(root, name);
1463 "Unknown file extension '{}'; supported extensions include "
1464 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1469void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1471 if (!m_extra->count(name)) {
1473 "Component '{}' does not exist.", name);
1475 auto& extra = (*m_extra)[name];
1476 if (!extra.is<
void>()) {
1478 "Component '{}' is already initialized.", name);
1481 if (value.
is<
long int>()) {
1482 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1483 }
else if (value.
is<
double>()) {
1484 extra = vector<double>(m_dataSize, value.
as<
double>());
1485 }
else if (value.
is<
string>()) {
1486 extra = vector<string>(m_dataSize, value.
as<
string>());
1487 }
else if (value.
isVector<
long int>()) {
1488 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1489 }
else if (value.
isVector<
double>()) {
1490 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1491 }
else if (value.
isVector<
string>()) {
1492 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1493 }
else if (value.
is<
void>()) {
1498 "Unable to initialize component '{}' with type '{}'.",
1504 "Encountered incompatible value for initializing component '{}':\n{}",
1509void SolutionArray::_resizeExtra(
const string& name,
const AnyValue& value)
1511 if (!m_extra->count(name)) {
1513 "Component '{}' does not exist.", name);
1515 auto& extra = (*m_extra)[name];
1516 if (extra.is<
void>()) {
1522 if (extra.isVector<
long int>()) {
1523 resizeSingle<long int>(extra, m_dataSize, value);
1524 }
else if (extra.isVector<
double>()) {
1525 resizeSingle<double>(extra, m_dataSize, value);
1526 }
else if (extra.isVector<
string>()) {
1527 resizeSingle<string>(extra, m_dataSize, value);
1528 }
else if (extra.isVector<vector<double>>()) {
1529 resizeMulti<double>(extra, m_dataSize, value);
1530 }
else if (extra.isVector<vector<long int>>()) {
1531 resizeMulti<long int>(extra, m_dataSize, value);
1532 }
else if (extra.isVector<vector<string>>()) {
1533 resizeMulti<string>(extra, m_dataSize, value);
1536 "Unable to resize using type '{}'.", extra.type_str());
1541 "Encountered incompatible value for resizing component '{}':\n{}",
1546void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1548 if (!m_extra->count(name)) {
1550 "Extra component '{}' does not exist.", name);
1553 auto& extra = m_extra->at(name);
1554 if (data.
is<
void>() && !_isSliced()) {
1561 if (extra.is<
void>()) {
1564 "Unable to replace '{}' for sliced data.", name);
1574 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1579 "Unable to initialize '{}' with empty array when SolutionArray is not "
1582 _initExtra(name, data);
1583 _resizeExtra(name, data);
1587 if (data.
is<
long int>()) {
1588 setScalar<long int>(extra, data, m_active);
1589 }
else if (data.
is<
double>()) {
1590 setScalar<double>(extra, data, m_active);
1591 }
else if (data.
is<
string>()) {
1592 setScalar<string>(extra, data, m_active);
1593 }
else if (data.
isVector<
long int>()) {
1594 setSingle<long int>(extra, data, m_active);
1595 }
else if (data.
isVector<
double>()) {
1596 setSingle<double>(extra, data, m_active);
1597 }
else if (data.
isVector<
string>()) {
1598 setSingle<string>(extra, data, m_active);
1599 }
else if (data.
isVector<vector<long int>>()) {
1600 setMulti<long int>(extra, data, m_active);
1601 }
else if (data.
isVector<vector<double>>()) {
1602 setMulti<double>(extra, data, m_active);
1603 }
else if (data.
isVector<vector<string>>()) {
1604 setMulti<string>(extra, data, m_active);
1607 "Unable to set sliced data for component '{}' with type '{}'.",
1612string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1616 const auto& nativeState = m_sol->thermo()->nativeState();
1617 bool usesNativeState =
false;
1618 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1620 for (
const auto& item : m_sol->thermo()->fullStates()) {
1623 usesNativeState =
true;
1624 for (
size_t i = 0; i < item.size(); i++) {
1626 name = string(1, item[i]);
1627 if (surf && (name ==
"X" || name ==
"Y")) {
1630 usesNativeState =
false;
1633 if (names.count(name)) {
1635 usesNativeState &= nativeState.count(name) > 0;
1636 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1638 usesNativeState &= nativeState.count(name) > 0;
1645 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1650 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1652 return "legacySurf";
1654 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1656 return "legacyInlet";
1659 "Detected incomplete thermodynamic information. Full states for a '{}' "
1660 "phase\nmay be defined by the following modes:\n'{}'\n"
1661 "Available data are: '{}'", m_sol->thermo()->type(),
1662 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1664 if (usesNativeState && native) {
1670set<string> SolutionArray::_stateProperties(
1671 const string& mode,
bool alias)
1674 if (mode ==
"native") {
1675 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1676 states.insert(alias ? aliasMap.at(name) : name);
1679 for (
const auto& m : mode) {
1680 const string name = string(1, m);
1681 states.insert(alias ? aliasMap.at(name) : name);
1688string getName(
const set<string>& names,
const string& name)
1690 if (names.count(name)) {
1693 const auto& alias = aliasMap.at(name);
1694 if (names.count(alias)) {
1700void SolutionArray::readEntry(
const string& fname,
const string& name,
1706 "Group name specifying root location must not be empty.");
1709 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1711 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1717 "Group name specifying data entry is empty.");
1720 auto [size, names] = file.
contents(path);
1722 if (m_meta.hasKey(
"size")) {
1724 resize(m_meta[
"size"].as<long int>());
1725 m_meta.erase(
"size");
1726 }
else if (m_meta.hasKey(
"api-shape")) {
1728 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1729 m_meta.erase(
"api-shape");
1732 resize(
static_cast<int>(size));
1740 string mode = _detectMode(names);
1741 set<string> states = _stateProperties(mode);
1742 if (states.count(
"C")) {
1743 if (names.count(
"X")) {
1746 mode = mode.substr(0, 2) +
"X";
1747 }
else if (names.count(
"Y")) {
1750 mode = mode.substr(0, 2) +
"Y";
1755 size_t nSpecies = m_sol->thermo()->nSpecies();
1756 size_t nState = m_sol->thermo()->stateSize();
1757 const auto& nativeStates = m_sol->thermo()->nativeState();
1758 if (mode ==
"native") {
1760 for (
const auto& [name,
offset] : nativeStates) {
1761 if (name ==
"X" || name ==
"Y") {
1763 data = file.
readData(path, name, m_size, nSpecies);
1764 auto prop = data.
asVector<vector<double>>();
1765 for (
size_t i = 0; i < m_dataSize; i++) {
1766 std::copy(prop[i].begin(), prop[i].end(),
1767 m_data->data() +
offset + i * m_stride);
1771 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1772 setComponent(name, data);
1775 }
else if (mode ==
"TPX") {
1777 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1778 vector<double> T = std::move(data.
asVector<
double>());
1779 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1780 vector<double> P = std::move(data.
asVector<
double>());
1781 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1782 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1783 for (
size_t i = 0; i < m_dataSize; i++) {
1784 m_sol->thermo()->setMoleFractions_NoNorm(X[i]);
1785 m_sol->thermo()->setState_TP(T[i], P[i]);
1786 m_sol->thermo()->saveState(
1787 span<double>(m_data->data() + i * m_stride, nState));
1789 }
else if (mode ==
"TDX") {
1791 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1792 vector<double> T = std::move(data.
asVector<
double>());
1793 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1794 vector<double> D = std::move(data.
asVector<
double>());
1795 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1796 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1797 for (
size_t i = 0; i < m_dataSize; i++) {
1798 m_sol->thermo()->setMoleFractions_NoNorm(X[i]);
1799 m_sol->thermo()->setState_TD(T[i], D[i]);
1800 m_sol->thermo()->saveState(
1801 span<double>(m_data->data() + i * m_stride, nState));
1803 }
else if (mode ==
"TPY") {
1805 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1806 vector<double> T = std::move(data.
asVector<
double>());
1807 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1808 vector<double> P = std::move(data.
asVector<
double>());
1809 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1810 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1811 for (
size_t i = 0; i < m_dataSize; i++) {
1812 m_sol->thermo()->setMassFractions_NoNorm(Y[i]);
1813 m_sol->thermo()->setState_TP(T[i], P[i]);
1814 m_sol->thermo()->saveState(
1815 span<double>(m_data->data() + i * m_stride, nState));
1817 }
else if (mode ==
"legacySurf") {
1820 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1821 vector<double> T = std::move(data.
asVector<
double>());
1822 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1823 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1824 for (
size_t i = 0; i < m_dataSize; i++) {
1825 m_sol->thermo()->setMoleFractions_NoNorm(X[i]);
1826 m_sol->thermo()->setTemperature(T[i]);
1827 m_sol->thermo()->saveState(
1828 span<double>(m_data->data() + i * m_stride, nState));
1831 "Detected legacy HDF format with incomplete state information\nfor name "
1832 "'{}' (pressure missing).", path);
1833 }
else if (mode ==
"") {
1835 "Data are not consistent with full state modes.");
1838 "Import of '{}' data is not supported.", mode);
1842 if (m_meta.hasKey(
"components")) {
1843 const auto& components = m_meta[
"components"].asVector<
string>();
1845 for (
const auto& name : components) {
1846 if (hasComponent(name,
false) || name ==
"X" || name ==
"Y") {
1850 if (reverseAliasMap.count(name)) {
1851 _name = reverseAliasMap.at(name);
1853 addExtra(_name, back);
1855 data = file.
readData(path, name, m_dataSize);
1856 setComponent(_name, data);
1859 m_meta.erase(
"components");
1862 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1863 for (
const auto& name : names) {
1864 if (!hasComponent(name,
false) && name !=
"X" && name !=
"Y") {
1866 if (reverseAliasMap.count(name)) {
1867 _name = reverseAliasMap.at(name);
1871 data = file.
readData(path, name, m_dataSize);
1872 setComponent(_name, data);
1877 if (m_meta.hasKey(
"transport-model")) {
1878 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1882void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1886 "Field name specifying root location must not be empty.");
1888 auto path = locateField(root, name);
1889 if (path.hasKey(
"generator") && sub !=
"") {
1891 path = locateField(root, name +
"/" + sub);
1892 }
else if (sub ==
"" && path.hasKey(
"data")) {
1894 path = locateField(root, name +
"/data");
1899 if (path.hasKey(
"size")) {
1901 resize(path[
"size"].asInt());
1902 }
else if (path.hasKey(
"api-shape")) {
1904 auto& shape = path[
"api-shape"].asVector<
long int>();
1908 size = path.getInt(
"points", 0);
1909 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1913 resize(
static_cast<int>(size));
1918 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1919 set<string> names = path.keys();
1920 size_t nState = m_sol->thermo()->stateSize();
1921 if (m_dataSize == 0) {
1923 }
else if (m_dataSize == 1) {
1925 string mode = _detectMode(names,
false);
1926 if (mode ==
"TPY") {
1927 double T = path[getName(names,
"T")].asDouble();
1928 double P = path[getName(names,
"P")].asDouble();
1929 auto Y = path[
"mass-fractions"].asMap<
double>();
1930 m_sol->thermo()->setState_TPY(T, P, Y);
1931 }
else if (mode ==
"TPC") {
1932 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1933 double T = path[getName(names,
"T")].asDouble();
1934 double P = path[
"pressure"].asDouble();
1935 m_sol->thermo()->setState_TP(T, P);
1936 auto cov = path[
"coverages"].asMap<
double>();
1937 surf->setCoveragesByName(cov);
1938 }
else if (mode ==
"legacyInlet") {
1941 double T = path[getName(names,
"T")].asDouble();
1942 auto Y = path[
"mass-fractions"].asMap<
double>();
1943 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1945 "Detected legacy YAML format with incomplete state information\n"
1946 "for name '{}' (pressure missing).", name +
"/" + sub);
1947 }
else if (mode ==
"") {
1949 "Data are not consistent with full state modes.");
1952 "Import of '{}' data is not supported.", mode);
1954 m_sol->thermo()->saveState(span<double>(m_data->data(), nState));
1955 auto props = _stateProperties(mode,
true);
1956 exclude.insert(props.begin(), props.end());
1959 if (path.hasKey(
"components")) {
1960 const auto& components = path[
"components"].asVector<
string>();
1962 for (
const auto& name : components) {
1964 if (hasComponent(name,
false)) {
1967 if (reverseAliasMap.count(name)) {
1968 _name = reverseAliasMap.at(name);
1970 addExtra(_name, back);
1972 setComponent(_name, path[name]);
1973 exclude.insert(name);
1977 for (
const auto& [name, value] : path) {
1978 if (value.isVector<
double>()) {
1979 const vector<double>& data = value.asVector<
double>();
1980 if (data.size() == m_dataSize) {
1982 if (!hasComponent(name,
false)) {
1983 if (reverseAliasMap.count(name)) {
1984 _name = reverseAliasMap.
at(name);
1988 setComponent(_name, value);
1989 exclude.insert(name);
1996 const auto& nativeState = m_sol->thermo()->nativeState();
1998 set<string> missingProps;
1999 for (
const auto& [name,
offset] : nativeState) {
2000 if (exclude.count(name)) {
2003 missingProps.insert(name);
2007 set<string> TY = {
"T",
"Y"};
2008 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
2010 double P = path[
"pressure"].asDouble();
2011 const size_t offset_T = nativeState.find(
"T")->second;
2012 const size_t offset_D = nativeState.find(
"D")->second;
2013 const size_t offset_Y = nativeState.find(
"Y")->second;
2014 for (
size_t i = 0; i < m_dataSize; i++) {
2015 double T = (*m_data)[offset_T + i * m_stride];
2016 span<double> Y(m_data->data() + offset_Y + i * m_stride,
2017 m_sol->thermo()->nSpecies());
2018 m_sol->thermo()->setState_TPY(T, P, Y);
2019 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
2021 }
else if (missingProps.size()) {
2023 "Incomplete state information: missing '{}'.",
2024 ba::join(missingProps,
"', '"));
2029 for (
const auto& [name, value] : path) {
2030 if (!exclude.count(name)) {
2031 m_meta[name] = value;
2035 if (m_meta.hasKey(
"transport-model")) {
2036 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
2045 vector<T> data(slice.size());
2046 const auto& vec = extra.
asVector<T>();
2047 for (
size_t k = 0; k < slice.size(); ++k) {
2048 data[k] = vec[slice[k]];
2056AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
2058 vector<vector<T>> data(slice.size());
2059 const auto& vec = extra.asVector<vector<T>>();
2060 for (
size_t k = 0; k < slice.size(); ++k) {
2061 data[k] = vec[slice[k]];
2069void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2071 T value = data.as<T>();
2072 if (extra.isVector<T>()) {
2073 auto& vec = extra.asVector<T>();
2074 for (
size_t k = 0; k < slice.size(); ++k) {
2075 vec[slice[k]] = value;
2078 throw CanteraError(
"SolutionArray::setScalar",
2079 "Incompatible input data: unable to assign '{}' data to '{}'.",
2080 data.type_str(), extra.type_str());
2085void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2087 size_t size = slice.size();
2088 if (extra.vectorSize() == size && data.vectorSize() == size) {
2092 if (extra.matrixShape().first == size && data.vectorSize() == size) {
2096 if (extra.type_str() != data.type_str()) {
2098 throw CanteraError(
"SolutionArray::setSingle",
2099 "Incompatible types: expected '{}' but received '{}'.",
2100 extra.type_str(), data.type_str());
2102 const auto& vData = data.asVector<T>();
2103 if (vData.size() != size) {
2104 throw CanteraError(
"SolutionArray::setSingle",
2105 "Invalid input data size: expected {} entries but received {}.",
2106 size, vData.size());
2108 auto& vec = extra.asVector<T>();
2109 for (
size_t k = 0; k < size; ++k) {
2110 vec[slice[k]] = vData[k];
2115void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2117 if (!data.isMatrix<T>()) {
2118 throw CanteraError(
"SolutionArray::setMulti",
2119 "Invalid input data shape: inconsistent number of columns.");
2121 size_t size = slice.size();
2122 auto [rows, cols] = data.matrixShape();
2123 if (extra.matrixShape().first == size && rows == size) {
2127 if (extra.vectorSize() == size && rows == size) {
2131 if (extra.type_str() != data.type_str()) {
2133 throw CanteraError(
"SolutionArray::setMulti",
2134 "Incompatible types: expected '{}' but received '{}'.",
2135 extra.type_str(), data.type_str());
2138 throw CanteraError(
"SolutionArray::setMulti",
2139 "Invalid input data shape: expected {} rows but received {}.",
2142 if (extra.matrixShape().second != cols) {
2143 throw CanteraError(
"SolutionArray::setMulti",
2144 "Invalid input data shape: expected {} columns but received {}.",
2145 extra.matrixShape().second, cols);
2147 const auto& vData = data.asVector<vector<T>>();
2148 auto& vec = extra.asVector<vector<T>>();
2149 for (
size_t k = 0; k < slice.size(); ++k) {
2150 vec[slice[k]] = vData[k];
2155void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2158 if (value.is<
void>()) {
2159 defaultValue = vector<T>(1)[0];
2161 defaultValue = value.as<T>();
2163 extra.asVector<T>().resize(size, defaultValue);
2167void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2169 vector<T> defaultValue;
2170 if (value.is<
void>()) {
2171 defaultValue = vector<T>(extra.matrixShape().second);
2173 defaultValue = value.as<vector<T>>();
2175 extra.asVector<vector<T>>().resize(size, defaultValue);
2179void resetSingle(AnyValue& extra,
const vector<int>& slice)
2181 T defaultValue = vector<T>(1)[0];
2182 vector<T>& data = extra.asVector<T>();
2183 for (
size_t k = 0; k < slice.size(); ++k) {
2184 data[slice[k]] = defaultValue;
2189void resetMulti(AnyValue& extra,
const vector<int>& slice)
2191 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2192 vector<vector<T>>& data = extra.asVector<vector<T>>();
2193 for (
size_t k = 0; k < slice.size(); ++k) {
2194 data[slice[k]] = defaultValue;
2199void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2201 extra.asVector<T>()[loc] = value.as<T>();
2205void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2207 const auto& value = data.asVector<T>();
2208 auto& vec = extra.asVector<vector<T>>();
2209 if (value.size() != vec[loc].size()) {
2210 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2211 "New element size {} does not match existing column size {}.",
2212 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...