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) {
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();
564vector<string> SolutionArray::componentNames()
const
566 vector<string> components;
569 while (m_order->count(pos)) {
570 components.push_back(m_order->at(pos));
575 auto phase = m_sol->thermo();
576 for (
auto code : phase->nativeMode()) {
577 string name = string(1, code);
578 if (name ==
"X" || name ==
"Y") {
579 for (
auto& spc : phase->speciesNames()) {
580 components.push_back(spc);
583 components.push_back(name);
589 while (m_order->count(pos)) {
590 components.push_back(m_order->at(pos));
597void SolutionArray::addExtra(
const string& name,
bool back)
599 if (m_extra->count(name)) {
601 "Component '{}' already exists.", name);
605 if (m_order->size()) {
607 m_order->emplace(m_order->begin()->first - 1, name);
610 m_order->emplace(-1, name);
613 if (m_order->size()) {
615 m_order->emplace(m_order->rbegin()->first + 1, name);
618 m_order->emplace(0, name);
623vector<string> SolutionArray::listExtra(
bool all)
const
625 vector<string> names;
627 while (m_order->count(pos)) {
628 const auto& name = m_order->at(pos);
629 if (all || !m_extra->at(name).is<
void>()) {
630 names.push_back(name);
637 while (m_order->count(pos)) {
638 const auto& name = m_order->at(pos);
639 if (all || !m_extra->at(name).is<
void>()) {
640 names.push_back(name);
647bool SolutionArray::hasComponent(
const string& name)
const
649 if (m_extra->count(name)) {
653 if (m_sol->thermo()->speciesIndex(name) !=
npos) {
657 if (name ==
"X" || name ==
"Y") {
662 return (m_sol->thermo()->nativeState().count(name));
665AnyValue SolutionArray::getComponent(
const string& name)
const
667 if (!hasComponent(name)) {
669 "Unknown component '{}'.", name);
673 if (m_extra->count(name)) {
675 const auto& extra = m_extra->at(name);
676 if (extra.is<
void>()) {
679 if (m_size == m_dataSize) {
682 if (extra.isVector<
long int>()) {
683 return getSingle<long int>(extra, m_active);
685 if (extra.isVector<
double>()) {
686 return getSingle<double>(extra, m_active);
688 if (extra.isVector<
string>()) {
689 return getSingle<string>(extra, m_active);
691 if (extra.isVector<vector<double>>()) {
692 return getMulti<double>(extra, m_active);
694 if (extra.isVector<vector<long int>>()) {
695 return getMulti<long int>(extra, m_active);
697 if (extra.isVector<vector<string>>()) {
698 return getMulti<string>(extra, m_active);
701 "Unable to get sliced data for component '{}' with type '{}'.",
702 name, extra.type_str());
706 vector<double> data(m_size);
707 size_t ix = m_sol->thermo()->speciesIndex(name);
710 ix = m_sol->thermo()->nativeState()[name];
713 ix += m_stride - m_sol->thermo()->nSpecies();
715 for (
size_t k = 0; k < m_size; ++k) {
716 data[k] = (*m_data)[m_active[k] * m_stride + ix];
722bool isSimpleVector(
const AnyValue& any) {
729void SolutionArray::setComponent(
const string& name,
const AnyValue& data)
731 if (!hasComponent(name)) {
733 "Unknown component '{}'.", name);
735 if (m_extra->count(name)) {
736 _setExtra(name, data);
743 "Invalid type of component '{}': expected simple array type, "
744 "but received '{}'.", name, data.
type_str());
746 if (size != m_size) {
748 "Invalid size of component '{}': expected size {} but received {}.",
752 auto& vec = data.
asVector<
double>();
753 size_t ix = m_sol->thermo()->speciesIndex(name);
755 ix = m_sol->thermo()->nativeState()[name];
757 ix += m_stride - m_sol->thermo()->nSpecies();
759 for (
size_t k = 0; k < m_size; ++k) {
760 (*m_data)[m_active[k] * m_stride + ix] = vec[k];
764void SolutionArray::setLoc(
int loc,
bool restore)
766 size_t loc_ =
static_cast<size_t>(loc);
769 "Unable to set location in empty SolutionArray.");
770 }
else if (loc < 0) {
773 "Both current and buffered indices are invalid.");
776 }
else if (
static_cast<size_t>(m_active[loc_]) == m_loc) {
778 }
else if (loc_ >= m_size) {
779 throw IndexError(
"SolutionArray::setLoc",
"indices", loc_, m_size - 1);
781 m_loc =
static_cast<size_t>(m_active[loc_]);
783 size_t nState = m_sol->thermo()->stateSize();
784 m_sol->thermo()->restoreState(nState, m_data->data() + m_loc * m_stride);
788void SolutionArray::updateState(
int loc)
791 size_t nState = m_sol->thermo()->stateSize();
792 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
795vector<double> SolutionArray::getState(
int loc)
798 size_t nState = m_sol->thermo()->stateSize();
799 vector<double> out(nState);
800 m_sol->thermo()->saveState(out);
804void SolutionArray::setState(
int loc,
const vector<double>& state)
806 size_t nState = m_sol->thermo()->stateSize();
807 if (state.size() != nState) {
809 "Expected array to have length {}, but received an array of length {}.",
810 nState, state.size());
813 m_sol->thermo()->restoreState(state);
814 m_sol->thermo()->saveState(nState, m_data->data() + m_loc * m_stride);
817void SolutionArray::normalize() {
818 auto phase = m_sol->thermo();
819 auto nativeState = phase->nativeState();
820 if (nativeState.size() < 3) {
823 size_t nState = phase->stateSize();
824 vector<double> out(nState);
825 if (nativeState.count(
"Y")) {
826 size_t offset = nativeState[
"Y"];
827 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
829 phase->setMassFractions(m_data->data() + m_loc * m_stride +
offset);
830 m_sol->thermo()->saveState(out);
833 }
else if (nativeState.count(
"X")) {
834 size_t offset = nativeState[
"X"];
835 for (
int loc = 0; loc < static_cast<int>(m_size); loc++) {
837 phase->setMoleFractions(m_data->data() + m_loc * m_stride +
offset);
838 m_sol->thermo()->saveState(out);
843 "Not implemented for mode '{}'.", phase->nativeMode());
847AnyMap SolutionArray::getAuxiliary(
int loc)
851 for (
const auto& [key, extra] : *m_extra) {
852 if (extra.is<
void>()) {
854 }
else if (extra.isVector<
long int>()) {
855 out[key] = extra.asVector<
long int>()[m_loc];
856 }
else if (extra.isVector<
double>()) {
857 out[key] = extra.asVector<
double>()[m_loc];
858 }
else if (extra.isVector<
string>()) {
859 out[key] = extra.asVector<
string>()[m_loc];
860 }
else if (extra.isVector<vector<long int>>()) {
861 out[key] = extra.asVector<vector<long int>>()[m_loc];
862 }
else if (extra.isVector<vector<double>>()) {
863 out[key] = extra.asVector<vector<double>>()[m_loc];
864 }
else if (extra.isVector<vector<string>>()) {
865 out[key] = extra.asVector<vector<string>>()[m_loc];
868 "Unable to retrieve data for component '{}' with type '{}'.",
869 key, extra.type_str());
875void SolutionArray::setAuxiliary(
int loc,
const AnyMap& data)
878 for (
const auto& [name, value] : data) {
879 if (!m_extra->count(name)) {
881 "Unknown auxiliary component '{}'.", name);
883 auto& extra = m_extra->at(name);
884 if (extra.is<
void>()) {
885 if (m_dataSize > 1) {
887 "Unable to set location for type '{}': "
888 "component is not initialized.", name);
890 _initExtra(name, value);
894 if (extra.isVector<
long int>()) {
895 setAuxiliarySingle<long int>(m_loc, extra, value);
896 }
else if (extra.isVector<
double>()) {
897 setAuxiliarySingle<double>(m_loc, extra, value);
898 }
else if (extra.isVector<
string>()) {
899 setAuxiliarySingle<string>(m_loc, extra, value);
900 }
else if (extra.isVector<vector<long int>>()) {
901 setAuxiliaryMulti<long int>(m_loc, extra, value);
902 }
else if (extra.isVector<vector<double>>()) {
903 setAuxiliaryMulti<double>(m_loc, extra, value);
904 }
else if (extra.isVector<vector<string>>()) {
905 setAuxiliaryMulti<string>(m_loc, extra, value);
908 "Unable to set entry for type '{}'.", extra.type_str());
913 "Encountered incompatible value for component '{}':\n{}",
919AnyMap preamble(
const string& desc)
923 data[
"description"] = desc;
925 data[
"generator"] =
"Cantera SolutionArray";
926 data[
"cantera-version"] = CANTERA_VERSION;
927 data[
"git-commit"] = gitCommit();
932 struct tm* newtime = localtime(&aclock);
933 data[
"date"] = stripnonprint(asctime(newtime));
938AnyMap& openField(AnyMap& root,
const string& name)
945 vector<string> tokens;
949 for (
auto& field : tokens) {
952 if (sub.hasKey(field) && !sub[field].is<AnyMap>()) {
953 throw CanteraError(
"openField",
954 "Encountered invalid existing field '{}'.", path);
955 }
else if (!sub.hasKey(field)) {
956 sub[field] = AnyMap();
958 ptr = &sub[field].as<AnyMap>();
963void SolutionArray::writeHeader(
const string& fname,
const string& name,
964 const string& desc,
bool overwrite)
970 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
978void SolutionArray::writeHeader(
AnyMap& root,
const string& name,
979 const string& desc,
bool overwrite)
981 AnyMap& data = openField(root, name);
985 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
989 data.
update(preamble(desc));
992void SolutionArray::writeEntry(
const string& fname,
bool overwrite,
const string& basis)
994 if (apiNdim() != 1) {
996 "Tabular output of CSV data only works for 1D SolutionArray objects.");
998 set<string> speciesNames;
999 for (
const auto& species : m_sol->thermo()->speciesNames()) {
1000 speciesNames.insert(species);
1004 const auto& nativeState = m_sol->thermo()->nativeState();
1005 mole = nativeState.find(
"X") != nativeState.end();
1006 }
else if (basis ==
"X" || basis ==
"mole") {
1008 }
else if (basis ==
"Y" || basis ==
"mass") {
1012 "Invalid species basis '{}'.", basis);
1015 auto names = componentNames();
1016 size_t last = names.size() - 1;
1017 vector<AnyValue> components;
1018 vector<bool> isSpecies;
1019 fmt::memory_buffer header;
1020 for (
const auto& key : names) {
1023 if (speciesNames.find(key) == speciesNames.end()) {
1025 isSpecies.push_back(
false);
1026 components.emplace_back(getComponent(key));
1027 col = components.size() - 1;
1028 if (!components[col].isVector<double>() &&
1029 !components[col].isVector<
long int>() &&
1030 !components[col].isVector<string>())
1033 "Multi-dimensional column '{}' is not supported for CSV output.",
1038 isSpecies.push_back(
true);
1039 components.emplace_back(
AnyValue());
1040 col = components.size() - 1;
1042 label =
"X_" + label;
1044 label =
"Y_" + label;
1047 if (label.find(
"\"") != string::npos || label.find(
"\n") != string::npos) {
1049 "Detected column name containing double quotes or line feeds: '{}'.",
1052 string sep = (col == last) ?
"" :
",";
1053 if (label.find(
",") != string::npos) {
1061 if (std::ifstream(fname).good()) {
1064 "File '{}' already exists; use option 'overwrite' to replace CSV file.",
1067 std::remove(fname.c_str());
1069 std::ofstream output(fname);
1070 output << to_string(header) << std::endl;
1072 size_t maxLen =
npos;
1073 vector<double> buf(speciesNames.size(), 0.);
1074 for (
int row = 0; row < static_cast<int>(m_size); row++) {
1075 fmt::memory_buffer line;
1076 if (maxLen !=
npos) {
1077 line.reserve(maxLen);
1081 m_sol->thermo()->getMoleFractions(buf.data());
1083 m_sol->thermo()->getMassFractions(buf.data());
1087 for (
size_t col = 0; col < components.size(); col++) {
1088 string sep = (col == last) ?
"" :
",";
1089 if (isSpecies[col]) {
1090 fmt_append(line,
"{:.9g}{}", buf[idx++], sep);
1092 auto& data = components[col];
1093 if (data.isVector<
double>()) {
1094 fmt_append(line,
"{:.9g}{}", data.asVector<
double>()[row], sep);
1095 }
else if (data.isVector<
long int>()) {
1096 fmt_append(line,
"{}{}", data.asVector<
long int>()[row], sep);
1097 }
else if (data.isVector<
bool>()) {
1099 static_cast<bool>(data.asVector<
bool>()[row]), sep);
1101 auto value = data.asVector<
string>()[row];
1102 if (value.find(
"\"") != string::npos ||
1103 value.find(
"\n") != string::npos)
1106 "Detected value containing double quotes or line feeds: "
1109 if (value.find(
",") != string::npos) {
1117 output << to_string(line) << std::endl;
1118 maxLen = std::max(maxLen, line.size());
1122void SolutionArray::writeEntry(
const string& fname,
const string& name,
1123 const string& sub,
bool overwrite,
int compression)
1127 "Group name specifying root location must not be empty.");
1129 if (m_size < m_dataSize) {
1131 "Unable to save sliced data.");
1146 "Group name '{}' exists; use 'overwrite' argument to overwrite.", name);
1153 if (apiNdim() == 1) {
1154 more[
"size"] = int(m_dataSize);
1156 more[
"api-shape"] = m_apiShape;
1158 more[
"components"] = componentNames();
1164 const auto& nativeState = m_sol->thermo()->nativeState();
1165 size_t nSpecies = m_sol->thermo()->nSpecies();
1166 for (
auto& [key,
offset] : nativeState) {
1167 if (key ==
"X" || key ==
"Y") {
1168 vector<vector<double>> prop;
1169 for (
size_t i = 0; i < m_size; i++) {
1170 size_t first =
offset + i * m_stride;
1171 prop.emplace_back(m_data->begin() + first,
1172 m_data->begin() + first + nSpecies);
1178 auto data = getComponent(key);
1183 for (
const auto& [key, value] : *m_extra) {
1184 if (isSimpleVector(value)) {
1186 }
else if (value.is<
void>()) {
1190 "Unable to save component '{}' with data type {}.",
1191 key, value.type_str());
1196void SolutionArray::writeEntry(
AnyMap& root,
const string& name,
const string& sub,
1201 "Field name specifying root location must not be empty.");
1203 if (m_size < m_dataSize) {
1205 "Unable to save sliced data.");
1213 AnyMap& data = openField(root, path);
1214 bool preexisting = !data.
empty();
1215 if (preexisting && !overwrite) {
1217 "Field name '{}' exists; use 'overwrite' argument to overwrite.", name);
1219 if (apiNdim() == 1) {
1220 data[
"size"] = int(m_dataSize);
1222 data[
"api-shape"] = m_apiShape;
1227 data[
"components"] = componentNames();
1230 for (
auto& [_, key] : *m_order) {
1231 data[key] = m_extra->
at(key);
1234 auto phase = m_sol->thermo();
1237 data[
"temperature"] = phase->temperature();
1238 data[
"pressure"] = phase->pressure();
1239 auto surf = std::dynamic_pointer_cast<SurfPhase>(phase);
1240 auto nSpecies = phase->nSpecies();
1241 vector<double> values(nSpecies);
1243 surf->getCoverages(&values[0]);
1245 phase->getMassFractions(&values[0]);
1248 for (
size_t k = 0; k < nSpecies; k++) {
1249 if (values[k] != 0.0) {
1250 items[phase->speciesName(k)] = values[k];
1254 data[
"coverages"] = std::move(items);
1256 data[
"mass-fractions"] = std::move(items);
1258 }
else if (m_size > 1) {
1259 for (
auto& code : phase->nativeMode()) {
1260 string name(1, code);
1261 if (name ==
"X" || name ==
"Y") {
1262 for (
auto& spc : phase->speciesNames()) {
1263 data[spc] = getComponent(spc);
1265 data[
"basis"] = name ==
"X" ?
"mole" :
"mass";
1267 data[name] = getComponent(name);
1272 static bool reg = AnyMap::addOrderingRules(
"SolutionArray",
1273 {{
"head",
"type"}, {
"head",
"size"}, {
"head",
"basis"}});
1275 data[
"__type__"] =
"SolutionArray";
1284void SolutionArray::append(
const vector<double>& state,
const AnyMap& extra)
1286 if (apiNdim() > 1) {
1288 "Unable to append multi-dimensional arrays.");
1294 setState(pos, state);
1295 setAuxiliary(pos, extra);
1303void SolutionArray::save(
const string& fname,
const string& name,
const string& sub,
1304 const string& desc,
bool overwrite,
int compression,
1305 const string& basis)
1307 if (m_size < m_dataSize) {
1309 "Unable to save sliced data.");
1311 size_t dot = fname.find_last_of(
".");
1313 if (extension ==
"csv") {
1316 "Parameter 'name' not used for CSV output.");
1318 writeEntry(fname, overwrite, basis);
1323 "Argument 'basis' is not used for HDF or YAML output.", basis);
1325 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1326 writeHeader(fname, name, desc, overwrite);
1327 writeEntry(fname, name, sub,
true, compression);
1330 if (extension ==
"yaml" || extension ==
"yml") {
1333 if (std::ifstream(fname).good()) {
1334 data = AnyMap::fromYamlFile(fname);
1336 writeHeader(data, name, desc, overwrite);
1337 writeEntry(data, name, sub,
true);
1340 std::ofstream out(fname);
1341 out << data.toYamlString();
1342 AnyMap::clearCachedFile(fname);
1346 "Unknown file extension '{}'.", extension);
1349AnyMap SolutionArray::readHeader(
const string& fname,
const string& name)
1356const AnyMap& locateField(
const AnyMap& root,
const string& name)
1363 vector<string> tokens;
1364 tokenizePath(name, tokens);
1365 const AnyMap* ptr = &root;
1367 for (
auto& field : tokens) {
1368 path +=
"/" + field;
1369 const AnyMap& sub = *ptr;
1370 if (!sub.hasKey(field) || !sub[field].is<AnyMap>()) {
1371 throw CanteraError(
"SolutionArray::locateField",
1372 "No field or solution with name '{}'.", path);
1374 ptr = &sub[field].as<AnyMap>();
1381 auto sub = locateField(root, name);
1383 for (
const auto& [key, value] : sub) {
1384 if (!sub[key].is<AnyMap>()) {
1385 header[key] = value;
1391AnyMap SolutionArray::restore(
const string& fname,
1392 const string& name,
const string& sub)
1394 size_t dot = fname.find_last_of(
".");
1397 if (extension ==
"csv") {
1399 "CSV import not implemented; if using Python, data can be imported via "
1400 "'read_csv' instead.");
1402 if (extension ==
"h5" || extension ==
"hdf" || extension ==
"hdf5") {
1403 readEntry(fname, name, sub);
1404 header = readHeader(fname, name);
1405 }
else if (extension ==
"yaml" || extension ==
"yml") {
1406 const AnyMap& root = AnyMap::fromYamlFile(fname);
1407 readEntry(root, name, sub);
1408 header = readHeader(root, name);
1411 "Unknown file extension '{}'; supported extensions include "
1412 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
1417void SolutionArray::_initExtra(
const string& name,
const AnyValue& value)
1419 if (!m_extra->count(name)) {
1421 "Component '{}' does not exist.", name);
1423 auto& extra = (*m_extra)[name];
1424 if (!extra.is<
void>()) {
1426 "Component '{}' is already initialized.", name);
1429 if (value.
is<
long int>()) {
1430 extra = vector<long int>(m_dataSize, value.
as<
long int>());
1431 }
else if (value.
is<
double>()) {
1432 extra = vector<double>(m_dataSize, value.
as<
double>());
1433 }
else if (value.
is<
string>()) {
1434 extra = vector<string>(m_dataSize, value.
as<
string>());
1435 }
else if (value.
isVector<
long int>()) {
1436 extra = vector<vector<long int>>(m_dataSize, value.
asVector<
long int>());
1437 }
else if (value.
isVector<
double>()) {
1438 extra = vector<vector<double>>(m_dataSize, value.
asVector<
double>());
1439 }
else if (value.
isVector<
string>()) {
1440 extra = vector<vector<string>>(m_dataSize, value.
asVector<
string>());
1441 }
else if (value.
is<
void>()) {
1446 "Unable to initialize component '{}' with type '{}'.",
1452 "Encountered incompatible value for initializing component '{}':\n{}",
1457void SolutionArray::_resizeExtra(
const string& name,
const AnyValue& value)
1459 if (!m_extra->count(name)) {
1461 "Component '{}' does not exist.", name);
1463 auto& extra = (*m_extra)[name];
1464 if (extra.is<
void>()) {
1470 if (extra.isVector<
long int>()) {
1471 resizeSingle<long int>(extra, m_dataSize, value);
1472 }
else if (extra.isVector<
double>()) {
1473 resizeSingle<double>(extra, m_dataSize, value);
1474 }
else if (extra.isVector<
string>()) {
1475 resizeSingle<string>(extra, m_dataSize, value);
1476 }
else if (extra.isVector<vector<double>>()) {
1477 resizeMulti<double>(extra, m_dataSize, value);
1478 }
else if (extra.isVector<vector<long int>>()) {
1479 resizeMulti<long int>(extra, m_dataSize, value);
1480 }
else if (extra.isVector<vector<string>>()) {
1481 resizeMulti<string>(extra, m_dataSize, value);
1484 "Unable to resize using type '{}'.", extra.type_str());
1489 "Encountered incompatible value for resizing component '{}':\n{}",
1494void SolutionArray::_setExtra(
const string& name,
const AnyValue& data)
1496 if (!m_extra->count(name)) {
1498 "Extra component '{}' does not exist.", name);
1501 auto& extra = m_extra->at(name);
1502 if (data.
is<
void>() && m_size == m_dataSize) {
1509 if (extra.is<
void>()) {
1510 if (m_size != m_dataSize) {
1512 "Unable to replace '{}' for sliced data.", name);
1522 "Unable to initialize '{}' with non-empty values when SolutionArray is "
1527 "Unable to initialize '{}' with empty array when SolutionArray is not "
1530 _initExtra(name, data);
1531 _resizeExtra(name, data);
1535 if (data.
is<
long int>()) {
1536 setScalar<long int>(extra, data, m_active);
1537 }
else if (data.
is<
double>()) {
1538 setScalar<double>(extra, data, m_active);
1539 }
else if (data.
is<
string>()) {
1540 setScalar<string>(extra, data, m_active);
1541 }
else if (data.
isVector<
long int>()) {
1542 setSingle<long int>(extra, data, m_active);
1543 }
else if (data.
isVector<
double>()) {
1544 setSingle<double>(extra, data, m_active);
1545 }
else if (data.
isVector<
string>()) {
1546 setSingle<string>(extra, data, m_active);
1547 }
else if (data.
isVector<vector<long int>>()) {
1548 setMulti<long int>(extra, data, m_active);
1549 }
else if (data.
isVector<vector<double>>()) {
1550 setMulti<double>(extra, data, m_active);
1551 }
else if (data.
isVector<vector<string>>()) {
1552 setMulti<string>(extra, data, m_active);
1555 "Unable to set sliced data for component '{}' with type '{}'.",
1560string SolutionArray::_detectMode(
const set<string>& names,
bool native)
1564 const auto& nativeState = m_sol->thermo()->nativeState();
1565 bool usesNativeState =
false;
1566 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1568 for (
const auto& item : m_sol->thermo()->fullStates()) {
1571 usesNativeState =
true;
1572 for (
size_t i = 0; i < item.size(); i++) {
1574 name = string(1, item[i]);
1575 if (surf && (name ==
"X" || name ==
"Y")) {
1578 usesNativeState =
false;
1581 if (names.count(name)) {
1583 usesNativeState &= nativeState.count(name) > 0;
1584 }
else if (aliasMap.count(name) && names.count(aliasMap.at(name))) {
1586 usesNativeState &= nativeState.count(name) > 0;
1593 mode = (name ==
"C") ? item.substr(0, 2) +
"C" : item;
1598 if (surf && names.count(
"T") && names.count(
"X") && names.count(
"density")) {
1600 return "legacySurf";
1602 if (names.count(
"mass-flux") && names.count(
"mass-fractions")) {
1604 return "legacyInlet";
1607 "Detected incomplete thermodynamic information. Full states for a '{}' "
1608 "phase\nmay be defined by the following modes:\n'{}'\n"
1609 "Available data are: '{}'", m_sol->thermo()->type(),
1610 ba::join(m_sol->thermo()->fullStates(),
"', '"), ba::join(names,
"', '"));
1612 if (usesNativeState && native) {
1618set<string> SolutionArray::_stateProperties(
1619 const string& mode,
bool alias)
1622 if (mode ==
"native") {
1623 for (
const auto& [name,
offset] : m_sol->thermo()->nativeState()) {
1624 states.insert(alias ? aliasMap.at(name) : name);
1627 for (
const auto& m : mode) {
1628 const string name = string(1, m);
1629 states.insert(alias ? aliasMap.at(name) : name);
1636string getName(
const set<string>& names,
const string& name)
1638 if (names.count(name)) {
1641 const auto& alias = aliasMap.at(name);
1642 if (names.count(alias)) {
1648void SolutionArray::readEntry(
const string& fname,
const string& name,
1654 "Group name specifying root location must not be empty.");
1657 if (sub !=
"" && file.
checkGroup(name +
"/" + sub,
true)) {
1659 }
else if (sub ==
"" && file.
checkGroup(name +
"/data",
true)) {
1665 "Group name specifying data entry is empty.");
1668 auto [size, names] = file.
contents(path);
1670 if (m_meta.hasKey(
"size")) {
1672 resize(m_meta[
"size"].as<long int>());
1673 m_meta.erase(
"size");
1674 }
else if (m_meta.hasKey(
"api-shape")) {
1676 setApiShape(m_meta[
"api-shape"].asVector<long int>());
1677 m_meta.erase(
"api-shape");
1680 resize(
static_cast<int>(size));
1688 string mode = _detectMode(names);
1689 set<string> states = _stateProperties(mode);
1690 if (states.count(
"C")) {
1691 if (names.count(
"X")) {
1694 mode = mode.substr(0, 2) +
"X";
1695 }
else if (names.count(
"Y")) {
1698 mode = mode.substr(0, 2) +
"Y";
1703 size_t nSpecies = m_sol->thermo()->nSpecies();
1704 size_t nState = m_sol->thermo()->stateSize();
1705 const auto& nativeStates = m_sol->thermo()->nativeState();
1706 if (mode ==
"native") {
1708 for (
const auto& [name,
offset] : nativeStates) {
1709 if (name ==
"X" || name ==
"Y") {
1711 data = file.
readData(path, name, m_size, nSpecies);
1712 auto prop = data.
asVector<vector<double>>();
1713 for (
size_t i = 0; i < m_dataSize; i++) {
1714 std::copy(prop[i].begin(), prop[i].end(),
1715 m_data->data() +
offset + i * m_stride);
1719 data = file.
readData(path, getName(names, name), m_dataSize, 0);
1720 setComponent(name, data);
1723 }
else if (mode ==
"TPX") {
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,
"X", m_dataSize, nSpecies);
1730 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1731 for (
size_t i = 0; i < m_dataSize; i++) {
1732 m_sol->thermo()->setMoleFractions_NoNorm(X[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 ==
"TDX") {
1738 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1739 vector<double> T = std::move(data.
asVector<
double>());
1740 data = file.
readData(path, getName(names,
"D"), m_dataSize, 0);
1741 vector<double> D = std::move(data.
asVector<
double>());
1742 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1743 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1744 for (
size_t i = 0; i < m_dataSize; i++) {
1745 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1746 m_sol->thermo()->setState_TD(T[i], D[i]);
1747 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1749 }
else if (mode ==
"TPY") {
1751 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1752 vector<double> T = std::move(data.
asVector<
double>());
1753 data = file.
readData(path, getName(names,
"P"), m_dataSize, 0);
1754 vector<double> P = std::move(data.
asVector<
double>());
1755 data = file.
readData(path,
"Y", m_dataSize, nSpecies);
1756 vector<vector<double>> Y = std::move(data.
asVector<vector<double>>());
1757 for (
size_t i = 0; i < m_dataSize; i++) {
1758 m_sol->thermo()->setMassFractions_NoNorm(Y[i].data());
1759 m_sol->thermo()->setState_TP(T[i], P[i]);
1760 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1762 }
else if (mode ==
"legacySurf") {
1765 data = file.
readData(path, getName(names,
"T"), m_dataSize, 0);
1766 vector<double> T = std::move(data.
asVector<
double>());
1767 data = file.
readData(path,
"X", m_dataSize, nSpecies);
1768 vector<vector<double>> X = std::move(data.
asVector<vector<double>>());
1769 for (
size_t i = 0; i < m_dataSize; i++) {
1770 m_sol->thermo()->setMoleFractions_NoNorm(X[i].data());
1771 m_sol->thermo()->setTemperature(T[i]);
1772 m_sol->thermo()->saveState(nState, m_data->data() + i * m_stride);
1775 "Detected legacy HDF format with incomplete state information\nfor name "
1776 "'{}' (pressure missing).", path);
1777 }
else if (mode ==
"") {
1779 "Data are not consistent with full state modes.");
1782 "Import of '{}' data is not supported.", mode);
1786 if (m_meta.hasKey(
"components")) {
1787 const auto& components = m_meta[
"components"].asVector<
string>();
1789 for (
const auto& name : components) {
1790 if (hasComponent(name) || name ==
"X" || name ==
"Y") {
1793 addExtra(name, back);
1795 data = file.
readData(path, name, m_dataSize);
1796 setComponent(name, data);
1799 m_meta.erase(
"components");
1802 warn_user(
"SolutionArray::readEntry",
"Detected legacy HDF format.");
1803 for (
const auto& name : names) {
1804 if (!hasComponent(name) && name !=
"X" && name !=
"Y") {
1807 data = file.
readData(path, name, m_dataSize);
1808 setComponent(name, data);
1814void SolutionArray::readEntry(
const AnyMap& root,
const string& name,
const string& sub)
1818 "Field name specifying root location must not be empty.");
1820 auto path = locateField(root, name);
1821 if (path.hasKey(
"generator") && sub !=
"") {
1823 path = locateField(root, name +
"/" + sub);
1824 }
else if (sub ==
"" && path.hasKey(
"data")) {
1826 path = locateField(root, name +
"/data");
1831 if (path.hasKey(
"size")) {
1833 resize(path[
"size"].asInt());
1834 }
else if (path.hasKey(
"api-shape")) {
1836 auto& shape = path[
"api-shape"].asVector<
long int>();
1840 size = path.getInt(
"points", 0);
1841 if (!path.hasKey(
"T") && !path.hasKey(
"temperature")) {
1845 resize(
static_cast<int>(size));
1850 set<string> exclude = {
"size",
"api-shape",
"points",
"X",
"Y"};
1851 set<string> names = path.keys();
1852 size_t nState = m_sol->thermo()->stateSize();
1853 if (m_dataSize == 0) {
1855 }
else if (m_dataSize == 1) {
1857 string mode = _detectMode(names,
false);
1858 if (mode ==
"TPY") {
1859 double T = path[getName(names,
"T")].asDouble();
1860 double P = path[getName(names,
"P")].asDouble();
1861 auto Y = path[
"mass-fractions"].asMap<
double>();
1862 m_sol->thermo()->setState_TPY(T, P, Y);
1863 }
else if (mode ==
"TPC") {
1864 auto surf = std::dynamic_pointer_cast<SurfPhase>(m_sol->thermo());
1865 double T = path[getName(names,
"T")].asDouble();
1866 double P = path[
"pressure"].asDouble();
1867 m_sol->thermo()->setState_TP(T, P);
1868 auto cov = path[
"coverages"].asMap<
double>();
1869 surf->setCoveragesByName(cov);
1870 }
else if (mode ==
"legacyInlet") {
1873 double T = path[getName(names,
"T")].asDouble();
1874 auto Y = path[
"mass-fractions"].asMap<
double>();
1875 m_sol->thermo()->setState_TPY(T, m_sol->thermo()->pressure(), Y);
1877 "Detected legacy YAML format with incomplete state information\n"
1878 "for name '{}' (pressure missing).", name +
"/" + sub);
1879 }
else if (mode ==
"") {
1881 "Data are not consistent with full state modes.");
1884 "Import of '{}' data is not supported.", mode);
1886 m_sol->thermo()->saveState(nState, m_data->data());
1887 auto props = _stateProperties(mode,
true);
1888 exclude.insert(props.begin(), props.end());
1891 if (path.hasKey(
"components")) {
1892 const auto& components = path[
"components"].asVector<
string>();
1894 for (
const auto& name : components) {
1895 if (hasComponent(name)) {
1898 addExtra(name, back);
1900 setComponent(name, path[name]);
1901 exclude.insert(name);
1905 for (
const auto& [name, value] : path) {
1906 if (value.isVector<
double>()) {
1907 const vector<double>& data = value.asVector<
double>();
1908 if (data.size() == m_dataSize) {
1909 if (!hasComponent(name)) {
1912 setComponent(name, value);
1913 exclude.insert(name);
1920 const auto& nativeState = m_sol->thermo()->nativeState();
1922 set<string> missingProps;
1923 for (
const auto& [name,
offset] : nativeState) {
1924 if (exclude.count(name)) {
1927 missingProps.insert(name);
1931 set<string> TY = {
"T",
"Y"};
1932 if (props == TY && missingProps.count(
"D") && path.hasKey(
"pressure")) {
1934 double P = path[
"pressure"].asDouble();
1935 const size_t offset_T = nativeState.find(
"T")->second;
1936 const size_t offset_D = nativeState.find(
"D")->second;
1937 const size_t offset_Y = nativeState.find(
"Y")->second;
1938 for (
size_t i = 0; i < m_dataSize; i++) {
1939 double T = (*m_data)[offset_T + i * m_stride];
1940 m_sol->thermo()->setState_TPY(
1941 T, P, m_data->data() + offset_Y + i * m_stride);
1942 (*m_data)[offset_D + i * m_stride] = m_sol->thermo()->density();
1944 }
else if (missingProps.size()) {
1946 "Incomplete state information: missing '{}'.",
1947 ba::join(missingProps,
"', '"));
1952 for (
const auto& [name, value] : path) {
1953 if (!exclude.count(name)) {
1954 m_meta[name] = value;
1964 vector<T> data(slice.size());
1965 const auto& vec = extra.
asVector<T>();
1966 for (
size_t k = 0; k < slice.size(); ++k) {
1967 data[k] = vec[slice[k]];
1975AnyValue getMulti(
const AnyValue& extra,
const vector<int>& slice)
1977 vector<vector<T>> data(slice.size());
1978 const auto& vec = extra.asVector<vector<T>>();
1979 for (
size_t k = 0; k < slice.size(); ++k) {
1980 data[k] = vec[slice[k]];
1988void setScalar(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
1990 T value = data.as<T>();
1991 if (extra.isVector<T>()) {
1992 auto& vec = extra.asVector<T>();
1993 for (
size_t k = 0; k < slice.size(); ++k) {
1994 vec[slice[k]] = value;
1997 throw CanteraError(
"SolutionArray::setScalar",
1998 "Incompatible input data: unable to assign '{}' data to '{}'.",
1999 data.type_str(), extra.type_str());
2004void setSingle(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2006 size_t size = slice.size();
2007 if (extra.vectorSize() == size && data.vectorSize() == size) {
2011 if (extra.matrixShape().first == size && data.vectorSize() == size) {
2015 if (extra.type_str() != data.type_str()) {
2017 throw CanteraError(
"SolutionArray::setSingle",
2018 "Incompatible types: expected '{}' but received '{}'.",
2019 extra.type_str(), data.type_str());
2021 const auto& vData = data.asVector<T>();
2022 if (vData.size() != size) {
2023 throw CanteraError(
"SolutionArray::setSingle",
2024 "Invalid input data size: expected {} entries but received {}.",
2025 size, vData.size());
2027 auto& vec = extra.asVector<T>();
2028 for (
size_t k = 0; k < size; ++k) {
2029 vec[slice[k]] = vData[k];
2034void setMulti(AnyValue& extra,
const AnyValue& data,
const vector<int>& slice)
2036 if (!data.isMatrix<T>()) {
2037 throw CanteraError(
"SolutionArray::setMulti",
2038 "Invalid input data shape: inconsistent number of columns.");
2040 size_t size = slice.size();
2041 auto [rows, cols] = data.matrixShape();
2042 if (extra.matrixShape().first == size && rows == size) {
2046 if (extra.vectorSize() == size && rows == size) {
2050 if (extra.type_str() != data.type_str()) {
2052 throw CanteraError(
"SolutionArray::setMulti",
2053 "Incompatible types: expected '{}' but received '{}'.",
2054 extra.type_str(), data.type_str());
2057 throw CanteraError(
"SolutionArray::setMulti",
2058 "Invalid input data shape: expected {} rows but received {}.",
2061 if (extra.matrixShape().second != cols) {
2062 throw CanteraError(
"SolutionArray::setMulti",
2063 "Invalid input data shape: expected {} columns but received {}.",
2064 extra.matrixShape().second, cols);
2066 const auto& vData = data.asVector<vector<T>>();
2067 auto& vec = extra.asVector<vector<T>>();
2068 for (
size_t k = 0; k < slice.size(); ++k) {
2069 vec[slice[k]] = vData[k];
2074void resizeSingle(AnyValue& extra,
size_t size,
const AnyValue& value)
2077 if (value.is<
void>()) {
2078 defaultValue = vector<T>(1)[0];
2080 defaultValue = value.as<T>();
2082 extra.asVector<T>().resize(size, defaultValue);
2086void resizeMulti(AnyValue& extra,
size_t size,
const AnyValue& value)
2088 vector<T> defaultValue;
2089 if (value.is<
void>()) {
2090 defaultValue = vector<T>(extra.matrixShape().second);
2092 defaultValue = value.as<vector<T>>();
2094 extra.asVector<vector<T>>().resize(size, defaultValue);
2098void resetSingle(AnyValue& extra,
const vector<int>& slice)
2100 T defaultValue = vector<T>(1)[0];
2101 vector<T>& data = extra.asVector<T>();
2102 for (
size_t k = 0; k < slice.size(); ++k) {
2103 data[slice[k]] = defaultValue;
2108void resetMulti(AnyValue& extra,
const vector<int>& slice)
2110 vector<T> defaultValue = vector<T>(extra.matrixShape().second);
2111 vector<vector<T>>& data = extra.asVector<vector<T>>();
2112 for (
size_t k = 0; k < slice.size(); ++k) {
2113 data[slice[k]] = defaultValue;
2118void setAuxiliarySingle(
size_t loc, AnyValue& extra,
const AnyValue& value)
2120 extra.asVector<T>()[loc] = value.as<T>();
2124void setAuxiliaryMulti(
size_t loc, AnyValue& extra,
const AnyValue& data)
2126 const auto& value = data.asVector<T>();
2127 auto& vec = extra.asVector<vector<T>>();
2128 if (value.size() != vec[loc].size()) {
2129 throw CanteraError(
"SolutionArray::setAuxiliaryMulti",
2130 "New element size {} does not match existing column size {}.",
2131 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 fmt_append(fmt::memory_buffer &b, const std::string &tmpl, Args... args)
Versions 6.2.0 and 6.2.1 of fmtlib do not include this define before they include windows....
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...