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 if (std::ifstream(fname).good()) {
1345 data = AnyMap::fromYamlFile(fname);
1347 writeHeader(data, name, desc, overwrite);
1348 writeEntry(data, name, sub,
true);
1351 std::ofstream out(fname);
1352 out << data.toYamlString();
1353 AnyMap::clearCachedFile(fname);
1357 "Unknown file extension '{}'.", extension);
1360AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1367const AnyMap& locateField(
const AnyMap& root,
const string& name)
1374 vector<string> tokens;
1375 tokenizePath(name, tokens);
1376 const AnyMap* ptr = &root;
1378 for (
auto& field : tokens) {
1379 path +=
"/" + field;
1380 const AnyMap& sub = *ptr;
1381 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1382 throw CanteraError(
"SolutionArray::locateField",
1383 "No field or solution with name '{}'.", path);
1385 ptr = &sub[field].as<AnyMap>();
1392 auto sub = locateField(root, name);
1394 for (
const auto& [key, value] : sub) {
1395 if (!sub[key].is<AnyMap>()) {
1396 header[key] = value;
1402AnyMap SolutionArray::restore(
const string& fname,
1403 const string& name,
const string& sub)
1405 size_t dot = fname.find_last_of(
".");
1408 if (extension ==
"csv") {
1410 "CSV import not implemented; if using Python, data can be imported via "
1411 "'read_csv' instead.");
1413 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1414 readEntry(fname, name, sub);
1415 header = readHeader(fname, name);
1416 }
else if (extension ==
"yaml" || extension ==
"yml") {
1417 const AnyMap& root = AnyMap::fromYamlFile(fname);
1418 readEntry(root, name, sub);
1419 header = readHeader(root, name);
1422 "Unknown file extension '{}'; supported extensions include "
1423 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1428void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1430 if (!m_extra->count(name)) {
1432 "Component '{}' does not exist.", name);
1434 auto& extra = (*m_extra)[name];
1435 if (!extra.is<
void>()) {
1437 "Component '{}' is already initialized.", name);
1440 if (value.
is<
long int>()) {
1441 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1442 }
else if (value.
is<
double>()) {
1443 extra = vector<double>(m_dataSize, value.
as<
double>());
1444 }
else if (value.
is<
string>()) {
1445 extra = vector<string>(m_dataSize, value.
as<
string>());
1446 }
else if (value.
isVector<
long int>()) {
1447 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1448 }
else if (value.
isVector<
double>()) {
1449 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1450 }
else if (value.
isVector<
string>()) {
1451 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1452 }
else if (value.
is<
void>()) {
1457 "Unable to initialize component '{}' with type '{}'.",
1463 "Encountered incompatible value for initializing component '{}':\n{}",
1468void SolutionArray::_resizeExtra(
const string& name,
const AnyValue& value)
1470 if (!m_extra->count(name)) {
1472 "Component '{}' does not exist.", name);
1474 auto& extra = (*m_extra)[name];
1475 if (extra.is<
void>()) {
1481 if (extra.isVector<
long int>()) {
1482 resizeSingle<long int>(extra, m_dataSize, value);
1483 }
else if (extra.isVector<
double>()) {
1484 resizeSingle<double>(extra, m_dataSize, value);
1485 }
else if (extra.isVector<
string>()) {
1486 resizeSingle<string>(extra, m_dataSize, value);
1487 }
else if (extra.isVector<vector<double>>()) {
1488 resizeMulti<double>(extra, m_dataSize, value);
1489 }
else if (extra.isVector<vector<long int>>()) {
1490 resizeMulti<long int>(extra, m_dataSize, value);
1491 }
else if (extra.isVector<vector<string>>()) {
1492 resizeMulti<string>(extra, m_dataSize, value);
1495 "Unable to resize using type '{}'.", extra.type_str());
1500 "Encountered incompatible value for resizing component '{}':\n{}",
1505void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1507 if (!m_extra->count(name)) {
1509 "Extra component '{}' does not exist.", name);
1512 auto& extra = m_extra->at(name);
1513 if (data.
is<
void>() && m_size == m_dataSize) {
1520 if (extra.is<
void>()) {
1521 if (m_size != m_dataSize) {
1523 "Unable to replace '{}' for sliced data.", name);
1533 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1538 "Unable to initialize '{}' with empty array when SolutionArray is not "
1541 _initExtra(name, data);
1542 _resizeExtra(name, data);
1546 if (data.
is<
long int>()) {
1547 setScalar<long int>(extra, data, m_active);
1548 }
else if (data.
is<
double>()) {
1549 setScalar<double>(extra, data, m_active);
1550 }
else if (data.
is<
string>()) {
1551 setScalar<string>(extra, data, m_active);
1552 }
else if (data.
isVector<
long int>()) {
1553 setSingle<long int>(extra, data, m_active);
1554 }
else if (data.
isVector<
double>()) {
1555 setSingle<double>(extra, data, m_active);
1556 }
else if (data.
isVector<
string>()) {
1557 setSingle<string>(extra, data, m_active);
1558 }
else if (data.
isVector<vector<long int>>()) {
1559 setMulti<long int>(extra, data, m_active);
1560 }
else if (data.
isVector<vector<double>>()) {
1561 setMulti<double>(extra, data, m_active);
1562 }
else if (data.
isVector<vector<string>>()) {
1563 setMulti<string>(extra, data, m_active);
1566 "Unable to set sliced data for component '{}' with type '{}'.",
1571string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1575 const auto& nativeState = m_sol->thermo()->nativeState();
1576 bool usesNativeState =
false;
1577 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1579 for (
const auto& item : m_sol->thermo()->fullStates()) {
1582 usesNativeState =
true;
1583 for (
size_t i = 0; i < item.size(); i++) {
1585 name = string(1, item[i]);
1586 if (surf && (name ==
"X" || name ==
"Y")) {
1589 usesNativeState =
false;
1592 if (names.count(name)) {
1594 usesNativeState &= nativeState.count(name) > 0;
1595 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1597 usesNativeState &= nativeState.count(name) > 0;
1604 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1609 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1611 return "legacySurf";
1613 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1615 return "legacyInlet";
1618 "Detected incomplete thermodynamic information. Full states for a '{}' "
1619 "phase\nmay be defined by the following modes:\n'{}'\n"
1620 "Available data are: '{}'", m_sol->thermo()->type(),
1621 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1623 if (usesNativeState && native) {
1629set<string> SolutionArray::_stateProperties(
1630 const string& mode,
bool alias)
1633 if (mode ==
"native") {
1634 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1635 states.insert(alias ? aliasMap.at(name) : name);
1638 for (
const auto& m : mode) {
1639 const string name = string(1, m);
1640 states.insert(alias ? aliasMap.at(name) : name);
1647string getName(
const set<string>& names,
const string& name)
1649 if (names.count(name)) {
1652 const auto& alias = aliasMap.at(name);
1653 if (names.count(alias)) {
1659void SolutionArray::readEntry(
const string& fname,
const string& name,
1665 "Group name specifying root location must not be empty.");
1668 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1670 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1676 "Group name specifying data entry is empty.");
1679 auto [size, names] = file.
contents(path);
1681 if (m_meta.hasKey(
"size")) {
1683 resize(m_meta[
"size"].as<long int>());
1684 m_meta.erase(
"size");
1685 }
else if (m_meta.hasKey(
"api-shape")) {
1687 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1688 m_meta.erase(
"api-shape");
1691 resize(
static_cast<int>(size));
1699 string mode = _detectMode(names);
1700 set<string> states = _stateProperties(mode);
1701 if (states.count(
"C")) {
1702 if (names.count(
"X")) {
1705 mode = mode.substr(0, 2) +
"X";
1706 }
else if (names.count(
"Y")) {
1709 mode = mode.substr(0, 2) +
"Y";
1714 size_t nSpecies = m_sol->thermo()->nSpecies();
1715 size_t nState = m_sol->thermo()->stateSize();
1716 const auto& nativeStates = m_sol->thermo()->nativeState();
1717 if (mode ==
"native") {
1719 for (
const auto& [name,
offset] : nativeStates) {
1720 if (name ==
"X" || name ==
"Y") {
1722 data = file.
readData(path, name, m_size, nSpecies);
1723 auto prop = data.
asVector<vector<double>>();
1724 for (
size_t i = 0; i < m_dataSize; i++) {
1725 std::copy(prop[i].begin(), prop[i].end(),
1726 m_data->data() +
offset + i * m_stride);
1730 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1731 setComponent(name, data);
1734 }
else if (mode ==
"TPX") {
1736 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1737 vector<double> T = std::move(data.
asVector<
double>());
1738 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1739 vector<double> P = std::move(data.
asVector<
double>());
1740 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1741 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1742 for (
size_t i = 0; i < m_dataSize; i++) {
1743 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1744 m_sol->thermo()->setState_TP(T[i], P[i]);
1745 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1747 }
else if (mode ==
"TDX") {
1749 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1750 vector<double> T = std::move(data.
asVector<
double>());
1751 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1752 vector<double> D = std::move(data.
asVector<
double>());
1753 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1754 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1755 for (
size_t i = 0; i < m_dataSize; i++) {
1756 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1757 m_sol->thermo()->setState_TD(T[i], D[i]);
1758 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1760 }
else if (mode ==
"TPY") {
1762 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1763 vector<double> T = std::move(data.
asVector<
double>());
1764 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1765 vector<double> P = std::move(data.
asVector<
double>());
1766 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1767 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1768 for (
size_t i = 0; i < m_dataSize; i++) {
1769 m_sol->thermo()->setMassFractions_NoNorm(Y[i].data());
1770 m_sol->thermo()->setState_TP(T[i], P[i]);
1771 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1773 }
else if (mode ==
"legacySurf") {
1776 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1777 vector<double> T = std::move(data.
asVector<
double>());
1778 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1779 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1780 for (
size_t i = 0; i < m_dataSize; i++) {
1781 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1782 m_sol->thermo()->setTemperature(T[i]);
1783 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1786 "Detected legacy HDF format with incomplete state information\nfor name "
1787 "'{}' (pressure missing).", path);
1788 }
else if (mode ==
"") {
1790 "Data are not consistent with full state modes.");
1793 "Import of '{}' data is not supported.", mode);
1797 if (m_meta.hasKey(
"components")) {
1798 const auto& components = m_meta[
"components"].asVector<
string>();
1800 for (
const auto& name : components) {
1801 if (hasComponent(name) || name ==
"X" || name ==
"Y") {
1804 addExtra(name, back);
1806 data = file.
readData(path, name, m_dataSize);
1807 setComponent(name, data);
1810 m_meta.erase(
"components");
1813 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1814 for (
const auto& name : names) {
1815 if (!hasComponent(name) && name !=
"X" && name !=
"Y") {
1818 data = file.
readData(path, name, m_dataSize);
1819 setComponent(name, data);
1824 if (m_meta.hasKey(
"transport-model")) {
1825 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1829void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1833 "Field name specifying root location must not be empty.");
1835 auto path = locateField(root, name);
1836 if (path.hasKey(
"generator") && sub !=
"") {
1838 path = locateField(root, name +
"/" + sub);
1839 }
else if (sub ==
"" && path.hasKey(
"data")) {
1841 path = locateField(root, name +
"/data");
1846 if (path.hasKey(
"size")) {
1848 resize(path[
"size"].asInt());
1849 }
else if (path.hasKey(
"api-shape")) {
1851 auto& shape = path[
"api-shape"].asVector<
long int>();
1855 size = path.getInt(
"points", 0);
1856 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1860 resize(
static_cast<int>(size));
1865 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1866 set<string> names = path.keys();
1867 size_t nState = m_sol->thermo()->stateSize();
1868 if (m_dataSize == 0) {
1870 }
else if (m_dataSize == 1) {
1872 string mode = _detectMode(names,
false);
1873 if (mode ==
"TPY") {
1874 double T = path[getName(names,
"T")].asDouble();
1875 double P = path[getName(names,
"P")].asDouble();
1876 auto Y = path[
"mass-fractions"].asMap<
double>();
1877 m_sol->thermo()->setState_TPY(T, P, Y);
1878 }
else if (mode ==
"TPC") {
1879 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1880 double T = path[getName(names,
"T")].asDouble();
1881 double P = path[
"pressure"].asDouble();
1882 m_sol->thermo()->setState_TP(T, P);
1883 auto cov = path[
"coverages"].asMap<
double>();
1884 surf->setCoveragesByName(cov);
1885 }
else if (mode ==
"legacyInlet") {
1888 double T = path[getName(names,
"T")].asDouble();
1889 auto Y = path[
"mass-fractions"].asMap<
double>();
1890 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1892 "Detected legacy YAML format with incomplete state information\n"
1893 "for name '{}' (pressure missing).", name +
"/" + sub);
1894 }
else if (mode ==
"") {
1896 "Data are not consistent with full state modes.");
1899 "Import of '{}' data is not supported.", mode);
1901 m_sol->thermo()->saveState(nState, m_data->data());
1902 auto props = _stateProperties(mode,
true);
1903 exclude.insert(props.begin(), props.end());
1906 if (path.hasKey(
"components")) {
1907 const auto& components = path[
"components"].asVector<
string>();
1909 for (
const auto& name : components) {
1910 if (hasComponent(name)) {
1913 addExtra(name, back);
1915 setComponent(name, path[name]);
1916 exclude.insert(name);
1920 for (
const auto& [name, value] : path) {
1921 if (value.isVector<
double>()) {
1922 const vector<double>& data = value.asVector<
double>();
1923 if (data.size() == m_dataSize) {
1924 if (!hasComponent(name)) {
1927 setComponent(name, value);
1928 exclude.insert(name);
1935 const auto& nativeState = m_sol->thermo()->nativeState();
1937 set<string> missingProps;
1938 for (
const auto& [name,
offset] : nativeState) {
1939 if (exclude.count(name)) {
1942 missingProps.insert(name);
1946 set<string> TY = {
"T",
"Y"};
1947 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
1949 double P = path[
"pressure"].asDouble();
1950 const size_t offset_T = nativeState.find(
"T")->second;
1951 const size_t offset_D = nativeState.find(
"D")->second;
1952 const size_t offset_Y = nativeState.find(
"Y")->second;
1953 for (
size_t i = 0; i < m_dataSize; i++) {
1954 double T = (*m_data)[offset_T + i * m_stride];
1955 m_sol->thermo()->setState_TPY(
1956 T, P, m_data->data() + offset_Y + i * m_stride);
1957 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
1959 }
else if (missingProps.size()) {
1961 "Incomplete state information: missing '{}'.",
1962 ba::join(missingProps,
"', '"));
1967 for (
const auto& [name, value] : path) {
1968 if (!exclude.count(name)) {
1969 m_meta[name] = value;
1973 if (m_meta.hasKey(
"transport-model")) {
1974 m_sol->setTransportModel(m_meta[
"transport-model"].asString());
1983 vector<T> data(slice.size());
1984 const auto& vec = extra.
asVector<T>();
1985 for (
size_t k = 0; k < slice.size(); ++k) {
1986 data[k] = vec[slice[k]];
1994AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
1996 vector<vector<T>> data(slice.size());
1997 const auto& vec = extra.asVector<vector<T>>();
1998 for (
size_t k = 0; k < slice.size(); ++k) {
1999 data[k] = vec[slice[k]];
2007void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2009 T value = data.as<T>();
2010 if (extra.isVector<T>()) {
2011 auto& vec = extra.asVector<T>();
2012 for (
size_t k = 0; k < slice.size(); ++k) {
2013 vec[slice[k]] = value;
2016 throw CanteraError(
"SolutionArray::setScalar",
2017 "Incompatible input data: unable to assign '{}' data to '{}'.",
2018 data.type_str(), extra.type_str());
2023void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2025 size_t size = slice.size();
2026 if (extra.vectorSize() == size && data.vectorSize() == size) {
2030 if (extra.matrixShape().first == size && data.vectorSize() == size) {
2034 if (extra.type_str() != data.type_str()) {
2036 throw CanteraError(
"SolutionArray::setSingle",
2037 "Incompatible types: expected '{}' but received '{}'.",
2038 extra.type_str(), data.type_str());
2040 const auto& vData = data.asVector<T>();
2041 if (vData.size() != size) {
2042 throw CanteraError(
"SolutionArray::setSingle",
2043 "Invalid input data size: expected {} entries but received {}.",
2044 size, vData.size());
2046 auto& vec = extra.asVector<T>();
2047 for (
size_t k = 0; k < size; ++k) {
2048 vec[slice[k]] = vData[k];
2053void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2055 if (!data.isMatrix<T>()) {
2056 throw CanteraError(
"SolutionArray::setMulti",
2057 "Invalid input data shape: inconsistent number of columns.");
2059 size_t size = slice.size();
2060 auto [rows, cols] = data.matrixShape();
2061 if (extra.matrixShape().first == size && rows == size) {
2065 if (extra.vectorSize() == size && rows == size) {
2069 if (extra.type_str() != data.type_str()) {
2071 throw CanteraError(
"SolutionArray::setMulti",
2072 "Incompatible types: expected '{}' but received '{}'.",
2073 extra.type_str(), data.type_str());
2076 throw CanteraError(
"SolutionArray::setMulti",
2077 "Invalid input data shape: expected {} rows but received {}.",
2080 if (extra.matrixShape().second != cols) {
2081 throw CanteraError(
"SolutionArray::setMulti",
2082 "Invalid input data shape: expected {} columns but received {}.",
2083 extra.matrixShape().second, cols);
2085 const auto& vData = data.asVector<vector<T>>();
2086 auto& vec = extra.asVector<vector<T>>();
2087 for (
size_t k = 0; k < slice.size(); ++k) {
2088 vec[slice[k]] = vData[k];
2093void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2096 if (value.is<
void>()) {
2097 defaultValue = vector<T>(1)[0];
2099 defaultValue = value.as<T>();
2101 extra.asVector<T>().resize(size, defaultValue);
2105void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2107 vector<T> defaultValue;
2108 if (value.is<
void>()) {
2109 defaultValue = vector<T>(extra.matrixShape().second);
2111 defaultValue = value.as<vector<T>>();
2113 extra.asVector<vector<T>>().resize(size, defaultValue);
2117void resetSingle(AnyValue& extra,
const vector<int>& slice)
2119 T defaultValue = vector<T>(1)[0];
2120 vector<T>& data = extra.asVector<T>();
2121 for (
size_t k = 0; k < slice.size(); ++k) {
2122 data[slice[k]] = defaultValue;
2127void resetMulti(AnyValue& extra,
const vector<int>& slice)
2129 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2130 vector<vector<T>>& data = extra.asVector<vector<T>>();
2131 for (
size_t k = 0; k < slice.size(); ++k) {
2132 data[slice[k]] = defaultValue;
2137void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2139 extra.asVector<T>()[loc] = value.as<T>();
2143void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2145 const auto& value = data.asVector<T>();
2146 auto& vec = extra.asVector<vector<T>>();
2147 if (value.size() != vec[loc].size()) {
2148 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2149 "New element size {} does not match existing column size {}.",
2150 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...