Skip to content
Open
27 changes: 11 additions & 16 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,15 +590,16 @@ void Parser::ParserImpl::loadModel(const ModelPtr &model, const std::string &inp
childNode = childNode->next();
}

if (!encapsulationNodes.empty()) {
loadEncapsulation(model, encapsulationNodes.at(0));
if (encapsulationNodes.size() > 1) {
auto issue = Issue::IssueImpl::create();
issue->mPimpl->setDescription("Model '" + model->name() + "' has more than one encapsulation element.");
issue->mPimpl->mItem->mPimpl->setEncapsulation(model);
issue->mPimpl->setReferenceRule(Issue::ReferenceRule::MODEL_MORE_THAN_ONE_ENCAPSULATION);
addIssue(issue);
}
for (const auto &encapsulationNode : encapsulationNodes) {
loadEncapsulation(model, encapsulationNode);
Comment on lines +593 to +594
}

if (mParsing20Version && encapsulationNodes.size() > 1) {
auto issue = Issue::IssueImpl::create();
issue->mPimpl->setDescription("Model '" + model->name() + "' has more than one encapsulation element.");
issue->mPimpl->mItem->mPimpl->setEncapsulation(model);
issue->mPimpl->setReferenceRule(Issue::ReferenceRule::MODEL_MORE_THAN_ONE_ENCAPSULATION);
addIssue(issue);
}

NamePairList usedConnections;
Expand Down Expand Up @@ -1109,7 +1110,7 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
issue->mPimpl->setReferenceRule(Issue::ReferenceRule::CONNECTION_EXCLUDE_SELF);
addIssue(issue);
} else {
if (component1Name > component2Name) {
if (!mParsing1XVersion && (component1Name > component2Name)) {
std::string tmp = component1Name;
component1Name = component2Name;
component2Name = tmp;
Expand Down Expand Up @@ -1240,12 +1241,6 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
mapVariablesFound = true;

if (!variable1Missing && !variable2Missing) {
if (variable1Name > variable2Name) {
std::string tmp = variable1Name;
variable1Name = variable2Name;
variable2Name = tmp;
}

auto variableNamePair = std::make_pair(variable1Name, variable2Name);

NamePairList::const_iterator it = std::find_if(usedMapVariables.begin(), usedMapVariables.end(),
Expand Down
63 changes: 62 additions & 1 deletion tests/connection/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1435,7 +1435,7 @@ TEST(Connection, repeatedMapVariables)

const std::vector<std::string> expectedIssues = {
"Connection in model 'connection' between 'variable1' and 'variable2' is not unique.",
"Connection in model 'connection' between 'variable3' and 'variable6' is not unique.",
"Connection in model 'connection' between 'variable6' and 'variable3' is not unique.",
};

EXPECT_EQ_ISSUES(expectedIssues, p);
Expand Down Expand Up @@ -1535,3 +1535,64 @@ TEST(Connection, addEquivalenceConnectionIdClearedAfterDisconnect)
libcellml::Variable::addEquivalence(v1, v2);
EXPECT_EQ("", libcellml::Variable::equivalenceConnectionId(v1, v2));
}

TEST(Connection, twoWayConnectionFromCellML1)
{
auto p = libcellml::Parser::create(false);
auto m = p->parseModel(fileContents("cellml1X/connections.cellml"));

EXPECT_EQ(size_t(0), p->errorCount());
}

TEST(Connection, sameNamedVariablesInDifferentComponents)
{
auto m = libcellml::Model::create("m");
auto c1 = libcellml::Component::create("c1");
auto c2 = libcellml::Component::create("c2");
auto v1c1 = libcellml::Variable::create("v1");
auto v2c1 = libcellml::Variable::create("v2");
auto v1c2 = libcellml::Variable::create("v1");
auto v2c2 = libcellml::Variable::create("v2");

m->addComponent(c1);
m->addComponent(c2);

c1->addVariable(v1c1);
c1->addVariable(v2c1);
c2->addVariable(v1c2);
c2->addVariable(v2c2);

v1c1->setUnits("dimensionless");
v1c1->setInterfaceType("public");
v2c1->setUnits("dimensionless");
v2c1->setInterfaceType("public");
v1c2->setUnits("dimensionless");
v1c2->setInterfaceType("public");
v2c2->setUnits("dimensionless");
v2c2->setInterfaceType("public");

libcellml::Variable::addEquivalence(v1c1, v2c2);
libcellml::Variable::addEquivalence(v2c1, v1c2);

auto v = libcellml::Validator::create();
v->validateModel(m);

EXPECT_EQ(size_t(0), v->issueCount());

auto printer = libcellml::Printer::create();

auto modelString = printer->printModel(m);

auto p = libcellml::Parser::create();
p->parseModel(modelString);

EXPECT_EQ(size_t(0), p->issueCount());
}

TEST(Connection, mapComponentsAreOrderDependentInCellML1X)
{
auto p = libcellml::Parser::create(false);
auto m = p->parseModel(fileContents("cellml1X/map_components.cellml"));

EXPECT_EQ(size_t(0), p->errorCount());
}
15 changes: 15 additions & 0 deletions tests/parser/cellml_1_1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,18 @@ TEST(ParserTransform, cellmlNsCn)

EXPECT_EQ(size_t(0), validator->issueCount());
}

TEST(ParserTransform, parseCellMl1XWithMultipleEncapsulationRelationshipGroups)
{
std::vector<std::string> expectedIssues = {
"Given model is a CellML 1.1 model, the parser will try to represent this model in CellML 2.0.",
};

auto parser = libcellml::Parser::create(false);
auto model = parser->parseModel(fileContents("cellml1X/multiple_encapsulations.cellml"));

printIssues(parser);

EXPECT_EQ_ISSUES(expectedIssues, parser);
EXPECT_EQ("Example_MassAction", model->name());
Comment on lines +156 to +157
}
1 change: 1 addition & 0 deletions tests/parser/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ TEST(Parser, invalidEncapsulations)
"Encapsulation in model 'ringo' does not have a valid component attribute in a component_ref element.",
"Encapsulation in model 'ringo' does not have a valid component attribute in a component_ref element.",
"Encapsulation in model 'ringo' specifies an invalid parent component_ref that also does not have any children.",
"Encapsulation in model 'ringo' specifies 'bob' as a parent component_ref but it does not have any children.",
"Model 'ringo' has more than one encapsulation element.",
};

Expand Down
56 changes: 56 additions & 0 deletions tests/resources/cellml1X/connections.cellml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<model xmlns="http://www.cellml.org/cellml/1.1#" xmlns:cmeta="http://www.cellml.org/metadata/1.0#" cmeta:id="MainDVad" name="MainDVad">

<units name="UnitP">
<unit multiplier="133" units="pascal"/>
</units>

<units name="UnitQ">
<unit units="UnitV"/>
<unit exponent="-1" units="second"/>
</units>

<component name="environment">
<variable cmeta:id="environment_t" name="t" public_interface="out" units="second"/>
</component>

<component name="Vessel">
<variable name="Pi" private_interface="in" public_interface="out" units="UnitP"/>
<variable name="Po" private_interface="out" public_interface="in" units="UnitP"/>
<variable name="Qi" private_interface="out" public_interface="in" units="UnitQ"/>
<variable name="Qm" private_interface="out" public_interface="in" units="UnitQ"/>
<variable name="Qo" private_interface="in" public_interface="out" units="UnitQ"/>
<variable name="t" private_interface="out" public_interface="in" units="second"/>
<variable name="Pm" private_interface="in" public_interface="out" units="UnitP"/>
</component>

<component name="LHeart">
<variable name="Pi" private_interface="in" public_interface="out" units="UnitP"/>
<variable name="Po" private_interface="out" public_interface="in" units="UnitP"/>
<variable name="Qi" private_interface="out" public_interface="in" units="UnitQ"/>
<variable name="Qm" private_interface="out" public_interface="in" units="UnitQ"/>
<variable name="Qo" private_interface="in" public_interface="out" units="UnitQ"/>
<variable name="t" private_interface="out" public_interface="in" units="second"/>
<variable name="Pm" private_interface="in" public_interface="out" units="UnitP"/>
</component>

<connection>
<map_components component_1="environment" component_2="LHeart"/>
<map_variables variable_1="t" variable_2="t"/>
</connection>


<connection>
<map_components component_1="environment" component_2="Vessel"/>
<map_variables variable_1="t" variable_2="t"/>
</connection>

<connection>
<map_components component_1="LHeart" component_2="Vessel"/>
<map_variables variable_1="Po" variable_2="Pi"/>
<map_variables variable_1="Qo" variable_2="Qi"/>
<map_variables variable_1="Pi" variable_2="Po"/>
<map_variables variable_1="Qi" variable_2="Qo"/>
</connection>

</model>
30 changes: 30 additions & 0 deletions tests/resources/cellml1X/map_components.cellml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version='1.0' encoding='utf-8'?>
<model xmlns="http://www.cellml.org/cellml/1.0#" xmlns:cellml="http://www.cellml.org/cellml/1.0#" name="map_components">
<units name="molar">
<unit units="mole"/>
<unit units="litre" exponent="-1"/>
</units>
<units name="micromolar">
<unit units="molar" prefix="micro"/>
</units>
<component name="R">
<variable units="micromolar" public_interface="out" name="R" initial_value="0.001"/>
<variable units="micromolar" public_interface="out" name="D" initial_value="0.01"/>
<variable units="micromolar" public_interface="in" name="DR"/>
</component>
<component name="DR">
<variable units="micromolar" public_interface="in" name="D" initial_value=""/>
<variable units="micromolar" public_interface="in" name="R" initial_value=""/>
<variable units="micromolar" public_interface="out" name="DR"/>
</component>
<connection>
<map_components component_2="R" component_1="DR"/>
<map_variables variable_2="DR" variable_1="DR"/>
</connection>
<connection>
<map_components component_2="DR" component_1="R"/>
<map_variables variable_2="R" variable_1="R"/>
<map_variables variable_2="D" variable_1="D"/>
</connection>
</model>

30 changes: 30 additions & 0 deletions tests/resources/cellml1X/multiple_encapsulations.cellml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0"?>
<model xmlns="http://www.cellml.org/cellml/1.1#" name="Example_MassAction">

<import xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="library.cellml">
<component component_ref="MassAction" name="R1_MassAction" />
</import>

<import xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="library.cellml">
<component component_ref="MassAction" name="R2_MassAction" />
</import>

<component name="Reaction_1">
</component>

<component name="Reaction_2">
</component>

<group>
<relationship_ref relationship="encapsulation" />
<component_ref component="Reaction_1">
<component_ref component="R1_MassAction" />
</component_ref>
</group>
<group>
<relationship_ref relationship="encapsulation" />
<component_ref component="Reaction_2">
<component_ref component="R2_MassAction" />
</component_ref>
</group>
</model>
Loading