Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add functionality for iding connections and map_variable elements.
The functionality to id connections and the map variables in the connection has been added.  To unset one of these ids you must set the id to the empty string. hasEquivalentVariable now checks to make sure the share_ptr is still valid. Minor tidy up of commented out code and test values.
  • Loading branch information
hsorby committed Jun 13, 2018
1 parent dbb185c commit 88b285e
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 129 deletions.
9 changes: 9 additions & 0 deletions src/api/libcellml/variable.h
Expand Up @@ -37,6 +37,7 @@ class LIBCELLML_EXPORT Variable: public NamedEntity
Variable(const Variable &rhs); /**< Copy constructor */
Variable(Variable &&rhs); /**< Move constructor */
Variable& operator=(Variable n); /**< Assignment operator */
//friend bool operator<(const Variable& l, const Variable& r); /**< Comparison operator */

/**
* @brief The InterfaceType enum class.
Expand Down Expand Up @@ -68,6 +69,14 @@ class LIBCELLML_EXPORT Variable: public NamedEntity
*/
static void addEquivalence(const VariablePtr &variable1, const VariablePtr &variable2);

static void addEquivalence(const VariablePtr &variable1, const VariablePtr &variable2, const std::string &mappingId, const std::string &connectionId="");

static void setEquivalenceMappingId(const VariablePtr &variable1, const VariablePtr &variable2, const std::string &mappingId);
static void setEquivalenceConnectionId(const VariablePtr &variable1, const VariablePtr &variable2, const std::string &connectionId);

static std::string getEquivalenceMappingId(const VariablePtr &variable1, const VariablePtr &variable2);
static std::string getEquivalenceConnectionId(const VariablePtr &variable1, const VariablePtr &variable2);

