// // XSPEC12 November 2003 // // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace XSContainer; class FakeitError : public YellowAlert { public: FakeitError(const string& msg); }; FakeitError::FakeitError(const string& msg) : YellowAlert("Fakeit Error: ") { tcerr << msg << std::endl; } class FakeitHandler { public: typedef FakeDataInputRecord::BackLocator BackLocator; FakeitHandler(int objc, Tcl_Obj* CONST objv[], std::vector& fakeDataSets); ~FakeitHandler() {} void perform(); private: FakeitHandler(const FakeitHandler&); void operator=(const FakeitHandler&); void collateArgs(); void determineNSpectra(const StringArray& argArray, const string& lastArg); void determineDataSetFormats(); void earlyResponsePrompt(); void getAuxFileInfo(); void oneTimePrompts(); void getAdditionalFDataSetInfo(); void constructFakeDataSets(); size_t m_nOrigSpectra; size_t m_nOrigDataSets; size_t m_nSpectra; size_t m_nDataSets; bool m_type2Flag; bool m_isModular; bool m_useCountStat; string m_prefix; int m_objc; Tcl_Obj* CONST* m_objv; std::deque m_batchPrompts; DataUtility::recordList m_inputData; string m_earlyResponseName; BackLocator m_earlyArfLoc; std::vector m_fDataRecs; DataPrototype* m_prototype; std::vector& m_fakeDataSets; }; int XSGlobal::xsFakeit(ClientData cdata,Tcl_Interp* tclInterp,int objc, Tcl_Obj* CONST objv[]) { string cmd = string(static_cast(cdata)); if (objc == 2 && string(Tcl_GetString(objv[1])) == "?") { printDocs(cmd.c_str(),"?"); } else { // If no models are active, there is no reason to proceed. IntegerArray activeSources; models->explActiveSources(activeSources); if (!activeSources.size()) { tcout << "\n***Error: Cannot create fake data. There are no active models." <autoSave(TCL_ERROR); } std::vector dataSetHooks(0); try { FakeitHandler(objc, objv, dataSetHooks).perform(); } catch (YellowAlert&) { for (size_t i=0; iautoSave(TCL_ERROR); } // If we reached here, assume all fake data sets are in condition // to be generated. Any pre-existing data sets must be cleared // out at this point. datasets->dgHistory().initOldArray(datasets->numberOfGroups()); datasets->clear(); for (size_t i=0; iaddToList(dataSetHooks[i]); } // This is a patch hack. Want to really just get at datasets' // enumerateGroups function, but it is private. So get // at through the deleteRange function as in xsData, but // don't actually delete anything. // This must be at least size 1 inside deleteRange: std::vector dummy(1, false); datasets->deleteRange(dummy, false); datasets->Notify(); models->fold(); for (size_t i=0; ifillFakeData(fakeDs); fakeDs->generateFake(); fakeDs->outputData(); } models->Notify(); } return globalData->autoSave(TCL_OK); } FakeitHandler::FakeitHandler(int objc, Tcl_Obj* CONST objv[], std::vector& fakeDataSets) : m_nOrigSpectra(datasets->numberOfSpectra()), m_nOrigDataSets(datasets->dataArray().size()), m_nSpectra(0), m_nDataSets(0), m_type2Flag(false), m_isModular(false), m_useCountStat(true), m_prefix(), m_objc(objc), m_objv(objv), m_batchPrompts(), m_inputData(), m_earlyResponseName(), m_earlyArfLoc(), m_fDataRecs(), m_prototype(0), m_fakeDataSets(fakeDataSets) { } void FakeitHandler::perform() { collateArgs(); determineDataSetFormats(); getAuxFileInfo(); oneTimePrompts(); getAdditionalFDataSetInfo(); constructFakeDataSets(); } void FakeitHandler::collateArgs() { string cmdLine(""); string cmdStr(""); if (m_objc > 1) { // check for parameters from batch file. for (int j=1; jdataArray().begin()->second; m_prototype = XSContainer::xsRegistry->returnPrototype(typeid(*origDset)); m_isModular = origDset->isModular(); // isModular now known, type2Flag not relevant here since new sets // will just be same type as orig sets. } else { // Fake data will be created in addition to replacing whatever // data is currently loaded. if (m_nOrigSpectra) { DataArrayConstIt dsIt = datasets->dataArray().begin(); DataArrayConstIt dsItEnd = datasets->dataArray().end(); const DataSet* origDset = dsIt->second; // ASSUMPTION: Prototype and hence isModular is same for all. m_prototype = XSContainer::xsRegistry->returnPrototype(typeid(*origDset)); m_isModular = origDset->isModular(); while (dsIt != dsItEnd) { // If any loaded data is Type 2, then make the extra // fake data Type 2. if (dsIt->second->isMultiple()) { m_type2Flag = true; break; } ++dsIt; } m_nDataSets = m_nOrigDataSets + (m_type2Flag ? 1 : m_nSpectra-m_nOrigSpectra); // type2Flag and isModular now known. } else { // No loaded data with which to base Type of output. Instead, // check all input records and look for any evidence of Type2 // background files. If any, then additional data will be // placed in 1 Type2 file. However, if no evidence of Type2, // then we are still undetermined and final test will come // from response prompting. (ie. a SPI-type response will tell // us to use type2) DataUtility::recordListConstIter recIter(m_inputData.begin()); const DataUtility::recordListConstIter last(m_inputData.end()); while (recIter != last) { if (recIter->spectrumRange(0) != 0) { m_type2Flag = true; m_nDataSets = 1; // isModular not known. break; } ++recIter; } // OK, from the user input we still don't know if new data // sets are modular format, and perhaps not even type I // vs type II. So, jump ahead and do the first response // prompt now. earlyResponsePrompt(); } } } void FakeitHandler::earlyResponsePrompt() { // To be used when there is no original data loaded from which to // determine isModular, and perhaps m_type2Flag as well. XSparse::promptResponseArf(m_earlyResponseName, DUMMY_RSP, m_earlyArfLoc.first, m_earlyArfLoc.second, 1, false, true, &m_batchPrompts); if (XSutility::lowerCase(m_earlyArfLoc.first) == XSparse::NONE()) m_earlyArfLoc.first = string(""); m_prototype = DataUtility::prototypeFromResponse(m_earlyResponseName); // This will just be used briefly, hence no auto ptr. DataSet *tmpDset = m_prototype->MakeDataSet(); // if type2Flag has already been determined to be true, keep it that way. if (!m_type2Flag) m_type2Flag = tmpDset->isMultiple(); m_isModular = tmpDset->isModular(); m_nDataSets = m_type2Flag ? 1 : m_nSpectra; delete tmpDset; } void FakeitHandler::getAuxFileInfo() { // Aside from getting aux file info, function also creates all // fDataInputRecs, and fills in whatever it can from original data. if (m_nOrigDataSets) { size_t nProcessed=0; std::set origSpecNumsRemaining; for (size_t i=1; i<=m_nOrigSpectra; ++i) { origSpecNumsRemaining.insert(i); } // First process spectra based on original data. while (nProcessed < m_nOrigDataSets) { // Find the data set which contains the lowest remaining // spectrum number. size_t row=0; size_t lowestSpec = *origSpecNumsRemaining.begin(); DataSet* origDset = datasets->dataSetLookup(lowestSpec, row); IntegerArray rowNums; if (!origDset->specNumOrder(rowNums)) { tcout << "\n***Warning: Gaps exist in the spectrum numbers currently"; tcout << "\n assigned to the spectra in file "; tcout << "\n" << origDset->dataName(); tcout << "\nFake spectra will be processed in the order of the spectra"; tcout <<"\nin this file, not in the order of increasing spectrum number."<numSourcesForSpectra()); fRec.setOrigRowNums(rowNums); fRec.groupNumber(origDset->dataGroup()); fRec.isType2(origDset->isMultiple()); fRec.data(origDset); DataUtility::recordListConstIter recIter(m_inputData.begin()); for (size_t i=0; isourceData(rowNums[i]); size_t specNum = origSd->spectrumNumber(); // Fill FakeDataRecord with info fRec.spectrumNumber(i, specNum); // This may be changed later, but for now just number rows consecutively // from 1. fRec.spectrumRange(i, i+1); size_t index=0; FakeDataInputRecord::Detectors responseNames; FakeDataInputRecord::Arfs arfLocs; // Did the user enter a background file or "none" on the // command line for this particular spectrum? The following // function may increment recIter. if (DataUtility::searchRecordListSpecNums(m_inputData, specNum, recIter, index)) { if (XSutility::lowerCase(recIter->fileName()) == XSparse::NONE()) { fRec.enteredNone(true); fRec.inputBackgrounds(i, BackLocator("",0)); } else { responseNames = origDset->getResponseName(rowNums[i]); arfLocs = origDset->getAncillaryLocation(rowNums[i],responseNames); BackLocator bckLoc(recIter->fileName(), recIter->spectrumRange(index)); fRec.inputBackgrounds(i, bckLoc); } } else { responseNames = origDset->getResponseName(rowNums[i]); arfLocs = origDset->getAncillaryLocation(rowNums[i],responseNames); fRec.inputBackgrounds(i, origDset->getBackCorrLocation(rowNums[i])); } fRec.inputCorrFiles(i, origDset->getBackCorrLocation(rowNums[i],true)); if (!responseNames.size()) { // Can get here if the user typed "none" for this // spectrum, or if it had no response to begin with. FakeDataInputRecord::ResponseID respID; FakeDataInputRecord::ArfID arfID; if (m_isModular) { // Do not prompt for every spectrum, only the first // in the set. Also, no need to prompt for Arf. if (i == 0) { XSparse::promptResponseArf(respID.first, DUMMY_RSP,arfID.first.first, arfID.first.second, specNum, true, false, &m_batchPrompts); } } else { XSparse::promptResponseArf(respID.first, DUMMY_RSP, arfID.first.first, arfID.first.second, specNum, true, true, &m_batchPrompts); } // Will just have to assume this is for source 1 respID.second = 0; arfID.second = 0; if (XSutility::lowerCase(arfID.first.first) == XSparse::NONE()) arfID.first.first = string(""); fRec.inputResponses(i, FakeDataInputRecord::Detectors(1,respID)); fRec.inputArfs(i, FakeDataInputRecord::Arfs(1,arfID)); } else { fRec.inputResponses(i, responseNames); fRec.inputArfs(i, arfLocs); } origSpecNumsRemaining.erase(origSd->spectrumNumber()); } // end spectrum loop m_fDataRecs.push_back(fRec); ++nProcessed; } } // end if original data sets if (m_nDataSets > m_nOrigDataSets) { // In current implementation, responses and arfs which come // from prompts (which is everything in this section) can // only be assigned to source 1. If this constraint is lifted, // be sure to also modify the DataSet initializeFake functions. // (This assumption is also used in getAdditionalFDataSetInfo) size_t specNum = m_nOrigSpectra + 1; size_t index = 0; FakeDataInputRecord::Detectors respIDs(1); FakeDataInputRecord::Arfs arfIDs(1); FakeDataInputRecord::ResponseID& respID0 = respIDs[0]; FakeDataInputRecord::ArfID& arfID0 = arfIDs[0]; respID0.second = 0; arfID0.second = 0; if (m_type2Flag) { // Make just 1 type 2 data set containing all extra spectra. size_t nSpec = m_nSpectra-m_nOrigSpectra; FakeDataInputRecord fRec(nSpec); fRec.numSourcesForSpectra(1); // See note in original data section. fRec.isType2(true); fRec.groupNumber(1); fRec.data(0); DataUtility::recordListConstIter recIter = m_inputData.begin(); for (size_t i=0; ifileName()) != XSparse::NONE()) { BackLocator bckLoc(recIter->fileName(), recIter->spectrumRange(index)); fRec.inputBackgrounds(i, bckLoc); } } } // end if i == 0 else if (!m_isModular) { XSparse::promptResponseArf(respID0.first, DUMMY_RSP, arfID0.first.first, arfID0.first.second, specNum, false, true, &m_batchPrompts); fRec.inputResponses(i, respIDs); if (XSutility::lowerCase(arfID0.first.first) == XSparse::NONE()) arfID0.first.first = string(""); fRec.inputArfs(i, arfIDs); if (DataUtility::searchRecordListSpecNums(m_inputData, specNum, recIter, index)) { // For this case 'none' is meaningless, simply skip it. if (XSutility::lowerCase(recIter->fileName()) != XSparse::NONE()) { BackLocator bckLoc(recIter->fileName(), recIter->spectrumRange(index)); fRec.inputBackgrounds(i, bckLoc); } } } ++specNum; } // end spectra loop m_fDataRecs.push_back(fRec); } // end if type2Flag else // Extra data sets are type 1. { DataUtility::recordListConstIter recIter = m_inputData.begin(); for (size_t i=0; i<(m_nDataSets-m_nOrigDataSets); ++i) { // Make type 1 data sets for each spectrum FakeDataInputRecord fRec(1); fRec.isType2(false); fRec.numSourcesForSpectra(1); // See note in original data section. fRec.groupNumber(1); fRec.data(0); fRec.spectrumNumber(0, specNum); fRec.spectrumRange(0,0); if (i==0 && m_earlyResponseName.length()) { respID0.first = m_earlyResponseName; fRec.inputResponses(0, respIDs); arfID0.first = m_earlyArfLoc; fRec.inputArfs(0, arfIDs); } else { XSparse::promptResponseArf(respID0.first, DUMMY_RSP, arfID0.first.first, arfID0.first.second, specNum, false, true, &m_batchPrompts); fRec.inputResponses(0, respIDs); if (XSutility::lowerCase(arfID0.first.first) == XSparse::NONE()) arfID0.first.first = string(""); fRec.inputArfs(0, arfIDs); } if (DataUtility::searchRecordListSpecNums(m_inputData, specNum, recIter, index)) { // For this case 'none' is meaningless, simply skip it. if (XSutility::lowerCase(recIter->fileName()) != XSparse::NONE()) { BackLocator bckLoc(recIter->fileName(), recIter->spectrumRange(index)); fRec.inputBackgrounds(0, bckLoc); } } ++specNum; m_fDataRecs.push_back(fRec); } // end spectra loop } // end type I } // end if nDataSets > nOrigDataSets } void FakeitHandler::oneTimePrompts() { // Helper function for user input that should be prompted for // once and only once per each fakeit command. while (1) { string userInput(""); XSparse::basicPrompt(" Use counting statistics in creating fake data? (y): ", userInput, &m_batchPrompts); userInput = XSutility::lowerCase(userInput); if (userInput.length()) { if (userInput[0] == 'n') { m_useCountStat = false; break; } else if (userInput[0] == 'y' || userInput[0] == '/') { m_useCountStat = true; break; } } else { m_useCountStat = true; break; } } DataSet::useFakeCountingStat(m_useCountStat); XSparse::basicPrompt(" Input optional fake file prefix: ", m_prefix, &m_batchPrompts); if (m_prefix == "/") m_prefix = ""; } void FakeitHandler::getAdditionalFDataSetInfo() { if (m_nDataSets != m_fDataRecs.size()) { throw RedAlert("Internal fakeit error: data sets vs. data records size mismatch."); } string defOutFile(""); Real defExpTime=1.0; Real defCorScale=1.0; const string defExt(".fak"); const string bkgExt("_bkg"); const string expCorPrompt(" Exposure time, correction norm"); for (size_t i=0; idataName().rfind('/'); if (slashLoc != string::npos) { defOutFile = origDset->dataName(); defOutFile.insert(slashLoc+1, m_prefix); } else defOutFile = m_prefix + origDset->dataName(); // Get default exposure and corr scaling from only the // first spectrum (irrel. if this is type 1). For fake // data, all spectra will have the same exposure and // corrScale. There won't be prompting for each individual // spectrum. if (!fRec.enteredNone()) { size_t firstSpecNum = static_cast(fRec.origRowNums(0)); const SpectralData* origSd = origDset->sourceData(firstSpecNum); defExpTime = origSd->exposureTime(); defCorScale = origSd->correctionScale(); } } else { // Not based on original data. Check for background file // for any of the spectra. If any, use first background // file's exposure time as the default for the whole set. // ASSUME resp must only be in source 1 if not based on // original data. defOutFile = (fRec.inputResponses(0)[0].first == DUMMY_RSP) ? m_prefix + "dummy_rsp" : m_prefix + fRec.inputResponses(0)[0].first; size_t nAttempts = m_isModular ? 1: nSpec; bool isRead = false; for (size_t j=0; j pFits(OGIP_92aIO::openFitsExtension (bckName, XspecDataIO::SpectrumType)); // First, try for keyword. float keyVal = .0; pFits->currentExtension().readKey(exposureStr, keyVal); defExpTime = static_cast(keyVal); isRead = true; } catch (...) { } if (!isRead) { // Try for column. try { std::auto_ptr pFits(OGIP_92aIO::openFitsExtension (bckName, XspecDataIO::SpectrumType)); RealArray colVals; size_t row = fRec.inputBackgrounds(j).second; pFits->currentExtension().column(exposureStr).read(colVals, row, row); defExpTime = colVals[0]; isRead = true; } catch (...) { } } } } } XSparse::changeExtension(defOutFile, defExt); fRec.fileName() = XSparse::promptFilename(defOutFile, &m_batchPrompts); std::pair tmpExpCor(defExpTime, defCorScale); while (!XSparse::promptRealPair(expCorPrompt, tmpExpCor, &m_batchPrompts)) { tmpExpCor.first = defExpTime; tmpExpCor.second = defCorScale; tcout << "\n***Improper input" << std::endl; } fRec.exposureTime(tmpExpCor.first); fRec.correctionNorm(tmpExpCor.second); // If any spectra have a bkg file name attached (or // just the first for modular case), then // make an output bkg file name. for (size_t j=0; j<(m_isModular ? 1 : nSpec); ++j) { if (fRec.inputBackgrounds(j).first.length()) { string tmp = fRec.fileName(); string::size_type period = tmp.rfind('.'); if (period == string::npos) { fRec.backgndFile(tmp + bkgExt); } else { tmp.insert(period, bkgExt); fRec.backgndFile(tmp); } break; } } } // end data sets loop } void FakeitHandler::constructFakeDataSets() { for (size_t i=0; i dSet(m_prototype->MakeDataSet()); dSet->initializeFake(m_prototype, fRec); DataSet* validDs(dSet.release()); m_fakeDataSets.push_back(validDs); } }