/** * 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))); } };
/** * 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; }