// Read the documentation to learn more about C++ code generator // versioning. // %X% %Q% %Z% %W% // ModelContainer #include // Parameter #include // SwitchParam #include // ScaleParam #include // XSutility #include // ModParam #include // ParameterLink #include #include #include #include #include #include #include #include #include #include using std::endl; // Class ParameterLink::InvalidInput ParameterLink::InvalidInput::InvalidInput (const string& msg) : YellowAlert("Invalid link expression: ") { tcerr << msg << endl; } // Class ParameterLink::ParseFailure ParameterLink::ParseFailure::ParseFailure (const string& msg) : RedAlert(msg) { } // Class ParameterLink::DivideCheck ParameterLink::DivideCheck::DivideCheck (const string& msg) : YellowAlert(" Link expression gives divide by zero error: ") { tcerr << msg << endl; } // Class ParameterLink::NoValidParametersInExpression ParameterLink::NoValidParametersInExpression::NoValidParametersInExpression (const string& msg) : YellowAlert("Link expression contains no valid parameters: = ") { tcerr << msg << endl; } // Class ParameterLink const int ParameterLink::s_FLAGVALUE = -999; ParameterLink::ParameterLink(const ParameterLink &right) : m_boolean(right.m_boolean), m_linkVal(right.m_linkVal), m_parent(right.m_parent), m_members(right.m_members), m_linkString(right.m_linkString) { } ParameterLink::ParameterLink (const string& linkExpression, const Parameter* p, bool isBoolean) : m_boolean(isBoolean), m_linkVal(0), m_parent(p), m_members(), m_linkString() { m_linkString.init(linkExpression); if (isBoolean && m_linkString.tokenList().size() > 1) { throw ParameterLink::InvalidInput("Switches may only take links of type par x = par y"); } std::vector isParFound(m_linkString.parIDs().size(), false); LinkExpression::ParamInfo::const_iterator itParID = m_linkString.parIDs().begin(); LinkExpression::ParamInfo::const_iterator itParIDEnd = m_linkString.parIDs().end(); size_t iCount=0; bool atLeastOneFound = false; while (itParID != itParIDEnd) { if (wordToParam(itParID->first)) { isParFound[iCount] = true; atLeastOneFound = true; } ++itParID, ++iCount; } if (!atLeastOneFound) throw NoValidParametersInExpression(linkExpression); m_linkString.resetParamInfo(isParFound); } ParameterLink::~ParameterLink() { // manages no heap memory: no owning pointers, just stack allocated // LinkExpression instance. } ParameterLink & ParameterLink::operator=(const ParameterLink &right) { ParameterLink __temp(right); swap(__temp); return *this; } void ParameterLink::swap (ParameterLink& right) { XSutility::swap(m_linkString,right.m_linkString); XSutility::swap(m_parent,right.m_parent); XSutility::swap(m_boolean,right.m_boolean); XSutility::swap(m_members,right.m_members); } bool ParameterLink::wordToParam (const string& word) { // "words" that evaluate to parameters are strings of the form: // 1) n - integers [no decimal point or exponent] as per XSPEC11 // 2) modelName:n - same but with a model name qualifier. Without, it refers to // the default model. string modelName; size_t parNum = XSutility::isInteger(word); bool added(false); if (parNum != string::npos) { // If it can't find a parameter named with only an integer, // we'll let it pass. The LinkExpression class will treat // it as a numerical constant (and issue a warning). added = addPointer(parNum); } else { // If word is coming from LinkExpression parIDs list, it // must either be an int or a string:int. Still, it can't // hurt to double check here. XSparse::stringIntPair(word,modelName,parNum); if (parNum == string::npos) { throw ParameterLink::InvalidInput (" Parameter reference must be of form [ModelName:]n"); } else { if (!(added = addPointer(parNum,modelName))) { // the only possible response here is to throw an // InvalidInput exception. // because a string has been entered that doesn't correspond to a // valid model parameter. std::ostringstream msg; msg << "No matching parameter for parameter reference " << word << " in link expression."; throw ParameterLink::InvalidInput(msg.str()); } } } return added; } bool ParameterLink::addPointer (int num, const string& modelName) { bool added(false); Parameter* ptr = 0; // First check if link is to a parameter within same model // (and NOT a previously existing model with same name in global // container that is about to be replaced). const ComponentBase* cparent = m_parent->parent(); const ModelBase* modelParent = 0; if(cparent) { modelParent = cparent->parent()->parent(); if (modelParent && modelName == modelParent->name()) ptr = modelParent->getLocalParameter(num); } if (!ptr) ptr = XSContainer::models->lookupParameter(num,modelName); if (ptr == parent()) { throw ParameterLink::InvalidInput(" Attempt to link parameter to itself"); } // we apparently have a valid parameter, but not all requested links // make sense. if (ptr != 0) { // disallow some conditions. const Parameter* cptr(ptr); if (boolean() && !dynamic_cast(cptr)) { // a boolean may not be linked to anything other than a boolean. throw ParameterLink::InvalidInput (" Attempt to create link from switch to real valued parameter"); } else if (!boolean() && dynamic_cast(cptr)) { // a real-valued parameter (scaling factor or fitting parameter) may not // (at this point) depend on a boolean. throw ParameterLink::InvalidInput (" Attempt to link real valued parameter to a switch"); } else if (dynamic_cast(parent())) { // a scaling factor may not be made variable by being dependent on a // fitting parameter (we already filtered out the case of SwitchParam // here). if (dynamic_cast(cptr)) throw ParameterLink::InvalidInput (" Attempt to link constant scaling factor to variable fitting parameter"); } // if it got past all of that... m_members.push_back(ptr); added = true; } return added; } Real ParameterLink::linkValue () const { RealArray parVals(0.0,m_members.size()); for (size_t i=0; ivalue(); } return m_linkString.evaluate(parVals); } void ParameterLink::putLink (std::ostream& s) const { // linkExpression returns a string with the full expression of the // link and names resolved (the parameter says "resolve names"). string outputExpression(linkExpression(true)); s << "= " << outputExpression; } ParameterLink* ParameterLink::clone () const { return new ParameterLink(*this); } bool ParameterLink::findParam (const Parameter* param) const { if (std::find(m_members.begin(),m_members.end(),const_cast(param)) != m_members.end()) return true; return false; } string ParameterLink::linkExpression (bool fullname) const { string outputExpression; const string SPACE(" "); const size_t nT = m_linkString.tokenList().size(); LinkExpression::ParamInfo::const_iterator itParID = m_linkString.parIDs().begin(); LinkExpression::ParamInfo::const_iterator itParIDEnd = m_linkString.parIDs().end(); size_t nextParLoc = (itParID == itParIDEnd) ? string::npos : itParID->second; size_t iPar = 0; for (size_t t = 0; t < nT; ++t) { const AbstractExpression::TokenType& curTok = m_linkString.tokenList()[t]; if (t == nextParLoc) { if (fullname) { const Parameter* par = m_members[iPar]; const string parIdent(XSContainer::models->indexToName(par->index(),par->modelName())); // Data group number is appended to the end following a '$'. // Need to remove it and possibly add it to the front for display. string::size_type dgLoc = parIdent.find('$'); string modifiedParID = parIdent.substr(0,dgLoc); modifiedParID = (modifiedParID.find(Model::DEFAULT()) == string::npos ? modifiedParID : modifiedParID.substr(modifiedParID.find(':')+1)); if ( XSContainer::datasets->numberOfGroups() > 1) { string dgNum(parIdent.substr(dgLoc+1)); outputExpression += dgNum; outputExpression += string(":"); } outputExpression += modifiedParID; } else { if (curTok.tokenString.find(Model::DEFAULT()) == string::npos) outputExpression += curTok.tokenString; else { string::size_type pos = curTok.tokenString.find(':') + 1; outputExpression += curTok.tokenString.substr(pos); } } ++itParID; nextParLoc = (itParID == itParIDEnd) ? string::npos : itParID->second; ++iPar; } else { bool isPm = false; if (curTok.type == AbstractExpression::Plus) isPm = true; else if (curTok.type == AbstractExpression::Minus) { isPm = true; // But check if this is part of an exponent. if (curTok.location > 0) { char prevChar = m_linkString.exprString()[curTok.location-1]; if (prevChar == 'e' || prevChar == 'E') isPm = false; } } if (isPm) outputExpression += SPACE; outputExpression += curTok.tokenString; if (isPm) outputExpression += SPACE; } } return outputExpression; } void ParameterLink::rerouteLink (const std::vector& newPars) { if (newPars.size() != m_members.size()) throw RedAlert("Parameter reroute mismatch."); m_members = newPars; regenerateExpression(); } void ParameterLink::regenerateExpression () { // This is for use in the context of an update after broken // links have been rerouted. // IMPORTANT: This ASSUMES that only the parameters may have changed, // and that the number of parameters remains constant. string newExprStr; const size_t nT = m_linkString.tokenList().size(); LinkExpression::ParamInfo::const_iterator itParID = m_linkString.parIDs().begin(); LinkExpression::ParamInfo::const_iterator itParIDEnd = m_linkString.parIDs().end(); size_t nextParLoc = (itParID == itParIDEnd) ? string::npos : itParID->second; size_t iPar = 0; for (size_t iTok=0; iTok= m_members.size()) throw RedAlert("Parameter retrieval error in ParameterLink::regenerateExpression"); std::ostringstream oss; const Parameter* newPar = m_members[iPar]; if (newPar->modelName() != Model::DEFAULT()) oss << newPar->modelName() << ':'; oss << newPar->index(); newExprStr += oss.str(); ++itParID; nextParLoc = (itParID == itParIDEnd) ? string::npos : itParID->second; ++iPar; } else newExprStr += curTok.tokenString; } LinkExpression newExpr; newExpr.init(newExprStr); // The new LinkExpression object also has a new parIDs list. // This list naturally contains all the par strings that we // inserted into the expression above, but it may also contain // integer strings that don't correspond to any parameter (and // therefore need to be removed from parIDs). They are easy to // spot though since they have token positions without counterparts // in the old parID list. std::vector isParFound(newExpr.parIDs().size(), false); itParID = m_linkString.parIDs().begin(); LinkExpression::ParamInfo::const_iterator itNewParID = newExpr.parIDs().begin(); LinkExpression::ParamInfo::const_iterator itNewParIDEnd = newExpr.parIDs().end(); size_t iCount=0; size_t trueCount=0; while (itNewParID != itNewParIDEnd) { if (itParID != itParIDEnd && itNewParID->second == itParID->second) { isParFound[iCount] = true; ++trueCount; ++itParID; } ++itNewParID; ++iCount; } if (trueCount != m_linkString.parIDs().size()) throw RedAlert("Parse error in ParameterLink::regenerateExpression"); int savConVerbose = tpout.conVerbose(); int savLogVerbose = tpout.logVerbose(); try { // Temporarily raising verbose levels to prevent warning message // for param-to-integer-const conversion. To display it from // here would be overkill. XSstream::verbose(tpout, 99, 99); newExpr.resetParamInfo(isParFound); XSstream::verbose(tpout, savConVerbose, savLogVerbose); } catch (...) { XSstream::verbose(tpout, savConVerbose, savLogVerbose); throw; } m_linkString = newExpr; } // Additional Declarations