/**
* @brief Remove each argument variable to the other's equivalent variable set.
*
Expand Down
14 changes: 13 additions & 1 deletion src/parser.cpp
Expand Up @@ -633,12 +633,16 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
// Check connection for component_{1, 2} attributes and get the name pair.
std::string component1Name = "";
std::string component2Name = "";
std::string mappingId = "";
std::string connectionId = "";
XmlAttributePtr attribute = node->getFirstAttribute();
while (attribute) {
if (attribute->isType("component_1")) {
component1Name = attribute->getValue();
} else if (attribute->isType("component_2")) {
component2Name = attribute->getValue();
} else if (attribute->isType("id")) {
connectionId = attribute->getValue();
} else {
ErrorPtr err = std::make_shared<Error>();
err->setDescription("Connection in model '" + model->getName() +
Expand Down Expand Up @@ -720,6 +724,8 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
variable1Name = attribute->getValue();
} else if (attribute->isType("variable_2")) {
variable2Name = attribute->getValue();
} else if (attribute->isType("id")) {
mappingId = attribute->getValue();
} else {
ErrorPtr err = std::make_shared<Error>();
err->setDescription("Connection in model '" + model->getName() +
Expand Down Expand Up @@ -878,7 +884,7 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
}
// Set the variable equivalence relationship for this variable pair.
if ((variable1) && (variable2)) {
Variable::addEquivalence(variable1, variable2);
Variable::addEquivalence(variable1, variable2, mappingId, connectionId);
}
}
} else {
Expand Down Expand Up @@ -989,6 +995,7 @@ void Parser::ParserImpl::loadEncapsulation(const ModelPtr &model, const XmlNodeP
}

// Loop over encapsulated children.
std::string childEncapsulationId = "";
while (childComponentNode) {
ComponentPtr childComponent = nullptr;
if (childComponentNode->isType("component_ref")) {
Expand All @@ -1012,6 +1019,8 @@ void Parser::ParserImpl::loadEncapsulation(const ModelPtr &model, const XmlNodeP
mParser->addError(err);
childComponentMissing = true;
}
} else if (attribute->isType("id")) {
childEncapsulationId = attribute->getValue();
} else {
ErrorPtr err = std::make_shared<Error>();
err->setDescription("Encapsulation in model '" + model->getName() +
Expand Down Expand Up @@ -1041,6 +1050,9 @@ void Parser::ParserImpl::loadEncapsulation(const ModelPtr &model, const XmlNodeP
err->setRule(SpecificationRule::COMPONENT_REF_COMPONENT_ATTRIBUTE);
mParser->addError(err);
}
if (childComponent) {
childComponent->setEncapsulationId(childEncapsulationId );
}

} else if (childComponentNode->isType("text")) {
const std::string textNode = childComponentNode->convertToString();
Expand Down
228 changes: 132 additions & 96 deletions src/printer.cpp
Expand Up @@ -35,6 +35,15 @@ limitations under the License.

namespace libcellml {

// VariableMap
typedef std::pair <VariablePtr, VariablePtr> VariablePair;
typedef std::vector<VariablePair> VariableMap;
typedef VariableMap::const_iterator VariableMapIterator;
// ComponentMap
typedef std::pair <Component*, Component*> ComponentPair;
typedef std::vector<ComponentPair> ComponentMap;
typedef ComponentMap::const_iterator ComponentMapIterator;

/**
* @brief The Printer::PrinterImpl struct.
*
Expand Down Expand Up @@ -283,6 +292,122 @@ std::string Printer::printVariable(Variable variable) const
return printVariable(std::shared_ptr<Variable>(std::shared_ptr<Variable>{}, &variable));
}

std::string printMapVariables(VariablePair variablePair)
{
std::string mapVariables = "<map_variables variable_1=\"" + variablePair.first->getName() + "\""
+ " variable_2=\"" + variablePair.second->getName() + "\"";
std::string mappingId = Variable::getEquivalenceMappingId(variablePair.first, variablePair.second);
if (mappingId.length() > 0) {
mapVariables += " id=\"" + mappingId + "\"";
}
mapVariables += "/>";
return mapVariables;
}

std::string printConnections(ComponentMap componentMap, VariableMap variableMap)
{
std::string connections = "";
ComponentMap serialisedComponentMap;
int componentMapIndex1 = 0;
for (ComponentMapIterator iterPair = componentMap.begin(); iterPair < componentMap.end(); ++iterPair) {
Component* currentComponent1 = iterPair->first;
Component* currentComponent2 = iterPair->second;
ComponentPair currentComponentPair = std::make_pair(currentComponent1, currentComponent2);
ComponentPair reciprocalCurrentComponentPair = std::make_pair(currentComponent2, currentComponent1);
// Check whether this set of connections has already been serialised.
bool pairFound = false;
for (ComponentMapIterator serialisedIterPair = serialisedComponentMap.begin(); serialisedIterPair < serialisedComponentMap.end(); ++serialisedIterPair) {
if ((*serialisedIterPair == currentComponentPair) || (*serialisedIterPair == reciprocalCurrentComponentPair)) {
pairFound = true;
break;
}
}
// Continue to the next component pair if the current pair has already been serialised.
if (pairFound) {
++componentMapIndex1;
continue;
}
std::string mappingVariables = "";
VariablePair variablePair = variableMap.at(componentMapIndex1);
std::string connectionId = Variable::getEquivalenceConnectionId(variablePair.first, variablePair.second);
mappingVariables += printMapVariables(variablePair);
// Check for subsequent variable equivalence pairs with the same parent components.
int componentMapIndex2 = componentMapIndex1 + 1;
for (ComponentMapIterator iterPair2 = iterPair + 1; iterPair2 < componentMap.end(); ++iterPair2) {
Component* nextComponent1 = iterPair2->first;
Component* nextComponent2 = iterPair2->second;
VariablePair variablePair2 = variableMap.at(componentMapIndex2);
if ((currentComponent1 == nextComponent1) && (currentComponent2 == nextComponent2)) {
mappingVariables += printMapVariables(variablePair2);
connectionId = Variable::getEquivalenceConnectionId(variablePair2.first, variablePair2.second);
}
++componentMapIndex2;
}
// Serialise out the new connection.
std::string connection = "<connection";
if (currentComponent1) {
connection += " component_1=\"" + currentComponent1->getName() + "\"";
}
if (currentComponent2) {
connection += " component_2=\"" + currentComponent2->getName() + "\"";
}
if (connectionId.length() > 0) {
connection += " id=\"" + connectionId + "\"";
}
connection += ">";
connection += mappingVariables;
connection += "</connection>";
connections += connection;
serialisedComponentMap.push_back(currentComponentPair);
++componentMapIndex1;
}

return connections;
}

void buildMaps(ModelPtr model, ComponentMap &componentMap, VariableMap &variableMap)
{
for (size_t i = 0; i < model->componentCount(); ++i) {
ComponentPtr component = model->getComponent(i);
for (size_t j = 0; j < component->variableCount(); ++j) {
VariablePtr variable = component->getVariable(j);
if (variable->equivalentVariableCount() > 0) {
for (size_t k = 0; k < variable->equivalentVariableCount(); ++k) {
VariablePtr equivalentVariable = variable->getEquivalentVariable(k);
if (equivalentVariable->hasEquivalentVariable(variable)) {
VariablePair variablePair = std::make_pair(variable, equivalentVariable);
VariablePair reciprocalVariablePair = std::make_pair(equivalentVariable, variable);
bool pairFound = false;
for (VariableMapIterator iter = variableMap.begin(); iter < variableMap.end(); ++iter) {
if ((*iter == variablePair) || (*iter == reciprocalVariablePair)) {
pairFound = true;
break;
}
}
if (!pairFound) {
// Get parent components.
Component* component1 = static_cast<Component*>(variable->getParent());
Component* component2 = static_cast<Component*>(equivalentVariable->getParent());
// Do not serialise a variable's parent component in a connection if that variable no longer
// exists in that component. Allow serialisation of one componentless variable as an empty component_2.
if (component2) {
if (!component2->hasVariable(equivalentVariable)) {
component2 = nullptr;
}
}
// Add new unique variable equivalence pair to the VariableMap.
variableMap.push_back(variablePair);
// Also create a component map pair corresponding with the variable map pair.
ComponentPair componentPair = std::make_pair(component1, component2);
componentMap.push_back(componentPair);
}
}
}
}
}
}
}

std::string Printer::printModel(ModelPtr model) const
{
// ImportMap
Expand All @@ -291,17 +416,8 @@ std::string Printer::printModel(ModelPtr model) const
typedef std::map <ImportSourcePtr, std::vector<ImportPair> > ImportMap;
typedef ImportMap::const_iterator ImportMapIterator;
ImportMap importMap;
// VariableMap
typedef std::pair <VariablePtr, VariablePtr> VariablePair;
typedef std::vector<VariablePair> VariableMap;
typedef VariableMap::const_iterator VariableMapIterator;
VariableMap variableMap;
// ComponentMap
typedef std::pair <Component*, Component*> ComponentPair;
typedef std::vector<ComponentPair> ComponentMap;
typedef ComponentMap::const_iterator ComponentMapIterator;
ComponentMap componentMap;
ComponentMap serialisedComponentMap;

// Gather all imports.
std::stack<size_t> indeciesStack;
Expand Down Expand Up @@ -400,94 +516,11 @@ std::string Printer::printModel(ModelPtr model) const
}
}

// Build unique variable equivalence pairs (VariableMap) for connections.
for (size_t i = 0; i < model->componentCount(); ++i) {
ComponentPtr component = model->getComponent(i);
for (size_t j = 0; j < component->variableCount(); ++j) {
VariablePtr variable = component->getVariable(j);
if (variable->equivalentVariableCount() > 0) {
for (size_t k = 0; k < variable->equivalentVariableCount(); ++k) {
VariablePtr equivalentVariable = variable->getEquivalentVariable(k);
if (equivalentVariable->hasEquivalentVariable(variable)) {
VariablePair variablePair = std::make_pair(variable, equivalentVariable);
VariablePair reciprocalVariablePair = std::make_pair(equivalentVariable, variable);
bool pairFound = false;
for (VariableMapIterator iter = variableMap.begin(); iter < variableMap.end(); ++iter) {
if ((*iter == variablePair) || (*iter == reciprocalVariablePair)) {
pairFound = true;
break;
}
}
if (!pairFound) {
// Get parent components.
Component* component1 = static_cast<Component*>(variable->getParent());
Component* component2 = static_cast<Component*>(equivalentVariable->getParent());
// Do not serialise a variable's parent component in a connection if that variable no longer
// exists in that component. Allow serialisation of one componentless variable as an empty component_2.
if (component2) {
if (!component2->hasVariable(equivalentVariable)) {
component2 = nullptr;
}
}
// Add new unique variable equivalence pair to the VariableMap.
variableMap.push_back(variablePair);
// Also create a component map pair corresponding with the variable map pair.
ComponentPair componentPair = std::make_pair(component1, component2);
componentMap.push_back(componentPair);
}
}
}
}
}
}
// Build unique variable equivalence pairs (ComponentMap, VariableMap) for connections.
buildMaps(model, componentMap, variableMap);
// Serialise connections of the model.
int componentMapIndex1 = 0;
for (ComponentMapIterator iterPair = componentMap.begin(); iterPair < componentMap.end(); ++iterPair) {
Component* currentComponent1 = iterPair->first;
Component* currentComponent2 = iterPair->second;
ComponentPair currentComponentPair = std::make_pair(currentComponent1, currentComponent2);
ComponentPair reciprocalCurrentComponentPair = std::make_pair(currentComponent2, currentComponent1);
// Check whether this set of connections has already been serialised.
bool pairFound = false;
for (ComponentMapIterator serialisedIterPair = serialisedComponentMap.begin(); serialisedIterPair < serialisedComponentMap.end(); ++serialisedIterPair) {
if ((*serialisedIterPair == currentComponentPair) || (*serialisedIterPair == reciprocalCurrentComponentPair)) {
pairFound = true;
break;
}
}
// Continue to the next component pair if the current pair has already been serialised.
if (pairFound) {
++componentMapIndex1;
continue;
}
// Serialise out the new connection.
std::string connection = "<connection";
if (currentComponent1) {
connection += " component_1=\"" + currentComponent1->getName() + "\"";
}
if (currentComponent2) {
connection += " component_2=\"" + currentComponent2->getName() + "\"";
}
VariablePair variablePair = variableMap.at(componentMapIndex1);
connection += "><map_variables variable_1=\"" + variablePair.first->getName() + "\""
+ " variable_2=\"" + variablePair.second->getName() + "\"/>";
// Check for subsequent variable equivalence pairs with the same parent components.
int componentMapIndex2 = componentMapIndex1 + 1;
for (ComponentMapIterator iterPair2 = iterPair + 1; iterPair2 < componentMap.end(); ++iterPair2) {
Component* nextComponent1 = iterPair2->first;
Component* nextComponent2 = iterPair2->second;
VariablePair variablePair2 = variableMap.at(componentMapIndex2);
if ((currentComponent1 == nextComponent1) && (currentComponent2 == nextComponent2)) {
connection += "<map_variables variable_1=\"" + variablePair2.first->getName() + "\""
" variable_2=\"" + variablePair2.second->getName() + "\"/>";
}
++componentMapIndex2;
}
connection += "</connection>";
repr += connection;
serialisedComponentMap.push_back(currentComponentPair);
++componentMapIndex1;
}
repr += printConnections(componentMap, variableMap);

if (componentEncapsulation.length() > 0) {
repr += "<encapsulation";
if (model->getEncapsulationId().length() > 0) {
Expand Down Expand Up @@ -524,6 +557,9 @@ std::string Printer::printEncapsulation(ComponentPtr component) const
if (componentName.length() > 0) {
repr += " component=\"" + componentName + "\"";
}
if (component->getEncapsulationId().length() > 0) {
repr += " id=\"" + component->getEncapsulationId() + "\"";
}
size_t componentCount = component->componentCount();
if (componentCount > 0) {
repr += ">";
Expand Down

0 comments on commit 88b285e

Please sign in to comment.