/** * Checks that the function has only one argument */ void NumberArgsMathCheck::checkUnary(const Model& m, const ASTNode& node, const SBase & sb) { if (node.getNumChildren() != 1) { logMathConflict(node, sb); } else { checkMath(m, *node.getLeftChild(), sb); } }
/** * Checks that the arguments of the branches of a piecewise are consistent * * If not, an error message is logged. */ void PiecewiseValueMathCheck::checkPiecewiseArgs (const Model& m, const ASTNode& node, const SBase & sb) { unsigned int numChildren = node.getNumChildren(); /* arguments must return consistent types */ for (unsigned int n = 0; n < numChildren; n += 2) { if (returnsNumeric(m, node.getChild(n)) && !returnsNumeric(m, node.getLeftChild())) { logMathConflict(node, sb); } else if (node.getChild(n)->isBoolean() && !node.getLeftChild()->isBoolean()) { logMathConflict(node, sb); } } }
/** * Checks that the arguments to eq or neq are consistent * i.e. have same type both boolean or both numeric * * If an inconsistency is found, an error message is logged. */ void EqualityArgsMathCheck::checkArgs (const Model& m, const ASTNode& node, const SBase & sb) { /* check that node has two children */ if (node.getNumChildren() != 2) { return; } /* arguments must return consistent value types */ if (returnsNumeric(m, node.getLeftChild()) && !returnsNumeric(m, node.getRightChild())) { logMathConflict(node, sb); } else if (node.getLeftChild()->isBoolean() && !node.getRightChild()->isBoolean()) { logMathConflict(node, sb); } }
/* * Checks that the units of the delay function are consistent * * If inconsistent units are found, an error message is logged. */ void ArgumentsUnitsCheck::checkUnitsFromDelay (const Model& m, const ASTNode& node, const SBase & sb, bool inKL, int reactNo) { /* check that node has two children */ if (node.getNumChildren() != 2) { return; } if (!m.getSBMLNamespaces()->getNamespaces()) { #if 0 cout << "[DEBUG] XMLNS IS NULL" << endl; #endif } /* delay(x, t) * no restrictions on units of x * but t must have units of time */ UnitDefinition *time = new UnitDefinition(m.getSBMLNamespaces()); Unit *unit = new Unit(m.getSBMLNamespaces()); unit->setKind(UNIT_KIND_SECOND); unit->initDefaults(); UnitDefinition * tempUD; time->addUnit(unit); UnitFormulaFormatter *unitFormat = new UnitFormulaFormatter(&m); tempUD = unitFormat->getUnitDefinition(node.getRightChild(), inKL, reactNo); if (!unitFormat->getContainsUndeclaredUnits()) { if (!UnitDefinition::areEquivalent(time, tempUD)) { logInconsistentDelay(node, sb); } } delete time; delete tempUD; delete unit; delete unitFormat; checkUnits(m, *node.getLeftChild(), sb, inKL, reactNo); }
/** * @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 PiecewiseValueMathCheck::getMessage (const ASTNode& node, const SBase& object) { ostringstream msg; //msg << getPreamble(); char * left = SBML_formulaToString(node.getLeftChild()); msg << "\nThe piecewise formula "; msg << "in the " << getFieldname() << " element of the " << getTypename(object); msg << " returns arguments" ; msg << " which have different value types from the first element '"; msg << left << "'."; safe_free(left); return msg.str(); }
/** * * Creates an SBML model represented in "7.2 Example involving units" * in the SBML Level 2 Version 4 Specification. * */ SBMLDocument* createExampleInvolvingUnits() { const unsigned int level = Level; const unsigned int version = Version; //--------------------------------------------------------------------------- // // Creates an SBMLDocument object // //--------------------------------------------------------------------------- SBMLDocument* sbmlDoc = new SBMLDocument(level,version); // Adds the namespace for XHTML to the SBMLDocument object. We need this // because we will add notes to the model. (By default, the SBML document // created by SBMLDocument only declares the SBML XML namespace.) sbmlDoc->getNamespaces()->add("http://www.w3.org/1999/xhtml", "xhtml"); //--------------------------------------------------------------------------- // // Creates a Model object inside the SBMLDocument object. // //--------------------------------------------------------------------------- Model* model = sbmlDoc->createModel(); model->setId("unitsExample"); //--------------------------------------------------------------------------- // // Creates UnitDefinition objects inside the Model object. // //--------------------------------------------------------------------------- // Temporary pointers (reused more than once below). UnitDefinition* unitdef; Unit *unit; //--------------------------------------------------------------------------- // (UnitDefinition1) Creates an UnitDefinition object ("substance"). // // This has the effect of redefining the default unit of subtance for the // whole model. //--------------------------------------------------------------------------- unitdef = model->createUnitDefinition(); unitdef->setId("substance"); // Creates an Unit inside the UnitDefinition object unit = unitdef->createUnit(); unit->setKind(UNIT_KIND_MOLE); unit->setScale(-3); //-------------------------------------------------------------------------------- // (UnitDefinition2) Creates an UnitDefinition object ("mmls") //-------------------------------------------------------------------------------- // Note that we can reuse the pointers 'unitdef' and 'unit' because the // actual UnitDefinition object (along with the Unit objects within it) // is already attached to the Model object. unitdef = model->createUnitDefinition(); unitdef->setId("mmls"); // Creates an Unit inside the UnitDefinition object ("mmls") unit = unitdef->createUnit(); unit->setKind(UNIT_KIND_MOLE); unit->setScale(-3); // Creates an Unit inside the UnitDefinition object ("mmls") unit = unitdef->createUnit(); unit->setKind(UNIT_KIND_LITRE); unit->setExponent(-1); // Creates an Unit inside the UnitDefinition object ("mmls") unit = unitdef->createUnit(); unit->setKind(UNIT_KIND_SECOND); unit->setExponent(-1); //-------------------------------------------------------------------------------- // (UnitDefinition3) Creates an UnitDefinition object ("mml") //-------------------------------------------------------------------------------- unitdef = model->createUnitDefinition(); unitdef->setId("mml"); // Creates an Unit inside the UnitDefinition object ("mml") unit = unitdef->createUnit(); unit->setKind(UNIT_KIND_MOLE); unit->setScale(-3); // Creates an Unit inside the UnitDefinition object ("mml") unit = unitdef->createUnit(); unit->setKind(UNIT_KIND_LITRE); unit->setExponent(-1); //--------------------------------------------------------------------------- // // Creates a Compartment object inside the Model object. // //--------------------------------------------------------------------------- Compartment* comp; const string compName = "cell"; // Creates a Compartment object ("cell") comp = model->createCompartment(); comp->setId(compName); // Sets the "size" attribute of the Compartment object. // // The units of this Compartment object is the default SBML // units of volume (litre), and thus we don't have to explicitly invoke // setUnits("litre") function to set the default units. // comp->setSize(1); //--------------------------------------------------------------------------- // // Creates Species objects inside the Model object. // //--------------------------------------------------------------------------- // Temporary pointer (reused more than once below). Species *sp; //--------------------------------------------------------------------------- // (Species1) Creates a Species object ("x0") //--------------------------------------------------------------------------- sp = model->createSpecies(); sp->setId("x0"); // Sets the "compartment" attribute of the Species object to identify the // compartnet in which the Species object located. sp->setCompartment(compName); // Sets the "initialConcentration" attribute of the Species object. // // The units of this Species object is determined by two attributes of this // Species object ("substanceUnits" and "hasOnlySubstanceUnits") and the // "spatialDimensions" attribute of the Compartment object ("cytosol") in which // this species object is located. // Since the default values are used for "substanceUnits" (substance (mole)) // and "hasOnlySubstanceUnits" (false) and the value of "spatialDimension" (3) // is greater than 0, the units of this Species object is moles/liters . // sp->setInitialConcentration(1); //--------------------------------------------------------------------------- // (Species2) Creates a Species object ("x1") //--------------------------------------------------------------------------- sp = model->createSpecies(); sp->setId("x1"); sp->setCompartment(compName); sp->setInitialConcentration(1); //--------------------------------------------------------------------------- // (Species3) Creates a Species object ("s1") //--------------------------------------------------------------------------- sp = model->createSpecies(); sp->setCompartment(compName); sp->setId("s1"); sp->setInitialConcentration(1); //--------------------------------------------------------------------------- // (Species4) Creates a Species object ("s2") //--------------------------------------------------------------------------- sp = model->createSpecies(); sp->setCompartment(compName); sp->setId("s2"); sp->setInitialConcentration(1); //--------------------------------------------------------------------------- // // Creates global Parameter objects inside the Model object. // //--------------------------------------------------------------------------- Parameter* para; // Creates a Parameter ("vm") para = model->createParameter(); para->setId("vm"); para->setValue(2); para->setUnits("mmls"); // Creates a Parameter ("km") para = model->createParameter(); para->setId("km"); para->setValue(2); para->setUnits("mml"); //--------------------------------------------------------------------------- // // Creates Reaction objects inside the Model object. // //--------------------------------------------------------------------------- // Temporary pointers. Reaction* reaction; SpeciesReference* spr; KineticLaw* kl; //--------------------------------------------------------------------------- // (Reaction1) Creates a Reaction object ("v1"). //--------------------------------------------------------------------------- reaction = model->createReaction(); reaction->setId("v1"); //--------------------------------------------------------------------------- // Creates Reactant objects inside the Reaction object ("v1"). //--------------------------------------------------------------------------- // (Reactant1) Creates a Reactant object that references Species "x0" // in the model. spr = reaction->createReactant(); spr->setSpecies("x0"); //--------------------------------------------------------------------------- // Creates a Product object inside the Reaction object ("v1"). //--------------------------------------------------------------------------- // Creates a Product object that references Species "s1" in the model. spr = reaction->createProduct(); spr->setSpecies("s1"); //--------------------------------------------------------------------------- // Creates a KineticLaw object inside the Reaction object ("v1"). //--------------------------------------------------------------------------- kl = reaction->createKineticLaw(); // Creates a <notes> element in the KineticLaw object. // Here we illustrate how to do it using a literal string. This requires // known the required syntax of XHTML and the requirements for SBML <notes> // elements. Later below, we show how to create notes using objects instead // of strings. string notesString = "<xhtml:p> ((vm * s1)/(km + s1)) * cell </xhtml:p>"; kl->setNotes(notesString); //--------------------------------------------------------------------------- // Creates an ASTNode object which represents the following KineticLaw object. // // <math xmlns=\"http://www.w3.org/1998/Math/MathML\"> // <apply> // <times/> // <apply> // <divide/> // <apply> // <times/> // <ci> vm </ci> // <ci> s1 </ci> // </apply> // <apply> // <plus/> // <ci> km </ci> // <ci> s1 </ci> // </apply> // </apply> // <ci> cell </ci> // </apply> // </math> //--------------------------------------------------------------------------- // // In the following code, ASTNode objects, which construct an ASTNode tree // of the above math, are created and added in the order of preorder traversal // of the tree (i.e. the order corresponds to the nested structure of the above // MathML elements), and thus the following code maybe a bit more efficient but // maybe a bit difficult to read. // ASTNode* astMath = new ASTNode(AST_TIMES); astMath->addChild(new ASTNode(AST_DIVIDE)); ASTNode* astDivide = astMath->getLeftChild(); astDivide->addChild(new ASTNode(AST_TIMES)); ASTNode* astTimes = astDivide->getLeftChild(); astTimes->addChild(new ASTNode(AST_NAME)); astTimes->getLeftChild()->setName("vm"); astTimes->addChild(new ASTNode(AST_NAME)); astTimes->getRightChild()->setName("s1"); astDivide->addChild(new ASTNode(AST_PLUS)); ASTNode* astPlus = astDivide->getRightChild(); astPlus->addChild(new ASTNode(AST_NAME)); astPlus->getLeftChild()->setName("km"); astPlus->addChild(new ASTNode(AST_NAME)); astPlus->getRightChild()->setName("s1"); astMath->addChild(new ASTNode(AST_NAME)); astMath->getRightChild()->setName("cell"); //--------------------------------------------- // // set the Math element // //------------------------------------------------ kl->setMath(astMath); delete astMath; //--------------------------------------------------------------------------- // (Reaction2) Creates a Reaction object ("v2"). //--------------------------------------------------------------------------- reaction = model->createReaction(); reaction->setId("v2"); //--------------------------------------------------------------------------- // Creates Reactant objects inside the Reaction object ("v2"). //--------------------------------------------------------------------------- // (Reactant2) Creates a Reactant object that references Species "s1" // in the model. spr = reaction->createReactant(); spr->setSpecies("s1"); //--------------------------------------------------------------------------- // Creates a Product object inside the Reaction object ("v2"). //--------------------------------------------------------------------------- // Creates a Product object that references Species "s2" in the model. spr = reaction->createProduct(); spr->setSpecies("s2"); //--------------------------------------------------------------------------- // Creates a KineticLaw object inside the Reaction object ("v2"). //--------------------------------------------------------------------------- kl = reaction->createKineticLaw(); // Sets a notes (by XMLNode) to the KineticLaw object. // // The following code is an alternative to using setNotes(const string&). // The equivalent code would be like this: // // notesString = "<xhtml:p>((vm * s2)/(km + s2))*cell</xhtml:p>"; // kl->setNotes(notesString); // Creates an XMLNode of start element (<xhtml:p>) without attributes. XMLNode notesXMLNode(XMLTriple("p", "", "xhtml"), XMLAttributes()); // Adds a text element to the start element. notesXMLNode.addChild(XMLNode(" ((vm * s2)/(km + s2)) * cell ")); // Adds it to the kineticLaw object. kl->setNotes(¬esXMLNode); //--------------------------------------------------------------------------- // Sets a math (ASTNode object) to the KineticLaw object. //--------------------------------------------------------------------------- // To create mathematical expressions, one would typically construct // an ASTNode tree as the above example code which creates a math of another // KineticLaw object. Here, to save some space and illustrate another approach // of doing it, we will write out the formula in MathML form and then use a // libSBML convenience function to create the ASTNode tree for us. // (This is a bit dangerous; it's very easy to make mistakes when writing MathML // by hand, so in a real program, we would not really want to do it this way.) string mathXMLString = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">" " <apply>" " <times/>" " <apply>" " <divide/>" " <apply>" " <times/>" " <ci> vm </ci>" " <ci> s2 </ci>" " </apply>" " <apply>" " <plus/>" " <ci> km </ci>" " <ci> s2 </ci>" " </apply>" " </apply>" " <ci> cell </ci>" " </apply>" "</math>"; astMath = readMathMLFromString(mathXMLString.c_str()); kl->setMath(astMath); delete astMath; //--------------------------------------------------------------------------- // (Reaction3) Creates a Reaction object ("v3"). //--------------------------------------------------------------------------- reaction = model->createReaction(); reaction->setId("v3"); //--------------------------------------------------------------------------- // Creates Reactant objects inside the Reaction object ("v3"). //--------------------------------------------------------------------------- // (Reactant2) Creates a Reactant object that references Species "s2" // in the model. spr = reaction->createReactant(); spr->setSpecies("s2"); //--------------------------------------------------------------------------- // Creates a Product object inside the Reaction object ("v3"). //--------------------------------------------------------------------------- // Creates a Product object that references Species "x1" in the model. spr = reaction->createProduct(); spr->setSpecies("x1"); //--------------------------------------------------------------------------- // Creates a KineticLaw object inside the Reaction object ("v3"). //--------------------------------------------------------------------------- kl = reaction->createKineticLaw(); // Sets a notes (by string) to the KineticLaw object. notesString = "<xhtml:p> ((vm * x1)/(km + x1)) * cell </xhtml:p>"; kl->setNotes(notesString); //--------------------------------------------------------------------------- // Sets a math (ASTNode object) to the KineticLaw object. //--------------------------------------------------------------------------- mathXMLString = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">" " <apply>" " <times/>" " <apply>" " <divide/>" " <apply>" " <times/>" " <ci> vm </ci>" " <ci> x1 </ci>" " </apply>" " <apply>" " <plus/>" " <ci> km </ci>" " <ci> x1 </ci>" " </apply>" " </apply>" " <ci> cell </ci>" " </apply>" "</math>"; astMath = readMathMLFromString(mathXMLString.c_str()); kl->setMath(astMath); delete astMath; // Returns the created SBMLDocument object. // The returned object must be explicitly deleted by the caller, // otherwise memory leak will happen. return sbmlDoc; }
/** * 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; }