// Read the documentation to learn more about C++ code generator // versioning. // %X% %Q% %Z% %W% // DataSet #include // OGIP-92aBackground #include #include #include #include #include #include "XSstreams.h" #include "XSsymbol.h" #include #include // Class OGIP_92aBackCorr OGIP_92aBackCorr::OGIP_92aBackCorr() { } OGIP_92aBackCorr::OGIP_92aBackCorr(const OGIP_92aBackCorr &right) : BackCorr(right), OGIP_92aIO(right) { } OGIP_92aBackCorr::~OGIP_92aBackCorr() { } bool OGIP_92aBackCorr::fileFormat (const string& fileName, XspecDataIO::DataType type) { return OGIP_92aIO::fileFormat(fileName,type); } OGIP_92aBackCorr* OGIP_92aBackCorr::clone () const { return new OGIP_92aBackCorr(*this); } void OGIP_92aBackCorr::setDescription (size_t spectrumNumber) { static const string PULSEHEIGHT("PHA"); CCfits::ExtHDU& ext = dataSource()->extension(extensionName()); SpectralData* Source = source()->sourceData(sourceRow()); size_t row = data()->rowNumber(); data()->spectrumNumber(spectrumNumber); string keyval; float floatval; try { ext.keyWord(CHANNELTYPE()).value(keyval); // should print warnings if there are mismatches. data()->channelType(keyval); data()->telescope(ext.keyWord(TELESCOPE()).value(keyval)); data()->instrument(ext.keyWord(INSTRUMENT()).value(keyval)); } catch (CCfits::FitsException&) { tcout << "*** Warning: Channel Type, Telescope or Instrument Keyword not read " << " from Background/Correction file: using values from Spectrum File " << std::endl; data()->channelType(Source->channelType()); data()->telescope(Source->telescope()); data()->instrument(Source->instrument()); } const string fakeitPlaceholder("USE_FAKEIT_RMF"); if (Source->channelType() != fakeitPlaceholder && data()->channelType() != Source->channelType()) { tcout << "***Warning: Type mismatch between spectrum and background/correction files " << " using type from spectrum file:" << Source->channelType() << std::endl; data()->channelType(Source->channelType()); } if (Source->telescope() != fakeitPlaceholder && data()->telescope() != Source->telescope()) { tcout << "***Warning: Telescope name mismatch between spectrum and " << " background/correction files \n" << " Source: " << Source->telescope() << " Background/Correction " << keyval << std::endl; } if (Source->instrument() != fakeitPlaceholder && data()->instrument() != Source->instrument()) { tcout << "***Warning: Instrument name mismatch between spectrum and " << " background/correction files \n" << " Source: " << Source->instrument() << " Background/Correction " << keyval < floatValueFromCol; Real expTime = 1.; try { ext.column(EXPOSURE()).read(floatValueFromCol,row,row); expTime = floatValueFromCol[0]; } catch ( CCfits::Table::NoSuchColumn ) { expTime = ext.keyWord(EXPOSURE()).value(floatval); } if (expTime <= 0) { tcerr << "***Warning: exposure time is negative or zero - reset to 1.\n"; expTime = 1.; } data()->exposureTime(expTime); attemptToRead = CORRSCALE(); Real corrScale = 1.; try { ext.column(CORRSCALE()).read(floatValueFromCol,row,row); corrScale = floatValueFromCol[0]; } catch ( CCfits::Table::NoSuchColumn ) { corrScale = ext.keyWord(CORRSCALE()).value(floatval); } // correctionScale can in principle be any real number, although // uses with corrScale < 0 are unlikely. A user might want to turn // the correction off by editing corrScale to 0. data()->correctionScale(corrScale); attemptToRead = AREASCALE(); int last = data()->endChan() - data()->startChan(); int first = 0; int ungroupedChannels = last - first + 1; try { RealArray area; if ( row == 0 ) { ext.column(AREASCALE()).read(area,first,last); } else { // type II file CCfits::Column& col = ext.column(AREASCALE()); if (col.repeat() > 1) { col.read(area,row); } else { RealArray tmp; col.read(tmp,row,row); area.resize(ungroupedChannels,tmp[0]); } } for (int j = 0; j < ungroupedChannels; ++j) { if (std::fabs(area[j]) <= SMALL) area[j] = 1.; } data()->setAreaScale(area); } catch (CCfits::Table::NoSuchColumn) { Real area = 1.; area = ext.keyWord(AREASCALE()).value(floatval); if (std::fabs(area) > SMALL ) { data()->setAreaScale(RealArray(area,ungroupedChannels)); } else { data()->setAreaScale(RealArray(1.,ungroupedChannels)); } aScaleIsKeyword(true); } attemptToRead = BACKSCALE(); try { RealArray bkgScale; if ( row == 0 ) { ext.column(BACKSCALE()).read(bkgScale,first,last); } else { // type II file CCfits::Column& col = ext.column(BACKSCALE()); if (col.repeat() > 1) { col.read(bkgScale,row); } else { RealArray tmp; col.read(tmp,row,row); bkgScale.resize(ungroupedChannels,tmp[0]); } } bool badBscaleVal = false; for (int j = 0; j < ungroupedChannels; ++j) { if (bkgScale[j] == .0 && Source->qualityInfo()[j] != 1) { badBscaleVal = true; bkgScale[j] = 1.; } } if (badBscaleVal) tcout <<"***Warning: Background file " <backgroundFile() <<" contains 1 or more good\n quality channels with invalid BACKSCAL " <<"value = 0.0.\n XSPEC will instead use BACKSCAL = 1.0 for these channels." <setBackgroundScale(bkgScale); } catch (CCfits::Table::NoSuchColumn) { Real bkgScale = 1.; bkgScale = ext.keyWord(BACKSCALE()).value(floatval); if (bkgScale == 0.0 ) { tcout <<"***Warning: Background file " << Source->backgroundFile() <<" has invalid BACKSCAL value = .0, XSPEC will assume 1.0." << std::endl; data()->setBackgroundScale(RealArray(1.,ungroupedChannels)); } else { data()->setBackgroundScale(RealArray(bkgScale,ungroupedChannels)); } bScaleIsKeyword(true); } } catch (CCfits::FitsException&) { string msg = "Keyword "; msg += attemptToRead; msg += " is missing or of improper type in Background File: "; msg += dataSource()->name(); throw XspecDataIO::RequiredDataNotPresent(msg); } } void OGIP_92aBackCorr::setArrays (size_t backgrndRow, bool correction) { // prerequisite: source() != 0; data(new SpectralData(source(), source()->channels(sourceRow()), backgrndRow)); int start (0); int last (0); size_t legalStart (0); size_t legalEnd (0); channelBounds(start, last, backgrndRow); size_t lowDefault = start ? 1 : 0; channelLimits(lowDefault, legalStart, legalEnd); if (start < legalStart || last > legalEnd) { const string& fileName = dataSource()->name(); std::ostringstream msg; msg <<"BackCorr file " << fileName << " row " << backgrndRow << "\n has channel numbers outside legal limits.\n"; throw YellowAlert(msg.str()); } // get all of the required data on rows, grouping, etc. from the // DataSet object SpectralData* Source = source()->sourceData(sourceRow()); // Check that the number of BackCorr channels (prior to grouping // or ignore/notice) matches the number in the owning spectral data. int parentLStart=0, parentLEnd=0; source()->legalChannelBounds(parentLStart, parentLEnd); if ((legalEnd-legalStart) != (parentLEnd-parentLStart)) { const string& fileName = dataSource()->name(); std::ostringstream msg; msg <<"Background file " << fileName << " DETCHANS do not match data file.\n"; throw YellowAlert(msg.str()); } if ((start - legalStart) != (Source->startChan()-Source->firstChan())) { const string& fileName = dataSource()->name(); std::ostringstream msg; msg <<"Channel numbering mismatch between data file and" <<"\n background file " << fileName <<"\n"; throw YellowAlert(msg.str()); } if ((last-start+1) != Source->qualityInfo().size()) { const string& filename = dataSource()->name(); std::ostringstream msg; msg <<'\n'<<"Wrong number of channels in BackCorr file: " <startChan(start); data()->endChan(last); data()->firstChan(legalStart); data()->setGroupingInfo(Source->groupingInfo()); data()->setQualityInfo(Source->qualityInfo()); data()->setNoticedChannels(Source->noticedChannels()); } void OGIP_92aBackCorr::groupArrays (bool correction) { using namespace CCfits; ExtHDU& ext = dataSource()->extension(extensionName()); const Real ZERO (0); int last(data()->endChan() - data()->startChan()); int first(0); size_t row(data()->rowNumber()); bool groupingSet = source()->groupingSet(sourceRow()); bool qualitySet = source()->qualitySet(sourceRow()); // these are the arrays to be modified. //RealArray& outputSpectrum = data()->spectrum(); //IntegerArray& outputQuality = data()->quality(); size_t ungroupedChannels = (size_t)(last - first + 1); size_t chans = data()->channels(); // statistical arrays are ignored for correction files. // But IntegerArray is a std::vector (because we don't often do int calculations). // set quality vector to zeros (good everywhere). IntegerArray outputQuality(chans,0); RealArray outputSpectrum(0.,chans); RealArray outputArea(1.,chans); RealArray outputBackground(1., chans); RealArray outputVariance(0.,chans); RealArray ungroupedArea(data()->areaScale()); RealArray ungroupedBackground(data()->backgroundScale()); Column* sourceArray = 0; // COUNTS or RATE Column. Grab a pointer to whichever is there. // defaults to RATE if someone is silly enough to put in both. if (isCounts()) { sourceArray = &ext.column(COUNTS()); } else { sourceArray = &ext.column(RATE()); } // note that up to this point no allocations have been done, only // reference-finding. RealArray src(0.,ungroupedChannels); if (row == 0) { sourceArray->read(src,first,last); } else { RealArray srcTmp; sourceArray->read(srcTmp,row); src = RealArray(srcTmp[std::slice(first,last,1)]); } // Get statistical errors. Patch fix to v12.2.0: to make compatible // w/ v11, Poisserr = true will override presence of STAT_ERR col. // Even if Poisserr = true, still check for STAT_ERR col and // issue warning if it exists. RealArray stat(0.0,ungroupedChannels); RealArray syst(0.0,ungroupedChannels); bool poissonStatistics = false; bool poissonKey = false; bool systematicsPresent = false; if (!correction) { try { ext.readKey(POISSERR(),poissonKey); if (poissonKey) { poissonStatistics = true; } } catch (CCfits::FitsException&) { tcerr << "***Warning: Background file " << POISSERR() << " keyword is missing or of wrong format, assuming FALSE." <name() << " has both POISSERR key set to 'true' and a " << STATISTICAL() << " column.\n" << " XSPEC will use assume Poisson errors." << std::endl; XSstream::verbose(tpout, savCon, savLog); } } catch (Table::NoSuchColumn) { if (!poissonStatistics) { int savCon = tpout.conVerbose(); int savLog = tpout.logVerbose(); XSstream::verbose(tpout, 10, 10); tcout << "\nWarning: Statistics not present and Poisson Error Key "; tcout << "not set to 'true': setting errors to zero"<setVariance(outputVariance); data()->setRawVariance(outputVariance); } if (!groupingSet) { // Case I: simplest case: no quality or grouping information. // This should be a little more efficient than setting qArray to 0 // everywhere and executing Case II - which tactic is adopted for // the general case, III. if (!(correction || qualitySet)) { outputSpectrum = src; outputArea = ungroupedArea; outputBackground = ungroupedBackground; if (poissonStatistics) { if (poissonKey) { for (size_t j = 0; j < ungroupedChannels; ++j) outputVariance[j] = std::max(src[j],ZERO); if (!isCounts()) outputVariance /= data()->exposureTime(); } } else outputVariance = stat*stat; if (systematicsPresent) { outputVariance += (syst*src)*(syst*src); } } // Case II: no grouping information, but quality information. else { if (qualitySet) { const IntegerArray& quality = source()->qualityInfo(sourceRow()); // in this case // # channels = # ungroupedChannels - # of (quality = 1) entries. size_t k = 0; size_t j = 0; while (k < ungroupedChannels ) { if ( quality[k] != 1) { outputQuality[j] = quality[k]; outputSpectrum[j] = src[k]; outputArea[j] = ungroupedArea[k]; outputBackground[j] = ungroupedBackground[k]; if (!correction) { if (!poissonStatistics) { outputVariance[j] = stat[k]*stat[k]; } else { if (poissonKey) { outputVariance[j] = std::max(src[k],ZERO); if (!isCounts()) outputVariance[j] /= data()->exposureTime(); } } if (systematicsPresent) { outputVariance[j] += (src[k]*syst[k])*(src[k]*syst[k]); } } ++j; } ++k; } } if (correction) outputVariance.resize(0); } data()->setRawVariance(outputVariance); } else { // Case III General case: there is grouping information. // the complication arises primarily because of the statistics. // Since the correct thing to do seems to be to add channel // variances/systematics in quadrature the sums come out wrong unless // this is executed in two stages. const IntegerArray& grouping = source()->groupingInfo(sourceRow()); // in this case // # channels = # channels with GC=0 && # channels with GC = 1. // - # of (quality = 1) entries. // channels with GC=1 followed by GC=-1 are summed until the next // GC = 1 channel is reached. if (!correction) { if (!poissonStatistics) { RealArray __tmp0(stat*stat); stat = __tmp0; } else { if (poissonKey) { for (size_t l = 0; l < ungroupedChannels; ++l) { src[l] >= 0 ? stat[l] = src[l] : stat[l] = 0; } if (!isCounts()) stat /= data()->exposureTime(); } } if (systematicsPresent) stat += (syst*src)*(syst*src); } size_t k = 0; int j = -1; Real binSum = 0.; Real binSumA = 0.; Real binSumB = 0.; Real invSumA = 0.; Real invSumB = 0.; int nInGroup = 0; // the code checks for the case binSumA, binSumB > 0. For backgrounds, // only by error in the file can these be violated. if ( qualitySet ) { const IntegerArray& quality = source()->qualityInfo(sourceRow()); while (k < ungroupedChannels ) { if ( quality[k] != 1) { if ( grouping[k] != -1 ) { // "bad" - user ignorable data is GC = 0 (*) // startbin channel is GC = 1 ('+') // we already checked that grouping flag is (1,0,-1) ++j; outputQuality[j] = quality[k]; if ( j > 0) { outputSpectrum[j-1] = binSum; outputArea[j-1] = (binSumA > 0) ? binSum/binSumA : nInGroup/invSumA; outputBackground[j-1] = (binSumB > 0) ? binSum/binSumB : nInGroup/invSumB; } binSum = src[k]; invSumA = 1.0/ungroupedArea[k]; invSumB = 1.0/ungroupedBackground[k]; binSumA = binSum*invSumA; binSumB = binSum*invSumB; nInGroup = 1; if (!correction) outputVariance[j] = stat[k]; } else { // GC = -1 ('-') // new: if the quality array differs among grouped bins // set the quality for that bin equal to the worst value // found outputQuality[j] = std::max(quality[k],outputQuality[j]); binSum += src[k]; invSumA += 1.0/ungroupedArea[k]; invSumB += 1.0/ungroupedBackground[k]; binSumA += src[k]/ungroupedArea[k]; binSumB += src[k]/ungroupedBackground[k]; ++nInGroup; if (!correction) outputVariance[j] += stat[k]; } } // QC = 1 (GC = ' ') ++k; if (k == ungroupedChannels) { outputSpectrum[j] = binSum; outputArea[j] = (binSumA > 0) ? binSum/binSumA : nInGroup/invSumA; outputBackground[j] = (binSumB > 0) ? binSum/binSumB : nInGroup/invSumB; } } // end while data()->setQuality(outputQuality); } // end if quality set else { while (k < ungroupedChannels ) { if ( grouping[k] != -1) { // "bad" - user ignorable data is GC = 0 (*) // startbin channel is GC = 1 ('+') // we already checked that grouping flag is (1,0,-1) ++j; if ( j > 0) { outputSpectrum[j-1] = binSum; outputArea[j-1] = (binSumA > 0) ? binSum/binSumA : nInGroup/invSumA; outputBackground[j-1] = (binSumB > 0) ? binSum/binSumB : nInGroup/invSumB; } binSum = src[k]; invSumA = 1.0/ungroupedArea[k]; invSumB = 1.0/ungroupedBackground[k]; binSumA = binSum*invSumA; binSumB = binSum*invSumB; nInGroup = 1; if (!correction) outputVariance[j] = stat[k]; } else { // GC = -1 ('-') binSum += src[k]; binSumA += src[k]/ungroupedArea[k]; binSumB += src[k]/ungroupedBackground[k]; invSumA += 1.0/ungroupedArea[k]; invSumB += 1.0/ungroupedBackground[k]; ++nInGroup; if (!correction) outputVariance[j] += stat[k]; } ++k; if (k == ungroupedChannels) { outputSpectrum[j] = binSum; outputArea[j] = (binSumA > 0) ? binSum/binSumA : nInGroup/invSumA; outputBackground[j] = (binSumB > 0) ? binSum/binSumB : nInGroup/invSumB; } } // end while } // end if !quality set assert ( static_cast(j) == chans - 1 ); } // end case III data()->setSpectrum(outputSpectrum); data()->setAreaScale(outputArea); data()->setBackgroundScale(outputBackground); if (!correction) data()->setRawVariance(outputVariance); } size_t OGIP_92aBackCorr::read (const string& fileName, bool readFlag) { return OGIP_92aIO::read(fileName,readFlag); } void OGIP_92aBackCorr::closeSourceFiles () { OGIP_92aIO::closeFile(); } void OGIP_92aBackCorr::Swap (BackCorr& right) { OGIP_92aBackCorr& that = static_cast(right); BackCorr::Swap(right); OGIP_92aIO::swap(that); } void OGIP_92aBackCorr::copy (const BackCorr& right) { if ( typeid(*this) != typeid(right) ) throw RedAlert("*** Programmer Error: copying to wrong type ***"); OGIP_92aBackCorr __temp(static_cast(right)); Swap(__temp); } void OGIP_92aBackCorr::initialize (DataSet* parentData, size_t row, const string& fileName, size_t backCorRow) { source(parentData); sourceRow(row); runPath(XSutility::getRunPath()); int numberOfSpectra = read(fileName); CCfits::ExtHDU& ext = dataSource()->extension(extensionName()); // Test #1, check that the BackCorr file is the type the user expects if (backCorRow == 0) { // The user expects file to be Type 1. Is it? if (specNum(ext)) { string msg = fileName + '\n'; msg += "Detected to be of Type 2 format. "; msg += "Row specifier is needed.\n"; throw XspecDataIO::UnspecifiedSpectrumNumber(msg); } } else { // Type 2 file is expected if (!specNum(ext)) { string msg = '\n'+ fileName; msg += " is detected to be of Type 1 format. "; msg += "Row specifiers do not apply.\n"; throw XspecDataIO::SingleSpectrumOnly(msg); } // Test #2, for type2 file, check that the user specified row actually // exists in the file. if (backCorRow > numberOfSpectra) { std::ostringstream msg; msg << "Row number " << backCorRow << "is out of range of\n" << "file: " << fileName << '\n'; throw YellowAlert(msg.str()); } } } // Additional Declarations