mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
[Input] Use full AnyMap sorting algorithm in all cases
The simpler sorting algorithm used in Python did not account for the additional ordering rules allowed by the AnyMap class.
This commit is contained in:
committed by
Ingmar Schoegl
parent
8b8bf4b12b
commit
df262a0f92
@@ -528,6 +528,55 @@ public:
|
||||
return Iterator(m_data.end(), m_data.end());
|
||||
}
|
||||
|
||||
class OrderedIterator;
|
||||
|
||||
//! Proxy for iterating over an AnyMap in the defined output ordering.
|
||||
//! See ordered().
|
||||
class OrderedProxy {
|
||||
public:
|
||||
OrderedProxy() {}
|
||||
OrderedProxy(const AnyMap& data);
|
||||
OrderedIterator begin() const;
|
||||
OrderedIterator end() const;
|
||||
|
||||
typedef std::vector<std::pair<
|
||||
std::pair<int, int>,
|
||||
const std::pair<const std::string, AnyValue>*>> OrderVector;
|
||||
private:
|
||||
const AnyMap* m_data;
|
||||
OrderVector m_ordered;
|
||||
std::unique_ptr<std::pair<const std::string, AnyValue>> m_units;
|
||||
};
|
||||
|
||||
//! Defined to allow the OrderedProxy class to be used with range-based
|
||||
//! for loops.
|
||||
class OrderedIterator {
|
||||
public:
|
||||
OrderedIterator() {}
|
||||
OrderedIterator(const OrderedProxy::OrderVector::const_iterator& start,
|
||||
const OrderedProxy::OrderVector::const_iterator& stop);
|
||||
|
||||
const std::pair<const std::string, AnyValue>& operator*() const {
|
||||
return *m_iter->second;
|
||||
}
|
||||
const std::pair<const std::string, AnyValue>* operator->() const {
|
||||
return &(*m_iter->second);
|
||||
}
|
||||
bool operator!=(const OrderedIterator& right) const {
|
||||
return m_iter != right.m_iter;
|
||||
}
|
||||
OrderedIterator& operator++() { ++m_iter; return *this; }
|
||||
|
||||
private:
|
||||
OrderedProxy::OrderVector::const_iterator m_iter;
|
||||
OrderedProxy::OrderVector::const_iterator m_stop;
|
||||
};
|
||||
|
||||
// Return a proxy object that allows iteration in an order determined by the
|
||||
// order of insertion, the location in an input file, and rules specified by
|
||||
// the addOrderingRules() method.
|
||||
OrderedProxy ordered() const { return OrderedProxy(*this); }
|
||||
|
||||
//! Returns the number of elements in this map
|
||||
size_t size() const {
|
||||
return m_data.size();
|
||||
|
||||
@@ -58,9 +58,19 @@ cdef extern from "cantera/base/AnyMap.h" namespace "Cantera":
|
||||
Iterator& operator++()
|
||||
cbool operator!=(Iterator&)
|
||||
|
||||
cppclass OrderedIterator:
|
||||
pair[string, CxxAnyValue]& operator*()
|
||||
OrderedIterator& operator++()
|
||||
cbool operator!=(OrderedIterator&)
|
||||
|
||||
cppclass OrderedProxy:
|
||||
OrderedIterator begin()
|
||||
OrderedIterator end()
|
||||
|
||||
CxxAnyMap()
|
||||
Iterator begin()
|
||||
Iterator end()
|
||||
OrderedProxy ordered() except +translate_exception
|
||||
CxxAnyValue& operator[](string) except +translate_exception
|
||||
cbool hasKey(string)
|
||||
string keys_str()
|
||||
@@ -74,7 +84,6 @@ cdef extern from "cantera/base/AnyMap.h" namespace "Cantera":
|
||||
string type_str()
|
||||
cbool isType "is" [T]()
|
||||
cbool isScalar()
|
||||
pair[int, int] order()
|
||||
|
||||
CxxAnyMap AnyMapFromYamlFile "Cantera::AnyMap::fromYamlFile" (string) except +translate_exception
|
||||
CxxAnyMap AnyMapFromYamlString "Cantera::AnyMap::fromYamlString" (string) except +translate_exception
|
||||
|
||||
@@ -116,14 +116,9 @@ cdef anyvalueToPython(string name, CxxAnyValue& v):
|
||||
|
||||
|
||||
cdef anymapToPython(CxxAnyMap& m):
|
||||
py_items = []
|
||||
m.applyUnits()
|
||||
for item in m:
|
||||
py_items.append((item.second.order(),
|
||||
pystr(item.first),
|
||||
anyvalueToPython(item.first, item.second)))
|
||||
py_items.sort()
|
||||
return {key: value for (_, key, value) in py_items}
|
||||
return {pystr(item.first): anyvalueToPython(item.first, item.second)
|
||||
for item in m.ordered()}
|
||||
|
||||
|
||||
cdef mergeAnyMap(CxxAnyMap& primary, CxxAnyMap& extra):
|
||||
@@ -134,16 +129,9 @@ cdef mergeAnyMap(CxxAnyMap& primary, CxxAnyMap& extra):
|
||||
Used to combine generated data representing the current state of the object
|
||||
(primary) with user-supplied fields (extra) not directly used by Cantera.
|
||||
"""
|
||||
py_items = []
|
||||
for item in primary:
|
||||
py_items.append((item.second.order(),
|
||||
pystr(item.first),
|
||||
anyvalueToPython(item.first, item.second)))
|
||||
out = {pystr(item.first): anyvalueToPython(item.first, item.second)
|
||||
for item in primary.ordered()}
|
||||
for item in extra:
|
||||
if not primary.hasKey(item.first):
|
||||
py_items.append((item.second.order(),
|
||||
pystr(item.first),
|
||||
anyvalueToPython(item.first, item.second)))
|
||||
|
||||
py_items.sort()
|
||||
return {key: value for (_, key, value) in py_items}
|
||||
out[pystr(item.first)] = anyvalueToPython(item.first, item.second)
|
||||
return out
|
||||
|
||||
@@ -249,78 +249,14 @@ struct convert<Cantera::AnyMap> {
|
||||
|
||||
YAML::Emitter& operator<<(YAML::Emitter& out, const AnyMap& rhs)
|
||||
{
|
||||
// Initial sort based on the order in which items are added
|
||||
vector<std::tuple<std::pair<int, int>, std::string, const AnyValue*>> ordered;
|
||||
// Units always come first
|
||||
AnyValue units;
|
||||
if (rhs.hasKey("__units__") && rhs["__units__"].as<AnyMap>().size()) {
|
||||
units = rhs["__units__"];
|
||||
units.setFlowStyle();
|
||||
ordered.emplace_back(std::pair<int, int>{-2, 0}, std::string("units"), &units);
|
||||
}
|
||||
|
||||
int head = 0; // sort key of the first programmatically-added item
|
||||
int tail = 0; // sort key of the last programmatically-added item
|
||||
for (const auto& item : rhs) {
|
||||
const auto& order = item.second.order();
|
||||
if (order.first == -1) { // Item is not from an input file
|
||||
head = std::min(head, order.second);
|
||||
tail = std::max(tail, order.second);
|
||||
}
|
||||
ordered.emplace_back(order, item.first, &item.second);
|
||||
}
|
||||
std::sort(ordered.begin(), ordered.end());
|
||||
|
||||
// Adjust sort keys for items that should moved to the beginning or end of
|
||||
// the list
|
||||
if (rhs.hasKey("__type__")) {
|
||||
bool order_changed = false;
|
||||
const auto& itemType = rhs["__type__"].asString();
|
||||
std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
|
||||
if (AnyMap::s_headFields.count(itemType)) {
|
||||
for (const auto& key : AnyMap::s_headFields[itemType]) {
|
||||
for (auto& item : ordered) {
|
||||
if (std::get<0>(item).first >= 0) {
|
||||
// This and following items come from an input file and
|
||||
// should not be re-ordered
|
||||
break;
|
||||
}
|
||||
if (std::get<1>(item) == key) {
|
||||
std::get<0>(item).second = --head;
|
||||
order_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AnyMap::s_tailFields.count(itemType)) {
|
||||
for (const auto& key : AnyMap::s_tailFields[itemType]) {
|
||||
for (auto& item : ordered) {
|
||||
if (std::get<0>(item).first >= 0) {
|
||||
// This and following items come from an input file and
|
||||
// should not be re-ordered
|
||||
break;
|
||||
}
|
||||
if (std::get<1>(item) == key) {
|
||||
std::get<0>(item).second = ++tail;
|
||||
order_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (order_changed) {
|
||||
std::sort(ordered.begin(), ordered.end());
|
||||
}
|
||||
}
|
||||
|
||||
bool flow = rhs.getBool("__flow__", false);
|
||||
if (flow) {
|
||||
out << YAML::Flow;
|
||||
out << YAML::BeginMap;
|
||||
size_t width = 15;
|
||||
for (const auto& item : ordered) {
|
||||
const auto& name = std::get<1>(item);
|
||||
const auto& value = *std::get<2>(item);
|
||||
for (const auto& item : rhs.ordered()) {
|
||||
const auto& name = item.first;
|
||||
const auto& value = item.second;
|
||||
string valueStr;
|
||||
bool foundType = true;
|
||||
if (value.is<double>()) {
|
||||
@@ -354,9 +290,9 @@ YAML::Emitter& operator<<(YAML::Emitter& out, const AnyMap& rhs)
|
||||
}
|
||||
} else {
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& item : ordered) {
|
||||
out << std::get<1>(item);
|
||||
out << *std::get<2>(item);
|
||||
for (const auto& item : rhs.ordered()) {
|
||||
out << item.first;
|
||||
out << item.second;
|
||||
}
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
@@ -1526,6 +1462,90 @@ AnyMap::Iterator& AnyMap::Iterator::operator++()
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
AnyMap::OrderedProxy::OrderedProxy(const AnyMap& data)
|
||||
: m_data(&data)
|
||||
{
|
||||
// Units always come first
|
||||
if (m_data->hasKey("__units__") && m_data->at("__units__").as<AnyMap>().size()) {
|
||||
m_units.reset(new std::pair<const string, AnyValue>{"units", m_data->at("__units__")});
|
||||
m_units->second.setFlowStyle();
|
||||
m_ordered.emplace_back(std::pair<int, int>{-2, 0}, m_units.get());
|
||||
}
|
||||
|
||||
int head = 0; // sort key of the first programmatically-added item
|
||||
int tail = 0; // sort key of the last programmatically-added item
|
||||
for (auto& item : *m_data) {
|
||||
const auto& order = item.second.order();
|
||||
if (order.first == -1) { // Item is not from an input file
|
||||
head = std::min(head, order.second);
|
||||
tail = std::max(tail, order.second);
|
||||
}
|
||||
m_ordered.emplace_back(order, &item);
|
||||
}
|
||||
std::sort(m_ordered.begin(), m_ordered.end());
|
||||
|
||||
// Adjust sort keys for items that should moved to the beginning or end of
|
||||
// the list
|
||||
if (m_data->hasKey("__type__")) {
|
||||
bool order_changed = false;
|
||||
const auto& itemType = m_data->at("__type__").asString();
|
||||
std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
|
||||
if (AnyMap::s_headFields.count(itemType)) {
|
||||
for (const auto& key : AnyMap::s_headFields[itemType]) {
|
||||
for (auto& item : m_ordered) {
|
||||
if (item.first.first >= 0) {
|
||||
// This and following items come from an input file and
|
||||
// should not be re-ordered
|
||||
break;
|
||||
}
|
||||
if (item.second->first == key) {
|
||||
item.first.second = --head;
|
||||
order_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AnyMap::s_tailFields.count(itemType)) {
|
||||
for (const auto& key : AnyMap::s_tailFields[itemType]) {
|
||||
for (auto& item : m_ordered) {
|
||||
if (item.first.first >= 0) {
|
||||
// This and following items come from an input file and
|
||||
// should not be re-ordered
|
||||
break;
|
||||
}
|
||||
if (item.second->first == key) {
|
||||
item.first.second = ++tail;
|
||||
order_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (order_changed) {
|
||||
std::sort(m_ordered.begin(), m_ordered.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnyMap::OrderedIterator AnyMap::OrderedProxy::begin() const
|
||||
{
|
||||
return OrderedIterator(m_ordered.begin(), m_ordered.end());
|
||||
}
|
||||
|
||||
AnyMap::OrderedIterator AnyMap::OrderedProxy::end() const
|
||||
{
|
||||
return OrderedIterator(m_ordered.end(), m_ordered.end());
|
||||
}
|
||||
|
||||
AnyMap::OrderedIterator::OrderedIterator(
|
||||
const AnyMap::OrderedProxy::OrderVector::const_iterator& start,
|
||||
const AnyMap::OrderedProxy::OrderVector::const_iterator& stop)
|
||||
{
|
||||
m_iter = start;
|
||||
m_stop = stop;
|
||||
}
|
||||
|
||||
bool AnyMap::operator==(const AnyMap& other) const
|
||||
{
|
||||
// First, make sure that 'other' has all of the non-hidden keys that are in
|
||||
|
||||
Reference in New Issue
Block a user