// // XSPEC12 November 2003 // // #include #include #include #include #include #include #include #include #include #include #include #include #include #include int XSGlobal::xsModel (ClientData cdata,Tcl_Interp* tclInterp,int objc, Tcl_Obj* CONST objv[]) { using namespace XSContainer; // CREATE EXCEPTION HANDLING!! static const string NONE("none"); static const string CLEAR("clear"); static const string ACTIVE("act"); static const string INACTIVE("inact"); using XSContainer::models; using XSContainer::datasets; string cmdStr; if (objc == 1 ) { // if there's no arguments or the argument that exists is blank // print list of valid models and return. XSModelFunction::printComponentList(tcout); return globalData->autoSave(TCL_OK); } else { // check for commands that erase previously // defined models. string actualFirstArg(Tcl_GetString(objv[1])); string lFirstArg(XSutility::lowerCase(actualFirstArg)); bool patchedTableName = false; if ( lFirstArg.substr(0,5) == CLEAR ) { // remove all models. models->clearLists(); models->Notify(); return globalData->autoSave(TCL_OK); } else if (lFirstArg[0] == '?') { XSModelFunction::printComponentList(tcout); return globalData->autoSave(TCL_OK); } else if ( lFirstArg.substr(0,4) == XSparse::NONE() ) { // model none: remove default model std::vector doomedMods(models->lookupModelGroup()); models->rerouteBrokenParLinks(doomedMods); models->remove(Model::DEFAULT()); models->Notify(); return globalData->autoSave(TCL_OK); } else if ( objc >= 3 ) { string modName = actualFirstArg; if (lFirstArg == "unnamed") modName = Model::DEFAULT(); if ( Model* currMod = models->lookup(modName) ) { // check for model none: remove named model // check for model active: make active // check for model inactive: make inactive. string secondArg(XSutility::lowerCase(Tcl_GetString(objv[2]))); if ( secondArg.substr(0,4) == XSparse::NONE()) { std::vector doomedMods(models->lookupModelGroup(modName)); models->rerouteBrokenParLinks(doomedMods); models->remove(modName); models->Notify(); } else if ( secondArg.substr(0,3) == ACTIVE) { // If reached here, modName should be in // activeModelNames map. if (models->activeModelNames(modName)) { tcout << "\nModel is already active" << std::endl; return globalData->autoSave(TCL_OK); } else { if (currMod->sourceNumber() > XSContainer::datasets->numSourcesForSpectra()) { tcout << "\nUnable to activate model, no corresponding source in data." << std::endl; return globalData->autoSave(TCL_ERROR); } models->designateActive(currMod); datasets->Notify(); } } else if ( secondArg.substr(0,5) == INACTIVE) { if (models->activeModelNames(modName)) { models->deactivateModel(modName); models->Notify(); } else { tcout << "\nModel is already marked inactive."<autoSave(TCL_ERROR); } } else { // print out the models with the input name std::vector modGroup = models->lookupModelGroup(modName); for (size_t j = 0; j < modGroup.size(); ++j) { tcout << *modGroup[j] << std::endl; } } return globalData->autoSave(TCL_OK); } // end if model exists else { // Need to check for old-style syntax of "atable|mtable ". // If found, convert to "atable|mtable{}". const string ATABLE("atable"); const string MTABLE("mtable"); if (ATABLE.find(lFirstArg) == 0) { actualFirstArg = ATABLE; patchedTableName = true; } else if (MTABLE.find(lFirstArg) == 0) { actualFirstArg = MTABLE; patchedTableName = true; } if (patchedTableName) { string fileName(Tcl_GetString(objv[2])); actualFirstArg += "{" + fileName + "}"; } } } // end if objc >= 3 // okay, we actually are creating rather than deleting // models. parse into command and "batch" information, // supplied to parameter setting functions. string cmdLine = actualFirstArg; for (int j= patchedTableName ? 3 : 2; j < objc; ++j) { cmdLine += " "; cmdLine += string(Tcl_GetString(objv[j])); } std::deque parameterStrings; XSparse::findBatchString(cmdLine,cmdStr,parameterStrings); string modelName(""); string commandArgs(""); size_t sourceNum (1); // first, split off the model string // syntax is model modelName:{sourceNum} // sourceNum is optional assignment to source. // create a temporary empty model and add it to the list. // modify model constructor if necessary to take a name as an // argument that has a default value ($DEFAULT). // parse the part of the string between the command name and the ":" // as the model Name. //... //... // if there is a numeric value between the ":" and the next string token // [no space after the ":" allowed!], take it as a source number. // if the user typed an alpha value here, reject as syntax error. //... //... // define this here so copies can be deleted on an exception. Model* newModel (0); std::vector dataGroupCopies; bool added(false); try { XSparse::parseInputModelString(cmdStr,modelName,sourceNum,commandArgs); // potential memory leak in presence of exception: design // needs to be reconsidered a little. if (sourceNum > std::max(static_cast(1), XSContainer::datasets->numSourcesForSpectra())) { throw Model::NoSourceForModel(); } if ( modelName.length() == 0 ) { // change to exception // modelName = Model::DEFAULT(); if (sourceNum != 1 ) { string msg(" models for multiple source "); msg += "analysis must be named\n"; throw YellowAlert(msg); } } // set the mode of parameter printing for initializing parameters. Parameter::initPrint(true); // Patch fix: This section should really be placed in a function // in XSUtils when it's time to release an interface change. // It looks for any '{}' pairs and removes all whitespace in between. // The purpose is to make expression parsing of atable/mtable easier // further on, since we know in this context that there is no need // for any whitespace between curly braces. It does not check for // weird things like unmatched or nested braces, which will // presumably cause a throw later. string::size_type ipos=0; while (ipos != string::npos && ipos < commandArgs.size()) { string::size_type bpos = commandArgs.find_first_of('{',ipos); ipos = string::npos; if (bpos != string::npos) { string::size_type epos = commandArgs.find_first_of('}',bpos); if (epos != string::npos) { string betweenCurls; for (string::size_type jpos=bpos+1; jpos >::const_iterator itStoG = datasets->sourceToDgs().find(sourceNum); std::vector neededCopies; if (itStoG != datasets->sourceToDgs().end()) { const std::map& dGroups = itStoG->second; nGroupsForSource = dGroups.size(); std::map::const_iterator itGroups = dGroups.begin(); std::map::const_iterator itGroupsEnd = dGroups.end(); if (itGroups != itGroupsEnd) { // Assign this the lowest data group number for the source. newModel->dataGroupNumber(itGroups->first); ++itGroups; } while (itGroups != itGroupsEnd) { neededCopies.push_back(itGroups->first); ++itGroups; } } if (newModel->mixingPresent()) { if (newModel->sourceNumber() != 1) { string msg("At present, mixing models can only be assigned to source number 1\n"); throw YellowAlert(msg); } // At this point only check for the mere presence // of data. If data is present, its validity will // have to be checked later with the MixBase object's // verifyData call. if (!datasets->dataArray().size()) { string msg("Mixing model requires that data be loaded first\n"); throw YellowAlert(msg); } } // process parameter strings here. bool promptUser(parameterStrings.empty()); bool skipEntered(false); if (promptUser) { // getParamValuesFromUser prompts only for the strings // related to the current model, not for the higher data groups. parameterStrings = newModel->getParamValuesFromUser(); } newModel->setParamValues(parameterStrings,skipEntered); if (tpout.logChatterLevel() >= 25) { // debugging info tcout << " Model: Input Parameter Definition Strings: \n" << " Model name: " << newModel->name() << " data group: " << newModel->dataGroupNumber() << " source number " << newModel->sourceNumber() << '\n'; std::ostream_iterator ss(tcout,"\n"); std::copy(parameterStrings.begin(), parameterStrings.end(),ss); } // this has to be done here, rather than after all of the datagroup models // have been constructed. So for strong exception safety we need to remove this // if the data groups do not create correctly. But... models->setFirstOfModelName(newModel); models->addToList(newModel); added = true; // if there are higher data groups, then the parameter strings deque (set from // a script) will have the next needed string at the front. if (nGroupsForSource > 1) { dataGroupCopies.resize(nGroupsForSource-1); dataGroupCopies = newModel->makeDataGroupCopies(neededCopies); for (size_t j = 0; j < nGroupsForSource - 1; ++j) { if (skipEntered) { dataGroupCopies[j]->linkDataGroupParams(); } else { if (promptUser) { parameterStrings = dataGroupCopies[j]->getParamValuesFromUser(); } if (tpout.logChatterLevel() >= 25) { // debugging info tcout << " Model: Input Parameter Definition Strings: \n" << " Model name: " << dataGroupCopies[j]->name() << " data group: " << dataGroupCopies[j]->dataGroupNumber() << " source number " << dataGroupCopies[j]->sourceNumber() << '\n'; std::ostream_iterator ss(tcout,"\n"); std::copy(parameterStrings.begin(), parameterStrings.end(),ss); } dataGroupCopies[j]->setParamValues(parameterStrings,skipEntered); } models->addToList(dataGroupCopies[j]); } } // attach responses to the new models. If // no responses have been defined, this does nothing. models->attachResponses(modelName); // This needs to be called after responses are attached, // and does nothing if extended energies aren't in use. models->applyExtendedEnergies(); // force computation of the models just created. models->setCompute(modelName); // If there is data, new models will be active // and calculated through Fit::Update. Else, // do it here. if (!datasets->dataArray().size()) { // Note: for mixing models this can never occur, as it // has already been prevented above. std::vector newComps; newModel->bundleComponents(newComps); for (size_t i=0; iinitializeForFit(); } newModel->calculate(); } // check for mixing model, and initialize the mixing transformation // if present. This has to happen after all the models are allocated. newModel->initializeMixingTransformation(); const size_t nShow = nGroupsForSource ? nGroupsForSource : 1; if (fit->renormType() != Fit::AUTO) { // If in Fit::AUTO mode, renormalizing will // take place during the Notify call below, // so let's not print out the params just yet. for (size_t j = 0; j < nShow; ++j) { size_t nGroup = j ? neededCopies[j-1] : newModel->dataGroupNumber(); tcout << *models->lookup(modelName,nGroup); } tcout << std::endl; } models->Notify(); if (fit->renormType() == Fit::AUTO) { // OK, now let's print them out. tcout << "\nParameters have been automatically renormalized." << std::endl; for (size_t j = 0; j < nShow; ++j) { size_t nGroup = j ? neededCopies[j-1] : newModel->dataGroupNumber(); tcout << *models->lookup(modelName,nGroup); } tcout << std::endl; } // set the mode of parameter printing for fit output. Parameter::initPrint(false); return globalData->autoSave(TCL_OK); } catch (YellowAlert&) { if (added) models->remove(modelName); else delete newModel; Parameter::initPrint(false); return globalData->autoSave(TCL_ERROR); } } }