void AssignmentCycles::addRuleDependencies(const Model& m, const Rule& object) { unsigned int ns; std::string thisId = object.getVariable(); /* loop thru the list of names in the Math * if they refer to a Reaction, an Assignment Rule * or an Initial Assignment add to the map * with the variable as key */ List* variables = object.getMath()->getListOfNodes( ASTNode_isName ); for (ns = 0; ns < variables->getSize(); ns++) { ASTNode* node = static_cast<ASTNode*>( variables->get(ns) ); string name = node->getName() ? node->getName() : ""; if (m.getReaction(name)) { mIdMap.insert(pair<const std::string, std::string>(thisId, name)); } else if (m.getRule(name) && m.getRule(name)->isAssignment()) { mIdMap.insert(pair<const std::string, std::string>(thisId, name)); } else if (m.getInitialAssignment(name)) { mIdMap.insert(pair<const std::string, std::string>(thisId, name)); } } delete variables; }
/* * @return the error message to use when logging constraint violations. * This method is called by logFailure. * * Returns a message that the given @p id and its corresponding object are * in conflict with an object previously defined. */ const string CiElementMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); char * formula = SBML_formulaToString(&node); msg << "The formula '" << formula; msg << "' in the " << getFieldname() << " element of the <" << object.getElementName(); msg << "> "; switch(object.getTypeCode()) { case SBML_INITIAL_ASSIGNMENT: case SBML_EVENT_ASSIGNMENT: case SBML_ASSIGNMENT_RULE: case SBML_RATE_RULE: //LS DEBUG: could use other attribute values, or 'isSetActualId'. break; default: if (object.isSetId()) { msg << "with id '" << object.getId() << "' "; } break; } if (object.getLevel() == 2 && object.getVersion() == 1) msg << "uses '" << node.getName() << "' that is not the id of a species/compartment/parameter."; else if (object.getLevel() < 3) msg << "uses '" << node.getName() << "' that is not the id of a species/compartment/parameter/reaction."; else msg << "uses '" << node.getName() << "' that is not the id of a species/compartment/parameter/reaction/speciesReference."; safe_free(formula); return msg.str(); }
/** * Checks that the functionDefinition referred to by a <ci> element * has the appropriate number of arguments. * * If not, an error message is logged. */ void FunctionNoArgsMathCheck::checkNumArgs (const Model& m, const ASTNode& node, const SBase & sb) { /* this rule was only introduced level 2 version 4 */ if (m.getLevel() > 2 || (m.getLevel() == 2 && m.getVersion() > 3)) { if (m.getFunctionDefinition(node.getName()) != NULL) { /* functiondefinition math */ const ASTNode * fdMath = m.getFunctionDefinition(node.getName())->getMath(); if (fdMath != NULL) { /* We have a definition for this function. Does the defined number of arguments equal the number used here? */ if (node.getNumChildren() != m.getFunctionDefinition(node.getName())->getNumArguments()) { logMathConflict(node, sb); } } } } }
void FunctionDefinitionRecursion::addDependencies(const Model& m, const FunctionDefinition& object) { unsigned int ns; std::string thisId = object.getId(); /* loop thru the list of names in the Math * if they refer to a FunctionDefinition add to the map * with the variable as key */ List* variables = object.getMath()->getListOfNodes( ASTNode_isFunction ); for (ns = 0; ns < variables->getSize(); ns++) { ASTNode* node = static_cast<ASTNode*>( variables->get(ns) ); string name = node->getName() ? node->getName() : ""; if (m.getFunctionDefinition(name)) { mIdMap.insert(pair<const std::string, std::string>(thisId, name)); } } delete variables; }
END_TEST START_TEST (test_element_selector) { const char* s = wrapMathML ( " <apply>" " <selector/>" " <ci> A </ci>" " <ci> i </ci>" " </apply>" ); N = readMathMLFromStringWithNamespaces(s, NS); fail_unless( N != NULL ); fail_unless( N->getType() == AST_ORIGINATES_IN_PACKAGE); fail_unless( N->getExtendedType() == AST_LINEAR_ALGEBRA_SELECTOR); fail_unless( N->getNumChildren() == 2); fail_unless( N->getPackageName() == "arrays"); ASTNode * child = N->getChild(0); fail_unless( child != NULL ); fail_unless( child->getType() == AST_NAME); fail_unless( child->getExtendedType() == AST_NAME); fail_unless( strcmp(child->getName(), "A") == 0); fail_unless( child->getNumChildren() == 0); fail_unless( child->getPackageName() == "core"); child = N->getChild(1); fail_unless( child != NULL ); fail_unless( child->getType() == AST_NAME); fail_unless( child->getExtendedType() == AST_NAME); fail_unless( strcmp(child->getName(), "i") == 0); fail_unless( child->getNumChildren() == 0); ArraysASTPlugin* plugin = static_cast<ArraysASTPlugin*>(N->getPlugin("arrays")); fail_unless(plugin != NULL); fail_unless(plugin->getASTType() == AST_LINEAR_ALGEBRA_SELECTOR); }
/** * Constructor that makes a ConverterASTNode from an ASTNode. */ ConverterASTNode::ConverterASTNode(const ASTNode &templ): ASTNode(templ.getType()) { if (this->getType() == AST_RATIONAL) { this->mDenominator = templ.getDenominator(); this->mInteger = templ.getNumerator(); } else if (this->getType() == AST_REAL || this->getType() == AST_REAL_E) { this->mExponent = templ.getExponent(); this->mReal = templ.getMantissa(); } if (this->getType() == AST_PLUS || this->getType() == AST_MINUS || this->getType() == AST_TIMES || this->getType() == AST_DIVIDE || this->getType() == AST_POWER) { this->mChar = templ.getCharacter(); } else if (this->getType() == AST_INTEGER) { this->mInteger = templ.getInteger(); } if ((!this->isOperator()) && (!this->isNumber())) { this->setName(templ.getName()); } unsigned int counter; for (counter = 0; counter < templ.getNumChildren(); counter++) { this->addChild(new ConverterASTNode(*templ.getChild(counter))); } };
/* * @return the error message to use when logging constraint violations. * This method is called by logFailure. * * Returns a message that the given @p id and its corresponding object are * in conflict with an object previously defined. */ const string FunctionApplyMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); char * formula = SBML_formulaToString(&node); msg << "The formula '" << formula; msg << "' in the " << getFieldname() << " element of the <" << object.getElementName(); msg << "> "; switch(object.getTypeCode()) { case SBML_INITIAL_ASSIGNMENT: case SBML_EVENT_ASSIGNMENT: case SBML_ASSIGNMENT_RULE: case SBML_RATE_RULE: //LS DEBUG: could use other attribute values, or 'isSetActualId'. break; default: if (object.isSetId()) { msg << "with id '" << object.getId() << "' "; } break; } msg << "uses '" << node.getName() << "' which is not a function definition id."; safe_free(formula); return msg.str(); }
/** * Checks any <ci> elements in the MathML of the ASTnode * contain the id of an appropriate component of the model * * If an inconsistency is found, an error message is logged. */ void LocalParameterMathCheck::checkCiElement (const Model& m, const ASTNode& node, const SBase & sb) { std::string name = node.getName(); const KineticLaw * kl; if (m.getCompartment(name) == NULL && m.getSpecies(name) == NULL && m.getParameter(name) == NULL && m.getReaction(name) == NULL) { /* check whether we are in a kinetic law since there * may be local parameters to this law that are allowed */ if (sb.getTypeCode() == SBML_KINETIC_LAW) { kl = m.getReaction(mKLCount)->getKineticLaw(); if (kl->getParameter(name) == NULL && mLocalParameters.contains(name)) { logMathConflict(node, sb); } } else if (mLocalParameters.contains(name)) { logMathConflict(node, sb); } } }
void TestASTNode::testConstructor() { ASTNode* node = new ASTNode("value", PROGRAM, 1); CPPUNIT_ASSERT("value" == node->getName()); CPPUNIT_ASSERT_EQUAL(PROGRAM, node->getType()); CPPUNIT_ASSERT_EQUAL(1, node->getStatementNumber()); }
/* * Checks that the functionDefinition referred to by a <ci> element exists * * If <ci> does not refer to functionDefinition id, an error message is logged. */ void FunctionApplyMathCheck::checkExists (const Model& m, const ASTNode& node, const SBase & sb) { std::string name = node.getName(); if (!m.getFunctionDefinition(name)) logMathConflict(node, sb); }
/** * @return the error message to use when logging constraint violations. * This method is called by logFailure. * * Returns a message that the given id and its corresponding object are * in conflict with an object previously defined. */ const string CiElementMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); char * formula = SBML_formulaToString(&node); msg << "\nThe formula '" << formula; msg << "' in the " << getFieldname() << " element of the " << getTypename(object); if (object.getLevel() == 2 && object.getVersion() == 1) msg << " uses '" << node.getName() << "' that is not the id of a species/compartment/parameter."; else msg << " uses '" << node.getName() << "' that is not the id of a species/compartment/parameter/reaction."; safe_free(formula); return msg.str(); }
std::string AST::getCalledProcedure(int stmtNum) { ASTNode* node = getStatementNode(stmtNum); if (node->getType() == CALL) { return node->getName(); } else { return ""; } }
bool containsId(const ASTNode* ast, std::string id) { bool present = false; List* variables = ast->getListOfNodes(ASTNode_isName); IdList vars; for (unsigned int i = 0; i < variables->getSize(); i++) { ASTNode* node = static_cast<ASTNode*>(variables->get(i)); string name = node->getName() ? node->getName() : ""; vars.append(name); } if (vars.contains(id)) { present = true; } delete variables; return present; }
void AssignmentRuleOrdering::checkRuleForVariable(const Model& m, const Rule& object) { /* list the <ci> elements */ List* variables = object.getMath()->getListOfNodes( ASTNode_isName ); std::string variable = object.getVariable(); if (variables) { for (unsigned int i = 0; i < variables->getSize(); i++) { ASTNode* node = static_cast<ASTNode*>( variables->get(i) ); const char * name = node->getName() ? node->getName() : ""; if (!(strcmp(variable.c_str(), name))) logRuleRefersToSelf(*(object.getMath()), object); } // return value of ASTNode::getListOfNodes() needs to be // deleted by caller. delete variables; } }
/** * Checks that all variables referenced in FunctionDefinition bodies are * bound variables (function arguments). */ void FunctionDefinitionVars::check_ (const Model& m, const FunctionDefinition& fd) { if ( fd.getLevel() == 1 ) return; if ( !fd.isSetMath() ) return; if ( fd.getBody() == NULL ) return; if ( fd.getNumArguments() == 0 ) return; List* variables = fd.getBody()->getListOfNodes( ASTNode_isName ); for (unsigned int n = 0; n < variables->getSize(); ++n) { ASTNode* node = static_cast<ASTNode*>( variables->get(n) ); string name = node->getName() ? node->getName() : ""; if ( fd.getArgument(name) == NULL ) { /* if this is the csymbol time - technically it is allowed * in L2v1 and L2v2 */ if (node->getType() == AST_NAME_TIME) { if (fd.getLevel() > 2 || (fd.getLevel() == 2 && fd.getVersion() > 2)) { logUndefined(fd, name); } } else { logUndefined(fd, name); } } } delete variables; }
/** * @return the error message to use when logging constraint violations. * This method is called by logFailure. * * Returns a message that the given id and its corresponding object are * in conflict with an object previously defined. */ const string LocalParameterMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); msg << "\nThe formula '"; msg << "' in the " << getFieldname() << " element of the " << getTypename(object); msg << " uses '" << node.getName() << "' that is the id of a local parameter."; return msg.str(); }
/* * Checks that all variables referenced in FunctionDefinition bodies are * bound variables (function arguments). */ void KineticLawVars::check_ (const Model& m, const Reaction& r) { unsigned int n; /* create list of all species in the reaction */ for (n = 0; n < r.getNumReactants(); n++) { mSpecies.append(r.getReactant(n)->getSpecies()); } for (n = 0; n < r.getNumProducts(); n++) { mSpecies.append(r.getProduct(n)->getSpecies()); } for (n = 0; n < r.getNumModifiers(); n++) { mSpecies.append(r.getModifier(n)->getSpecies()); } if ( r.isSetKineticLaw() && r.getKineticLaw()->isSetMath() ) { const ASTNode* math = r.getKineticLaw()->getMath(); List* names = math->getListOfNodes( ASTNode_isName ); for (n = 0; n < names->getSize(); ++n) { ASTNode* node = static_cast<ASTNode*>( names->get(n) ); string name = node->getName() ? node->getName() : ""; if (m.getSpecies(name) != NULL && !mSpecies.contains(name) ) logUndefined(r, name); } delete names; } mSpecies.clear(); }
/* * Checks any <ci> elements in the MathML of the ASTnode * contain the id of an appropriate component of the model * * If an inconsistency is found, an error message is logged. */ void CiElementMathCheck::checkCiElement (const Model& m, const ASTNode& node, const SBase & sb) { std::string name = node.getName(); const KineticLaw * kl; /* leave out this check if the ci element is a local parameter in a kineticLaw * caught by LocalParameterMathCheck instead */ if (!mLocalParameters.contains(name)) { bool allowReactionId = true; bool allowSpeciesRef = false; if ( (m.getLevel() == 2) && (m.getVersion() == 1) ) allowReactionId = false; if (m.getLevel() > 2) allowSpeciesRef = true; if (m.getCompartment(name) == NULL && m.getSpecies(name) == NULL && m.getParameter(name) == NULL && (!allowReactionId || m.getReaction(name) == NULL ) && (!allowSpeciesRef || m.getSpeciesReference(name) == NULL ) ) { /* check whether we are in a kinetic law since there * may be local parameters */ if (sb.getTypeCode() == SBML_KINETIC_LAW) { kl = m.getReaction(mKLCount)->getKineticLaw(); if (kl->getParameter(name) == NULL) { logMathConflict(node, sb); } } else { logMathConflict(node, sb); } } } }
void AssignmentRuleOrdering::checkRuleForLaterVariables(const Model& m, const Rule& object, unsigned int n) { /* list the <ci> elements of this rule*/ List* variables = object.getMath()->getListOfNodes( ASTNode_isName ); if (variables) { unsigned int index; for (unsigned int i = 0; i < variables->getSize(); i++) { ASTNode* node = static_cast<ASTNode*>( variables->get(i) ); const char * name = node->getName() ? node->getName() : ""; if (mVariableList.contains(name)) { // this <ci> is a variable // check that it occurs later index = 0; while(index < mVariableList.size()) { if (!strcmp(name, mVariableList.at(index).c_str())) break; index++; } if (index > n) logForwardReference(*(object.getMath()), object, name); } } // return value of ASTNode::getListOfNodes() needs to be // deleted by caller. delete variables; } }
/** * @return the error message to use when logging constraint violations. * This method is called by logFailure. * * Returns a message that the given id and its corresponding object are * in conflict with an object previously defined. */ const string FunctionApplyMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); char * formula = SBML_formulaToString(&node); msg << "\nThe formula '" << formula; msg << "' in the " << getFieldname() << " element of the " << getTypename(object); msg << " uses '" << node.getName() << "' which is not a function definition id."; safe_free(formula); return msg.str(); }
/** * @return the error message to use when logging constraint violations. * This method is called by logFailure. * * Returns a message that the given id and its corresponding object are * in conflict with an object previously defined. */ const string FunctionNoArgsMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); char * formula = SBML_formulaToString(&node); msg << "\nThe formula '" << formula; msg << "' in the " << getFieldname() << " element of the "; msg << getTypename(object); msg << " uses the function '" << node.getName() << "' which requires "; msg << "a different number of arguments than the number supplied."; safe_free(formula); return msg.str(); }
/** * Checks the MathML of the ASTnode * is appropriate for the function being performed * * If an inconsistency is found, an error message is logged. */ void NumberArgsMathCheck::checkMath (const Model& m, const ASTNode& node, const SBase & sb) { ASTNodeType_t type = node.getType(); switch (type) { case AST_FUNCTION_ABS: case AST_FUNCTION_ARCCOS: case AST_FUNCTION_ARCCOSH: case AST_FUNCTION_ARCCOT: case AST_FUNCTION_ARCCOTH: case AST_FUNCTION_ARCCSC: case AST_FUNCTION_ARCCSCH: case AST_FUNCTION_ARCSEC: case AST_FUNCTION_ARCSECH: case AST_FUNCTION_ARCSIN: case AST_FUNCTION_ARCSINH: case AST_FUNCTION_ARCTAN: case AST_FUNCTION_ARCTANH: case AST_FUNCTION_CEILING: case AST_FUNCTION_COS: case AST_FUNCTION_COSH: case AST_FUNCTION_COT: case AST_FUNCTION_COTH: case AST_FUNCTION_CSC: case AST_FUNCTION_CSCH: case AST_FUNCTION_EXP: case AST_FUNCTION_FACTORIAL: case AST_FUNCTION_FLOOR: case AST_FUNCTION_LN: case AST_FUNCTION_SEC: case AST_FUNCTION_SECH: case AST_FUNCTION_SIN: case AST_FUNCTION_SINH: case AST_FUNCTION_TAN: case AST_FUNCTION_TANH: case AST_LOGICAL_NOT: checkUnary(m, node, sb); break; case AST_DIVIDE: case AST_POWER: case AST_RELATIONAL_NEQ: case AST_FUNCTION_DELAY: case AST_FUNCTION_POWER: case AST_FUNCTION_LOG: // a log ASTNode has a child for base checkBinary(m, node, sb); break; case AST_TIMES: case AST_PLUS: case AST_LOGICAL_AND: case AST_LOGICAL_OR: case AST_LOGICAL_XOR: case AST_RELATIONAL_EQ: case AST_RELATIONAL_GEQ: case AST_RELATIONAL_GT: case AST_RELATIONAL_LEQ: case AST_RELATIONAL_LT: case AST_FUNCTION_PIECEWISE: checkNary(m, node, sb); break; case AST_FUNCTION_ROOT: case AST_MINUS: checkSpecialCases(m, node, sb); break; case AST_FUNCTION: /* the case for a functionDefinition has its own rule from l2v4*/ if (m.getLevel() < 3 && m.getVersion() < 4) { if (m.getFunctionDefinition(node.getName()) != NULL) { /* functiondefinition math */ const ASTNode * fdMath = m.getFunctionDefinition(node.getName())->getMath(); if (fdMath != NULL) { /* We have a definition for this function. Does the defined number of arguments equal the number used here? */ if (node.getNumChildren() + 1 != fdMath->getNumChildren()) { logMathConflict(node, sb); } } } } break; default: checkChildren(m, node, sb); break; } }
/* * Checks that all variables referenced in FunctionDefinition bodies are * bound variables (function arguments). */ void FunctionDefinitionVars::check_ (const Model& m, const FunctionDefinition& fd) { if ( fd.getLevel() == 1 ) return; if ( !fd.isSetMath() ) return; if ( fd.getBody() == NULL ) return; //if ( fd.getNumArguments() == 0 ) return; List* variables = fd.getBody()->getListOfNodes( ASTNode_isName ); for (unsigned int n = 0; n < variables->getSize(); ++n) { ASTNode* node = static_cast<ASTNode*>( variables->get(n) ); string name = node->getName() ? node->getName() : ""; if ( fd.getArgument(name) == NULL ) { /* if this is the csymbol time - technically it is allowed * in L2v1 and L2v2 */ if (node->getType() == AST_NAME_TIME) { if (fd.getLevel() > 2 || (fd.getLevel() == 2 && fd.getVersion() > 2)) { logUndefined(fd, name); } } else { logUndefined(fd, name); } } } if ((m.getLevel() == 2 && m.getVersion() == 5) || (m.getLevel() == 3 && m.getVersion() > 1)) { // check we dont use delay csymbol delete variables; variables = fd.getBody()->getListOfNodes( ASTNode_isFunction ); for (unsigned int n = 0; n < variables->getSize(); ++n) { ASTNode* node = static_cast<ASTNode*>( variables->get(n) ); if (node->getType() == AST_FUNCTION_DELAY) { logUndefined(fd, node->getName()); } } } //Check we don't use a function defined in a plugin (like rateOf) delete variables; variables = fd.getBody()->getListOfNodes(ASTNode_isFunction); for (unsigned int n = 0; n < variables->getSize(); ++n) { ASTNode* node = static_cast<ASTNode*>(variables->get(n)); const ASTBasePlugin* plugin = node->getASTPlugin(node->getType()); if (plugin != NULL) { if (plugin->allowedInFunctionDefinition(node->getType()) == 0) { logUndefined(fd, node->getName()); } } } delete variables; }
CK_CPPSTART START_TEST (test_element_vector) { const char* s = wrapMathML ( "<vector>" " <apply>" " <cos/>" " <cn type=\"integer\"> 5 </cn>" " </apply>" " <ci> y </ci>" "</vector>\n" ); N = readMathMLFromStringWithNamespaces(s, NS); fail_unless( N != NULL ); fail_unless( N->getType() == AST_ORIGINATES_IN_PACKAGE); fail_unless( N->getExtendedType() == AST_LINEAR_ALGEBRA_VECTOR_CONSTRUCTOR); fail_unless( N->getNumChildren() == 2); fail_unless( N->getPackageName() == "arrays"); ASTNode * child = N->getChild(0); fail_unless( child != NULL ); fail_unless( child->getType() == AST_FUNCTION_COS); fail_unless( child->getExtendedType() == AST_FUNCTION_COS); fail_unless( child->getNumChildren() == 1); fail_unless( child->getPackageName() == "core"); ASTNode *c1 = child->getChild(0); fail_unless( c1 != NULL ); fail_unless( c1->getType() == AST_INTEGER); fail_unless( c1->getExtendedType() == AST_INTEGER); fail_unless( c1->getNumChildren() == 0); fail_unless( c1->getInteger() == 5); child = N->getChild(1); fail_unless( child != NULL ); fail_unless( child->getType() == AST_NAME); fail_unless( child->getExtendedType() == AST_NAME); fail_unless( strcmp(child->getName(), "y") == 0); fail_unless( child->getNumChildren() == 0); ArraysASTPlugin* plugin = static_cast<ArraysASTPlugin*>(N->getPlugin("arrays")); fail_unless(plugin != NULL); fail_unless(plugin->getASTType() == AST_LINEAR_ALGEBRA_VECTOR_CONSTRUCTOR); plugin = static_cast<ArraysASTPlugin*>(child->getPlugin("arrays")); fail_unless(plugin != NULL); fail_unless(plugin->getASTType() == AST_ARRAYS_UNKNOWN); }
void AssignmentCycles::checkForImplicitCompartmentReference(const Model& m) { mIdMap.clear(); unsigned int i, ns; std::string id; for (i = 0; i < m.getNumInitialAssignments(); i++) { if (m.getInitialAssignment(i)->isSetMath()) { id = m.getInitialAssignment(i)->getSymbol(); if (m.getCompartment(id) && m.getCompartment(id)->getSpatialDimensions() > 0) { List* variables = m.getInitialAssignment(i)->getMath() ->getListOfNodes( ASTNode_isName ); for (ns = 0; ns < variables->getSize(); ns++) { ASTNode* node = static_cast<ASTNode*>( variables->get(ns) ); string name = node->getName() ? node->getName() : ""; if (!name.empty() && !alreadyExistsInMap(mIdMap, pair<const std::string, std::string>(id, name))) mIdMap.insert(pair<const std::string, std::string>(id, name)); } delete variables; } } } for (i = 0; i < m.getNumRules(); i++) { if (m.getRule(i)->isSetMath() && m.getRule(i)->isAssignment()) { id = m.getRule(i)->getVariable(); if (m.getCompartment(id) && m.getCompartment(id)->getSpatialDimensions() > 0) { List* variables = m.getRule(i)->getMath()->getListOfNodes( ASTNode_isName ); for (ns = 0; ns < variables->getSize(); ns++) { ASTNode* node = static_cast<ASTNode*>( variables->get(ns) ); string name = node->getName() ? node->getName() : ""; if (!name.empty() && !alreadyExistsInMap(mIdMap, pair<const std::string, std::string>(id, name))) mIdMap.insert(pair<const std::string, std::string>(id, name)); } delete variables; } } } IdIter it; IdRange range; for (i = 0; i < m.getNumCompartments(); i++) { std::string id = m.getCompartment(i)->getId(); range = mIdMap.equal_range(id); for (it = range.first; it != range.second; it++) { const Species *s = m.getSpecies((*it).second); if (s && s->getCompartment() == id && s->getHasOnlySubstanceUnits() == false) { logImplicitReference(m, id, s); } } } }
/** * Checks that the units of the power function are consistent * * If inconsistent units are found, an error message is logged. * * The two arguments to power, which are of the form power(a, b) * with the meaning a^b, should be as follows: * (1) if the second argument is an integer, * then the first argument can have any units; * (2) if the second argument b is a rational number n/m, * it must be possible to derive the m-th root of (a{unit})n, * where {unit} signifies the units associated with a; * otherwise, (3) the units of the first argument must be “dimensionless”. * The second argument (b) should always have units of “dimensionless”. * */ void PowerUnitsCheck::checkUnitsFromPower (const Model& m, const ASTNode& node, const SBase & sb, bool inKL, int reactNo) { /* check that node has 2 children */ if (node.getNumChildren() != 2) { return; } double value; UnitDefinition dim(m.getSBMLNamespaces()); Unit unit(m.getSBMLNamespaces()); unit.setKind(UNIT_KIND_DIMENSIONLESS); unit.initDefaults(); dim.addUnit(&unit); UnitFormulaFormatter *unitFormat = new UnitFormulaFormatter(&m); UnitDefinition *tempUD = NULL; UnitDefinition *unitsArg1, *unitsArgPower; unitsArg1 = unitFormat->getUnitDefinition(node.getLeftChild(), inKL, reactNo); unsigned int undeclaredUnits = unitFormat->getContainsUndeclaredUnits(); ASTNode *child = node.getRightChild(); unitFormat->resetFlags(); unitsArgPower = unitFormat->getUnitDefinition(child, inKL, reactNo); unsigned int undeclaredUnitsPower = unitFormat->getContainsUndeclaredUnits(); // The second argument (b) should always have units of “dimensionless”. // or it has undeclared units that we assume are correct if (undeclaredUnitsPower == 0 && !UnitDefinition::areEquivalent(&dim, unitsArgPower)) { logNonDimensionlessPowerConflict(node, sb); } // The first argument is dimensionless then it doesnt matter // what the power is if (undeclaredUnits == 0 && !UnitDefinition::areEquivalent(&dim, unitsArg1)) { // if not argument needs to be an integer or a rational unsigned int isInteger = 0; unsigned int isRational = 0; unsigned int isExpression = 0; /* power must be an integer * but need to check that it is not a real * number that is integral * i.e. mathml <cn> 2 </cn> will record a "real" */ if (child->isRational()) { isRational = 1; } else if (child->isInteger()) { isInteger = 1; } else if (child->isReal()) { if (ceil(child->getReal()) == child->getReal()) { isInteger = 1; } } else if (child->getNumChildren() > 0) { // power might itself be an expression tempUD = unitFormat->getUnitDefinition(child, inKL, reactNo); UnitDefinition::simplify(tempUD); if (tempUD->isVariantOfDimensionless()) { SBMLTransforms::mapComponentValues(&m); double value = SBMLTransforms::evaluateASTNode(child); SBMLTransforms::clearComponentValues(); #if defined(__linux) if (!std::isnan(value)) #else if (!isnan(value)) #endif { if (floor(value) != value) isExpression = 1; else isInteger = 1; } else { isExpression = 1; } } else { /* here the child is an expression with units * flag the expression as not checked */ isExpression = 1; } } else { // power could be a parameter or a speciesReference in l3 const Parameter *param = NULL; const SpeciesReference *sr = NULL; if (child->isName()) { /* Parameters may be declared in two places (the model and the * kinetic law's local parameter list), so we have to check both. */ if (sb.getTypeCode() == SBML_KINETIC_LAW) { const KineticLaw* kl = dynamic_cast<const KineticLaw*>(&sb); /* First try local parameters and if null is returned, try * the global parameters */ if (kl != NULL) { param = kl->getParameter(child->getName()); } } if (param == NULL) { param = m.getParameter(child->getName()); } if (param == NULL && m.getLevel() > 2) { // could be a species reference sr = m.getSpeciesReference(child->getName()); } } if (param != NULL) { /* We found a parameter with this identifier. */ if (UnitDefinition::areEquivalent(&dim, unitsArgPower) || undeclaredUnitsPower) { value = param->getValue(); if (value != 0) { if (ceil(value) == value) { isInteger = 1; } } } else { /* No parameter definition found for child->getName() */ logUnitConflict(node, sb); } } else if (sr != NULL) { // technically here there is an issue // stoichiometry is dimensionless SBMLTransforms::mapComponentValues(&m); double value = SBMLTransforms::evaluateASTNode(child, &m); SBMLTransforms::clearComponentValues(); // but it may not be an integer #if defined(__linux) if (!std::isnan(value)) #else if (!isnan(value)) #endif // we cant check { isExpression = 1; } else { if (ceil(value) == value) { isInteger = 1; } } } } if (isRational == 1) { //FIX-ME will need sorting for double exponents //* (2) if the second argument b is a rational number n/m, //* it must be possible to derive the m-th root of (a{unit})n, //* where {unit} signifies the units associated with a; unsigned int impossible = 0; for (unsigned int n = 0; impossible == 0 && n < unitsArg1->getNumUnits(); n++) { if ((int)(unitsArg1->getUnit(n)->getExponent()) * child->getInteger() % child->getDenominator() != 0) impossible = 1; } if (impossible) logRationalPowerConflict(node, sb); } else if (isExpression == 1) { logExpressionPowerConflict(node, sb); } else if (isInteger == 0) { logNonIntegerPowerConflict(node, sb); } } // if (!areEquivalent(dim, unitsPower)) // { // /* 'v' does not have units of dimensionless. */ // /* If the power 'n' is a parameter, check if its units are either // * undeclared or declared as dimensionless. If either is the case, // * the value of 'n' must be an integer. // */ // const Parameter *param = NULL; // if (child->isName()) // { // /* Parameters may be declared in two places (the model and the // * kinetic law's local parameter list), so we have to check both. // */ // if (sb.getTypeCode() == SBML_KINETIC_LAW) // { // const KineticLaw* kl = dynamic_cast<const KineticLaw*>(&sb); // /* First try local parameters and if null is returned, try // * the global parameters */ // if (kl != NULL) // { // param = kl->getParameter(child->getName()); // } // } // if (param == NULL) // { // param = m.getParameter(child->getName()); // } // // } // if (param != NULL) // { // /* We found a parameter with this identifier. */ // if (areEquivalent(dim, unitsArgPower) || unitFormat->hasUndeclaredUnits(child)) // { // value = param->getValue(); // if (value != 0) // { // if (ceil(value) != value) // { // logUnitConflict(node, sb); // } // } // } // else // { ///* No parameter definition found for child->getName() */ // logUnitConflict(node, sb); // } // } // else if (child->isFunction() || child->isOperator()) // { // /* cannot test whether the value will be appropriate */ // if (!areEquivalent(dim, unitsArgPower)) // { // logUnitConflict(node, sb); // } // } // /* power must be an integer // * but need to check that it is not a real // * number that is integral // * i.e. mathml <cn> 2 </cn> will record a "real" // */ // else if (!child->isInteger()) // { // if (!child->isReal()) // { // logUnitConflict(node, sb); // } // else if (ceil(child->getReal()) != child->getReal()) // { // logUnitConflict(node, sb); // } // } // } // else if (!areEquivalent(dim, unitsArgPower)) // { // /* power (3, k) */ // logUnitConflict(node, sb); // } checkUnits(m, *node.getLeftChild(), sb, inKL, reactNo); delete unitFormat; delete unitsArg1; delete unitsArgPower; }