// Read the documentation to learn more about C++ code generator // versioning. // %X% %Q% %Z% %W% #include "DataFactory/OGIP-92aIO.h" // SpectralData #include // Response #include // DataSet #include // Background #include // DataUtility #include #include "Parse/XSparse.h" #include "Data/Detector/RMF.h" #include "xsTypes.h" #include "XSstreams.h" #include #include "Utils/XSstream.h" #include "DataFactory/XspecRegistry.h" #include "XSContainer.h" #include "XSsymbol.h" // Class Utility DataUtility XSContainer::Weight* DataUtility::s_weightScheme; void DataUtility::readArrays (RMF& rmf, const CCfits::ExtHDU& fitsData) { size_t nE = fitsData.rows(); static const size_t ONE(1); // kill boring compiler warnings; string stringval(""); // save the current chatter output level for cases where we switch to // a higher level for diagnostic output int savConVer = tpout.conVerbose(); int savLogVer = tpout.logVerbose(); //fitsData.readKey(OGIP_92aIO::TELESCOPE(),stringval); try { rmf.telescope(fitsData.keyWord(OGIP_92aIO::TELESCOPE()).value(stringval)); } catch (...) { XSstream::verbose(tpout,25,25); tcout << "Failed to read TELESCOPE keyword\n"; XSstream::verbose(tpout, savConVer, savLogVer); throw; } //rmf.telescope(stringval); //fitsData.readKey(OGIP_92aIO::INSTRUMENT(),stringval); //rmf.instrument(stringval); //fitsData.readKey(OGIP_92aIO::CHANNELTYPE(),stringval); //rmf.channelType(stringval == "PHA" ? PHA : PI); // Load the energy ranges and grouping columns into RMF object try { fitsData.column(OGIP_92aIO::ENERGYLO()).read(rmf.energyLow(), ONE, nE); } catch(...) { XSstream::verbose(tpout,25,25); tcout << "Failed to read ENERGYLO column\n"; XSstream::verbose(tpout, savConVer, savLogVer); throw; } try { fitsData.column(OGIP_92aIO::ENERGYHI()).read(rmf.energyHigh(), ONE, nE); } catch(...) { XSstream::verbose(tpout,25,25); tcout << "Failed to read ENERGYHI column\n"; XSstream::verbose(tpout, savConVer, savLogVer); throw; } try { fitsData.column(OGIP_92aIO::NGROUP()).read(rmf.binResponseGroups(), ONE, nE); } catch(...) { XSstream::verbose(tpout,25,25); tcout << "Failed to read NGROUP column\n"; XSstream::verbose(tpout, savConVer, savLogVer); throw; } // Prevent energy = 0 (or neg). RealArray& eLow = rmf.energyLow(); RealArray& eHigh = rmf.energyHigh(); bool smallEWarn = false; for (size_t i=0; i(&fitsData)->readKey(chanKey.str(),offset); } catch (CCfits::HDU::NoSuchKeyword) { offset = 1; } try // loading F_CHAN as vector column { std::vector > fchan; fitsData.column(OGIP_92aIO::FCHANNEL()).readArrays(fchan, ONE, nE); MatrixIndex& binStart = rmf.binStart(); binStart.resize(nE); for (size_t i=0; i > nchan; fitsData.column(OGIP_92aIO::NCHANNEL()).readArrays(nchan, 1, nE); MatrixIndex& binRunLengths = rmf.binRunLengths(); binRunLengths.resize(nE); for (size_t i=0; i tmpScalarCol; fitsData.column(OGIP_92aIO::MATRIX()).read(tmpScalarCol, ONE, nE); MatrixValue& rmfVals = rmf.matrixValue(); rmfVals.clear(); size_t sz = tmpScalarCol.size(); for (size_t i=0; i apChan2bin(new int[ungroupedSize]); int* chan2bin(apChan2bin.get()); // chan2bin is maps chans (which may be offset by startChan) to // 0-based post-grouping bin number. Its size should // always be ungroupedSize, which we'll assume is non-zero by // this point. int firstGood = 0; while (firstGood < ungroupedSize) { if (quality[firstGood] != 1) break; chan2bin[firstGood] = 0; ++firstGood; } if (firstGood == ungroupedSize) throw YellowAlert("All channels marked bad quality, cannot group RMF values.\n"); int currBin = -1; for (int i=firstGood; i apGroupedRMFrow(new Real[nGroupedBins]); Real* groupedRMFrow(apGroupedRMFrow.get()); MatrixIndex& allFChans = rmf.binStart(); MatrixIndex& allNChans = rmf.binRunLengths(); MatrixValue& allRmfVals = rmf.matrixValue(); for (int iRow=0; iRow= ungroupedSize) { // No need for any groupedRMFrow workspace: startWorkspace = endWorkspace; } else { startWorkspace = chan2bin[startWorkspace]; } for (int i=startWorkspace; i= ungroupedSize) { beyondSpec = true; } else if (zeroBasedChan >= 0) { if (quality[zeroBasedChan] != 1) { int newIndex = chan2bin[zeroBasedChan]; groupedRMFrow[newIndex] += rmfVals[iCompressed]; // newIndex is always being accessed in // increasing order. if (newIndex != newElemIndices.back()) newElemIndices.push_back(newIndex); } ++iCompressed; } else { ++iCompressed; } } } size_t ngroups=0; fChan.clear(); nChan.clear(); size_t rowSize = newElemIndices.size() - 1; rmfVals.resize(rowSize); int prevChan = -999; int chanCount = 0; for (size_t i=1; i<=rowSize; ++i) { int currChan = newElemIndices[i]; if (currChan != prevChan+1) { // start of new group, possibly the end // of a previous one. if (ngroups) nChan.push_back(chanCount); ngroups++; fChan.push_back(currChan); chanCount = 0; } rmfVals[i-1] = groupedRMFrow[currChan]; prevChan = currChan; ++chanCount; } // Fill in nChan for last group (if there are any). if (chanCount) nChan.push_back(chanCount); rmf.binResponseGroups(iRow,ngroups); } // end if nGrp } // end nE loop rmf.createConvolutionArray(); } void DataUtility::setSource (SpectralData* spectrum, Response* response, size_t sourceNum) { response->source(spectrum); // there's always at lead one 'slot' in the detector array. // it's the job of the calling routine to ensure an index position // exists. spectrum->attachDetector(response,sourceNum); } const CodeContainer& DataUtility::encodeGQ (const IntegerArray& group, const IntegerArray& quality, CodeContainer& coded) { encode(group, coded); coded.push_back(static_cast(7)); encode(quality, coded); return coded; } void DataUtility::decodeGQ (const CodeContainer& coded, IntegerArray& group, IntegerArray& quality) { const CodeField delim = 7; CodeContainer::const_iterator boundary = std::find(coded.begin(), coded.end(), delim); CodeContainer gString(coded.begin(), boundary); decode(gString, group); if (boundary != coded.end()) { ++boundary; CodeContainer qString(boundary, coded.end()); decode(qString, quality); } } void DataUtility::encode (const IntegerArray& inputArray, CodeContainer& coded) { bitCard b; size_t nvalues = inputArray.size(); const CodeField MAXNTIMES = 268435456; // = 2^28 size_t i=1; // Patch fix for unusual case of only 1 channel. if (nvalues == 1) { // inputArray[0] can't be -1. It would have been corrected by now. coded.push_back((1 << 3) + inputArray[0]); return; } while (i < nvalues) { CodeField n=1; while ((i < nvalues) && (inputArray[i] == inputArray[i-1])) ++i, ++n; if (n > MAXNTIMES || n<0) { throw YellowAlert("Cannot process qual/group columnn: Too many equivalent consecutive values."); } b.ntimes = n; b.flag = ((inputArray[i-1]==-1) ? 6 : inputArray[i-1]); // b.flag setting has already been verified to be within // correct limits. coded.push_back((b.ntimes << 3) + b.flag); if (i == nvalues-1) { n = 1; b.ntimes = n; b.flag = ((inputArray[i]==-1) ? 6 : inputArray[i]); coded.push_back((b.ntimes << 3) + b.flag); } ++i; } } void DataUtility::decode (const CodeContainer& coded, IntegerArray& inputArray) { size_t sz = coded.size(); for (size_t i=0; i> 3; int j = static_cast(w & 7); j = ((j == 6) ? -1 : j); std::fill_n(back_inserter(inputArray), num, j); } } void DataUtility::fixSequence (DataUtility::recordList& records, size_t spectraDefined) { // we need to check the following: // 1) no dataset defined by the input command overwrites the spectrum // numbers of an earlier dataset referenced on the line (the recordList // has been stable-sorted by the time this is called, so we can simply // iterate once through the list. // 2) the largest spectrum number referenced, or the first of // a set of spectrum numbers referenced is no larger than spectraDefined+1, // where spectraDefined is the number of spectra defined prior to the // current command. // 3) [New Rule in XSPEC12] data group number may not exceed spectrum number // for any spectrum. If additionally datagroups are renumbered with spectra, // this simple rule ensures that there are no datagroups with no spectra // assigned to them, and thus no models with dangling parameters. // it's possible to get here with an empty input record, for example // if the user was prompted for replacement input and typed "none". fixOverlap(records); fixSpectrumGaps(records, spectraDefined); fixDataGroupsVsSpecNum(records); } void DataUtility::makeInputRecords (size_t firstSpectrumNumber, size_t groupNumber, const string& inputString, recordList& inputRecordList, XSutility::fileType defaultSuffix) { // function for parsing command line string of the form: // [,]s_1[{r_1}][,s_2{r_2},...s_m{r_m},][/] // Input string has already been delimited by commas and spaces before getting here. string inString(inputString); while (inString.size() > 0) { // range is the row number of spectra in a vector Data file, e.g. OGIP-II IntegerArray range; string fName(""); fName = XSparse::returnDelimitedArgument(inString, "{"); inString = XSparse::returnDelimitedArgument(inString, "}"); string strSingleRange; int begin, end; static int sLastLeft = -2, sLastRight = -2; while(!(strSingleRange = XSparse::returnDelimitedArgument(inString, ",")).empty()) { XSparse::oneRange(strSingleRange, begin, end); begin = (begin == -1 ? sLastLeft : begin); end = (end == -1 ? sLastRight : end); range.push_back(begin); range.push_back(end); sLastLeft = begin; sLastRight = end; } //other functions expect this if(range.size() == 0) range = IntegerArray(1, 0); if (!(XSutility::lowerCase(fName) == XSparse::NONE() && range[0] == 0)) fName = XSutility::addSuffix(fName, defaultSuffix); //inString = XSparse::processStringToken(inString,fName,range, defaultSuffix); #ifndef STD_COUNT_DEFECT //was counting -1's, not sure why int wild(std::count(range.begin(),range.end(),-2)); #else int wild(0); std::count(range.begin(),range.end(),-2,wild); #endif IntegerArray spectraNumbers(1,firstSpectrumNumber); //Call XSparse::expandRange if no wild card values were found. Otherwise, //delay calling this until DataInputRecord::updateSpectrumCounts, when we //know the total number of spectra if (!wild) { range = XSparse::expandRange(range); size_t N(range.size()); spectraNumbers.resize(N); for (size_t j = 1; j < N; ++j ) spectraNumbers[j] = firstSpectrumNumber + j; } inputRecordList.push_back(DataInputRecord(fName,spectraNumbers,groupNumber,range)); } } DataUtility::recordList DataUtility::parseDataCommandString (StringArray& args, XSutility::fileType defaultSuffix) { // this overload is used for parsing dataset strings. // data command syntaxes to be dealt with: // data command syntaxes to be dealt with: // syntax action // data do nothing. // data fN1 (no numbers) destroy all datasets, and create new dataset from fN1 // data fN1,, replace dataset #1 with fN1, preserve dataset #2 and destroy others // data fN1/ replace dataset #1 with one created from fN1 and preserve other datasets // data ,,fN1 (no numbers) keep dataset #1, add dataset #2 from fN2, destroy all other sets. // data fN1 fN2,fN3,fN4,... dataset #1 is replaced by dataset fN1: dataset , , // are created by fN2, fN3, fN4 respectively. dataset #2, if it exists // is left unchanged. // data none/ destroy dataset #n and leave other datasets unchanged // data fN1 fN2 set fN1 to dataset n1 with datagroup g1 etc. // data fN1{ range } create multiple datasets from fN1 according to the range, starting // with dataset #1. All datasets will be in group 1. // data fN1{range} create multiple dataset from fN1 starting with dataset #n. All datasets // will be in group m int first = -1; int second = -1; size_t spectrumNumber = 1; size_t groupNumber = 1; size_t saveGroupNumber = 1; size_t trailingCommas = 0; enum entryType {comma, name, intPair} previous = comma; recordList inputRecords; // CheckBrackets will make sure brackets are properly paired off, // and will remove any unnecessary spaces between the brackets. // In doing so, it could end up modifying the size of args. XSparse::checkBrackets(args); size_t N(args.size()); for (size_t i=1; i 1) { // After the sort, does the last record still have the same // spectrum number? (This isn't fool-proof, but it's close.) recordListConstIter rCheck = inputRecords.end(); --rCheck; if (backNum != rCheck->spectrumNumber(0)) { string msg("\n***Warning: The trailing commas do not belong to the set"); msg += "\n with the highest spectrum number. They will be ignored."; tcout << msg < 1 && (--rCheck)->spectrumNumber(0) == backNum) { string msg("\n***Warning: Multiple sets assigned highest spectrum number"); msg += "\n trailing commas will be ignored."; tcout << msg <spectrumNumber().size(); ++rl; } return total; } const XSContainer::Weight& DataUtility::statWeight () { if (s_weightScheme) { return *s_weightScheme; } else { throw RedAlert("Global weight scheme not set"); } } void DataUtility::fixOverlap (DataUtility::recordList& records) { // Fix any errors from the user input to the data command of the kind // where one record would overwrite the spectra of another. For // type 1, this is simply cases such as: // >data 1 file1 2 file2 1 file3 // For type 2, this could also be cases like: // >data 1 file1{3-6} 3 file2{7-9} // (ie. specNums 3 and 4 from the second record overwrite the last 2 // of the first record). The correction is made by renumbering the // spectra of the offending record starting at 1 above the previously // highest spectrum number. // This works on the assumption that by the time this is called, the // records will be in sorted order (sorted by the first element in // the spectrumNumber array). recordListIter rl = records.begin(); recordListIter rlEnd = records.end(); size_t previousHigh = 0; if (!records.empty()) { previousHigh = rl->spectrumNumber(rl->spectrumNumber().size()-1); ++rl; } while (rl != rlEnd) { size_t startSpec = rl->spectrumNumber(0); if (startSpec <= previousHigh) { tcout << "\n*** Warning: coincident spectrum numbers requested: file "; tcout << rl->fileName() << std::endl; tcout << "*** Correcting from " << startSpec << " to " << previousHigh+1 <renumber(previousHigh+1); } previousHigh = rl->spectrumNumber(rl->spectrumNumber().size()-1); ++rl; } } void DataUtility::fixSpectrumGaps (DataUtility::recordList& records, size_t spectraDefined) { // This function will ensure that 1) the first record's first spectrum // begins at a number <= spectraDefined+1, and 2) each following record's // first spectrum begins at a number <= spectraDefined+1 or the maximum // spectrum number from the input list so far + 1, whichever is the larger. // At this point, it DOES NOT take into account the result of entering // "none" in any of the records. if (records.empty()) return; size_t maxSpectra = spectraDefined + 1; recordListIter rl = records.begin(); recordListIter rlEnd = records.end(); while (rl != rlEnd) { size_t currentNumber = rl->spectrumNumber(0); if (currentNumber > maxSpectra) { tcout << "\n***Warning: invalid input spectrum number:"; tcout << "\n Correcting from " << currentNumber << " to " << maxSpectra <renumber(maxSpectra); } size_t rlHigh = rl->spectrumNumber(rl->spectrumNumber().size()-1); maxSpectra = std::max(rlHigh+1, maxSpectra); ++rl; } } void DataUtility::fixDataGroupsVsSpecNum (DataUtility::recordList& records) { // This will correct for the case where an input record contains // a data group number higher than its spectrum number. It only // looks for the obvious cases -- it does not check for entries of // "none". It presumes the effects of a "none" entry will be // corrected for at some later stage. if (records.empty()) return; recordListIter rl = records.begin(); recordListIter rlEnd = records.end(); while (rl != rlEnd) { if (rl->groupNumber() > (size_t)rl->spectrumNumber(0)) { tcout << "\n***Warning: Data group number for a spectrum in file " << rl->fileName() << "\n must be no greater than the spectrum number: " << "correcting " << rl->groupNumber() << " to " << rl->spectrumNumber(0) <(rl->spectrumNumber(0)); rl->data()->dataGroup(newGroupNum); } ++rl; } } bool DataUtility::searchRecordListSpecNums (const recordList& records, const size_t specNum, recordListConstIter& currRec, size_t& index) { // This is designed for use by xsFakeit. Given a specNum, it will search // a recordList starting at the input currRec and determine if any of the // records contain a spectrum number == specNum. If found, it returns // "true", currRec will now point the record containing the spectrum, and // index will tell the position of that spectrum within the spectrumNumber // array . // Assumptions: The input recordList is in order sorted by each record's // spectrumNumber(0) value, and within each record the specNums are // consecutive and increasing. Though, there may be gaps in spectrum number // between the records. const recordListConstIter rlEnd = records.end(); bool isFound = false; index = 0; while (currRec != rlEnd) { size_t low = currRec->spectrumNumber(0); size_t high = currRec->spectrumNumber().back(); if (specNum < low) { return isFound; } else if (specNum <= high) { index = specNum - low; isFound = true; break; } ++currRec; } return isFound; } DataPrototype* DataUtility::prototypeFromResponse (string& responseName) { DataPrototype *proto=0; bool isOk = false; try { proto = XSContainer::xsRegistry->returnPrototype(responseName, XspecDataIO::ResponseType); } catch (YellowAlert&) { while (!isOk) { tcout <<"\n***Error: Unable to determine data type from response name: " << responseName << std::endl; try { XSparse::basicPrompt("New response name?: ", responseName); proto = XSContainer::xsRegistry->returnPrototype(responseName, XspecDataIO::ResponseType); isOk = true; } catch (XSparse::AbortLoop) { throw; } catch (YellowAlert&) { } } } return proto; } void DataUtility::getLostChannelNumbers (const IntegerArray& quality, const IntegerArray& grouping, IntegerArray& lostChannels) { // This assumes that the input quality and grouping arrays have // been verified elsewhere. No error checking is performed on them. // Quality array should consist of ints [0-5], grouping should have // ints {-1, 0, 1}, and there should be no anomalies such as the // first bin = -1. If they both exist, quality and grouping arrays // must also be the same size // The idea here is to check each channel for bad quality = 1 OR // grouping = -1. Each time this condition is met, increment by 1 // the element of lostChannels which corresponds to the element // of the spectrum AFTER to where this lost channel would have // been inserted. ie. if grouping = {1,-1,-1}, lostChannels[0] = 0, // lostChannels[1] = 2 (lostChannels.size() = sd->channels.size()+1.) size_t qsz = quality.size(); size_t gsz = grouping.size(); if (qsz && gsz && (qsz != gsz)) { throw RedAlert("DataUtility: size mismatch between quality and grouping arrays."); } if (!qsz && !gsz) { lostChannels.clear(); return; } lostChannels.resize(1,0); if (qsz && gsz) { for (size_t i=0; i= sz) break; if (quality[i] != 1) { enterRun = (quality[i] == 0 && grouping[i] == -1); // if (!enterRun) // { // tcout << " End Run " << i << " q " << quality[i] // << " g " << grouping[i] << std::endl; // } } } } // end if enterRun else { if ( quality[i] > 1) { int lastQuality ( quality[i] ); // tcout << " poor quality: " << i << " " << lastQuality ; grouping[i] = 1; // NOTE: following loop always iterates at least once. while (i