#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct RangeSpecs { RangeSpecs(Real lowE=.0, Real highE=.0, int nE=0, bool isLog=false) : m_lowEng(lowE), m_highEng(highE), m_nEngs(nE), m_isLog(isLog) {} ~RangeSpecs() {} Real m_lowEng; Real m_highEng; int m_nEngs; bool m_isLog; }; namespace { void buildEnergyArray(const std::vector& ranges, RealArray& outputArray); void readArrayFromFile(std::ifstream& inFile, RealArray& outputArray); size_t countTrailingCommas(const StringArray& inArgs); size_t determineNumberOfRanges(const IntegerArray& iParams, size_t trailingCommas, size_t nSavedRanges, size_t NSPEC); void parseExtend(const StringArray& xsArgs, const IntegerArray& iPars, XSContainer::ExtendRecord& extendPars, bool& isHigh); } int XSGlobal::xsEnergies(ClientData cdata,Tcl_Interp* tclInterp,int objc, Tcl_Obj* CONST objv[]) { using namespace XSContainer; string cmd = string(static_cast(cdata)); const string RESET("reset"); const string EXTEND("extend"); static std::vector savedSpecs; const size_t NSPEC = 3; static ExtendRecord saveExtend(100.0, true, 200); static bool isHigh = true; if (savedSpecs.empty()) { savedSpecs.push_back(RangeSpecs(.1, 10., 1000, false)); } try { if (objc == 1) { printDocs(cmd.c_str(),"?"); } else { // Don't want to take lower case here, it could screw up a file name. const string secondArg = string(Tcl_GetString(objv[1])); if (secondArg == "?") { printDocs(cmd.c_str(),"?"); } else { RealArray energyArray; Real dummyReal=.0; bool doRecalculate = true; // Criteria for assuming we are dealing with 1 or more energy // ranges: Either the 2nd arg begins with a comma, OR // the characters up to the 1st comma (if any) can be // parsed as a Real. if (XSutility::isReal(secondArg.substr(0, secondArg.find(',')), dummyReal) || secondArg[0] == ',') { // Assume we are dealing with 1 or more energy ranges. StringArray inArgs; IntegerArray iParams; StringArray xsArgs; for (int i=1; i nRanges) { // If one of the new ranges causes a throw savedSpecs // beyond nRanges won't be recoverable, but I // suppose that's OK. savedSpecs.resize(nRanges); } std::vector rangeSpecs(nRanges); size_t iParamIdx = 0; if (!nValsEntered) { // Dealing with case of all commas. This may reduce the // number of saved ranges but it certainly can't // increase it ( a warning message is issued in // determineNumberOfRanges if user gives too many commas). rangeSpecs = savedSpecs; } else { for (size_t i=0; i> testInt) || !iss.eof()) { errMsg += "n energies parameter: "+xsArgs[iParamIdx]+"\n"; throw YellowAlert(errMsg); } if (testInt <= 0) { string msg("N energies parameter must be > 0\n"); throw YellowAlert(msg); } subRange.m_nEngs = testInt; } break; case 2: // log/lin { string testStr = XSutility::lowerCase(xsArgs[iParamIdx]); if (testStr.find("log") == 0) { subRange.m_isLog = true; } else if (testStr.find("lin") == 0) { subRange.m_isLog = false; } else { errMsg += "log/lin parameter: "+xsArgs[iParamIdx]+"\n"; throw YellowAlert(errMsg); } } break; default: break; } // end switch ++iParamIdx; } // end while loop for subRange if (subRange.m_highEng <= subRange.m_lowEng) { // Can get here if user didn't specifiy high E for // range group > 1. std::ostringstream oss; oss << "Must specify high energy value for range group " << i+1 << "\n"; throw YellowAlert(oss.str()); } // If we made it this far things are all valid within // subRange. if (i < nSavedRanges) savedSpecs[i] = subRange; else savedSpecs.push_back(subRange); } // end for loop for all ranges } // end if not dealing with only commas tcout << "\nModels will now use energy array created from:" << std::endl; for (size_t i=0; iapplyAutonomousEnergies(energyArray); } // end if parsing range values else if (XSutility::lowerCase(secondArg) == RESET) { // Simply leave energyArray empty. // Sending in an empty energyArray below causes // models to delete their autonomous energy objects. tcout << "\nAll model energies will be taken from non-extended response energies.\n" << std::endl; models->applyAutonomousEnergies(energyArray); } else if (XSutility::lowerCase(secondArg) == EXTEND) { if (!datasets->numberOfSpectra()) { tcout << "No spectra loaded, nothing to extend." << std::endl; doRecalculate = false; } else { ExtendRecord extendPars(saveExtend); bool tmpIsHigh = isHigh; StringArray inArgs; IntegerArray iParams; StringArray xsArgs; for (int i=2; imodifyExtendedEnergies(extendPars, tmpIsHigh); saveExtend = extendPars; isHigh = tmpIsHigh; const ExtendRecord& lowSetting = models->extendedEnergy().first; const ExtendRecord& highSetting = models->extendedEnergy().second; tcout << "\nModels will use response energies extended to:"<applyAutonomousEnergies(energyArray); } size_t nEngs = energyArray.size(); if (tpout.maxChatter() >= 30) { tcout << "New energy array: " << std::endl; size_t iEng = 0; while (iEng < nEngs) { for (size_t i=0; i<5 && iEngmodelSet().begin(); ModelMap::const_iterator itEnd = models->modelSet().end(); while (itMod != itEnd) { Model* mod = itMod->second; if (!mod->isActive()) { // Table components are troublesome. If energy array has // changed, they explicitly need an initializeForFit // call BEFORE recalculating. This sets up the bin // weighting. std::vector comps; mod->bundleComponents(comps); for (size_t i=0; iinitializeForFit(); mod->setComputeFlag(true); mod->calculate(); } ++itMod; } } models->Notify(); } // end if not help request } // end if objc > 1 } catch (YellowAlert&) { return globalData->autoSave(TCL_ERROR); } return globalData->autoSave(TCL_OK); } namespace { void buildEnergyArray(const std::vector& ranges, RealArray& outputArray) { // Presumably all errors have been tested for by the time this // is called, so simply build the array. const size_t nRanges = ranges.size(); size_t nTotal = 0; for (size_t i=0; i tmpEngs; size_t lineCount = 0; while (!inFile.eof()) { string line; std::getline(inFile, line); ++lineCount; if (line.length()) { const string WS(" \t"); const char COMMENT = '#'; // Ignore if line contains only whitespace or // begins with a '#'. string::size_type nwsPos = line.find_first_not_of(WS); if (nwsPos != string::npos && line[nwsPos] != COMMENT) { std::istringstream issTest(line); Real testReal=.0; if (!(issTest >> testReal)) { std::ostringstream err; err << "Unable to read floating-point value on line " << lineCount << " of input energy file\n"; throw YellowAlert(err.str()); } else if (!issTest.eof()) { // Allow case of a real followed by '#'. string next; issTest >> next; if (next.length() && next[0] == '#') { tmpEngs.push_back(testReal); } else { std::ostringstream err; err << "Unable to read floating-point value on line " << lineCount << " of input energy file\n"; throw YellowAlert(err.str()); } } else { tmpEngs.push_back(testReal); } } } } // Now validate all the Reals in tmpEngs. const size_t nVals = tmpEngs.size(); Real prevVal = -1.0; for (size_t i=0; i(nArgs-1); i>=0 && !nonCommaFound; --i) { const string& arg = inArgs[i]; string::size_type argPos = arg.rfind(','); if (argPos != arg.length()-1) nonCommaFound = true; else { ++trailingCommas; while (!nonCommaFound && argPos != 0) { --argPos; if (arg[argPos] == ',') ++trailingCommas; else nonCommaFound = true; } } } } return trailingCommas; } size_t determineNumberOfRanges(const IntegerArray& iParams, size_t trailingCommas, size_t nSavedRanges, size_t NSPEC) { size_t nRanges = 1; // is only entered for the first range group. // For all others, rangeSpecs[n].m_lowEng = rangeSpecs[n-1].m_highEng. const size_t nValsEntered = iParams.size(); // 0-3 = 1 range, 4-6 = 2 ranges, etc... // Need to watch out for the case where trailingCommas add // additional ranges AND there are no saved defaults for them. // Can't have both nValsEntered == 0 and no trailing commas, // otherwise objc = 1 and this would be handled at the top. if (nValsEntered == 0) { // Will get in here if only commas are entered. nRanges = (trailingCommas - 1)/NSPEC + 1; if (nRanges > nSavedRanges) { tcout <<"***Warning: Default parameters currently only exist for the first " << nSavedRanges << " range(s).\n Commas after that will be ignored." << std::endl; nRanges = nSavedRanges; } } else { size_t lastParEntered = static_cast(iParams[nValsEntered-1]); // First try without adding trailing commas. if (lastParEntered > 0) nRanges = (lastParEntered - 1)/NSPEC + 1; if (trailingCommas) { size_t nRangesWithCommas = (lastParEntered+trailingCommas-1)/NSPEC + 1; if (nRangesWithCommas != nRanges && nRangesWithCommas > nSavedRanges) { tcout <<"***Warning: Default parameters currently only exist for the first " << nSavedRanges << " range(s).\n Trailing commas will be ignored." << std::endl; nRangesWithCommas = std::max(nSavedRanges, nRanges); } nRanges = nRangesWithCommas; } } return nRanges; } void parseExtend(const StringArray& xsArgs, const IntegerArray& iPars, XSContainer::ExtendRecord& extendPars, bool& isHigh) { size_t nPars = iPars.size(); bool warned = false; for (size_t i=0; i> energy) || !iss.eof() || energy < .0) { string errMsg("Improper energy value for the \"extend\" option: "); errMsg += xsArgs[i] + "\n"; throw YellowAlert(errMsg); } extendPars.energy = energy; } break; case 2: // nBins { int nBins = 0; if (!(iss >> nBins) || !iss.eof() || nBins <= 0) { string errMsg("Improper nBins value for the \"extend\" option: "); errMsg += xsArgs[i] + "\n"; throw YellowAlert(errMsg); } extendPars.nBins = nBins; } break; case 3: // log|linear { string lc(XSutility::lowerCase(xsArgs[i])); const string LOG("log"); const string LINEAR("linear"); if (LOG.find(lc) == 0) extendPars.isLog = true; else if (LINEAR.find(lc) == 0) extendPars.isLog = false; else { string errMsg("Fourth parameter after \"extend\" option must be log|linear\n"); throw YellowAlert(errMsg); } } break; default: if (!warned) { tcout <<"***Warning: \"extend\" option takes a maximum of 4 parameters." <<"\n Additional arguments will be ignored." << std::endl; warned = true; } break; } } } }