16 #include <boost/algorithm/string.hpp>
21 namespace ba = boost::algorithm;
23 const std::map<std::string, std::string> aliasMap = {
27 {
"Y",
"mass-fractions"},
28 {
"X",
"mole-fractions"},
30 {
"U",
"specific-internal-energy"},
31 {
"V",
"specific-volume"},
32 {
"H",
"specific-enthalpy"},
33 {
"S",
"specific-entropy"},
34 {
"Q",
"vapor-fraction"},
40 SolutionArray::SolutionArray(
const shared_ptr<Solution>& sol,
41 int size,
const AnyMap& meta)
48 throw CanteraError(
"SolutionArray::SolutionArray",
49 "Unable to create SolutionArray from invalid Solution object.");
51 m_stride = m_sol->thermo()->stateSize();
52 m_data = make_shared<vector<double>>(m_dataSize * m_stride, 0.);
53 m_extra = make_shared<map<string, AnyValue>>();
54 m_order = make_shared<map<int, string>>();
55 for (
size_t i = 0; i < m_dataSize; ++i) {
56 m_active.push_back(
static_cast<int>(i));
60 m_apiShape[0] =
static_cast<long>(m_dataSize);
63 SolutionArray::SolutionArray(
const SolutionArray& other,
64 const vector<int>& selected)
66 , m_size(selected.size())
67 , m_dataSize(other.m_data->size())
68 , m_stride(other.m_stride)
69 , m_data(other.m_data)
70 , m_extra(other.m_extra)
71 , m_order(other.m_order)
75 for (
auto loc : m_active) {
76 if (loc < 0 || loc >= (
int)m_dataSize) {
77 IndexError(
"SolutionArray::SolutionArray",
"indices", loc, m_dataSize);
80 set<int> unique(selected.begin(), selected.end());
81 if (unique.size() < selected.size()) {
82 throw CanteraError(
"SolutionArray::SolutionArray",
"Indices must be unique.");
89 void resetSingle(AnyValue& extra,
const vector<int>& slice);
92 AnyValue getSingle(
const AnyValue& extra,
const vector<int>& slice);
95 void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
98 void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value);
101 void resetMulti(AnyValue& extra,
const vector<int>& slice);
104 AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice);
107 void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
110 void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value);
113 void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value);
116 void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data);
119 void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice);
123 void SolutionArray::reset()
125 size_t nState = m_sol->thermo()->stateSize();
126 vector<double> state(nState);
127 m_sol->thermo()->saveState(state);
128 for (
size_t k = 0; k < m_size; ++k) {
129 std::copy(state.begin(), state.end(), m_data->data() + m_active[k] * m_stride);
131 for (
auto& [key, extra] : *m_extra) {
132 if (extra.is<
void>()) {
134 }
else if (extra.isVector<
double>()) {
135 resetSingle<double>(extra, m_active);
136 }
else if (extra.isVector<
long int>()) {
137 resetSingle<long int>(extra, m_active);
138 }
else if (extra.isVector<
string>()) {
139 resetSingle<string>(extra, m_active);
140 }
else if (extra.isVector<vector<double>>()) {
141 resetMulti<double>(extra, m_active);
142 }
else if (extra.isVector<vector<long int>>()) {
143 resetMulti<long int>(extra, m_active);
144 }
else if (extra.isVector<vector<string>>()) {
145 resetMulti<string>(extra, m_active);
148 "Unable to reset component '{}' with type '{}'.",
149 key, extra.type_str());
154 void SolutionArray::resize(
int size)
158 "Resize is ambiguous for multi-dimensional arrays; use setApiShape "
161 if (m_data.use_count() > 1) {
163 "Unable to resize as data are shared by multiple objects.");
165 _resize(
static_cast<size_t>(size));
166 m_apiShape[0] =
static_cast<long>(size);
169 void SolutionArray::setApiShape(
const vector<long int>& shape)
172 for (
auto dim : shape) {
175 if (m_shared && size != m_size) {
177 "Unable to set shape of shared data as sizes are inconsistent:\n"
178 "active size is {} but shape implies {}.", m_size, size);
180 if (!m_shared && size != m_dataSize) {
181 if (m_data.use_count() > 1) {
183 "Unable to set shape as data are shared by multiple objects.");
190 void SolutionArray::_resize(
size_t size)
194 m_data->resize(m_dataSize * m_stride, 0.);
195 for (
auto& [key, data] : *m_extra) {
199 for (
size_t i = 0; i < m_dataSize; ++i) {
200 m_active.push_back(
static_cast<int>(i));
206 vector<string> doubleColumn(
string name,
const vector<double>& comp,
212 string notation = fmt::format(
"{{:{}.{}g}}", width, (width - 1) / 2);
213 int csize =
static_cast<int>(comp.size());
214 int dots = csize + 1;
216 for (
const auto& val : comp) {
218 raw.push_back(boost::trim_copy(fmt::format(notation, val)));
221 dots = (rows + 1) / 2;
222 for (
int row = 0; row < dots; row++) {
223 data.push_back(comp[row]);
224 raw.push_back(boost::trim_copy(fmt::format(notation, comp[row])));
226 for (
int row = csize - rows / 2; row < csize; row++) {
227 data.push_back(comp[row]);
228 raw.push_back(boost::trim_copy(fmt::format(notation, comp[row])));
233 bool isFloat =
false;
234 bool isScientific =
false;
238 for (
const auto& repr : raw) {
240 if (name[0] ==
'-') {
242 name = name.substr(1);
248 tail = name.find(
'e') - name.find(
'.');
251 tail = std::max(tail, name.find(
'e') - name.find(
'.'));
259 head = std::max(head, name.find(
'.'));
260 tail = std::max(tail, name.size() - name.find(
'.'));
263 head = std::max(head, name.size());
266 size_t maxLen = std::max((
size_t)4, head + tail + exp + isFloat + 1);
267 size_t over = std::max(0, (
int)name.size() - (
int)maxLen);
270 notation = fmt::format(
" {{:>{}.{}e}}", over + maxLen, tail);
271 }
else if (isFloat) {
273 notation = fmt::format(
" {{:>{}.{}f}}", over + maxLen, tail);
276 notation = fmt::format(
" {{:>{}.0f}}", over + maxLen);
278 maxLen = fmt::format(notation, 0.).size();
281 string section = fmt::format(
"{{:>{}}}", maxLen);
282 vector<string> col = {fmt::format(section, name)};
284 for (
const auto& val : data) {
285 col.push_back(fmt::format(notation, val));
288 col.push_back(fmt::format(section,
"..."));
294 vector<string> integerColumn(
string name,
const vector<long int>& comp,
298 vector<long int> data;
299 string notation = fmt::format(
"{{:{}}}", width);
301 int csize =
static_cast<int>(comp.size());
302 int dots = csize + 1;
304 for (
const auto& val : comp) {
306 string formatted = boost::trim_copy(fmt::format(notation, val));
307 if (formatted[0] ==
'-') {
308 formatted = formatted.substr(1);
310 maxLen = std::max(maxLen, formatted.size());
313 dots = (rows + 1) / 2;
314 for (
int row = 0; row < dots; row++) {
315 data.push_back(comp[row]);
316 string formatted = boost::trim_copy(fmt::format(notation, comp[row]));
317 if (formatted[0] ==
'-') {
318 formatted = formatted.substr(1);
320 maxLen = std::max(maxLen, formatted.size());
322 for (
int row = csize - rows / 2; row < csize; row++) {
323 data.push_back(comp[row]);
324 string formatted = boost::trim_copy(fmt::format(notation, comp[row]));
325 if (formatted[0] ==
'-') {
326 formatted = formatted.substr(1);
328 maxLen = std::max(maxLen, formatted.size());
334 notation = fmt::format(
"{{:<{}}}", maxLen);
337 maxLen = std::max(maxLen, name.size());
338 notation = fmt::format(
" {{:>{}}}", maxLen + 1);
342 vector<string> col = {fmt::format(notation, name)};
344 for (
const auto& val : data) {
345 col.push_back(fmt::format(notation, val));
348 col.push_back(fmt::format(notation,
".."));
354 vector<string> stringColumn(
string name,
const vector<string>& comp,
359 string notation = fmt::format(
"{{:{}}}", width);
361 int csize =
static_cast<int>(comp.size());
362 int dots = csize + 1;
364 for (
const auto& val : comp) {
366 maxLen = std::max(maxLen,
367 boost::trim_copy(fmt::format(notation, val)).size());
370 dots = (rows + 1) / 2;
371 for (
int row = 0; row < dots; row++) {
372 data.push_back(comp[row]);
373 maxLen = std::max(maxLen,
374 boost::trim_copy(fmt::format(notation, comp[row])).size());
376 for (
int row = csize - rows / 2; row < csize; row++) {
377 data.push_back(comp[row]);
378 maxLen = std::max(maxLen,
379 boost::trim_copy(fmt::format(notation, comp[row])).size());
384 notation = fmt::format(
" {{:>{}}}", maxLen);
385 vector<string> col = {fmt::format(notation, name)};
387 for (
const auto& val : data) {
388 col.push_back(fmt::format(notation, val));
391 col.push_back(fmt::format(notation,
"..."));
397 vector<string> formatColumn(
string name,
const AnyValue& comp,
int rows,
int width)
399 if (comp.isVector<
double>()) {
400 return doubleColumn(name, comp.asVector<
double>(), rows, width);
402 if (comp.isVector<
long int>()) {
403 return integerColumn(name, comp.asVector<
long int>(), rows, width);
405 if (comp.isVector<
string>()) {
406 return stringColumn(name, comp.asVector<
string>(), rows, width);
412 if (comp.isVector<vector<double>>()) {
413 repr =
"[ <double> ]";
414 size =
len(comp.asVector<vector<double>>());
415 }
else if (comp.isVector<vector<long int>>()) {
416 repr =
"[ <long int> ]";
417 size =
len(comp.asVector<vector<long int>>());
418 }
else if (comp.isVector<vector<string>>()) {
419 repr =
"[ <string> ]";
420 size =
len(comp.asVector<vector<string>>());
423 "formatColumn",
"Encountered invalid data for component '{}'.", name);
425 size_t maxLen = std::max(repr.size(), name.size());
428 string notation = fmt::format(
" {{:>{}}}", maxLen);
429 repr = fmt::format(notation, repr);
430 vector<string> col = {fmt::format(notation, name)};
432 for (
int row = 0; row < size; row++) {
436 int dots = (rows + 1) / 2;
437 for (
int row = 0; row < dots; row++) {
440 col.push_back(fmt::format(notation,
"..."));
441 for (
int row = size - rows / 2; row < size; row++) {
450 string SolutionArray::info(
const vector<string>& keys,
int rows,
int width)
452 fmt::memory_buffer b;
454 vector<string> components;
458 components = componentNames();
462 vector<long int> index;
463 for (
const auto ix : m_active) {
466 vector<vector<string>> cols = {integerColumn(
"", index, rows, col_width)};
467 vector<vector<string>> tail;
468 size_t size = cols.back().size();
474 int back =
len(components) - 1;
475 int fLen =
len(cols.back()[0]);
479 while (!done && front <= back) {
481 while (bLen + sep <= fLen && front <= back) {
483 key = components[back];
484 auto col = formatColumn(key, getComponent(key), rows, col_width);
485 if (
len(col[0]) + fLen + bLen + sep > width) {
490 bLen +=
len(tail.back()[0]);
493 if (done || front > back) {
496 while (fLen <= bLen + sep && front <= back) {
498 key = components[front];
499 auto col = formatColumn(key, getComponent(key), rows, col_width);
500 if (
len(col[0]) + fLen + bLen + sep > width) {
505 fLen +=
len(cols.back()[0]);
509 if (cols.size() + tail.size() < components.size() + 1) {
511 cols.push_back(vector<string>(size + 1,
" ..."));
514 cols.insert(cols.end(), tail.rbegin(), tail.rend());
517 for (
size_t row = 0; row < size; row++) {
518 for (
const auto& col : cols) {
525 fmt_append(b,
"\n[{} rows x {} components; state='{}']",
526 m_size, components.size(), m_sol->thermo()->nativeMode());
529 return to_string(b) + err.
what();
534 shared_ptr<ThermoPhase> SolutionArray::thermo()
536 return m_sol->thermo();
539 vector<string> SolutionArray::componentNames()
const
541 vector<string> components;
544 while (m_order->count(pos)) {
545 components.push_back(m_order->at(pos));
550 auto phase = m_sol->thermo();
551 for (
auto code : phase->nativeMode()) {
552 string name = string(1, code);
553 if (name ==
"X" || name ==
"Y") {
554 for (
auto& spc : phase->speciesNames()) {
555 components.push_back(spc);
558 components.push_back(name);
564 while (m_order->count(pos)) {
565 components.push_back(m_order->at(pos));
572 void SolutionArray::addExtra(
const string& name,
bool back)
574 if (m_extra->count(name)) {
576 "Component '{}' already exists.", name);
580 if (m_order->size()) {
582 m_order->emplace(m_order->begin()->first - 1, name);
585 m_order->emplace(-1, name);
588 if (m_order->size()) {
590 m_order->emplace(m_order->rbegin()->first + 1, name);
593 m_order->emplace(0, name);
598 vector<string> SolutionArray::listExtra(
bool all)
const
600 vector<string> names;
602 while (m_order->count(pos)) {
603 const auto& name = m_order->at(pos);
604 if (all || !m_extra->at(name).is<
void>()) {
605 names.push_back(name);
612 while (m_order->count(pos)) {
613 const auto& name = m_order->at(pos);
614 if (all || !m_extra->at(name).is<
void>()) {
615 names.push_back(name);
622 bool SolutionArray::hasComponent(
const string& name)
const
624 if (m_extra->count(name)) {
628 if (m_sol->thermo()->speciesIndex(name) !=
npos) {
632 if (name ==
"X" || name ==
"Y") {
637 return (m_sol->thermo()->nativeState().count(name));
640 AnyValue SolutionArray::getComponent(
const string& name)
const
642 if (!hasComponent(name)) {
644 "Unknown component '{}'.", name);
648 if (m_extra->count(name)) {
650 const auto& extra = m_extra->at(name);
651 if (extra.is<
void>()) {
654 if (m_size == m_dataSize) {
657 if (extra.isVector<
long int>()) {
658 return getSingle<long int>(extra, m_active);
660 if (extra.isVector<
double>()) {
661 return getSingle<double>(extra, m_active);
663 if (extra.isVector<
string>()) {
664 return getSingle<string>(extra, m_active);
666 if (extra.isVector<vector<double>>()) {
667 return getMulti<double>(extra, m_active);
669 if (extra.isVector<vector<long int>>()) {
670 return getMulti<long int>(extra, m_active);
672 if (extra.isVector<vector<string>>()) {
673 return getMulti<string>(extra, m_active);
676 "Unable to get sliced data for component '{}' with type '{}'.",
677 name, extra.type_str());
681 vector<double> data(m_size);
682 size_t ix = m_sol->thermo()->speciesIndex(name);
685 ix = m_sol->thermo()->nativeState()[name];
688 ix += m_stride - m_sol->thermo()->nSpecies();
690 for (
size_t k = 0; k < m_size; ++k) {
691 data[k] = (*m_data)[m_active[k] * m_stride + ix];
697 bool isSimpleVector(
const AnyValue& any) {
704 void SolutionArray::setComponent(
const string& name,
const AnyValue& data)
706 if (!hasComponent(name)) {
708 "Unknown component '{}'.", name);
710 if (m_extra->count(name)) {
711 _setExtra(name, data);
718 "Invalid type of component '{}': expected simple array type, "
719 "but received '{}'.", name, data.
type_str());
721 if (size != m_size) {
723 "Invalid size of component '{}': expected size {} but received {}.",
727 auto& vec = data.
asVector<
double>();
728 size_t ix = m_sol->thermo()->speciesIndex(name);
730 ix = m_sol->thermo()->nativeState()[name];
732 ix += m_stride - m_sol->thermo()->nSpecies();
734 for (
size_t k = 0; k < m_size; ++k) {
735 (*m_data)[m_active[k] * m_stride + ix] = vec[k];
739 void SolutionArray::setLoc(
int loc,
bool restore)
741 size_t loc_ =
static_cast<size_t>(loc);
744 "Unable to set location in empty SolutionArray.");
745 }
else if (loc < 0) {
748 "Both current and buffered indices are invalid.");
751 }
else if (
static_cast<size_t>(m_active[loc_]) == m_loc) {
753 }
else if (loc_ >= m_size) {
754 throw IndexError(
"SolutionArray::setLoc",
"indices", loc_, m_size - 1);
756 m_loc =
static_cast<size_t>(m_active[loc_]);
758 size_t nState = m_sol->thermo()->stateSize();
759 m_sol->thermo()->restoreState(nState, m_data->data() + m_loc * m_stride);
763 void SolutionArray::updateState(
int loc)
766 size_t nState = m_sol->thermo()->stateSize();
767 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
770 vector<double> SolutionArray::getState(
int loc)
773 size_t nState = m_sol->thermo()->stateSize();
774 vector<double> out(nState);
775 m_sol->thermo()->saveState(out);
779 void SolutionArray::setState(
int loc,
const vector<double>& state)
781 size_t nState = m_sol->thermo()->stateSize();
782 if (state.size() != nState) {
784 "Expected array to have length {}, but received an array of length {}.",
785 nState, state.size());
788 m_sol->thermo()->restoreState(state);
789 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
792 void SolutionArray::normalize() {
793 auto phase = m_sol->thermo();
794 auto nativeState = phase->nativeState();
795 if (nativeState.size() < 3) {
798 size_t nState = phase->stateSize();
799 vector<double> out(nState);
800 if (nativeState.count(
"Y")) {
801 size_t offset = nativeState[
"Y"];
802 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
804 phase->setMassFractions(m_data->data() + m_loc * m_stride +
offset);
805 m_sol->thermo()->saveState(out);
808 }
else if (nativeState.count(
"X")) {
809 size_t offset = nativeState[
"X"];
810 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
812 phase->setMoleFractions(m_data->data() + m_loc * m_stride +
offset);
813 m_sol->thermo()->saveState(out);
818 "Not implemented for mode '{}'.", phase->nativeMode());
822 AnyMap SolutionArray::getAuxiliary(
int loc)
826 for (
const auto& [key, extra] : *m_extra) {
827 if (extra.is<
void>()) {
829 }
else if (extra.isVector<
long int>()) {
830 out[key] = extra.asVector<
long int>()[m_loc];
831 }
else if (extra.isVector<
double>()) {
832 out[key] = extra.asVector<
double>()[m_loc];
833 }
else if (extra.isVector<
string>()) {
834 out[key] = extra.asVector<
string>()[m_loc];
835 }
else if (extra.isVector<vector<long int>>()) {
836 out[key] = extra.asVector<vector<long int>>()[m_loc];
837 }
else if (extra.isVector<vector<double>>()) {
838 out[key] = extra.asVector<vector<double>>()[m_loc];
839 }
else if (extra.isVector<vector<string>>()) {
840 out[key] = extra.asVector<vector<string>>()[m_loc];
843 "Unable to retrieve data for component '{}' with type '{}'.",
844 key, extra.type_str());
850 void SolutionArray::setAuxiliary(
int loc,
const AnyMap& data)
853 for (
const auto& [name, value] : data) {
854 if (!m_extra->count(name)) {
856 "Unknown auxiliary component '{}'.", name);
858 auto& extra = m_extra->at(name);
859 if (extra.is<
void>()) {
860 if (m_dataSize > 1) {
862 "Unable to set location for type '{}': "
863 "component is not initialized.", name);
865 _initExtra(name, value);
869 if (extra.isVector<
long int>()) {
870 setAuxiliarySingle<long int>(m_loc, extra, value);
871 }
else if (extra.isVector<
double>()) {
872 setAuxiliarySingle<double>(m_loc, extra, value);
873 }
else if (extra.isVector<
string>()) {
874 setAuxiliarySingle<string>(m_loc, extra, value);
875 }
else if (extra.isVector<vector<long int>>()) {
876 setAuxiliaryMulti<long int>(m_loc, extra, value);
877 }
else if (extra.isVector<vector<double>>()) {
878 setAuxiliaryMulti<double>(m_loc, extra, value);
879 }
else if (extra.isVector<vector<string>>()) {
880 setAuxiliaryMulti<string>(m_loc, extra, value);
883 "Unable to set entry for type '{}'.", extra.type_str());
888 "Encountered incompatible value for component '{}':\n{}",
894 AnyMap preamble(
const string& desc)
898 data[
"description"] = desc;
900 data[
"generator"] =
"Cantera SolutionArray";
901 data[
"cantera-version"] = CANTERA_VERSION;
904 data[
"git-commit"] =
"'" +
gitCommit() +
"'";
909 struct tm* newtime = localtime(&aclock);
913 if (data.
hasKey(
"description")) {
914 data[
"description"].
setLoc(-6, 0);
916 data[
"generator"].
setLoc(-5, 0);
917 data[
"cantera-version"].
setLoc(-4, 0);
918 data[
"git-commit"].
setLoc(-3, 0);
919 data[
"date"].
setLoc(-2, 0);
924 AnyMap& openField(AnyMap& root,
const string& name)
931 vector<string> tokens;
935 for (
auto& field : tokens) {
938 if (sub.hasKey(field) && !sub[field].is<AnyMap>()) {
939 throw CanteraError(
"openField",
940 "Encountered invalid existing field '{}'.", path);
941 }
else if (!sub.hasKey(field)) {
942 sub[field] = AnyMap();
944 ptr = &sub[field].as<AnyMap>();
949 void SolutionArray::writeHeader(
const string& fname,
const string& name,
950 const string& desc,
bool overwrite)
956 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
964 void SolutionArray::writeHeader(
AnyMap& root,
const string& name,
965 const string& desc,
bool overwrite)
967 AnyMap& data = openField(root, name);
968 if (!data.
empty() && !overwrite) {
970 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
972 data.
update(preamble(desc));
975 void SolutionArray::writeEntry(
const string& fname,
bool overwrite,
const string& basis)
977 if (apiNdim() != 1) {
979 "Tabular output of CSV data only works for 1D SolutionArray objects.");
981 set<string> speciesNames;
982 for (
const auto& species : m_sol->thermo()->speciesNames()) {
983 speciesNames.insert(species);
987 const auto& nativeState = m_sol->thermo()->nativeState();
988 mole = nativeState.find(
"X") != nativeState.end();
989 }
else if (basis ==
"X" || basis ==
"mole") {
991 }
else if (basis ==
"Y" || basis ==
"mass") {
995 "Invalid species basis '{}'.", basis);
998 auto names = componentNames();
999 size_t last = names.size() - 1;
1000 vector<AnyValue> components;
1001 vector<bool> isSpecies;
1002 fmt::memory_buffer header;
1003 for (
const auto& key : names) {
1006 if (speciesNames.find(key) == speciesNames.end()) {
1008 isSpecies.push_back(
false);
1009 components.emplace_back(getComponent(key));
1010 col = components.size() - 1;
1011 if (!components[col].isVector<double>() &&
1012 !components[col].isVector<long int>() &&
1013 !components[col].isVector<string>())
1016 "Multi-dimensional column '{}' is not supported for CSV output.",
1021 isSpecies.push_back(
true);
1022 components.emplace_back(
AnyValue());
1023 col = components.size() - 1;
1025 label =
"X_" + label;
1027 label =
"Y_" + label;
1032 "Detected column name containing double quotes or line feeds: '{}'.",
1035 string sep = (col == last) ?
"" :
",";
1044 if (std::ifstream(fname).good()) {
1047 "File '{}' already exists; use option 'overwrite' to replace CSV file.",
1050 std::remove(fname.c_str());
1052 std::ofstream output(fname);
1053 output << to_string(header) << std::endl;
1055 size_t maxLen =
npos;
1056 vector<double> buf(speciesNames.size(), 0.);
1057 for (
int row = 0; row < static_cast<int>(m_size); row++) {
1058 fmt::memory_buffer line;
1059 if (maxLen !=
npos) {
1060 line.reserve(maxLen);
1064 m_sol->thermo()->getMoleFractions(buf.data());
1066 m_sol->thermo()->getMassFractions(buf.data());
1070 for (
size_t col = 0; col < components.size(); col++) {
1071 string sep = (col == last) ?
"" :
",";
1072 if (isSpecies[col]) {
1073 fmt_append(line,
"{:.9g}{}", buf[idx++], sep);
1075 auto& data = components[col];
1076 if (data.isVector<
double>()) {
1077 fmt_append(line,
"{:.9g}{}", data.asVector<
double>()[row], sep);
1078 }
else if (data.isVector<
long int>()) {
1079 fmt_append(line,
"{}{}", data.asVector<
long int>()[row], sep);
1080 }
else if (data.isVector<
bool>()) {
1082 static_cast<bool>(data.asVector<
bool>()[row]), sep);
1084 auto value = data.asVector<
string>()[row];
1089 "Detected value containing double quotes or line feeds: "
1100 output << to_string(line) << std::endl;
1101 maxLen = std::max(maxLen, line.size());
1105 void SolutionArray::writeEntry(
const string& fname,
const string& name,
1106 const string& sub,
bool overwrite,
int compression)
1110 "Group name specifying root location must not be empty.");
1112 if (m_size < m_dataSize) {
1114 "Unable to save sliced data.");
1129 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1136 if (apiNdim() == 1) {
1137 more[
"size"] = int(m_dataSize);
1139 more[
"api-shape"] = m_apiShape;
1141 more[
"components"] = componentNames();
1147 const auto& nativeState = m_sol->thermo()->nativeState();
1148 size_t nSpecies = m_sol->thermo()->nSpecies();
1149 for (
auto& [key,
offset] : nativeState) {
1150 if (key ==
"X" || key ==
"Y") {
1151 vector<vector<double>> prop;
1152 for (
size_t i = 0; i < m_size; i++) {
1153 size_t first =
offset + i * m_stride;
1154 prop.emplace_back(m_data->begin() + first,
1155 m_data->begin() + first + nSpecies);
1161 auto data = getComponent(key);
1166 for (
const auto& [key, value] : *m_extra) {
1167 if (isSimpleVector(value)) {
1169 }
else if (value.is<
void>()) {
1173 "Unable to save component '{}' with data type {}.",
1174 key, value.type_str());
1179 void SolutionArray::writeEntry(
AnyMap& root,
const string& name,
const string& sub,
1184 "Field name specifying root location must not be empty.");
1186 if (m_size < m_dataSize) {
1188 "Unable to save sliced data.");
1196 AnyMap& data = openField(root, path);
1197 bool preexisting = !data.
empty();
1198 if (preexisting && !overwrite) {
1200 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1202 if (apiNdim() == 1) {
1203 data[
"size"] = int(m_dataSize);
1205 data[
"api-shape"] = m_apiShape;
1209 for (
auto& [key, value] : *m_extra) {
1213 auto phase = m_sol->thermo();
1216 data[
"temperature"] = phase->temperature();
1217 data[
"pressure"] = phase->pressure();
1218 auto surf = std::dynamic_pointer_cast<SurfPhase>(phase);
1219 auto nSpecies = phase->nSpecies();
1220 vector<double> values(nSpecies);
1222 surf->getCoverages(&values[0]);
1224 phase->getMassFractions(&values[0]);
1227 for (
size_t k = 0; k < nSpecies; k++) {
1228 if (values[k] != 0.0) {
1229 items[phase->speciesName(k)] = values[k];
1233 data[
"coverages"] = std::move(items);
1235 data[
"mass-fractions"] = std::move(items);
1237 }
else if (m_size > 1) {
1238 const auto& nativeState = phase->nativeState();
1239 for (
auto& [key,
offset] : nativeState) {
1240 if (key ==
"X" || key ==
"Y") {
1241 for (
auto& spc : phase->speciesNames()) {
1242 data[spc] = getComponent(spc);
1244 data[
"basis"] = key ==
"X" ?
"mole" :
"mass";
1246 data[key] = getComponent(key);
1249 data[
"components"] = componentNames();
1258 void SolutionArray::append(
const vector<double>& state,
const AnyMap& extra)
1260 if (apiNdim() > 1) {
1262 "Unable to append multi-dimensional arrays.");
1268 setState(pos, state);
1269 setAuxiliary(pos, extra);
1277 void SolutionArray::save(
const string& fname,
const string& name,
const string& sub,
1278 const string& desc,
bool overwrite,
int compression,
1279 const string& basis)
1281 if (m_size < m_dataSize) {
1283 "Unable to save sliced data.");
1285 size_t dot = fname.find_last_of(
".");
1287 if (extension ==
"csv") {
1290 "Parameter 'name' not used for CSV output.");
1292 writeEntry(fname, overwrite, basis);
1297 "Argument 'basis' is not used for HDF or YAML output.", basis);
1299 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1300 writeHeader(fname, name, desc, overwrite);
1301 writeEntry(fname, name, sub,
true, compression);
1304 if (extension ==
"yaml" || extension ==
"yml") {
1307 if (std::ifstream(fname).good()) {
1308 data = AnyMap::fromYamlFile(fname);
1310 writeHeader(data, name, desc, overwrite);
1311 writeEntry(data, name, sub,
true);
1314 std::ofstream out(fname);
1315 out << data.toYamlString();
1316 AnyMap::clearCachedFile(fname);
1320 "Unknown file extension '{}'.", extension);
1323 AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1330 const AnyMap& locateField(
const AnyMap& root,
const string& name)
1337 vector<string> tokens;
1339 const AnyMap* ptr = &root;
1341 for (
auto& field : tokens) {
1342 path +=
"/" + field;
1343 const AnyMap& sub = *ptr;
1344 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1345 throw CanteraError(
"SolutionArray::locateField",
1346 "No field or solution with name '{}'.", path);
1348 ptr = &sub[field].as<AnyMap>();
1355 auto sub = locateField(root, name);
1357 for (
const auto& [key, value] : sub) {
1358 if (!sub[key].is<AnyMap>()) {
1359 header[key] = value;
1365 AnyMap SolutionArray::restore(
const string& fname,
1366 const string& name,
const string& sub)
1368 size_t dot = fname.find_last_of(
".");
1371 if (extension ==
"csv") {
1373 "CSV import not implemented; if using Python, data can be imported via "
1374 "'read_csv' instead.");
1376 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1377 readEntry(fname, name, sub);
1378 header = readHeader(fname, name);
1379 }
else if (extension ==
"yaml" || extension ==
"yml") {
1380 const AnyMap& root = AnyMap::fromYamlFile(fname);
1381 readEntry(root, name, sub);
1382 header = readHeader(root, name);
1385 "Unknown file extension '{}'; supported extensions include "
1386 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1391 void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1393 if (!m_extra->count(name)) {
1395 "Component '{}' does not exist.", name);
1397 auto& extra = (*m_extra)[name];
1398 if (!extra.is<
void>()) {
1400 "Component '{}' is already initialized.", name);
1403 if (value.
is<
long int>()) {
1404 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1405 }
else if (value.
is<
double>()) {
1406 extra = vector<double>(m_dataSize, value.
as<
double>());
1407 }
else if (value.
is<
string>()) {
1408 extra = vector<string>(m_dataSize, value.
as<
string>());
1409 }
else if (value.
isVector<
long int>()) {
1410 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1411 }
else if (value.
isVector<
double>()) {
1412 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1413 }
else if (value.
isVector<
string>()) {
1414 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1415 }
else if (value.
is<
void>()) {
1420 "Unable to initialize component '{}' with type '{}'.",
1426 "Encountered incompatible value for initializing component '{}':\n{}",
1431 void SolutionArray::_resizeExtra(
const string& name,
const AnyValue& value)
1433 if (!m_extra->count(name)) {
1435 "Component '{}' does not exist.", name);
1437 auto& extra = (*m_extra)[name];
1438 if (extra.is<
void>()) {
1444 if (extra.isVector<
long int>()) {
1445 resizeSingle<long int>(extra, m_dataSize, value);
1446 }
else if (extra.isVector<
double>()) {
1447 resizeSingle<double>(extra, m_dataSize, value);
1448 }
else if (extra.isVector<
string>()) {
1449 resizeSingle<string>(extra, m_dataSize, value);
1450 }
else if (extra.isVector<vector<double>>()) {
1451 resizeMulti<double>(extra, m_dataSize, value);
1452 }
else if (extra.isVector<vector<long int>>()) {
1453 resizeMulti<long int>(extra, m_dataSize, value);
1454 }
else if (extra.isVector<vector<string>>()) {
1455 resizeMulti<string>(extra, m_dataSize, value);
1458 "Unable to resize using type '{}'.", extra.type_str());
1463 "Encountered incompatible value for resizing component '{}':\n{}",
1468 void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1470 if (!m_extra->count(name)) {
1472 "Extra component '{}' does not exist.", name);
1475 auto& extra = m_extra->at(name);
1476 if (data.
is<
void>() && m_size == m_dataSize) {
1483 if (extra.is<
void>()) {
1484 if (m_size != m_dataSize) {
1486 "Unable to replace '{}' for sliced data.", name);
1496 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1501 "Unable to initialize '{}' with empty array when SolutionArray is not "
1504 _initExtra(name, data);
1505 _resizeExtra(name, data);
1509 if (data.
is<
long int>()) {
1510 setScalar<long int>(extra, data, m_active);
1511 }
else if (data.
is<
double>()) {
1512 setScalar<double>(extra, data, m_active);
1513 }
else if (data.
is<
string>()) {
1514 setScalar<string>(extra, data, m_active);
1515 }
else if (data.
isVector<
long int>()) {
1516 setSingle<long int>(extra, data, m_active);
1517 }
else if (data.
isVector<
double>()) {
1518 setSingle<double>(extra, data, m_active);
1519 }
else if (data.
isVector<
string>()) {
1520 setSingle<string>(extra, data, m_active);
1521 }
else if (data.
isVector<vector<long int>>()) {
1522 setMulti<long int>(extra, data, m_active);
1523 }
else if (data.
isVector<vector<double>>()) {
1524 setMulti<double>(extra, data, m_active);
1525 }
else if (data.
isVector<vector<string>>()) {
1526 setMulti<string>(extra, data, m_active);
1529 "Unable to set sliced data for component '{}' with type '{}'.",
1534 string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1538 const auto& nativeState = m_sol->thermo()->nativeState();
1539 bool usesNativeState =
false;
1540 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1542 for (
const auto& item : m_sol->thermo()->fullStates()) {
1545 usesNativeState =
true;
1546 for (
size_t i = 0; i < item.size(); i++) {
1548 name = string(1, item[i]);
1549 if (surf && (name ==
"X" || name ==
"Y")) {
1552 usesNativeState =
false;
1555 if (names.count(name)) {
1557 usesNativeState &= nativeState.count(name) > 0;
1558 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1560 usesNativeState &= nativeState.count(name) > 0;
1567 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1572 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1574 return "legacySurf";
1576 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1578 return "legacyInlet";
1581 "Detected incomplete thermodynamic information. Full states for a '{}' "
1582 "phase\nmay be defined by the following modes:\n'{}'\n"
1583 "Available data are: '{}'", m_sol->thermo()->type(),
1584 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1586 if (usesNativeState && native) {
1592 set<string> SolutionArray::_stateProperties(
1593 const string& mode,
bool alias)
1596 if (mode ==
"native") {
1597 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1598 states.insert(alias ? aliasMap.at(name) : name);
1601 for (
const auto& m : mode) {
1602 const string name = string(1, m);
1603 states.insert(alias ? aliasMap.at(name) : name);
1610 string getName(
const set<string>& names,
const string& name)
1612 if (names.count(name)) {
1615 const auto& alias = aliasMap.at(name);
1616 if (names.count(alias)) {
1622 void SolutionArray::readEntry(
const string& fname,
const string& name,
1628 "Group name specifying root location must not be empty.");
1631 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1633 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1639 "Group name specifying data entry is empty.");
1642 auto [size, names] = file.
contents(path);
1644 if (m_meta.hasKey(
"size")) {
1646 resize(m_meta[
"size"].as<long int>());
1647 m_meta.erase(
"size");
1648 }
else if (m_meta.hasKey(
"api-shape")) {
1650 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1651 m_meta.erase(
"api-shape");
1654 resize(
static_cast<int>(size));
1662 string mode = _detectMode(names);
1663 set<string> states = _stateProperties(mode);
1664 if (states.count(
"C")) {
1665 if (names.count(
"X")) {
1668 mode = mode.substr(0, 2) +
"X";
1669 }
else if (names.count(
"Y")) {
1672 mode = mode.substr(0, 2) +
"Y";
1677 size_t nSpecies = m_sol->thermo()->nSpecies();
1678 size_t nState = m_sol->thermo()->stateSize();
1679 const auto& nativeStates = m_sol->thermo()->nativeState();
1680 if (mode ==
"native") {
1682 for (
const auto& [name,
offset] : nativeStates) {
1683 if (name ==
"X" || name ==
"Y") {
1685 data = file.
readData(path, name, m_size, nSpecies);
1686 auto prop = data.
asVector<vector<double>>();
1687 for (
size_t i = 0; i < m_dataSize; i++) {
1688 std::copy(prop[i].begin(), prop[i].end(),
1689 m_data->data() +
offset + i * m_stride);
1693 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1694 setComponent(name, data);
1697 }
else if (mode ==
"TPX") {
1699 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1700 vector<double> T = std::move(data.
asVector<
double>());
1701 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1702 vector<double> P = std::move(data.
asVector<
double>());
1703 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1704 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1705 for (
size_t i = 0; i < m_dataSize; i++) {
1706 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1707 m_sol->thermo()->setState_TP(T[i], P[i]);
1708 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1710 }
else if (mode ==
"TDX") {
1712 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1713 vector<double> T = std::move(data.
asVector<
double>());
1714 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1715 vector<double> D = std::move(data.
asVector<
double>());
1716 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1717 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1718 for (
size_t i = 0; i < m_dataSize; i++) {
1719 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1720 m_sol->thermo()->setState_TD(T[i], D[i]);
1721 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1723 }
else if (mode ==
"TPY") {
1725 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1726 vector<double> T = std::move(data.
asVector<
double>());
1727 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1728 vector<double> P = std::move(data.
asVector<
double>());
1729 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1730 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1731 for (
size_t i = 0; i < m_dataSize; i++) {
1732 m_sol->thermo()->setMassFractions_NoNorm(Y[i].data());
1733 m_sol->thermo()->setState_TP(T[i], P[i]);
1734 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1736 }
else if (mode ==
"legacySurf") {
1739 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1740 vector<double> T = 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()->setTemperature(T[i]);
1746 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1749 "Detected legacy HDF format with incomplete state information\nfor name "
1750 "'{}' (pressure missing).", path);
1751 }
else if (mode ==
"") {
1753 "Data are not consistent with full state modes.");
1756 "Import of '{}' data is not supported.", mode);
1760 if (m_meta.hasKey(
"components")) {
1761 const auto& components = m_meta[
"components"].asVector<
string>();
1763 for (
const auto& name : components) {
1764 if (hasComponent(name) || name ==
"X" || name ==
"Y") {
1767 addExtra(name, back);
1769 data = file.
readData(path, name, m_dataSize);
1770 setComponent(name, data);
1773 m_meta.erase(
"components");
1776 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1777 for (
const auto& name : names) {
1778 if (!hasComponent(name) && name !=
"X" && name !=
"Y") {
1781 data = file.
readData(path, name, m_dataSize);
1782 setComponent(name, data);
1788 void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1792 "Field name specifying root location must not be empty.");
1794 auto path = locateField(root, name);
1795 if (path.hasKey(
"generator") && sub !=
"") {
1797 path = locateField(root, name +
"/" + sub);
1798 }
else if (sub ==
"" && path.hasKey(
"data")) {
1800 path = locateField(root, name +
"/data");
1805 if (path.hasKey(
"size")) {
1807 resize(path[
"size"].asInt());
1808 }
else if (path.hasKey(
"api-shape")) {
1810 auto& shape = path[
"api-shape"].asVector<
long int>();
1814 size = path.getInt(
"points", 0);
1815 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1819 resize(
static_cast<int>(size));
1824 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1825 set<string> names = path.keys();
1826 size_t nState = m_sol->thermo()->stateSize();
1827 if (m_dataSize == 0) {
1829 }
else if (m_dataSize == 1) {
1831 string mode = _detectMode(names,
false);
1832 if (mode ==
"TPY") {
1833 double T = path[getName(names,
"T")].asDouble();
1834 double P = path[getName(names,
"P")].asDouble();
1835 auto Y = path[
"mass-fractions"].asMap<
double>();
1836 m_sol->thermo()->setState_TPY(T, P, Y);
1837 }
else if (mode ==
"TPC") {
1838 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1839 double T = path[getName(names,
"T")].asDouble();
1840 double P = path[
"pressure"].asDouble();
1841 m_sol->thermo()->setState_TP(T, P);
1842 auto cov = path[
"coverages"].asMap<
double>();
1843 surf->setCoveragesByName(cov);
1844 }
else if (mode ==
"legacyInlet") {
1847 double T = path[getName(names,
"T")].asDouble();
1848 auto Y = path[
"mass-fractions"].asMap<
double>();
1849 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1851 "Detected legacy YAML format with incomplete state information\n"
1852 "for name '{}' (pressure missing).", name +
"/" + sub);
1853 }
else if (mode ==
"") {
1855 "Data are not consistent with full state modes.");
1858 "Import of '{}' data is not supported.", mode);
1860 m_sol->thermo()->saveState(nState, m_data->data());
1861 auto props = _stateProperties(mode,
true);
1862 exclude.insert(props.begin(), props.end());
1865 if (path.hasKey(
"components")) {
1866 const auto& components = path[
"components"].asVector<
string>();
1868 for (
const auto& name : components) {
1869 if (hasComponent(name)) {
1872 addExtra(name, back);
1874 setComponent(name, path[name]);
1875 exclude.insert(name);
1879 for (
const auto& [name, value] : path) {
1880 if (value.isVector<
double>()) {
1881 const vector<double>& data = value.asVector<
double>();
1882 if (data.size() == m_dataSize) {
1883 if (!hasComponent(name)) {
1886 setComponent(name, value);
1887 exclude.insert(name);
1894 const auto& nativeState = m_sol->thermo()->nativeState();
1896 set<string> missingProps;
1897 for (
const auto& [name,
offset] : nativeState) {
1898 if (exclude.count(name)) {
1901 missingProps.insert(name);
1905 set<string> TY = {
"T",
"Y"};
1906 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
1908 double P = path[
"pressure"].asDouble();
1909 const size_t offset_T = nativeState.find(
"T")->second;
1910 const size_t offset_D = nativeState.find(
"D")->second;
1911 const size_t offset_Y = nativeState.find(
"Y")->second;
1912 for (
size_t i = 0; i < m_dataSize; i++) {
1913 double T = (*m_data)[offset_T + i * m_stride];
1914 m_sol->thermo()->setState_TPY(
1915 T, P, m_data->data() + offset_Y + i * m_stride);
1916 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
1918 }
else if (missingProps.size()) {
1920 "Incomplete state information: missing '{}'.",
1921 ba::join(missingProps,
"', '"));
1926 for (
const auto& [name, value] : path) {
1927 if (!exclude.count(name)) {
1928 m_meta[name] = value;
1938 vector<T> data(slice.size());
1939 const auto& vec = extra.
asVector<T>();
1940 for (
size_t k = 0; k < slice.size(); ++k) {
1941 data[k] = vec[slice[k]];
1949 AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
1951 vector<vector<T>> data(slice.size());
1952 const auto& vec = extra.asVector<vector<T>>();
1953 for (
size_t k = 0; k < slice.size(); ++k) {
1954 data[k] = vec[slice[k]];
1962 void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
1964 T value = data.as<T>();
1965 if (extra.isVector<T>()) {
1966 auto& vec = extra.asVector<T>();
1967 for (
size_t k = 0; k < slice.size(); ++k) {
1968 vec[slice[k]] = value;
1971 throw CanteraError(
"SolutionArray::setScalar",
1972 "Incompatible input data: unable to assign '{}' data to '{}'.",
1973 data.type_str(), extra.type_str());
1978 void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
1980 size_t size = slice.size();
1981 if (extra.vectorSize() == size && data.vectorSize() == size) {
1985 if (extra.matrixShape().first == size && data.vectorSize() == size) {
1989 if (extra.type_str() != data.type_str()) {
1991 throw CanteraError(
"SolutionArray::setSingle",
1992 "Incompatible types: expected '{}' but received '{}'.",
1993 extra.type_str(), data.type_str());
1995 const auto& vData = data.asVector<T>();
1996 if (vData.size() != size) {
1997 throw CanteraError(
"SolutionArray::setSingle",
1998 "Invalid input data size: expected {} entries but received {}.",
1999 size, vData.size());
2001 auto& vec = extra.asVector<T>();
2002 for (
size_t k = 0; k < size; ++k) {
2003 vec[slice[k]] = vData[k];
2008 void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2010 if (!data.isMatrix<T>()) {
2011 throw CanteraError(
"SolutionArray::setMulti",
2012 "Invalid input data shape: inconsistent number of columns.");
2014 size_t size = slice.size();
2015 auto [rows, cols] = data.matrixShape();
2016 if (extra.matrixShape().first == size && rows == size) {
2020 if (extra.vectorSize() == size && rows == size) {
2024 if (extra.type_str() != data.type_str()) {
2026 throw CanteraError(
"SolutionArray::setMulti",
2027 "Incompatible types: expected '{}' but received '{}'.",
2028 extra.type_str(), data.type_str());
2031 throw CanteraError(
"SolutionArray::setMulti",
2032 "Invalid input data shape: expected {} rows but received {}.",
2035 if (extra.matrixShape().second != cols) {
2036 throw CanteraError(
"SolutionArray::setMulti",
2037 "Invalid input data shape: expected {} columns but received {}.",
2038 extra.matrixShape().second, cols);
2040 const auto& vData = data.asVector<vector<T>>();
2041 auto& vec = extra.asVector<vector<T>>();
2042 for (
size_t k = 0; k < slice.size(); ++k) {
2043 vec[slice[k]] = vData[k];
2048 void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2051 if (value.is<
void>()) {
2052 defaultValue = vector<T>(1)[0];
2054 defaultValue = value.as<T>();
2056 extra.asVector<T>().resize(size, defaultValue);
2060 void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2062 vector<T> defaultValue;
2063 if (value.is<
void>()) {
2064 defaultValue = vector<T>(extra.matrixShape().second);
2066 defaultValue = value.as<vector<T>>();
2068 extra.asVector<vector<T>>().resize(size, defaultValue);
2072 void resetSingle(AnyValue& extra,
const vector<int>& slice)
2074 T defaultValue = vector<T>(1)[0];
2075 vector<T>& data = extra.asVector<T>();
2076 for (
size_t k = 0; k < slice.size(); ++k) {
2077 data[slice[k]] = defaultValue;
2082 void resetMulti(AnyValue& extra,
const vector<int>& slice)
2084 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2085 vector<vector<T>>& data = extra.asVector<vector<T>>();
2086 for (
size_t k = 0; k < slice.size(); ++k) {
2087 data[slice[k]] = defaultValue;
2092 void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2094 extra.asVector<T>()[loc] = value.as<T>();
2098 void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2100 const auto& value = data.asVector<T>();
2101 auto& vec = extra.asVector<vector<T>>();
2102 if (value.size() != vec[loc].size()) {
2103 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2104 "New element size {} does not match existing column size {}.",
2105 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 hasKey(const string &key) const
Returns true if the map contains an item named key.
bool empty() const
Return boolean indicating whether AnyMap is empty.
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 fmt_append(fmt::memory_buffer &b, Args... args)
Versions 6.2.0 and 6.2.1 of fmtlib do not include this define before they include windows....
string stripnonprint(const string &s)
Strip non-printing characters wherever they are.
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.
string gitCommit()
Returns the hash of the git commit from which Cantera was compiled, if known.
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...