16#include <boost/algorithm/string.hpp>
17#include <boost/range/adaptor/reversed.hpp>
22namespace ba = boost::algorithm;
24const std::map<std::string, std::string> aliasMap = {
28 {
"Y",
"mass-fractions"},
29 {
"X",
"mole-fractions"},
31 {
"U",
"specific-internal-energy"},
32 {
"V",
"specific-volume"},
33 {
"H",
"specific-enthalpy"},
34 {
"S",
"specific-entropy"},
35 {
"Q",
"vapor-fraction"},
41SolutionArray::SolutionArray(
const shared_ptr<Solution>& sol,
42 int size,
const AnyMap& meta)
48 if (!m_sol || !m_sol->thermo()) {
49 throw CanteraError(
"SolutionArray::SolutionArray",
50 "Unable to create SolutionArray from invalid Solution object.");
52 m_stride = m_sol->thermo()->stateSize();
53 m_sol->thermo()->addSpeciesLock();
54 m_data = make_shared<vector<double>>(m_dataSize * m_stride, 0.);
55 m_extra = make_shared<map<string, AnyValue>>();
56 m_order = make_shared<map<int, string>>();
57 for (
size_t i = 0; i < m_dataSize; ++i) {
58 m_active.push_back(
static_cast<int>(i));
62 m_apiShape[0] =
static_cast<long>(m_dataSize);
65SolutionArray::SolutionArray(
const SolutionArray& other,
66 const vector<int>& selected)
68 , m_size(selected.size())
69 , m_dataSize(other.m_data->size())
70 , m_stride(other.m_stride)
71 , m_data(other.m_data)
72 , m_extra(other.m_extra)
73 , m_order(other.m_order)
76 m_sol->thermo()->addSpeciesLock();
77 if (!other.m_shared) {
83 m_active.reserve(selected.size());
84 for (
auto loc : selected) {
85 m_active.push_back(other.m_active.at(loc));
88 for (
auto loc : m_active) {
89 if (loc < 0 || loc >= (
int)m_dataSize) {
90 IndexError(
"SolutionArray::SolutionArray",
"indices", loc, m_dataSize);
93 set<int> unique(selected.begin(), selected.end());
94 if (unique.size() < selected.size()) {
95 throw CanteraError(
"SolutionArray::SolutionArray",
"Indices must be unique.");
99SolutionArray::~SolutionArray()
101 m_sol->thermo()->removeSpeciesLock();
107void resetSingle(AnyValue& extra,
const vector<int>& slice);
110AnyValue getSingle(
const AnyValue& extra,
const vector<int>& slice);
113void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
116void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value);
119void resetMulti(AnyValue& extra,
const vector<int>& slice);
122AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice);
125void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
128void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value);
131void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value);
134void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data);
137void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
141void SolutionArray::reset()
143 size_t nState = m_sol->thermo()->stateSize();
144 vector<double> state(nState);
145 m_sol->thermo()->saveState(state);
146 for (
size_t k = 0; k < m_size; ++k) {
147 std::copy(state.begin(), state.end(), m_data->data() + m_active[k] * m_stride);
149 for (
auto& [key, extra] : *m_extra) {
150 if (extra.is<
void>()) {
152 }
else if (extra.isVector<
double>()) {
153 resetSingle<double>(extra, m_active);
154 }
else if (extra.isVector<
long int>()) {
155 resetSingle<long int>(extra, m_active);
156 }
else if (extra.isVector<
string>()) {
157 resetSingle<string>(extra, m_active);
158 }
else if (extra.isVector<vector<double>>()) {
159 resetMulti<double>(extra, m_active);
160 }
else if (extra.isVector<vector<long int>>()) {
161 resetMulti<long int>(extra, m_active);
162 }
else if (extra.isVector<vector<string>>()) {
163 resetMulti<string>(extra, m_active);
166 "Unable to reset component '{}' with type '{}'.",
167 key, extra.type_str());
172void SolutionArray::resize(
int size)
176 "Resize is ambiguous for multi-dimensional arrays; use setApiShape "
179 if (m_data.use_count() > 1) {
181 "Unable to resize as data are shared by multiple objects.");
183 _resize(
static_cast<size_t>(size));
184 m_apiShape[0] =
static_cast<long>(size);
187void SolutionArray::setApiShape(
const vector<long int>& shape)
190 for (
auto dim : shape) {
193 if (m_shared && size != m_size) {
195 "Unable to set shape of shared data as sizes are inconsistent:\n"
196 "active size is {} but shape implies {}.", m_size, size);
198 if (!m_shared && size != m_dataSize) {
199 if (m_data.use_count() > 1) {
201 "Unable to set shape as data are shared by multiple objects.");
208void SolutionArray::_resize(
size_t size)
212 m_data->resize(m_dataSize * m_stride, 0.);
213 for (
auto& [key, data] : *m_extra) {
217 for (
size_t i = 0; i < m_dataSize; ++i) {
218 m_active.push_back(
static_cast<int>(i));
224vector<string> doubleColumn(
string name,
const vector<double>& comp,
230 string notation = fmt::format(
"{{:{}.{}g}}", width, (width - 1) / 2);
231 int csize =
static_cast<int>(comp.size());
232 int dots = csize + 1;
234 for (
const auto& val : comp) {
236 raw.push_back(boost::trim_copy(fmt::format(fmt::runtime(notation), val)));
239 dots = (rows + 1) / 2;
240 for (
int row = 0; row < dots; row++) {
241 data.push_back(comp[row]);
242 raw.push_back(boost::trim_copy(
243 fmt::format(fmt::runtime(notation), comp[row])));
245 for (
int row = csize - rows / 2; row < csize; row++) {
246 data.push_back(comp[row]);
247 raw.push_back(boost::trim_copy(
248 fmt::format(fmt::runtime(notation), comp[row])));
253 bool isFloat =
false;
254 bool isScientific =
false;
258 for (
const auto& repr : raw) {
260 if (name[0] ==
'-') {
262 name = name.substr(1);
264 if (name.find(
'e') != string::npos) {
268 tail = name.find(
'e') - name.find(
'.');
271 tail = std::max(tail, name.find(
'e') - name.find(
'.'));
275 }
else if (name.find(
'.') != string::npos) {
279 head = std::max(head, name.find(
'.'));
280 tail = std::max(tail, name.size() - name.find(
'.'));
283 head = std::max(head, name.size());
286 size_t maxLen = std::max((
size_t)4, head + tail + exp + isFloat + 1);
287 size_t over = std::max(0, (
int)name.size() - (
int)maxLen);
290 notation = fmt::format(
" {{:>{}.{}e}}", over + maxLen, tail);
291 }
else if (isFloat) {
293 notation = fmt::format(
" {{:>{}.{}f}}", over + maxLen, tail);
296 notation = fmt::format(
" {{:>{}.0f}}", over + maxLen);
298 maxLen = fmt::format(fmt::runtime(notation), 0.).size();
301 string section = fmt::format(
"{{:>{}}}", maxLen);
302 vector<string> col = {fmt::format(fmt::runtime(section), name)};
304 for (
const auto& val : data) {
305 col.push_back(fmt::format(fmt::runtime(notation), val));
308 col.push_back(fmt::format(fmt::runtime(section),
"..."));
314vector<string> integerColumn(
string name,
const vector<long int>& comp,
318 vector<long int> data;
319 string notation = fmt::format(
"{{:{}}}", width);
321 int csize =
static_cast<int>(comp.size());
322 int dots = csize + 1;
324 for (
const auto& val : comp) {
326 string formatted = boost::trim_copy(
327 fmt::format(fmt::runtime(notation), val));
328 if (formatted[0] ==
'-') {
329 formatted = formatted.substr(1);
331 maxLen = std::max(maxLen, formatted.size());
334 dots = (rows + 1) / 2;
335 for (
int row = 0; row < dots; row++) {
336 data.push_back(comp[row]);
337 string formatted = boost::trim_copy(
338 fmt::format(fmt::runtime(notation), comp[row]));
339 if (formatted[0] ==
'-') {
340 formatted = formatted.substr(1);
342 maxLen = std::max(maxLen, formatted.size());
344 for (
int row = csize - rows / 2; row < csize; row++) {
345 data.push_back(comp[row]);
346 string formatted = boost::trim_copy(
347 fmt::format(fmt::runtime(notation), comp[row]));
348 if (formatted[0] ==
'-') {
349 formatted = formatted.substr(1);
351 maxLen = std::max(maxLen, formatted.size());
357 notation = fmt::format(
"{{:<{}}}", maxLen);
360 maxLen = std::max(maxLen, name.size());
361 notation = fmt::format(
" {{:>{}}}", maxLen + 1);
365 vector<string> col = {fmt::format(fmt::runtime(notation), name)};
367 for (
const auto& val : data) {
368 col.push_back(fmt::format(fmt::runtime(notation), val));
371 col.push_back(fmt::format(fmt::runtime(notation),
".."));
377vector<string> stringColumn(
string name,
const vector<string>& comp,
382 string notation = fmt::format(
"{{:{}}}", width);
384 int csize =
static_cast<int>(comp.size());
385 int dots = csize + 1;
387 for (
const auto& val : comp) {
389 maxLen = std::max(maxLen,
390 boost::trim_copy(fmt::format(fmt::runtime(notation), val)).size());
393 dots = (rows + 1) / 2;
394 for (
int row = 0; row < dots; row++) {
395 data.push_back(comp[row]);
396 maxLen = std::max(maxLen,
398 fmt::format(fmt::runtime(notation), comp[row])).size());
400 for (
int row = csize - rows / 2; row < csize; row++) {
401 data.push_back(comp[row]);
402 maxLen = std::max(maxLen,
404 fmt::format(fmt::runtime(notation), comp[row])).size());
409 notation = fmt::format(
" {{:>{}}}", maxLen);
410 vector<string> col = {fmt::format(fmt::runtime(notation), name)};
412 for (
const auto& val : data) {
413 col.push_back(fmt::format(fmt::runtime(notation), val));
416 col.push_back(fmt::format(fmt::runtime(notation),
"..."));
422vector<string> formatColumn(
string name,
const AnyValue& comp,
int rows,
int width)
424 if (comp.isVector<
double>()) {
425 return doubleColumn(name, comp.asVector<
double>(), rows, width);
427 if (comp.isVector<
long int>()) {
428 return integerColumn(name, comp.asVector<
long int>(), rows, width);
430 if (comp.isVector<
string>()) {
431 return stringColumn(name, comp.asVector<
string>(), rows, width);
437 if (comp.isVector<vector<double>>()) {
438 repr =
"[ <double> ]";
439 size =
len(comp.asVector<vector<double>>());
440 }
else if (comp.isVector<vector<long int>>()) {
441 repr =
"[ <long int> ]";
442 size =
len(comp.asVector<vector<long int>>());
443 }
else if (comp.isVector<vector<string>>()) {
444 repr =
"[ <string> ]";
445 size =
len(comp.asVector<vector<string>>());
448 "formatColumn",
"Encountered invalid data for component '{}'.", name);
450 size_t maxLen = std::max(repr.size(), name.size());
453 string notation = fmt::format(
" {{:>{}}}", maxLen);
454 repr = fmt::format(fmt::runtime(notation), repr);
455 vector<string> col = {fmt::format(fmt::runtime(notation), name)};
457 for (
int row = 0; row < size; row++) {
461 int dots = (rows + 1) / 2;
462 for (
int row = 0; row < dots; row++) {
465 col.push_back(fmt::format(fmt::runtime(notation),
"..."));
466 for (
int row = size - rows / 2; row < size; row++) {
475string SolutionArray::info(
const vector<string>& keys,
int rows,
int width)
477 fmt::memory_buffer b;
479 vector<string> components;
483 components = componentNames();
487 vector<long int> index;
488 for (
const auto ix : m_active) {
491 vector<vector<string>> cols = {integerColumn(
"", index, rows, col_width)};
492 vector<vector<string>> tail;
493 size_t size = cols.back().size();
499 int back =
len(components) - 1;
500 int fLen =
len(cols.back()[0]);
504 while (!done && front <= back) {
506 while (bLen + sep <= fLen && front <= back) {
508 key = components[back];
509 auto col = formatColumn(key, getComponent(key), rows, col_width);
510 if (
len(col[0]) + fLen + bLen + sep > width) {
515 bLen +=
len(tail.back()[0]);
518 if (done || front > back) {
521 while (fLen <= bLen + sep && front <= back) {
523 key = components[front];
524 auto col = formatColumn(key, getComponent(key), rows, col_width);
525 if (
len(col[0]) + fLen + bLen + sep > width) {
530 fLen +=
len(cols.back()[0]);
534 if (cols.size() + tail.size() < components.size() + 1) {
536 cols.push_back(vector<string>(size + 1,
" ..."));
539 cols.insert(cols.end(), tail.rbegin(), tail.rend());
542 for (
size_t row = 0; row < size; row++) {
543 for (
const auto& col : cols) {
544 fmt_append(b, col[row]);
550 fmt_append(b,
"\n[{} rows x {} components; state='{}']",
551 m_size, components.size(), m_sol->thermo()->nativeMode());
554 return to_string(b) + err.
what();
559shared_ptr<ThermoPhase> SolutionArray::thermo()
561 return m_sol->thermo();
564string SolutionArray::transportModel()
566 return m_sol->transportModel();
569vector<string> SolutionArray::componentNames()
const
571 vector<string> components;
574 while (m_order->count(pos)) {
575 components.push_back(m_order->at(pos));
580 auto phase = m_sol->thermo();
581 for (
auto code : phase->nativeMode()) {
582 string name = string(1, code);
583 if (name ==
"X" || name ==
"Y") {
584 for (
auto& spc : phase->speciesNames()) {
585 components.push_back(spc);
588 components.push_back(name);
594 while (m_order->count(pos)) {
595 components.push_back(m_order->at(pos));
602void SolutionArray::addExtra(
const string& name,
bool back)
604 if (m_extra->count(name)) {
606 "Component '{}' already exists.", name);
610 if (m_order->size()) {
612 m_order->emplace(m_order->begin()->first - 1, name);
615 m_order->emplace(-1, name);
618 if (m_order->size()) {
620 m_order->emplace(m_order->rbegin()->first + 1, name);
623 m_order->emplace(0, name);
628vector<string> SolutionArray::listExtra(
bool all)
const
630 vector<string> names;
632 while (m_order->count(pos)) {
633 const auto& name = m_order->at(pos);
634 if (all || !m_extra->at(name).is<
void>()) {
635 names.push_back(name);
642 while (m_order->count(pos)) {
643 const auto& name = m_order->at(pos);
644 if (all || !m_extra->at(name).is<
void>()) {
645 names.push_back(name);
652bool SolutionArray::hasComponent(
const string& name)
const
654 if (m_extra->count(name)) {
658 if (m_sol->thermo()->speciesIndex(name) !=
npos) {
662 if (name ==
"X" || name ==
"Y") {
667 return (m_sol->thermo()->nativeState().count(name));
670AnyValue SolutionArray::getComponent(
const string& name)
const
672 if (!hasComponent(name)) {
674 "Unknown component '{}'.", name);
678 if (m_extra->count(name)) {
680 const auto& extra = m_extra->at(name);
681 if (extra.is<
void>()) {
684 if (m_size == m_dataSize) {
687 if (extra.isVector<
long int>()) {
688 return getSingle<long int>(extra, m_active);
690 if (extra.isVector<
double>()) {
691 return getSingle<double>(extra, m_active);
693 if (extra.isVector<
string>()) {
694 return getSingle<string>(extra, m_active);
696 if (extra.isVector<vector<double>>()) {
697 return getMulti<double>(extra, m_active);
699 if (extra.isVector<vector<long int>>()) {
700 return getMulti<long int>(extra, m_active);
702 if (extra.isVector<vector<string>>()) {
703 return getMulti<string>(extra, m_active);
706 "Unable to get sliced data for component '{}' with type '{}'.",
707 name, extra.type_str());
711 vector<double> data(m_size);
712 size_t ix = m_sol->thermo()->speciesIndex(name);
715 ix = m_sol->thermo()->nativeState()[name];
718 ix += m_stride - m_sol->thermo()->nSpecies();
720 for (
size_t k = 0; k < m_size; ++k) {
721 data[k] = (*m_data)[m_active[k] * m_stride + ix];
727bool isSimpleVector(
const AnyValue& any) {
734void SolutionArray::setComponent(
const string& name,
const AnyValue& data)
736 if (!hasComponent(name)) {
738 "Unknown component '{}'.", name);
740 if (m_extra->count(name)) {
741 _setExtra(name, data);
748 "Invalid type of component '{}': expected simple array type, "
749 "but received '{}'.", name, data.
type_str());
751 if (size != m_size) {
753 "Invalid size of component '{}': expected size {} but received {}.",
757 auto& vec = data.
asVector<
double>();
758 size_t ix = m_sol->thermo()->speciesIndex(name);
760 ix = m_sol->thermo()->nativeState()[name];
762 ix += m_stride - m_sol->thermo()->nSpecies();
764 for (
size_t k = 0; k < m_size; ++k) {
765 (*m_data)[m_active[k] * m_stride + ix] = vec[k];
769void SolutionArray::setLoc(
int loc,
bool restore)
771 size_t loc_ =
static_cast<size_t>(loc);
774 "Unable to set location in empty SolutionArray.");
775 }
else if (loc < 0) {
778 "Both current and buffered indices are invalid.");
781 }
else if (
static_cast<size_t>(m_active[loc_]) == m_loc) {
783 }
else if (loc_ >= m_size) {
784 throw IndexError(
"SolutionArray::setLoc",
"indices", loc_, m_size);
786 m_loc =
static_cast<size_t>(m_active[loc_]);
788 size_t nState = m_sol->thermo()->stateSize();
789 m_sol->thermo()->restoreState(nState, m_data->data() + m_loc * m_stride);
793void SolutionArray::updateState(
int loc)
796 size_t nState = m_sol->thermo()->stateSize();
797 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
800vector<double> SolutionArray::getState(
int loc)
803 size_t nState = m_sol->thermo()->stateSize();
804 vector<double> out(nState);
805 m_sol->thermo()->saveState(out);
809void SolutionArray::setState(
int loc,
const vector<double>& state)
811 size_t nState = m_sol->thermo()->stateSize();
812 if (state.size() != nState) {
814 "Expected array to have length {}, but received an array of length {}.",
815 nState, state.size());
818 m_sol->thermo()->restoreState(state);
819 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
822void SolutionArray::normalize() {
823 auto phase = m_sol->thermo();
824 auto nativeState = phase->nativeState();
825 if (nativeState.size() < 3) {
828 size_t nState = phase->stateSize();
829 vector<double> out(nState);
830 if (nativeState.count(
"Y")) {
831 size_t offset = nativeState[
"Y"];
832 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
834 phase->setMassFractions(m_data->data() + m_loc * m_stride +
offset);
835 m_sol->thermo()->saveState(out);
838 }
else if (nativeState.count(
"X")) {
839 size_t offset = nativeState[
"X"];
840 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
842 phase->setMoleFractions(m_data->data() + m_loc * m_stride +
offset);
843 m_sol->thermo()->saveState(out);
848 "Not implemented for mode '{}'.", phase->nativeMode());
852AnyMap SolutionArray::getAuxiliary(
int loc)
856 for (
const auto& [key, extra] : *m_extra) {
857 if (extra.is<
void>()) {
859 }
else if (extra.isVector<
long int>()) {
860 out[key] = extra.asVector<
long int>()[m_loc];
861 }
else if (extra.isVector<
double>()) {
862 out[key] = extra.asVector<
double>()[m_loc];
863 }
else if (extra.isVector<
string>()) {
864 out[key] = extra.asVector<
string>()[m_loc];
865 }
else if (extra.isVector<vector<long int>>()) {
866 out[key] = extra.asVector<vector<long int>>()[m_loc];
867 }
else if (extra.isVector<vector<double>>()) {
868 out[key] = extra.asVector<vector<double>>()[m_loc];
869 }
else if (extra.isVector<vector<string>>()) {
870 out[key] = extra.asVector<vector<string>>()[m_loc];
873 "Unable to retrieve data for component '{}' with type '{}'.",
874 key, extra.type_str());
880void SolutionArray::setAuxiliary(
int loc,
const AnyMap& data)
883 for (
const auto& [name, value] : data) {
884 if (!m_extra->count(name)) {
886 "Unknown auxiliary component '{}'.", name);
888 auto& extra = m_extra->at(name);
889 if (extra.is<
void>()) {
890 if (m_dataSize > 1) {
892 "Unable to set location for type '{}': "
893 "component is not initialized.", name);
895 _initExtra(name, value);
899 if (extra.isVector<
long int>()) {
900 setAuxiliarySingle<long int>(m_loc, extra, value);
901 }
else if (extra.isVector<
double>()) {
902 setAuxiliarySingle<double>(m_loc, extra, value);
903 }
else if (extra.isVector<
string>()) {
904 setAuxiliarySingle<string>(m_loc, extra, value);
905 }
else if (extra.isVector<vector<long int>>()) {
906 setAuxiliaryMulti<long int>(m_loc, extra, value);
907 }
else if (extra.isVector<vector<double>>()) {
908 setAuxiliaryMulti<double>(m_loc, extra, value);
909 }
else if (extra.isVector<vector<string>>()) {
910 setAuxiliaryMulti<string>(m_loc, extra, value);
913 "Unable to set entry for type '{}'.", extra.type_str());
918 "Encountered incompatible value for component '{}':\n{}",
924AnyMap preamble(
const string& desc)
928 data[
"description"] = desc;
930 data[
"generator"] =
"Cantera SolutionArray";
931 data[
"cantera-version"] = CANTERA_VERSION;
932 data[
"git-commit"] = gitCommit();
937 struct tm* newtime = localtime(&aclock);
938 data[
"date"] = stripnonprint(asctime(newtime));
943AnyMap& openField(AnyMap& root,
const string& name)
950 vector<string> tokens;
954 for (
auto& field : tokens) {
957 if (sub.hasKey(field) && !sub[field].is<AnyMap>()) {
958 throw CanteraError(
"openField",
959 "Encountered invalid existing field '{}'.", path);
960 }
else if (!sub.hasKey(field)) {
961 sub[field] = AnyMap();
963 ptr = &sub[field].as<AnyMap>();
968void SolutionArray::writeHeader(
const string& fname,
const string& name,
969 const string& desc,
bool overwrite)
975 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
983void SolutionArray::writeHeader(
AnyMap& root,
const string& name,
984 const string& desc,
bool overwrite)
986 AnyMap& data = openField(root, name);
990 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
994 data.
update(preamble(desc));
997void SolutionArray::writeEntry(
const string& fname,
bool overwrite,
const string& basis)
999 if (apiNdim() != 1) {
1001 "Tabular output of CSV data only works for 1D SolutionArray objects.");
1003 set<string> speciesNames;
1004 for (
const auto& species : m_sol->thermo()->speciesNames()) {
1005 speciesNames.insert(species);
1009 const auto& nativeState = m_sol->thermo()->nativeState();
1010 mole = nativeState.find(
"X") != nativeState.end();
1011 }
else if (basis ==
"X" || basis ==
"mole") {
1013 }
else if (basis ==
"Y" || basis ==
"mass") {
1017 "Invalid species basis '{}'.", basis);
1020 auto names = componentNames();
1021 size_t last = names.size() - 1;
1022 vector<AnyValue> components;
1023 vector<bool> isSpecies;
1024 fmt::memory_buffer header;
1025 for (
const auto& key : names) {
1028 if (speciesNames.find(key) == speciesNames.end()) {
1030 isSpecies.push_back(
false);
1031 components.emplace_back(getComponent(key));
1032 col = components.size() - 1;
1033 if (!components[col].isVector<double>() &&
1034 !components[col].isVector<
long int>() &&
1035 !components[col].isVector<string>())
1038 "Multi-dimensional column '{}' is not supported for CSV output.",
1043 isSpecies.push_back(
true);
1044 components.emplace_back(
AnyValue());
1045 col = components.size() - 1;
1047 label =
"X_" + label;
1049 label =
"Y_" + label;
1052 if (label.find(
"\"") != string::npos || label.find(
"\n") != string::npos) {
1054 "Detected column name containing double quotes or line feeds: '{}'.",
1057 string sep = (col == last) ?
"" :
",";
1058 if (label.find(
",") != string::npos) {
1059 fmt_append(header,
"\"{}\"{}", label, sep);
1061 fmt_append(header,
"{}{}", label, sep);
1066 if (std::ifstream(fname).good()) {
1069 "File '{}' already exists; use option 'overwrite' to replace CSV file.",
1072 std::remove(fname.c_str());
1074 std::ofstream output(fname);
1075 output << to_string(header) << std::endl;
1077 size_t maxLen =
npos;
1078 vector<double> buf(speciesNames.size(), 0.);
1079 for (
int row = 0; row < static_cast<int>(m_size); row++) {
1080 fmt::memory_buffer line;
1081 if (maxLen !=
npos) {
1082 line.reserve(maxLen);
1086 m_sol->thermo()->getMoleFractions(buf.data());
1088 m_sol->thermo()->getMassFractions(buf.data());
1092 for (
size_t col = 0; col < components.size(); col++) {
1093 string sep = (col == last) ?
"" :
",";
1094 if (isSpecies[col]) {
1095 fmt_append(line,
"{:.9g}{}", buf[idx++], sep);
1097 auto& data = components[col];
1098 if (data.isVector<
double>()) {
1099 fmt_append(line,
"{:.9g}{}", data.asVector<
double>()[row], sep);
1100 }
else if (data.isVector<
long int>()) {
1101 fmt_append(line,
"{}{}", data.asVector<
long int>()[row], sep);
1102 }
else if (data.isVector<
bool>()) {
1103 fmt_append(line,
"{}{}",
1104 static_cast<bool>(data.asVector<
bool>()[row]), sep);
1106 auto value = data.asVector<
string>()[row];
1107 if (value.find(
"\"") != string::npos ||
1108 value.find(
"\n") != string::npos)
1111 "Detected value containing double quotes or line feeds: "
1114 if (value.find(
",") != string::npos) {
1115 fmt_append(line,
"\"{}\"{}", value, sep);
1117 fmt_append(line,
"{}{}", value, sep);
1122 output << to_string(line) << std::endl;
1123 maxLen = std::max(maxLen, line.size());
1127void SolutionArray::writeEntry(
const string& fname,
const string& name,
1128 const string& sub,
bool overwrite,
int compression)
1132 "Group name specifying root location must not be empty.");
1134 if (m_size < m_dataSize) {
1136 "Unable to save sliced data.");
1151 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1158 if (apiNdim() == 1) {
1159 more[
"size"] = int(m_dataSize);
1161 more[
"api-shape"] = m_apiShape;
1163 if (!m_meta.hasKey(
"transport-model") && m_sol->transport()) {
1164 more[
"transport-model"] = m_sol->transportModel();
1166 more[
"components"] = componentNames();
1172 const auto& nativeState = m_sol->thermo()->nativeState();
1173 size_t nSpecies = m_sol->thermo()->nSpecies();
1174 for (
auto& [key,
offset] : nativeState) {
1175 if (key ==
"X" || key ==
"Y") {
1176 vector<vector<double>> prop;
1177 for (
size_t i = 0; i < m_size; i++) {
1178 size_t first =
offset + i * m_stride;
1179 prop.emplace_back(m_data->begin() + first,
1180 m_data->begin() + first + nSpecies);
1186 auto data = getComponent(key);
1191 for (
const auto& [key, value] : *m_extra) {
1192 if (isSimpleVector(value)) {
1194 }
else if (value.is<
void>()) {
1198 "Unable to save component '{}' with data type {}.",
1199 key, value.type_str());
1204void SolutionArray::writeEntry(
AnyMap& root,
const string& name,
const string& sub,
1209 "Field name specifying root location must not be empty.");
1211 if (m_size < m_dataSize) {
1213 "Unable to save sliced data.");
1221 AnyMap& data = openField(root, path);
1222 bool preexisting = !data.
empty();
1223 if (preexisting && !overwrite) {
1225 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1227 if (apiNdim() == 1) {
1228 data[
"size"] = int(m_dataSize);
1230 data[
"api-shape"] = m_apiShape;
1232 if (m_sol->transport() && m_sol->transportModel() !=
"none") {
1233 data[
"transport-model"] = m_sol->transportModel();
1238 data[
"components"] = componentNames();
1241 for (
auto& [_, key] : *m_order) {
1242 data[key] = m_extra->
at(key);
1245 auto phase = m_sol->thermo();
1248 data[
"temperature"] = phase->temperature();
1249 data[
"pressure"] = phase->pressure();
1250 auto surf = std::dynamic_pointer_cast<SurfPhase>(phase);
1251 auto nSpecies = phase->nSpecies();
1252 vector<double> values(nSpecies);
1254 surf->getCoverages(&values[0]);
1256 phase->getMassFractions(&values[0]);
1259 for (
size_t k = 0; k < nSpecies; k++) {
1260 if (values[k] != 0.0) {
1261 items[phase->speciesName(k)] = values[k];
1265 data[
"coverages"] = std::move(items);
1267 data[
"mass-fractions"] = std::move(items);
1269 }
else if (m_size > 1) {
1270 for (
auto& code : phase->nativeMode()) {
1271 string name(1, code);
1272 if (name ==
"X" || name ==
"Y") {
1273 for (
auto& spc : phase->speciesNames()) {
1274 data[spc] = getComponent(spc);
1276 data[
"basis"] = name ==
"X" ?
"mole" :
"mass";
1278 data[name] = getComponent(name);
1283 static bool reg = AnyMap::addOrderingRules(
"SolutionArray",
1284 {{
"head",
"type"}, {
"head",
"size"}, {
"head",
"basis"}});
1286 data[
"__type__"] =
"SolutionArray";
1295void SolutionArray::append(
const vector<double>& state,
const AnyMap& extra)
1297 if (apiNdim() > 1) {
1299 "Unable to append multi-dimensional arrays.");
1305 setState(pos, state);
1306 setAuxiliary(pos, extra);
1314void SolutionArray::save(
const string& fname,
const string& name,
const string& sub,
1315 const string& desc,
bool overwrite,
int compression,
1316 const string& basis)
1318 if (m_size < m_dataSize) {
1320 "Unable to save sliced data.");
1322 size_t dot = fname.find_last_of(
".");
1324 if (extension ==
"csv") {
1327 "Parameter 'name' not used for CSV output.");
1329 writeEntry(fname, overwrite, basis);
1334 "Argument 'basis' is not used for HDF or YAML output.", basis);
1336 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1337 writeHeader(fname, name, desc, overwrite);
1338 writeEntry(fname, name, sub,
true, compression);
1341 if (extension ==
"yaml" || extension ==
"yml") {
1344 std::ifstream file(fname);
1345 if (file.good() && file.peek() != std::ifstream::traits_type::eof()) {
1346 data = AnyMap::fromYamlFile(fname);
1348 writeHeader(data, name, desc, overwrite);
1349 writeEntry(data, name, sub,
true);
1352 std::ofstream out(fname);
1353 out << data.toYamlString();
1354 AnyMap::clearCachedFile(fname);
1358 "Unknown file extension '{}'.", extension);
1361AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1368const AnyMap& locateField(
const AnyMap& root,
const string& name)
1375 vector<string> tokens;
1376 tokenizePath(name, tokens);
1377 const AnyMap* ptr = &root;
1379 for (
auto& field : tokens) {
1380 path +=
"/" + field;
1381 const AnyMap& sub = *ptr;
1382 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1383 throw CanteraError(
"SolutionArray::locateField",
1384 "No field or solution with name '{}'.", path);
1386 ptr = &sub[field].as<AnyMap>();
1393 auto sub = locateField(root, name);
1395 for (
const auto& [key, value] : sub) {
1396 if (!sub[key].is<AnyMap>()) {
1397 header[key] = value;
1403AnyMap SolutionArray::restore(
const string& fname,
1404 const string& name,
const string& sub)
1406 size_t dot = fname.find_last_of(
".");
1409 if (extension ==
"csv") {
1411 "CSV import not implemented; if using Python, data can be imported via "
1412 "'read_csv' instead.");
1414 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1415 readEntry(fname, name, sub);
1416 header = readHeader(fname, name);
1417 }
else if (extension ==
"yaml" || extension ==
"yml") {
1418 const AnyMap& root = AnyMap::fromYamlFile(fname);
1419 readEntry(root, name, sub);
1420 header = readHeader(root, name);
1423 "Unknown file extension '{}'; supported extensions include "
1424 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1429void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1431 if (!m_extra->count(name)) {
1433 "Component '{}' does not exist.", name);
1435 auto& extra = (*m_extra)[name];
1436 if (!extra.is<
void>()) {
1438 "Component '{}' is already initialized.", name);
1441 if (value.
is<
long int>()) {
1442 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1443 }
else if (value.
is<
double>()) {
1444 extra = vector<double>(m_dataSize, value.
as<
double>());
1445 }
else if (value.
is<
string>()) {
1446 extra = vector<string>(m_dataSize, value.
as<
string>());
1447 }
else if (value.
isVector<
long int>()) {
1448 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1449 }
else if (value.
isVector<
double>()) {
1450 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1451 }
else if (value.
isVector<
string>()) {
1452 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1453 }
else if (value.
is<
void>()) {
1458 "Unable to initialize component '{}' with type '{}'.",
1464 "Encountered incompatible value for initializing component '{}':\n{}",
1469void SolutionArray::_resizeExtra(
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>()) {
1482 if (extra.isVector<
long int>()) {
1483 resizeSingle<long int>(extra, m_dataSize, value);
1484 }
else if (extra.isVector<
double>()) {
1485 resizeSingle<double>(extra, m_dataSize, value);
1486 }
else if (extra.isVector<
string>()) {
1487 resizeSingle<string>(extra, m_dataSize, value);
1488 }
else if (extra.isVector<vector<double>>()) {
1489 resizeMulti<double>(extra, m_dataSize, value);
1490 }
else if (extra.isVector<vector<long int>>()) {
1491 resizeMulti<long int>(extra, m_dataSize, value);
1492 }
else if (extra.isVector<vector<string>>()) {
1493 resizeMulti<string>(extra, m_dataSize, value);
1496 "Unable to resize using type '{}'.", extra.type_str());
1501 "Encountered incompatible value for resizing component '{}':\n{}",
1506void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1508 if (!m_extra->count(name)) {
1510 "Extra component '{}' does not exist.", name);
1513 auto& extra = m_extra->at(name);
1514 if (data.
is<
void>() && m_size == m_dataSize) {
1521 if (extra.is<
void>()) {
1522 if (m_size != m_dataSize) {
1524 "Unable to replace '{}' for sliced data.", name);
1534 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1539 "Unable to initialize '{}' with empty array when SolutionArray is not "
1542 _initExtra(name, data);
1543 _resizeExtra(name, data);
1547 if (data.
is<
long int>()) {
1548 setScalar<long int>(extra, data, m_active);
1549 }
else if (data.
is<
double>()) {
1550 setScalar<double>(extra, data, m_active);
1551 }
else if (data.
is<
string>()) {
1552 setScalar<string>(extra, data, m_active);
1553 }
else if (data.
isVector<
long int>()) {
1554 setSingle<long int>(extra, data, m_active);
1555 }
else if (data.
isVector<
double>()) {
1556 setSingle<double>(extra, data, m_active);
1557 }
else if (data.
isVector<
string>()) {
1558 setSingle<string>(extra, data, m_active);
1559 }
else if (data.
isVector<vector<long int>>()) {
1560 setMulti<long int>(extra, data, m_active);
1561 }
else if (data.
isVector<vector<double>>()) {
1562 setMulti<double>(extra, data, m_active);
1563 }
else if (data.
isVector<vector<string>>()) {
1564 setMulti<string>(extra, data, m_active);
1567 "Unable to set sliced data for component '{}' with type '{}'.",
1572string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1576 const auto& nativeState = m_sol->thermo()->nativeState();
1577 bool usesNativeState =
false;
1578 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1580 for (
const auto& item : m_sol->thermo()->fullStates()) {
1583 usesNativeState =
true;
1584 for (
size_t i = 0; i < item.size(); i++) {
1586 name = string(1, item[i]);
1587 if (surf && (name ==
"X" || name ==
"Y")) {
1590 usesNativeState =
false;
1593 if (names.count(name)) {
1595 usesNativeState &= nativeState.count(name) > 0;
1596 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1598 usesNativeState &= nativeState.count(name) > 0;
1605 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1610 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1612 return "legacySurf";
1614 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1616 return "legacyInlet";
1619 "Detected incomplete thermodynamic information. Full states for a '{}' "
1620 "phase\nmay be defined by the following modes:\n'{}'\n"
1621 "Available data are: '{}'", m_sol->thermo()->type(),
1622 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1624 if (usesNativeState && native) {
1630set<string> SolutionArray::_stateProperties(
1631 const string& mode,
bool alias)
1634 if (mode ==
"native") {
1635 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1636 states.insert(alias ? aliasMap.at(name) : name);
1639 for (
const auto& m : mode) {
1640 const string name = string(1, m);
1641 states.insert(alias ? aliasMap.at(name) : name);
1648string getName(
const set<string>& names,
const string& name)
1650 if (names.count(name)) {
1653 const auto& alias = aliasMap.at(name);
1654 if (names.count(alias)) {
1660void SolutionArray::readEntry(
const string& fname,
const string& name,
1666 "Group name specifying root location must not be empty.");
1669 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1671 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1677 "Group name specifying data entry is empty.");
1680 auto [size, names] = file.
contents(path);
1682 if (m_meta.hasKey(
"size")) {
1684 resize(m_meta[
"size"].as<long int>());
1685 m_meta.erase(
"size");
1686 }
else if (m_meta.hasKey(
"api-shape")) {
1688 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1689 m_meta.erase(
"api-shape");
1692 resize(
static_cast<int>(size));
1700 string mode = _detectMode(names);
1701 set<string> states = _stateProperties(mode);
1702 if (states.count(
"C")) {
1703 if (names.count(
"X")) {
1706 mode = mode.substr(0, 2) +
"X";
1707 }
else if (names.count(
"Y")) {
1710 mode = mode.substr(0, 2) +
"Y";
1715 size_t nSpecies = m_sol->thermo()->nSpecies();
1716 size_t nState = m_sol->thermo()->stateSize();
1717 const auto& nativeStates = m_sol->thermo()->nativeState();
1718 if (mode ==
"native") {
1720 for (
const auto& [name,
offset] : nativeStates) {
1721 if (name ==
"X" || name ==
"Y") {
1723 data = file.
readData(path, name, m_size, nSpecies);
1724 auto prop = data.
asVector<vector<double>>();
1725 for (
size_t i = 0; i < m_dataSize; i++) {
1726 std::copy(prop[i].begin(), prop[i].end(),
1727 m_data->data() +
offset + i * m_stride);
1731 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1732 setComponent(name, data);
1735 }
else if (mode ==
"TPX") {
1737 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1738 vector<double> T = std::move(data.
asVector<
double>());
1739 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1740 vector<double> P = std::move(data.
asVector<
double>());
1741 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1742 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1743 for (
size_t i = 0; i < m_dataSize; i++) {
1744 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1745 m_sol->thermo()->setState_TP(T[i], P[i]);
1746 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1748 }
else if (mode ==
"TDX") {
1750 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1751 vector<double> T = std::move(data.
asVector<
double>());
1752 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1753 vector<double> D = std::move(data.
asVector<
double>());
1754 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1755 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1756 for (
size_t i = 0; i < m_dataSize; i++) {
1757 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1758 m_sol->thermo()->setState_TD(T[i], D[i]);
1759 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1761 }
else if (mode ==
"TPY") {
1763 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1764 vector<double> T = std::move(data.
asVector<
double>());
1765 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1766 vector<double> P = std::move(data.
asVector<
double>());
1767 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1768 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1769 for (
size_t i = 0; i < m_dataSize; i++) {
1770 m_sol->thermo()->setMassFractions_NoNorm(Y[i].data());
1771 m_sol->thermo()->setState_TP(T[i], P[i]);
1772 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1774 }
else if (mode ==
"legacySurf") {
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,
"X", m_dataSize, nSpecies);
1780 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1781 for (
size_t i = 0; i < m_dataSize; i++) {
1782 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1783 m_sol->thermo()->setTemperature(T[i]);
1784 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1787 "Detected legacy HDF format with incomplete state information\nfor name "
1788 "'{}' (pressure missing).", path);
1789 }
else if (mode ==
"") {
1791 "Data are not consistent with full state modes.");
1794 "Import of '{}' data is not supported.", mode);
1798 if (m_meta.hasKey(
"components")) {
1799 const auto& components = m_meta[
"components"].asVector<
string>();
1801 for (
const auto& name : components) {
1802 if (hasComponent(name) || name ==
"X" || name ==
"Y") {
1805 addExtra(name, back);
1807 data = file.
readData(path, name, m_dataSize);
1808 setComponent(name, data);
1811 m_meta.erase(
"components");
1814 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1815 for (
const auto& name : names) {
1816 if (!hasComponent(name) && name !=
"X" && name !=
"Y") {
1819 data = file.
readData(path, name, m_dataSize);
1820 setComponent(name, data);
1825 if (m_meta.hasKey(
"transport-model")) {
1826 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1830void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1834 "Field name specifying root location must not be empty.");
1836 auto path = locateField(root, name);
1837 if (path.hasKey(
"generator") && sub !=
"") {
1839 path = locateField(root, name +
"/" + sub);
1840 }
else if (sub ==
"" && path.hasKey(
"data")) {
1842 path = locateField(root, name +
"/data");
1847 if (path.hasKey(
"size")) {
1849 resize(path[
"size"].asInt());
1850 }
else if (path.hasKey(
"api-shape")) {
1852 auto& shape = path[
"api-shape"].asVector<
long int>();
1856 size = path.getInt(
"points", 0);
1857 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1861 resize(
static_cast<int>(size));
1866 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1867 set<string> names = path.keys();
1868 size_t nState = m_sol->thermo()->stateSize();
1869 if (m_dataSize == 0) {
1871 }
else if (m_dataSize == 1) {
1873 string mode = _detectMode(names,
false);
1874 if (mode ==
"TPY") {
1875 double T = path[getName(names,
"T")].asDouble();
1876 double P = path[getName(names,
"P")].asDouble();
1877 auto Y = path[
"mass-fractions"].asMap<
double>();
1878 m_sol->thermo()->setState_TPY(T, P, Y);
1879 }
else if (mode ==
"TPC") {
1880 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1881 double T = path[getName(names,
"T")].asDouble();
1882 double P = path[
"pressure"].asDouble();
1883 m_sol->thermo()->setState_TP(T, P);
1884 auto cov = path[
"coverages"].asMap<
double>();
1885 surf->setCoveragesByName(cov);
1886 }
else if (mode ==
"legacyInlet") {
1889 double T = path[getName(names,
"T")].asDouble();
1890 auto Y = path[
"mass-fractions"].asMap<
double>();
1891 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1893 "Detected legacy YAML format with incomplete state information\n"
1894 "for name '{}' (pressure missing).", name +
"/" + sub);
1895 }
else if (mode ==
"") {
1897 "Data are not consistent with full state modes.");
1900 "Import of '{}' data is not supported.", mode);
1902 m_sol->thermo()->saveState(nState, m_data->data());
1903 auto props = _stateProperties(mode,
true);
1904 exclude.insert(props.begin(), props.end());
1907 if (path.hasKey(
"components")) {
1908 const auto& components = path[
"components"].asVector<
string>();
1910 for (
const auto& name : components) {
1911 if (hasComponent(name)) {
1914 addExtra(name, back);
1916 setComponent(name, path[name]);
1917 exclude.insert(name);
1921 for (
const auto& [name, value] : path) {
1922 if (value.isVector<
double>()) {
1923 const vector<double>& data = value.asVector<
double>();
1924 if (data.size() == m_dataSize) {
1925 if (!hasComponent(name)) {
1928 setComponent(name, value);
1929 exclude.insert(name);
1936 const auto& nativeState = m_sol->thermo()->nativeState();
1938 set<string> missingProps;
1939 for (
const auto& [name,
offset] : nativeState) {
1940 if (exclude.count(name)) {
1943 missingProps.insert(name);
1947 set<string> TY = {
"T",
"Y"};
1948 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
1950 double P = path[
"pressure"].asDouble();
1951 const size_t offset_T = nativeState.find(
"T")->second;
1952 const size_t offset_D = nativeState.find(
"D")->second;
1953 const size_t offset_Y = nativeState.find(
"Y")->second;
1954 for (
size_t i = 0; i < m_dataSize; i++) {
1955 double T = (*m_data)[offset_T + i * m_stride];
1956 m_sol->thermo()->setState_TPY(
1957 T, P, m_data->data() + offset_Y + i * m_stride);
1958 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
1960 }
else if (missingProps.size()) {
1962 "Incomplete state information: missing '{}'.",
1963 ba::join(missingProps,
"', '"));
1968 for (
const auto& [name, value] : path) {
1969 if (!exclude.count(name)) {
1970 m_meta[name] = value;
1974 if (m_meta.hasKey(
"transport-model")) {
1975 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1984 vector<T> data(slice.size());
1985 const auto& vec = extra.
asVector<T>();
1986 for (
size_t k = 0; k < slice.size(); ++k) {
1987 data[k] = vec[slice[k]];
1995AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
1997 vector<vector<T>> data(slice.size());
1998 const auto& vec = extra.asVector<vector<T>>();
1999 for (
size_t k = 0; k < slice.size(); ++k) {
2000 data[k] = vec[slice[k]];
2008void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2010 T value = data.as<T>();
2011 if (extra.isVector<T>()) {
2012 auto& vec = extra.asVector<T>();
2013 for (
size_t k = 0; k < slice.size(); ++k) {
2014 vec[slice[k]] = value;
2017 throw CanteraError(
"SolutionArray::setScalar",
2018 "Incompatible input data: unable to assign '{}' data to '{}'.",
2019 data.type_str(), extra.type_str());
2024void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2026 size_t size = slice.size();
2027 if (extra.vectorSize() == size && data.vectorSize() == size) {
2031 if (extra.matrixShape().first == size && data.vectorSize() == size) {
2035 if (extra.type_str() != data.type_str()) {
2037 throw CanteraError(
"SolutionArray::setSingle",
2038 "Incompatible types: expected '{}' but received '{}'.",
2039 extra.type_str(), data.type_str());
2041 const auto& vData = data.asVector<T>();
2042 if (vData.size() != size) {
2043 throw CanteraError(
"SolutionArray::setSingle",
2044 "Invalid input data size: expected {} entries but received {}.",
2045 size, vData.size());
2047 auto& vec = extra.asVector<T>();
2048 for (
size_t k = 0; k < size; ++k) {
2049 vec[slice[k]] = vData[k];
2054void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2056 if (!data.isMatrix<T>()) {
2057 throw CanteraError(
"SolutionArray::setMulti",
2058 "Invalid input data shape: inconsistent number of columns.");
2060 size_t size = slice.size();
2061 auto [rows, cols] = data.matrixShape();
2062 if (extra.matrixShape().first == size && rows == size) {
2066 if (extra.vectorSize() == size && rows == size) {
2070 if (extra.type_str() != data.type_str()) {
2072 throw CanteraError(
"SolutionArray::setMulti",
2073 "Incompatible types: expected '{}' but received '{}'.",
2074 extra.type_str(), data.type_str());
2077 throw CanteraError(
"SolutionArray::setMulti",
2078 "Invalid input data shape: expected {} rows but received {}.",
2081 if (extra.matrixShape().second != cols) {
2082 throw CanteraError(
"SolutionArray::setMulti",
2083 "Invalid input data shape: expected {} columns but received {}.",
2084 extra.matrixShape().second, cols);
2086 const auto& vData = data.asVector<vector<T>>();
2087 auto& vec = extra.asVector<vector<T>>();
2088 for (
size_t k = 0; k < slice.size(); ++k) {
2089 vec[slice[k]] = vData[k];
2094void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2097 if (value.is<
void>()) {
2098 defaultValue = vector<T>(1)[0];
2100 defaultValue = value.as<T>();
2102 extra.asVector<T>().resize(size, defaultValue);
2106void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2108 vector<T> defaultValue;
2109 if (value.is<
void>()) {
2110 defaultValue = vector<T>(extra.matrixShape().second);
2112 defaultValue = value.as<vector<T>>();
2114 extra.asVector<vector<T>>().resize(size, defaultValue);
2118void resetSingle(AnyValue& extra,
const vector<int>& slice)
2120 T defaultValue = vector<T>(1)[0];
2121 vector<T>& data = extra.asVector<T>();
2122 for (
size_t k = 0; k < slice.size(); ++k) {
2123 data[slice[k]] = defaultValue;
2128void resetMulti(AnyValue& extra,
const vector<int>& slice)
2130 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2131 vector<vector<T>>& data = extra.asVector<vector<T>>();
2132 for (
size_t k = 0; k < slice.size(); ++k) {
2133 data[slice[k]] = defaultValue;
2138void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2140 extra.asVector<T>()[loc] = value.as<T>();
2144void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2146 const auto& value = data.asVector<T>();
2147 auto& vec = extra.asVector<vector<T>>();
2148 if (value.size() != vec[loc].size()) {
2149 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2150 "New element size {} does not match existing column size {}.",
2151 value.size(), vec[loc].size());
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
void setLoc(int line, int column)
For values which are derived from an input file, set the line and column of this value in that file.
A map of string keys to values whose type can vary at runtime.
bool empty() const
Return boolean indicating whether AnyMap is empty.
void clear()
Erase all items in the mapping.
const AnyValue & at(const string &key) const
Get the value of the item stored in key.
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
A wrapper for a variable whose type is determined at runtime.
bool isVector() const
Returns true if the held value is a vector of the specified type, such as vector<double>.
pair< size_t, size_t > matrixShape() const
Returns rows and columns of a matrix.
size_t vectorSize() const
Returns size of the held vector.
bool isScalar() const
Returns true if the held value is a scalar type (such as double, long int, string,...
bool is() const
Returns true if the held value is of the specified type.
const vector< T > & asVector(size_t nMin=npos, size_t nMax=npos) const
Return the held value, if it is a vector of type T.
const T & as() const
Get the value of this key as the specified type.
string type_str() const
Returns a string specifying the type of the held value.
Base class for exceptions thrown by Cantera classes.
const char * what() const override
Get a description of the error.
virtual string getMessage() const
Method overridden by derived classes to format the error message.
An array index is out of range.
An error indicating that an unimplemented function has been called.
A wrapper class handling storage to HDF.
pair< size_t, set< string > > contents(const string &id) const
Retrieve contents of file from a specified location.
void writeAttributes(const string &id, const AnyMap &meta)
Write attributes to a specified location.
void deleteGroup(const string &id)
Delete group.
AnyMap readAttributes(const string &id, bool recursive) const
Read attributes from a specified location.
AnyValue readData(const string &id, const string &name, size_t rows, size_t cols=npos) const
Read dataset from a specified location.
bool checkGroup(const string &id, bool permissive=false)
Check whether path location exists.
void setCompressionLevel(int level)
Set compression level (0..9)
void writeData(const string &id, const string &name, const AnyValue &data)
Write dataset to a specified location.
void tokenizePath(const string &in_val, vector< string > &v)
This function separates a string up into tokens according to the location of path separators.
string toLowerCopy(const string &input)
Convert to lower case.
U len(const T &container)
Get the size of a container, cast to a signed integer type.
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
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...