//# CTPatchPanel.cc: Implementation of CTPatchPanel.h //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be adressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# #include <synthesis/CalTables/CLPatchPanel.h> #include <synthesis/CalTables/CTInterface.h> #include <synthesis/CalTables/CTIter.h> #include <casacore/scimath/Mathematics/InterpolateArray1D.h> #include <casacore/casa/Utilities/GenSort.h> #include <casacore/casa/OS/Path.h> #include <casacore/ms/MSSel/MSSelectableTable.h> #include <casacore/ms/MSSel/MSSelection.h> #include <casacore/ms/MSSel/MSSelectionTools.h> #include <casacore/casa/aips.h> #define CTPATCHPANELVERB false //#include <casa/BasicSL/Constants.h> //#include <casa/OS/File.h> #include <casacore/casa/Logging/LogMessage.h> #include <casacore/casa/Logging/LogSink.h> #include <casacore/casa/Logging/LogIO.h> using namespace casacore; namespace casa { //# NAMESPACE CASA - BEGIN CalPatchKey::CalPatchKey(IPosition keyids) : cpk_(keyids.asVector()) {} // Lexographical lessthan Bool CalPatchKey::operator<(const CalPatchKey& other) const { // This method does a lexigraphical less-than, wherein negative // elements in either operand behave as equal (reflexively not // less than) // Loop over elements in precendence order for (Int i=0;i<6;++i) { if (cpk_[i]>-1 && other.cpk_[i]>-1 && cpk_[i]!=other.cpk_[i]) // both non-negative and not equal, so evaluate element < return cpk_[i]<other.cpk_[i]; } // All apparently equal so "<" is false return false; } MSCalPatchKey::MSCalPatchKey(Int obs,Int scan,Int fld,Int ent,Int spw,Int ant) : CalPatchKey(IPosition(6,obs,scan,fld,ent,spw,ant)), obs_(obs),scan_(scan),fld_(fld),ent_(ent),spw_(spw),ant_(ant) {} // text output String MSCalPatchKey::print() const { return "obs="+(obs_<0 ? "*" : String::toString(obs_))+" " "scan="+(scan_<0 ? "*" : String::toString(scan_))+" " "fld="+(fld_<0 ? "*" : String::toString(fld_))+" " "intent="+(ent_<0 ? "*" : String::toString(ent_))+" " "spw="+(spw_<0 ? "*" : String::toString(spw_))+" " "ant="+(ant_<0 ? "*" : String::toString(ant_)); } CTCalPatchKey::CTCalPatchKey(Int clsl,Int obs,Int scan,Int fld,Int spw,Int ant) : CalPatchKey(IPosition(6,clsl,obs,scan,fld,spw,ant)), clsl_(clsl),obs_(obs),scan_(scan),fld_(fld),spw_(spw),ant_(ant) {} // text output String CTCalPatchKey::print() const { return "cl="+(clsl_<0 ? "*" : String::toString(clsl_))+" " "obs="+(obs_<0 ? "*" : String::toString(obs_))+" " "scan="+(scan_<0 ? "*" : String::toString(scan_))+" " "fld="+(fld_<0 ? "*" : String::toString(fld_))+" " "spw="+(spw_<0 ? "*" : String::toString(spw_))+" " "ant="+(ant_<0 ? "*" : String::toString(ant_)); } CalMap::CalMap() : vcalmap_() {} CalMap::CalMap(const Vector<Int>& calmap) : vcalmap_(calmap) { // cout << "calmap addresses: " << calmap.data() << " " << vcalmap_.data() // << " nrefs= " << calmap.nrefs() << " " << vcalmap_.nrefs() << endl; } Int CalMap::operator()(Int msid) const { // TBD: reconsider algorithm (maybe just return msid if over-run?) Int ncalmap=vcalmap_.nelements(); return (msid<ncalmap ? vcalmap_(msid) : (ncalmap>0 ? vcalmap_(ncalmap-1) : msid) ); // Avoid going off end } Vector<Int> CalMap::ctids(const Vector<Int>& msids) const { uInt ncalmap=vcalmap_.nelements(); // If vector map unspecified, the calling context must work it out // (for obs, scan, fld, it means no specific mapping, just use all avaiable; // for spw, ant, it means [probably] use the same id) if (ncalmap<1 || (ncalmap==1 && vcalmap_[0]<0)) return Vector<Int>(1,-1); Vector<Bool> calmask(ncalmap,false); if (msids.nelements()==1 && msids[0]<0) // MS ids indefinite, so all are ok calmask.set(true); else { // just do the ones that are requested for (uInt i=0;i<msids.nelements();++i) { const uInt& thismsid=msids(i); calmask(thismsid<ncalmap?thismsid:ncalmap-1)=true; } } Vector<Int> reqids; reqids=vcalmap_(calmask).getCompressedArray(); Int nsort=genSort(reqids,Sort::Ascending,(Sort::QuickSort | Sort::NoDuplicates)); reqids.resize(nsort,true); return reqids; } Vector<Int> CalMap::msids(Int ctid,const Vector<Int>& superset) const { uInt ncalmap=vcalmap_.nelements(); // If vector map unspecified, return [-1] which signals to calling // context that it must figure it out on its own (all or identity) if (ncalmap<1 || (ncalmap==1 && vcalmap_[0]<0) ) return Vector<Int>(1,-1); // mask vcalmap_ by specified ctid... Vector<Int> msidlist(ncalmap); indgen(msidlist); Vector<Bool> msidmask(vcalmap_==ctid); // ...and limit to superset, if specified if (superset.nelements()>0 && superset[0]>-1) for (Int i=0;i<Int(msidmask.nelements());++i) if (!anyEQ(superset,i)) msidmask[i]=false; // exclude if not in superset // Return the ms id list return msidlist(msidmask).getCompressedArray(); } FieldCalMap::FieldCalMap() : CalMap(), fieldcalmap_("") {} FieldCalMap::FieldCalMap(const Vector<Int>& calmap) : CalMap(calmap), fieldcalmap_("") {} FieldCalMap::FieldCalMap(const String fieldcalmap, const MeasurementSet& ms, const NewCalTable& ct) : CalMap(), fieldcalmap_(fieldcalmap) { if (fieldcalmap_=="nearest") // Calculate nearest map setNearestFieldMap(ms,ct); else // Attempt field selection setSelectedFieldMap(fieldcalmap,ms,ct); } FieldCalMap::FieldCalMap(const String fieldcalmap, const MeasurementSet& ms, const NewCalTable& ct, String& extfldsel) : CalMap(), fieldcalmap_(fieldcalmap) { if (fieldcalmap_=="nearest") // Calculate nearest map setNearestFieldMap(ms,ct); else // Attempt field selection setSelectedFieldMap(fieldcalmap,ms,ct,extfldsel); } void FieldCalMap::setNearestFieldMap(const MeasurementSet& ms, const NewCalTable& ct) { // Access MS and CT columns MSFieldColumns msfc(ms.field()); ROCTColumns ctc(ct); setNearestFieldMap(msfc,ctc); } void FieldCalMap::setNearestFieldMap(const NewCalTable& ctasms, const NewCalTable& ct) { // Access MS and CT columns ROCTFieldColumns msfc(ctasms.field()); ROCTColumns ctc(ct); setNearestFieldMap(msfc,ctc); } void FieldCalMap::setNearestFieldMap(const MSFieldColumns& msfc, const ROCTColumns& ctc) { // Nominally, this many field need a map Int nMSFlds=msfc.nrow(); vcalmap_.resize(nMSFlds); vcalmap_.set(-1); // no map // Discern _available_ fields in the CT Vector<Int> ctFlds; ctc.fieldId().getColumn(ctFlds); Int nAvFlds=genSort(ctFlds,Sort::Ascending,(Sort::QuickSort | Sort::NoDuplicates)); ctFlds.resize(nAvFlds,true); // If only one CT field, just use it if (nAvFlds==1) vcalmap_.set(ctFlds(0)); else { // For each MS field, find the nearest available CT field MDirection msdir,ctdir; Vector<Double> sep(nAvFlds); IPosition ipos(1,0); // get the first direction stored (no poly yet) for (Int iMSFld=0;iMSFld<nMSFlds;++iMSFld) { msdir=msfc.phaseDirMeas(iMSFld); // MS fld dir sep.set(DBL_MAX); for (Int iCTFld=0;iCTFld<nAvFlds;++iCTFld) { // Get cal field direction, converted to ms field frame ctdir=ctc.field().phaseDirMeas(ctFlds(iCTFld)); MDirection::Convert(ctdir,msdir.getRef()); sep(iCTFld)=ctdir.getValue().separation(msdir.getValue()); } // Sort separations Vector<uInt> ord; Int nsep=genSort(ord,sep,Sort::Ascending,Sort::QuickSort); //cout << iMSFld << ":" << endl; //cout << " ord=" << ord << endl; //cout << " nsep=" << nsep << endl; //cout << " sep=" << sep << " " << sep*(180.0/C::pi)<< endl; // Trap case of duplication of nearest separation if (nsep>1 && sep(ord(1))==sep(ord(0))) throw(AipsError("Found more than one field at minimum distance, can't decide!")); vcalmap_(iMSFld)=ctFlds(ord(0)); } } // cout << "FCM::setNearestFieldMap:*************** vcalmap_ = " << vcalmap_ << endl; } void FieldCalMap::setSelectedFieldMap(const String& fieldsel, const MeasurementSet& ms, const NewCalTable& ct) { // Nominally, this many fields need a map Int nMSFlds=ms.field().nrow(); vcalmap_.resize(nMSFlds); vcalmap_.set(-1); // no map CTInterface cti(ct); MSSelection mss; mss.setFieldExpr(fieldsel); TableExprNode ten=mss.toTableExprNode(&cti); Vector<Int> fieldlist=mss.getFieldList(); if (fieldlist.nelements()>1) throw(AipsError("Field mapping by selection can support only one field.")); if (fieldlist.nelements()>0) vcalmap_.set(fieldlist[0]); // cout << "FCM::setSelectedFieldMap:*************** vcalmap_ = " << vcalmap_ << endl; } void FieldCalMap::setSelectedFieldMap(const String& fieldsel, const MeasurementSet& ms, const NewCalTable& ct, String& extfldsel) { try { CTInterface cti(ct); MSSelection mss; mss.setFieldExpr(fieldsel); TableExprNode ten=mss.toTableExprNode(&cti); Vector<Int> fieldlist=mss.getFieldList(); // Nominally, don't apply selection externally extfldsel=""; if (fieldlist.nelements()>1) // trigger external selection (multi-field case; see old gainfield) extfldsel=fieldsel; else if (fieldlist.nelements()==1) { // exactly 1 means fldmap=[f]*nfld // Nominally, this many fields need a map Int nMSFlds=ms.field().nrow(); vcalmap_.resize(nMSFlds); vcalmap_.set(fieldlist[0]); } else // Field selection found no indices throw(AipsError(fieldsel+" matches no fields.")); } catch ( AipsError err ) { throw(AipsError("Field mapping by selection failure: "+err.getMesg())); } // cout << "FCM::setSelectedFieldMap:*************** vcalmap_ = " << vcalmap_ << endl; return; } ObsCalMap::ObsCalMap() : CalMap() {} ObsCalMap::ObsCalMap(const String obscalmap, const MeasurementSet& ms) : CalMap() { if (obscalmap=="self") { vcalmap_.resize(ms.observation().nrow()); indgen(vcalmap_); } else { throw(AipsError("Observation mapping failure: unrecognized keyword '" + obscalmap + "'.")); } } ScanCalMap::ScanCalMap() : CalMap() {} ScanCalMap::ScanCalMap(const String scancalmap, const MeasurementSet& ms) : CalMap() { if (scancalmap=="self") { MSColumns msc(ms); Vector<Int> msScans; msc.scanNumber().getColumn(msScans); vcalmap_.resize(max(msScans) + 1); indgen(vcalmap_); } else { throw(AipsError("Scan mapping failure: unrecognized keyword '" + scancalmap + "'.")); } } CalLibSlice::CalLibSlice(String obs, String scan, String fld, String ent, String spw, String tinterp,String finterp, Vector<Int> obsmap, Vector<Int> scanmap, Vector<Int> fldmap, Vector<Int> spwmap, Vector<Int> antmap) : obs(obs),scan(scan),fld(fld),ent(ent),spw(spw), tinterp(tinterp),finterp(finterp), obsmap(obsmap),scanmap(scanmap),fldmap(fldmap), spwmap(spwmap),antmap(antmap),extfldsel("") {} // Construct from a Record CalLibSlice::CalLibSlice(const Record& clslice, const MeasurementSet& ms, const NewCalTable& ct) : obs(),scan(),fld(),ent(),spw(), tinterp(),finterp(), obsmap(),scanmap(),fldmap(),spwmap(),antmap(), extfldsel("") { if (clslice.isDefined("obs")) { obs=clslice.asString("obs"); } if (clslice.isDefined("scan")) { scan=clslice.asString("scan"); } if (clslice.isDefined("field")) { fld=clslice.asString("field"); } if (clslice.isDefined("intent")) { ent=clslice.asString("intent"); } if (clslice.isDefined("spw")) { spw=clslice.asString("spw"); } if (clslice.isDefined("tinterp")) { tinterp=clslice.asString("tinterp"); if (tinterp=="") { tinterp="linear"; } } if (clslice.isDefined("finterp")) { finterp=clslice.asString("finterp"); } if (clslice.isDefined("obsmap")) { //cout << "obsmap.dataType() = " << clslice.dataType("obsmap") << endl; if (clslice.dataType("obsmap")==TpArrayInt) obsmap=CalMap(Vector<Int>(clslice.asArrayInt("obsmap"))); if (clslice.dataType("obsmap")==TpString) obsmap=ObsCalMap(clslice.asString("obsmap"),ms); } if (clslice.isDefined("scanmap")) { //cout << "scanmap.dataType() = " << clslice.dataType("scanmap") << endl; if (clslice.dataType("scanmap")==TpArrayInt) scanmap=CalMap(Vector<Int>(clslice.asArrayInt("scanmap"))); if (clslice.dataType("scanmap")==TpString) scanmap=ScanCalMap(clslice.asString("scanmap"),ms); } if (clslice.isDefined("fldmap")) { if (clslice.dataType("fldmap")==TpArrayInt) fldmap=FieldCalMap(clslice.asArrayInt("fldmap")); if (clslice.dataType("fldmap")==TpString) fldmap=FieldCalMap(clslice.asString("fldmap"),ms,ct,extfldsel); } if (clslice.isDefined("spwmap")) { // cout << "spwmap.dataType() = " << clslice.dataType("spwmap") << endl; if (clslice.dataType("spwmap")==TpArrayInt) spwmap=CalMap(clslice.asArrayInt("spwmap")); } if (clslice.isDefined("antmap")) { // cout << "antmap.dataType() = " << clslice.dataType("antmap") << endl; if (clslice.dataType("antmap")==TpArrayInt) antmap=CalMap(clslice.asArrayInt("antmap")); } } // Return contents as a Record Record CalLibSlice::asRecord() { Record rec; rec.define("obs",obs); rec.define("scan",scan); rec.define("field",fld); rec.define("intent",ent); rec.define("spw",spw); rec.define("tinterp",tinterp); rec.define("finterp",finterp); rec.define("obsmap",obsmap.vmap()); rec.define("scanmap",scanmap.vmap()); rec.define("fldmap",fldmap.vmap()); rec.define("spwmap",spwmap.vmap()); rec.define("antmap",antmap.vmap()); return rec; } Bool CalLibSlice::validateCLS(const Record& clslice) { String missing(""); if (!clslice.isDefined("obs")) missing+="obs "; if (!clslice.isDefined("scan")) missing+="scan "; if (!clslice.isDefined("field")) missing+="field "; if (!clslice.isDefined("intent")) missing+="intent "; if (!clslice.isDefined("spw")) missing+="spw "; if (!clslice.isDefined("tinterp")) missing+="tinterp "; if (!clslice.isDefined("finterp")) missing+="finterp "; if (!clslice.isDefined("obsmap")) missing+="obsmap "; if (!clslice.isDefined("scanmap")) missing+="scanmap "; if (!clslice.isDefined("fldmap")) missing+="fldmap "; if (!clslice.isDefined("spwmap")) missing+="swmap "; if (!clslice.isDefined("antmap")) missing+="antmap"; if (missing.length()>0) throw(AipsError(missing)); // Everything is ok if we get here return true; } String CalLibSlice::state() { ostringstream o; o << " MS: obs="+obs+" scan="+scan+" fld="+fld+" intent="+ent+" spw="+spw << endl << " CT: tinterp="+tinterp << " finterp="+finterp << endl << " obsmap=" << obsmap.vmap() << " scanmap=" << scanmap.vmap() << " fldmap="; if (extfldsel=="") o << fldmap.vmap(); else o << extfldsel+" (by selection)"; o << endl << " spwmap=" << spwmap.vmap() << " antmap=" << antmap.vmap() << endl; return String(o); } CLPPResult::CLPPResult() : result_(),resultFlag_() {} CLPPResult::CLPPResult(const IPosition& shape) : result_(shape,0.0), resultFlag_(shape,true) {} CLPPResult::CLPPResult(uInt nPar,uInt nFPar,uInt nChan,uInt nElem) : result_(nFPar,nChan,nElem,0.0), resultFlag_(nPar,nChan,nElem,true) {} CLPPResult& CLPPResult::operator=(const CLPPResult& other) { // Avoid Array deep copies! result_.reference(other.result_); resultFlag_.reference(other.resultFlag_); return *this; } void CLPPResult::resize(uInt nPar,uInt nFPar,uInt nChan,uInt nElem) { result_.resize(nFPar,nChan,nElem); result_.set(1.0); resultFlag_.resize(nPar,nChan,nElem); resultFlag_.set(true); } // (caltable only, mainly for testing) CLPatchPanel::CLPatchPanel(const String& ctname, const Record& callib, VisCalEnum::MatrixType mtype, Int nPar, const CTTIFactoryPtr cttifactoryptr) : ct_(NewCalTable::createCT(ctname,Table::Old,Table::Memory)), ctasms_(NewCalTable::createCT(ctname,Table::Old,Table::Memory)), ms_(), mtype_(mtype), isCmplx_(false), nPar_(nPar), nFPar_(nPar), nChanIn_(), freqIn_(), refFreqIn_(), nMSObs_(ct_.observation().nrow()), nMSFld_(ct_.field().nrow()), nMSSpw_(ct_.spectralWindow().nrow()), nMSAnt_(ct_.antenna().nrow()), nMSElem_(0), // set non-trivially in ctor body nCTObs_(ct_.observation().nrow()), nCTFld_(ct_.field().nrow()), nCTSpw_(ct_.spectralWindow().nrow()), nCTAnt_(ct_.antenna().nrow()), nCTElem_(0), // set non-trivially in ctor body lastresadd_(nMSSpw_,NULL), cttifactoryptr_(cttifactoryptr) // elemMap_(), // conjTab_(), { if (CTPATCHPANELVERB) cout << "CLPatchPanel::CLPatchPanel(<no MS>)" << endl; // ia1dmethod_=ftype(freqType_); // cout << "ia1dmethod_ = " << ia1dmethod_ << endl; switch(mtype_) { case VisCalEnum::GLOBAL: { throw(AipsError("CLPatchPanel::ctor: No non-Mueller/Jones yet.")); nCTElem_=1; nMSElem_=1; break; } case VisCalEnum::MUELLER: { nCTElem_=nCTAnt_*(nCTAnt_+1)/2; nMSElem_=nMSAnt_*(nMSAnt_+1)/2; break; } case VisCalEnum::JONES: { nCTElem_=nCTAnt_; nMSElem_=nMSAnt_; break; } } // How many _Float_ parameters? isCmplx_=(ct_.keywordSet().asString("ParType")=="Complex"); if (isCmplx_) // Complex input nFPar_*=2; // interpolating 2X as many Float values // Set CT channel/freq info CTSpWindowColumns ctspw(ct_.spectralWindow()); ctspw.numChan().getColumn(nChanIn_); freqIn_.resize(nCTSpw_); for (uInt iCTspw=0;iCTspw<ctspw.nrow();++iCTspw) ctspw.chanFreq().get(iCTspw,freqIn_(iCTspw),true); ctspw.refFrequency().getColumn(refFreqIn_); // Initialize maps // (ctids, msids, result arrays) // TBD //void CLPatchPanel::unpackCLinstance(CalLibSlice cls) { // Loop over callib slices // (a callib slice is one MS selection and interp setup for the present caltable) uInt nkey=callib.nfields(); Int icls=-1; for (uInt ikey=0;ikey<nkey;++ikey) { // skip if (callib.type(ikey)!=TpRecord) continue; ++icls; // The current callib slice CalLibSlice cls(callib.asRecord(ikey)); logsink_ << LogIO::NORMAL << ". " << icls << ":" << endl << cls.state() << LogIO::POST; // Apply callib instance MS selection to the "MS" (in this case it is a CT) NewCalTable clsms(ctasms_); this->selectOnCT(clsms,ctasms_,cls.obs,cls.scan,cls.fld,cls.spw,""); // Extract the MS indices we must calibrate Vector<Int> reqMSobs(1,-1),reqMSscan(1,-1),reqMSfld(1,-1); if (cls.obs.length()>0) reqMSobs.reference(this->getCLuniqueObsIds(clsms)); if (cls.scan.length()>0) reqMSscan.reference(this->getCLuniqueScanIds(clsms)); if (cls.fld.length()>0) reqMSfld.reference(this->getCLuniqueFldIds(clsms)); Vector<Int> reqMSspw(this->getCLuniqueSpwIds(clsms)); Vector<Int> reqMSant(nMSAnt_); indgen(reqMSant); // cout << "reqMSobs = " << reqMSobs << endl; // cout << "reqMSscan = " << reqMSscan << endl; // cout << "reqMSfld = " << reqMSfld << endl; // cout << "reqMSspw = " << reqMSspw << endl; // cout << "reqMSant = " << reqMSant << endl; // The intent list from the callib instance, to be used to index the msci_ Vector<Int> theseMSint(1,-1); // Details TBD // cout << "theseMSint = " << theseMSint << endl; // WE DO TIME-ISH (OBS,SCAN,FLD) AXES IN OUTER LOOPS // The net CT obs required for the MS obs according to the obsmap // (in principle, this may contain CT obs ids that aren't available) Vector<Int> reqCTobs=cls.obsmap.ctids(reqMSobs); // cout << "reqCTobs = " << reqCTobs << endl; // For each required CT obs (and thus the MS obs ids requiring it) NewCalTable obsselCT(ct_); for (uInt iCTobs=0;iCTobs<reqCTobs.nelements();++iCTobs) { Int& thisCTobs=reqCTobs(iCTobs); // Apply thisCTobs selection to the CT // (only if a meaningful obsid is specified) if (thisCTobs!=-1) this->selectOnCT(obsselCT,ct_,String::toString(thisCTobs),"","","",""); // The MS obss to be calibrated by thisCTobs (limited by the req superset) // (could be [-1], which means all) Vector<Int> theseMSobs=cls.obsmap.msids(thisCTobs,reqMSobs); if (theseMSobs.nelements()==1 && theseMSobs[0]<0) theseMSobs.reference(reqMSobs); // cout << " thisCTobs = " << thisCTobs << ": theseMSobs = " << theseMSobs << endl; // Apply theseMSobs selection to the MS // The net CT scan required for the MS scan according to the scanmap // (in principle, this may contain CT scan ids that aren't available) Vector<Int> reqCTscan=cls.scanmap.ctids(reqMSscan); // cout << "reqCTscan = " << reqCTscan << endl; // For each required CT scan (and thus the MS scan ids requiring it) NewCalTable scanselCT(obsselCT); for (uInt iCTscan=0;iCTscan<reqCTscan.nelements();++iCTscan) { Int& thisCTscan=reqCTscan(iCTscan); // Apply thisCTscan selection to the CT // (only if a meaningful scan is specified) if (thisCTscan!=-1) this->selectOnCT(scanselCT,ct_,"",String::toString(thisCTscan),"","",""); // The MS scans to be calibrated by thisCTscan (limited by the req superset) // (could be [-1], which means all) Vector<Int> theseMSscan=cls.scanmap.msids(thisCTscan,reqMSscan); if (theseMSscan.nelements()==1 && theseMSscan[0]<0) theseMSscan.reference(reqMSscan); // cout << " thisCTscan = " << thisCTscan << ": theseMSscan = " << theseMSscan << endl; // Apply theseMSscan selection to the MS // TBD: reqMSfld = ... // The net CT fld required for the MS fld according to the fldmap // (in principle, this may contain CT fld ids that aren't available) // NB: currently all [-1] or singles; "some" is TBD Vector<Int> reqCTfld=cls.fldmap.ctids(reqMSfld); // cout << " reqCTfld = " << reqCTfld << endl; // For each required CT fld: NewCalTable fldselCT(scanselCT); for (uInt iCTfld=0;iCTfld<reqCTfld.nelements();++iCTfld) { Int& thisCTfld=reqCTfld(iCTfld); // TBD: generalize to multiple fields? // Apply thisCTfld selection to the CT if (thisCTfld!=-1) this->selectOnCT(fldselCT,obsselCT,"","",String::toString(thisCTfld),"",""); // The MS flds to be calibrated by thisCTfld // (could be [-1], which means all) Vector<int> theseMSfld=cls.fldmap.msids(thisCTfld,reqMSfld); if (theseMSfld.nelements()==1 && theseMSfld[0]<0) theseMSfld.reference(reqMSfld); // cout << " thisCTfld = " << thisCTfld << ": theseMSfld = " << theseMSfld << endl; // Apply theseMSfld selection to the MS // TBD: reqMSspw = ... // ...AND HARDWARE AXES (SPW,ANT) IN INNER LOOPS // For each required _MS_ spw: NewCalTable spwselCT(fldselCT); for (uInt iMSspw=0;iMSspw<reqMSspw.nelements();++iMSspw) { Int& thisMSspw=reqMSspw(iMSspw); Int thisCTspw=cls.spwmap(thisMSspw); if (thisCTspw<0) thisCTspw=thisMSspw; // MUST BE DEFINITE! // Apply thisCTspw selection to CT this->selectOnCT(spwselCT,fldselCT,"","","",String::toString(thisCTspw),""); // Create time-dep interp result container CTCalPatchKey iclTres(icls,thisCTobs,-1,thisCTfld,thisMSspw,-1); clTres_[iclTres]=CLPPResult(nPar_,nFPar_,nChanIn_[thisCTspw],nMSElem_); NewCalTable antselCT(spwselCT); for (uInt iMSant=0;iMSant<reqMSant.nelements();++iMSant) { Int& thisMSant=reqMSant(iMSant); Int thisCTant=cls.antmap(thisMSant); if (thisCTant<0) thisCTant=thisMSant; // Apply thisCTant selection to CT this->selectOnCT(antselCT,spwselCT,"","","","",String::toString(thisCTant)); // (if null, warn and continue, or throw?) // Make the Cal Interpolator (icls is the CL slice index): CTCalPatchKey ici0(icls,thisCTobs,-1,thisCTfld,thisCTspw,thisCTant); // all CT indices CTCalPatchKey ici1(icls,thisCTobs,-1,thisCTfld,thisMSspw,thisMSant); // spw,ant are MS indices // (NB: Must use thisMSspw,thisMSant above to avoid duplication in resolved spwmap,antmap) if (ci_.count(ici1)<1) { ciname_[ici1]=ici0.print()+" rows="+String::toString(antselCT.nrow()); Array<Float> r(clTres_[iclTres].result(thisMSant)); Array<Bool> rf(clTres_[iclTres].resultFlag(thisMSant)); ci_[ici1]=(*cttifactoryptr_)(antselCT,cls.tinterp,r,rf); // cout << "Creating: CT("<<ici1.print() << ") --> CT(" << ici0.print() << ")" << endl; } else throw(AipsError("Attempted duplicate CTCalPatchKey!")); // Now distribute this CTTimeInterp1 instance to all relevant MS indices for (uInt iMSobs=0;iMSobs<theseMSobs.nelements();++iMSobs) { Int& thisMSobs=theseMSobs(iMSobs); for (uInt iMSscan=0;iMSscan<theseMSscan.nelements();++iMSscan) { Int& thisMSscan=theseMSscan(iMSscan); for (uInt iMSfld=0;iMSfld<theseMSfld.nelements();++iMSfld) { Int& thisMSfld=theseMSfld(iMSfld); for (uInt iMSint=0;iMSint<theseMSint.nelements();++iMSint) { Int& thisMSint=theseMSint(iMSint); MSCalPatchKey ims(thisMSobs,thisMSscan,thisMSfld,thisMSint,thisMSspw,thisMSant); if (msci_.count(ims)<1) { msciname_[ims]=ciname_[ici1]; msci_[ims]=ci_[ici1]; } else throw(AipsError("Attempted duplicate MSCalPatchKey!")); // cout << " Patching: MS(" << ims.print() << ") --> CT(" << ici0.print() << ")" << endl; // Link these obs,fld,ant,spw to the correct results object // (as a group over antennas; should move this out of ant loop, really) if (iMSant==0) { MSCalPatchKey imsgroup(thisMSobs,thisMSscan,thisMSfld,thisMSint,thisMSspw,-1); msTres_[imsgroup]=clTres_[iclTres]; msFres_[imsgroup]=CLPPResult(); // this will be resized on-demand ctspw_[imsgroup]=thisCTspw; finterp_[imsgroup]=cls.finterp; } } // iMSint } // iMSfld } // iMSscan } // iMSobs } // iMSant } // iMSspw } // iCTfld } // iCTscan } // iCTobs } // icls } // ctor // (MS sensitive) CLPatchPanel::CLPatchPanel(const String& ctname, const MeasurementSet& ms, const Record& callib, VisCalEnum::MatrixType mtype, Int nPar, const CTTIFactoryPtr cttifactoryptr) : ct_(NewCalTable::createCT(ctname,Table::Old,Table::Memory)), ctasms_(), // null, in this context ms_(ms), mtype_(mtype), isCmplx_(false), nPar_(nPar), nFPar_(nPar), nChanIn_(), freqIn_(), refFreqIn_(), nMSObs_(ms_.observation().nrow()), nMSFld_(ms_.field().nrow()), nMSSpw_(ms_.spectralWindow().nrow()), nMSAnt_(ms_.antenna().nrow()), nMSElem_(0), // set non-trivially in ctor body nCTObs_(ct_.observation().nrow()), nCTFld_(ct_.field().nrow()), nCTSpw_(ct_.spectralWindow().nrow()), nCTAnt_(ct_.antenna().nrow()), nCTElem_(0), // set non-trivially in ctor body lastresadd_(nMSSpw_,NULL), cttifactoryptr_(cttifactoryptr) // elemMap_(), // conjTab_(), { if (CTPATCHPANELVERB) cout << "CLPatchPanel::CLPatchPanel(<w/MS>)" << endl; // ia1dmethod_=ftype(freqType_); // cout << "ia1dmethod_ = " << ia1dmethod_ << endl; switch(mtype_) { case VisCalEnum::GLOBAL: { throw(AipsError("CLPatchPanel::ctor: No non-Mueller/Jones yet.")); nCTElem_=1; nMSElem_=1; break; } case VisCalEnum::MUELLER: { nCTElem_=nCTAnt_*(nCTAnt_+1)/2; nMSElem_=nMSAnt_*(nMSAnt_+1)/2; break; } case VisCalEnum::JONES: { nCTElem_=nCTAnt_; nMSElem_=nMSAnt_; break; } } // How many _Float_ parameters? isCmplx_=(ct_.keywordSet().asString("ParType")=="Complex"); if (isCmplx_) // Complex input nFPar_*=2; // interpolating 2X as many Float values // Set CT channel/freq info CTSpWindowColumns ctspw(ct_.spectralWindow()); ctspw.numChan().getColumn(nChanIn_); freqIn_.resize(nCTSpw_); for (uInt iCTspw=0;iCTspw<ctspw.nrow();++iCTspw) ctspw.chanFreq().get(iCTspw,freqIn_(iCTspw),true); ctspw.refFrequency().getColumn(refFreqIn_); // Initialize maps // (ctids, msids, result arrays) // TBD //void CLPatchPanel::unpackCLinstance(CalLibSlice cls) { // Loop over callib slices // (a callib slice is one MS selection and interp setup for the present caltable) uInt nkey=callib.nfields(); Int icls=-1; for (uInt ikey=0;ikey<nkey;++ikey) { // CalTable name might be needed below (eg, for log messages) String ctname=Path(ct_.getPartNames()[0]).baseName().before(".tempMemCalTable"); // Ignore non-Record members in the callib if (callib.type(ikey)!=TpRecord) continue; ++icls; // The current callib slice CalLibSlice cls(callib.asRecord(ikey),ms_,ct_); // Reference to the cal table that we'll use below NewCalTable ct0(ct_); if (cls.extfldsel!="") { // Select on the reference table try { this->selectOnCT(ct0,ct_,"","",cls.extfldsel,"",""); } catch ( MSSelectionError err ) { // Selection failed somehow: throw(AipsError("Problem selecting for multi-field field mapping ('"+cls.extfldsel+"') in caltable="+ctname+": "+err.getMesg())); } } // Apply callib instance MS selection to the incoming (selected MS) MeasurementSet clsms(ms_); // Trap Null selection exceptions, as they are not needed downstream try { this->selectOnMS(clsms,ms_,cls.obs,cls.scan,cls.fld,cls.ent,cls.spw,""); } catch ( MSSelectionNullSelection x ) { // Warn in logger that this slice doesn't match anything // in the selected MS //String msname=Path(ms_.tableName()).baseName(); String msname=Path(ms_.getPartNames()[0]).baseName(); logsink_ << LogIO::WARN << ". The following callib entry matches no data" << endl << ". in the selected MS ("+msname+") and will be ignored:" << endl << ". " << icls << ":" << endl << cls.state() << LogIO::POST; // Just jump to next slice (cal maps for this MS selection unneeded) continue; } // Report this relevant cal lib slice to the logger logsink_ << LogIO::NORMAL << ". " << icls << ":" << endl << cls.state() << LogIO::POST; // What MS indices will be calibrated by this CL instance? // TBD: we will want to calculate these within the loops below // so that per-obs and per-fld subsets will be correctly resolved // (and, e.g., nearest field can be obs-dep, etc.) // (gmoellen 2018/02/05: still true? Doesn't selection above // reduce requirements to the union of obs/fld/intent accounted // for by the current cl?) // OBS Ids in selected MS to be calibrated by this cl instance Vector<Int> reqMSobs(1,-1); // assume all, indescriminately if (cls.obs.length()>0) // if CL is obs-specific, we must not be indescriminate reqMSobs.reference(this->getMSuniqueIds(clsms,"obs")); //cout << "reqMSobs = " << reqMSobs << endl; // SCAN Ids in selected MS to be calibrated by this cl instance Vector<Int> reqMSscan(1,-1); // assume all, indescriminately if (cls.scan.length()>0) // if CL is scan-specific, we must not be indescriminate reqMSscan.reference(this->getMSuniqueIds(clsms,"scan")); //cout << "reqMSscan = " << reqMSscan << endl; // FIELD Ids in selected MS to be calibrated by this cl instance Vector<Int> reqMSfld(1,-1); // assume all, indescriminately if (cls.fld.length()>0) // if selected, maybe we only need a subset // if CL is fld-specific, we must not be indescriminate reqMSfld.reference(this->getMSuniqueIds(clsms,"fld")); //cout << "reqMSfld = " << reqMSfld << endl; // INTENT Ids in selected MS to be calibrated by this cl instance Vector<Int> reqMSint(1,-1); // assume all if (cls.ent.length()>0) reqMSint.reference(this->getMSuniqueIds(clsms,"intent")); //cout << "reqMSint = " << reqMSint << endl; Vector<Int> theseMSint(reqMSint); // SPW Ids in selected MS to be calibrated by this cl instance // We are never indescriminate about spw Vector<Int> reqMSspw(this->getMSuniqueIds(clsms,"spw")); //cout << "reqMSspw = " << reqMSspw << endl; // ANTENNA in selected MS to be calibrated by this cl instance // We are never indescriminate about ant // (For now, we will do all MS ants) Vector<Int> reqMSant(nMSAnt_); indgen(reqMSant); //cout << "reqMSant = " << reqMSant << endl; // SLICE CalTable by OBS, SCAN, FIELD, SPW, ANT, and map to // the corresponding MS indicies // WE DO TIME-ISH (OBS,SCAN,FLD) AXES IN OUTER LOOPS NewCalTable obsselCT(ct0); // The net CT obs required for the MS obs according to the obsmap // We will create separate interpolator groups for each Vector<Int> reqCTobs=cls.obsmap.ctids(reqMSobs); //cout << "reqCTobs = " << reqCTobs << endl; // For each required CT obs (and thus the MS obs ids requiring it) for (uInt iCTobs=0;iCTobs<reqCTobs.nelements();++iCTobs) { Int& thisCTobs=reqCTobs(iCTobs); // The MS OBSs (subset of reqMSobs) to be calibrated by thisCTobs // (could be [-1], which means all) Vector<Int> theseMSobs=cls.obsmap.msids(thisCTobs,reqMSobs); if (theseMSobs.nelements()==1 && theseMSobs[0]<0) theseMSobs.reference(reqMSobs); //cout << " thisCTobs = " << thisCTobs << ": theseMSobs = " << theseMSobs << endl; // Apply thisCTobs selection to the CT // (only if a meaningful obsid is specified) try { if (thisCTobs!=-1) this->selectOnCT(obsselCT,ct0,String::toString(thisCTobs),"","","",""); } catch (...) { // MSSelectionNullSelection x ) { // Required CT obs does not exist in the caltable recordBadMSIndices(theseMSobs,reqMSscan,reqMSfld,reqMSint,Vector<Int>(1,-1)); // all spws continue; // jump to next CT obs } // The net CT scan required for the MS scan according to the scanmap // We will create separate interpolator groups for each Vector<Int> reqCTscan=cls.scanmap.ctids(reqMSscan); //cout << "reqCTscan = " << reqCTscan << endl; // For each required CT scan (and thus the MS scan ids requiring it) NewCalTable scanselCT(obsselCT); for (uInt iCTscan=0;iCTscan<reqCTscan.nelements();++iCTscan) { Int& thisCTscan=reqCTscan(iCTscan); // The MS SCANs (subset of reqMSscan) to be calibrated by thisCTscan // (could be [-1], which means all) Vector<Int> theseMSscan=cls.scanmap.msids(thisCTscan,reqMSscan); if (theseMSscan.nelements()==1 && theseMSscan[0]<0) theseMSscan.reference(reqMSscan); //cout << " thisCTscan = " << thisCTscan << ": theseMSscan = " << theseMSscan << endl; // Apply thisCTscan selection to the CT // (only if a meaningful scanid is specified) try { if (thisCTscan!=-1) this->selectOnCT(scanselCT,obsselCT,"",String::toString(thisCTscan),"","",""); } catch (...) { // MSSelectionNullSelection x ) { // Required CT scan does not exist in the caltable recordBadMSIndices(reqMSobs,theseMSscan,reqMSfld,reqMSint,Vector<Int>(1,-1)); // all spws continue; // jump to next CT scan } // The net CT fld required for the MS fld according to the fldmap // NB: currently all [-1] or singles; "some" is TBD Vector<Int> reqCTfld=cls.fldmap.ctids(reqMSfld); //cout << " reqCTfld = " << reqCTfld << endl; // For each required CT fld: NewCalTable fldselCT(scanselCT); for (uInt iCTfld=0;iCTfld<reqCTfld.nelements();++iCTfld) { Int& thisCTfld=reqCTfld(iCTfld); // TBD: generalize to multiple fields? // The MS flds to be calibrated by thisCTfld // (could be [-1], which means all) Vector<int> theseMSfld=cls.fldmap.msids(thisCTfld,reqMSfld); if (theseMSfld.nelements()==1 && theseMSfld[0]<0) theseMSfld.reference(reqMSfld); //cout << " thisCTfld = " << thisCTfld << ": theseMSfld = " << theseMSfld << endl; // Apply thisCTfld selection to the CT try { if (thisCTfld!=-1) this->selectOnCT(fldselCT,scanselCT,"","",String::toString(thisCTfld),"",""); } catch (...) { // MSSelectionNullSelection x ) { // Required CT fld does not exist in the caltable (for current CT obs, scan) recordBadMSIndices(theseMSobs,theseMSscan,theseMSfld,reqMSint,Vector<Int>(1,-1)); // all spws continue; // jump to next fld } // ...AND HARDWARE AXES (SPW,ANT) IN INNER LOOPS // For each required _MS_ spw: NewCalTable spwselCT(fldselCT); for (uInt iMSspw=0;iMSspw<reqMSspw.nelements();++iMSspw) { Int& thisMSspw=reqMSspw(iMSspw); Int thisCTspw=cls.spwmap(thisMSspw); if (thisCTspw<0) thisCTspw=thisMSspw; // MUST BE DEFINITE! //cout << " thisCTspw=" << thisCTspw << "--> thisMSspw="<<thisMSspw<<endl; // Apply thisCTspw selection to CT try { this->selectOnCT(spwselCT,fldselCT,"","","",String::toString(thisCTspw),""); } catch (...) { // MSSelectionNullSelection x ) { // Required CT spw does not exist in the caltable (for current CT obs, scan, fld) recordBadMSIndices(theseMSobs,theseMSscan,theseMSfld,reqMSint,Vector<Int>(1,thisMSspw)); // current spw continue; // jump to next spw } // If this selection fails (zero rows), and exception is thrown. // What is the state of antselCT? // Is it still the unselected-upon spwselCT? // Or is an empty table? // Create time-dep interp result container // Indexed by CTobs, CTscan, CTfld, MSspw (for all antennas) CTCalPatchKey iclTres(icls,thisCTobs,thisCTscan,thisCTfld,thisMSspw,-1); clTres_[iclTres]=CLPPResult(nPar_,nFPar_,nChanIn_[thisCTspw],nMSElem_); NewCalTable antselCT(spwselCT); Bool doLinkResults(true); // initialize true, so it will happen if relevant code reached for (uInt iMSant=0;iMSant<reqMSant.nelements();++iMSant) { Int& thisMSant=reqMSant(iMSant); Int thisCTant=cls.antmap(thisMSant); if (thisCTant<0) thisCTant=thisMSant; // Apply thisCTant selection to CT try { this->selectOnCT(antselCT,spwselCT,"","","","",String::toString(thisCTant)); } catch ( MSSelectionNullSelection x ) { // Log a warning about the missing antenna logsink_ << LogIO::WARN << " Found no calibration for MS ant Id=" << thisMSant << " (CT ant Id=" << thisCTant << ")" << " in MS spw Id=" << thisMSspw << " (CT spw Id=" << thisCTspw << ") (" << ctname << ")" << LogIO::POST; // Step to next antenna continue; } // Make the Cal Interpolator (icls is the CL slice index): CTCalPatchKey ici0(icls,thisCTobs,thisCTscan,thisCTfld,thisCTspw,thisCTant); // all CT indices CTCalPatchKey ici1(icls,thisCTobs,thisCTscan,thisCTfld,thisMSspw,thisMSant); // spw,ant are MS indices // (NB: Must use thisMSspw,thisMSant above to avoid duplication in resolved spwmap,antmap) if (ci_.count(ici1)<1) { ciname_[ici1]=ici0.print()+" rows="+String::toString(antselCT.nrow()); Array<Float> r(clTres_[iclTres].result(thisMSant)); Array<Bool> rf(clTres_[iclTres].resultFlag(thisMSant)); ci_[ici1]=(*cttifactoryptr_)(antselCT,cls.tinterp,r,rf); //if (iMSant==0) cout << " Creating: CT("<<ici1.print() << ") --> CT(" << ici0.print() << ") (all antennas)" << endl; } else throw(AipsError("Attempted duplicate CTCalPatchKey!")); // Now distribute this CTTimeInterp1 instance to all relevant MS indices for (uInt iMSobs=0;iMSobs<theseMSobs.nelements();++iMSobs) { Int& thisMSobs=theseMSobs(iMSobs); for (uInt iMSscan=0;iMSscan<theseMSscan.nelements();++iMSscan) { Int& thisMSscan=theseMSscan(iMSscan); for (uInt iMSfld=0;iMSfld<theseMSfld.nelements();++iMSfld) { Int& thisMSfld=theseMSfld(iMSfld); for (uInt iMSint=0;iMSint<theseMSint.nelements();++iMSint) { Int& thisMSint=theseMSint(iMSint); MSCalPatchKey ims(thisMSobs,thisMSscan,thisMSfld,thisMSint,thisMSspw,thisMSant); if (msci_.count(ims)<1) { msciname_[ims]=ciname_[ici1]; msci_[ims]=ci_[ici1]; } else throw(AipsError("Attempted duplicate MSCalPatchKey!")); //if (doLinkResults) // cout << " Patching: MS(" << ims.print() << ") --> CT(" << ici0.print() << ")" << endl; // Link these obs,scan,fld,ant,spw to the correct results object // (as a group over antennas; should move this out of ant loop, really) if (doLinkResults) { MSCalPatchKey imsgroup(thisMSobs,thisMSscan,thisMSfld,thisMSint,thisMSspw,-1); msTres_[imsgroup]=clTres_[iclTres]; msFres_[imsgroup]=CLPPResult(); // this will be resized on-demand ctspw_[imsgroup]=thisCTspw; finterp_[imsgroup]=cls.finterp; } } // iMSint } // iMSfld } // iMSscan } // iMSobs doLinkResults = False; // Don't do it again } // iMSant } // iMSspw } // iCTfld } // iCTscan } // iCTobs } // icls } // ctor void CLPatchPanel::recordBadMSIndices(const Vector<Int>& obs, const Vector<Int>& scan, const Vector<Int>& fld, const Vector<Int>& ent, const Vector<Int>& spw) { // Record bad _MS_ indices for (uInt iobs=0;iobs<obs.nelements();++iobs) { for (uInt iscan=0;iscan<scan.nelements();++iscan) { for (uInt ifld=0;ifld<fld.nelements();++ifld) { for (uInt ient=0;ient<ent.nelements();++ient) { for (uInt ispw=0;ispw<spw.nelements();++ispw) { MSCalPatchKey ims(obs[iobs],scan[iscan],fld[ifld],ent[ient],spw[ispw],-1); // All ants if (badmsciname_.count(ims)<1) { badmsciname_[ims]=ims.print(); //cout << " Bad MS indices: " << ims.print() << endl; } } } } } } return; } void CLPatchPanel::selectOnCT(NewCalTable& ctout,const NewCalTable& ctin, const String& obs, const String& scan, const String& fld, const String& spw, const String& ant1) { String taql(""); if (ant1.length()>0) taql="ANTENNA1=="+ant1; // cout << "taql = >>" << taql << "<<" << endl; // Forward to generic method (sans intent) CTInterface cti(ctin); this->selectOnCTorMS(ctout,cti,obs,scan,fld,"",spw,"",taql); } void CLPatchPanel::selectOnMS(MeasurementSet& msout,const MeasurementSet& msin, const String& obs, const String& scan, const String& fld, const String& ent, const String& spw, const String& ant) { // Forward to generic method MSInterface msi(msin); this->selectOnCTorMS(msout,msi,obs,scan,fld,ent,spw,ant,""); } void CLPatchPanel::selectOnCTorMS(Table& ctout,MSSelectableTable& msst, const String& obs, const String& scan, const String& fld, const String& ent, const String& spw, const String& ant, const String& taql) { MSSelection mss; if (obs.length()>0) mss.setObservationExpr(obs); if (scan.length()>0) mss.setScanExpr(scan); if (fld.length()>0) mss.setFieldExpr(fld); if (ent.length()>0) mss.setStateExpr(ent); if (spw.length()>0) mss.setSpwExpr(spw); if (ant.length()>0) mss.setAntennaExpr(ant); // this will not behave as required for Jones caltables (its bl-based selection!) if (taql.length()>0) mss.setTaQLExpr(taql); TableExprNode ten=mss.toTableExprNode(&msst); getSelectedTable(ctout,*(msst.table()),ten,""); } Vector<Int> CLPatchPanel::getCLuniqueIds(NewCalTable& ct, String vcol) { ROCTMainColumns ctmc(ct); // Extract the requested column as a Vector Vector<Int> colv; if (vcol=="obs") ctmc.obsId().getColumn(colv); else if (vcol=="scan") ctmc.scanNo().getColumn(colv); else if (vcol=="fld") ctmc.fieldId().getColumn(colv); else if (vcol=="spw") ctmc.spwId().getColumn(colv); else throw(AipsError("Unsupported column in CLPatchPanel::getCLuniqueIds")); // Reduce to a unique list uInt nuniq=genSort(colv,Sort::Ascending,(Sort::QuickSort | Sort::NoDuplicates)); colv.resize(nuniq,true); return colv; } Vector<Int> CLPatchPanel::getMSuniqueIds(MeasurementSet& ms, String which) { MSColumns msc(ms); // Extract the requested column as a Vector Vector<Int> colv; if (which=="obs") msc.observationId().getColumn(colv); else if (which=="scan") msc.scanNumber().getColumn(colv); else if (which=="fld") msc.fieldId().getColumn(colv); else if (which=="intent") msc.stateId().getColumn(colv); else if (which=="spw") msc.dataDescId().getColumn(colv); // these are actually ddids! else throw(AipsError("Unsupported column in CLPatchPanel::getCLuniqueIds")); // Reduce to a unique list uInt nuniq=genSort(colv,Sort::Ascending,(Sort::QuickSort | Sort::NoDuplicates)); colv.resize(nuniq,true); // In case of spw, translate from ddid if (which=="spw") { Vector<Int> spwids; msc.dataDescription().spectralWindowId().getColumn(spwids); for (uInt i=0;i<colv.nelements();++i) colv[i]=spwids[colv[i]]; } // return the value return colv; } // Destructor CLPatchPanel::~CLPatchPanel() { if (CTPATCHPANELVERB) cout << "CLPatchPanel::~CLPatchPanel()" << endl; // Delete the atomic interpolators for (std::map<CTCalPatchKey,CTTimeInterp1*>::iterator it=ci_.begin(); it!=ci_.end(); ++it) delete it->second; } // Is specific calibration explicitly available for a obs,scan,fld,intent,spw,ant combination? Bool CLPatchPanel::calAvailable(casacore::Int msobs, casacore::Int msscan, casacore::Int msfld, casacore::Int msent, casacore::Int msspw, casacore::Int msant) { const MSCalPatchKey key(msobs,msscan,msfld,msent,msspw,msant); Bool avail=msTres_.count(key)>0; // if (!avail) { // cout << Path(ct_.tableName()).baseName().before(".tempMem") << " stepped over: " << key.print() << endl; // } return avail; } // The specified MS indices are "OK" if not recorded as expected-but-absent as per // the callibrary specification. Expected-but-absent occurs when a CL entry // indicates (even implicitly) that the MS index combination is supported but // the required CalTable indices (via *map params) are not actually available // in the CalTable, e.g., a missing spw. // Such data cannot be calibrated and this CL cannot be agnostic about it... // Note that MSIndicesOK can return true for MS index combinations for which // calibration is not actually available, if the CL entry does not purport // to support calibrating them. In such cases, calAvailable() returns false, and // this CL is agnostic w.r.t. such data and lets it pass thru unchanged. // Returns TRUE when the specified MS indices are calibrateable or passable. Bool CLPatchPanel::MSIndicesOK(casacore::Int msobs, casacore::Int msscan, casacore::Int msfld, casacore::Int msent, casacore::Int msspw, casacore::Int msant) { const MSCalPatchKey key(msobs,msscan,msfld,msent,msspw,msant); Bool bad=badmsciname_.count(key)>0; // if (bad) { // cout << Path(ct_.tableName()).baseName().before(".tempMem") << " should but can't calibrate: " << key.print() << endl; // } // Return TRUE if NOT bad return !bad; } Bool CLPatchPanel::interpolate(Cube<Complex>& resultC, Cube<Bool>& resFlag, Int msobs, Int msscan, Int msfld, Int msent, Int msspw, Double time, Double freq) { Bool newcal(false); // resultC and resFlag will be unchanged if newcal remains false Cube<Float> f; // temporary to reference Float interpolation result newcal=interpolate(f,resFlag,msobs,msscan,msfld,msent,msspw,time,freq); if (newcal) // convert to complex and have resultC take over ownership resultC.reference(RIorAPArray(f).c()); return newcal; // If T, calling scope should act nontrivially, if necessary } Bool CLPatchPanel::interpolate(Cube<Complex>& resultC, Cube<Bool>& resFlag, Int msobs, Int msscan, Int msfld, Int msent, Int msspw, Double time, const Vector<Double>& freq) { Bool newcal(false); // resultC and resFlag will be unchanged if newcal remains false Cube<Float> f; // temporary to reference Float interpolation result newcal=interpolate(f,resFlag,msobs,msscan,msfld,msent,msspw,time,freq); if (newcal) // convert to complex and have resultC take over ownership resultC.reference(RIorAPArray(f).c()); return newcal; // If T, calling scope should act nontrivially, if necessary } Bool CLPatchPanel::interpolate(Cube<Float>& resultR, Cube<Bool>& resFlag, Int msobs, Int msscan, Int msfld, Int msent, Int msspw, Double time, Double freq) { if (CTPATCHPANELVERB) cout << "CLPatchPanel::interpolate(...)" << endl; // NB: resultR and resFlag will be unchanged if newcal remains false Bool newcal(false); // Suppled arrays reference the result (if available) MSCalPatchKey ires(msobs,msscan,msfld,msent,msspw,-1); // Trap lack of available calibration for requested obs,fld,intent,spw if (msTres_.count(ires)==0) { throw(AipsError("No calibration arranged for "+ires.print()+ " in callib for caltable="+ Path(ct_.tableName()).baseName().before(".tempMemCalTable") )); } // If result_ is at a new address (cf last time, this spw), treat as new // TBD: do this is a less obscure way... (e.g., invent the CLCalGroup(nant)) if (lastresadd_(msspw)!=msTres_[ires].result_.data()) newcal=true; lastresadd_(msspw)=msTres_[ires].result_.data(); // Loop over _output_ elements for (Int iMSElem=0;iMSElem<nMSElem_;++iMSElem) { // Call fully _patched_ time-interpolator, keeping track of 'newness' // fills ctTres_ implicitly MSCalPatchKey ims(msobs,msscan,msfld,msent,msspw,iMSElem); if (msci_.count(ims)>0) { if (freq>0.0) newcal|=msci_[ims]->interpolate(time,freq); else newcal|=msci_[ims]->interpolate(time); } } if (newcal) { resultR.reference(msTres_[ires].result_); resFlag.reference(msTres_[ires].resultFlag_); } return newcal; // If true, calling scope should act } Bool CLPatchPanel::interpolate(Cube<Float>& resultR, Cube<Bool>& resFlag, Int msobs, Int msscan, Int msfld, Int msent, Int msspw, Double time, const Vector<Double>& freq) { if (CTPATCHPANELVERB) cout << "CLPatchPanel::interpolate(Cube<F>,...,Vector<D>)" << endl; // NB: resultR and resFlag will be unchanged if newcal remains false Bool newcal(false); // Suppled arrays reference the result (if available) MSCalPatchKey ires(msobs,msscan,msfld,msent,msspw,-1); // Trap lack of available calibration for requested obs,scan,fld,intent,spw if (msTres_.count(ires)==0) { throw(AipsError("No calibration arranged for "+ires.print()+ " in callib for caltable="+ Path(ct_.tableName()).baseName().before(".tempMemCalTable") )); } // If result_ is at a new address (cf last time, this msspw), treat as new // TBD: do this is a less obscure way... if (lastresadd_(msspw)!=msTres_[ires].result_.data()) newcal=true; lastresadd_(msspw)=msTres_[ires].result_.data(); // Sometimes we need to force the freq interp, even if the time-interp isn't new Bool forceFinterp=false || newcal; // The follow occurs unnecessarily in mosaics when newcal=false (i.e., when resampleInFreq won't be called below) uInt nMSChan=freq.nelements(); // The number of requested channels if (msFres_.count(ires)>0) { if (msFres_[ires].result_.nelements()==0 || msFres_[ires].result(0).ncolumn()!=nMSChan) { msFres_[ires].resize(nPar_,nFPar_,nMSChan,nMSElem_); } } else throw(AipsError("Error finding msFres_ in CLPatchPanel::interpolate(C<F>,...,V<D>)")); // Loop over _output_ antennas for (Int iMSElem=0;iMSElem<nMSElem_;++iMSElem) { // Call time interpolation calculation; resample in freq if new // (fills msTRes_ implicitly) MSCalPatchKey ims(msobs,msscan,msfld,msent,msspw,iMSElem); if (msci_.count(ims)>0) { if (msci_[ims]->interpolate(time) || forceFinterp) { // Resample in frequency Matrix<Float> fR( msFres_[ires].result(iMSElem) ); Matrix<Bool> fRflg( msFres_[ires].resultFlag(iMSElem) ); Matrix<Float> tR( msTres_[ires].result(iMSElem) ); Matrix<Bool> tRflg( msTres_[ires].resultFlag(iMSElem) ); resampleInFreq(fR,fRflg,freq,tR,tRflg,freqIn_(ctspw_[ires]),finterp_[ires]); // Calibration is new newcal=true; } } } if (newcal) { // Supplied arrays to reference the result resultR.reference(msFres_[ires].result_); resFlag.reference(msFres_[ires].resultFlag_); } return newcal; } Bool CLPatchPanel::getTresult(Cube<Float>& resultR, Cube<Bool>& resFlag, Int obs, Int scan, Int fld, Int ent, Int spw) { MSCalPatchKey mskey(obs,scan,fld,ent,spw,-1); if (msTres_.count(mskey)==0) throw(AipsError("No calibration arranged for "+mskey.print()+ " in callib for caltable="+ Path(ct_.tableName()).baseName().before(".tempMemCalTable") )); // Reference the requested Cube resultR.reference(msTres_[mskey].result_); resFlag.reference(msTres_[mskey].resultFlag_); return true; } void CLPatchPanel::listmappings() { cout << "CalTable: " << ct_.tableName() << endl; cout << "There are " << ci_.size() << " cal interpolation engines." << endl; cout << "There are " << msci_.size() << " unique MS id combinations mapped to them:" << endl; for (std::map<MSCalPatchKey,String>::iterator it=msciname_.begin(); it!=msciname_.end(); ++it) cout << "MS (" << it->first.print() << ") --> CL (" << it->second << ")" << endl; cout << endl << "There are " << badmsciname_.size() << " expected but ABSENT MS id combinations (all ants):" << endl; for (std::map<MSCalPatchKey,String>::iterator it=badmsciname_.begin(); it!=badmsciname_.end(); ++it) cout << "MS (" << it->first.print() << ")" << endl; cout << endl; } // Report state void CLPatchPanel::state() { if (CTPATCHPANELVERB) cout << "CLPatchPanel::state()" << endl; cout << "-state--------" << endl; cout << " ct_ = " << ct_.tableName() << endl; cout << boolalpha; cout << " isCmplx_ = " << isCmplx_ << endl; cout << " nPar_ = " << nPar_ << endl; cout << " nFPar_ = " << nFPar_ << endl; cout << " nMSObs_ = " << nMSObs_ << endl; cout << " nMSFld_ = " << nMSFld_ << endl; cout << " nMSSpw_ = " << nMSSpw_ << endl; cout << " nMSAnt_ = " << nMSAnt_ << endl; cout << " nMSElem_ = " << nMSElem_ << endl; cout << " nCTObs_ = " << nCTObs_ << endl; cout << " nCTFld_ = " << nCTFld_ << endl; cout << " nCTSpw_ = " << nCTSpw_ << endl; cout << " nCTAnt_ = " << nCTAnt_ << endl; cout << " nCTElem_ = " << nCTElem_ << endl; // cout << " timeType_ = " << timeType_ << endl; // cout << " freqType_ = " << freqType_ << endl; } // Resample in frequency void CLPatchPanel::resampleInFreq(Matrix<Float>& fres,Matrix<Bool>& fflg,const Vector<Double>& fout, Matrix<Float>& tres,Matrix<Bool>& tflg,const Vector<Double>& fin, String finterp) { if (CTPATCHPANELVERB) cout << " CLPatchPanel::resampleInFreq(...)" << endl; // if no good solutions coming in, return flagged if (nfalse(tflg)==0) { fflg.set(true); return; } Int flparmod=nFPar_/nPar_; // for indexing the flag Matrices on the par axis Bool unWrapPhase=flparmod>1; // cout << "nFPar_,nPar_,flparmod = " << nFPar_ << "," << nPar_ << "," << flparmod << endl; fres=0.0; for (Int ifpar=0;ifpar<nFPar_;++ifpar) { // Slice by par (each Matrix row) Vector<Float> fresi(fres.row(ifpar)), tresi(tres.row(ifpar)); Vector<Bool> fflgi(fflg.row(ifpar/flparmod)), tflgi(tflg.row(ifpar/flparmod)); // Mask time result by flags Vector<Double> mfin=fin(!tflgi).getCompressedArray(); if (mfin.nelements()==0) { // cout << ifpar << " All chans flagged!" << endl; // Everything flagged this par // Just flag, zero and go on to the next one fflgi.set(true); fresi.set(0.0); continue; } mfin/=1.0e9; // in GHz Vector<Float> mtresi=tresi(!tflgi).getCompressedArray(); // Trap case of same in/out frequencies if (fout.nelements()==mfin.nelements() && allNear(fout,mfin,1.e-10)) { // Just copy fresi=mtresi; fflgi.set(false); // none are flagged continue; } if (ifpar%2==1 && unWrapPhase) { for (uInt i=1;i<mtresi.nelements();++i) { while ( (mtresi(i)-mtresi(i-1))>C::pi ) mtresi(i)-=C::_2pi; while ( (mtresi(i)-mtresi(i-1))<-C::pi ) mtresi(i)+=C::_2pi; } } // TBD: ensure phases tracked on freq axis... // TBD: handle flags carefully! // (i.e., flag gaps larger than user's "freach") // For now,just unset them fflgi.set(false); // Set flags carefully resampleFlagsInFreq(fflgi,fout,tflgi,fin,finterp); // Always use nearest on edges // TBD: trap cases where frequencies don't overlap at all // (fout(hi)<mfin(0) || fout(lo)> mfin(ihi))..... already OK (lo>hi)? // TBD: optimize the following by forming Slices in the // while loops and doing Array assignment once afterwords Int nfreq=fout.nelements(); Int lo=0; Int hi=fresi.nelements()-1; Double inlo(mfin(0)); Int ihi=mtresi.nelements()-1; Double inhi(mfin(ihi)); // Handle 'nearest' extrapolation in sideband-dep way Bool inUSB(inhi>inlo); Bool outUSB(fout(hi)>fout(lo)); if (inUSB) { if (outUSB) { while (lo<nfreq && fout(lo)<=inlo) fresi(lo++)=mtresi(0); while (hi>-1 && fout(hi)>=inhi) fresi(hi--)=mtresi(ihi); } else { // "outLSB" while (lo<nfreq && fout(lo)>=inhi) fresi(lo++)=mtresi(ihi); while (hi>-1 && fout(hi)<=inlo) fresi(hi--)=mtresi(0); } } else { // "inLSB" if (outUSB) { while (lo<nfreq && fout(lo)<=inhi) fresi(lo++)=mtresi(ihi); while (hi>-1 && fout(hi)>=inlo) fresi(hi--)=mtresi(0); } else { // "outLSB" while (lo<nfreq && fout(lo)>=inlo) fresi(lo++)=mtresi(0); while (hi>-1 && fout(hi)<=inhi) fresi(hi--)=mtresi(ihi); } } // cout << "lo, hi = " << lo << ","<<hi << endl; if (lo>hi) continue; // Frequencies didn't overlap, nearest was used // Use InterpolateArray1D to fill in the middle IPosition blc(1,lo), trc(1,hi); Vector<Float> slfresi(fresi(blc,trc)); Vector<Double> slfout(fout(blc,trc)); InterpolateArray1D<Double,Float>::interpolate(slfresi,slfout,mfin,mtresi,this->ftype(finterp)); } } void CLPatchPanel::resampleFlagsInFreq(Vector<Bool>& flgout,const Vector<Double>& fout, Vector<Bool>& flgin,const Vector<Double>& fin, String finterp) { // cout << "resampleFlagsInFreq" << endl; #define NEAREST InterpolateArray1D<Double,Float>::nearestNeighbour #define LINEAR InterpolateArray1D<Double,Float>::linear #define CUBIC InterpolateArray1D<Double,Float>::cubic #define SPLINE InterpolateArray1D<Double,Float>::spline // Handle chan-dep flags if (finterp.contains("flag")) { InterpolateArray1D<Double,Float>::InterpolationMethod interpMeth=this->ftype(finterp); Vector<Double> finGHz=fin/1e9; // Determine implied mode-dep flags indexed by channel registration uInt nflg=flgin.nelements(); Vector<Bool> flreg(nflg,false); switch (interpMeth) { case NEAREST: { // Just use input flags flreg.reference(flgin); break; } case LINEAR: { for (uInt i=0;i<nflg-1;++i) flreg[i]=(flgin[i] || flgin[i+1]); flreg[nflg-1]=flreg[nflg-2]; break; } case CUBIC: case SPLINE: { for (uInt i=1;i<nflg-2;++i) flreg[i]=(flgin[i-1] || flgin[i] || flgin[i+1] || flgin[i+2]); flreg[0]=flreg[1]; flreg[nflg-2]=flreg[nflg-3]; flreg[nflg-1]=flreg[nflg-3]; break; } } // Now step through requested chans, setting flags uInt ireg=0; uInt nflgout=flgout.nelements(); for (uInt iflgout=0;iflgout<nflgout;++iflgout) { // Find nominal registration (the _index_ just left) Bool exact(false); ireg=binarySearch(exact,finGHz,fout(iflgout),nflg,0); // If registration is exact, assign verbatim // NB: the calibration value calculation occurs agnostically w.r.t. flags, // so the calculated value should also match // TBD: Add "|| near(finGHz[ireg],fout(iflgout),1e-10) in case "exact" has // precision issues? if (exact) { flgout[iflgout]=flgin[ireg]; continue; } // Not exact, so carefully handle bracketing if (ireg>0) ireg-=1; ireg=min(ireg,nflg-1); //while (finGHz(ireg)<=fout(iflgout) && ireg<nflg-1) { // ireg+=1; // USB specific! //} //if (ireg>0 && finGHz(ireg)!=fout(iflgout)) --ireg; // registration is one sample prior // refine registration by interp type switch (interpMeth) { case NEAREST: { // nearest might be forward sample if ( ireg<(nflg-1) && abs(fout[iflgout]-finGHz[ireg])>abs(finGHz[ireg+1]-fout[iflgout]) ) ireg+=1; break; } case LINEAR: { if (ireg==(nflg-1)) // need one more sample to the right ireg-=1; break; } case CUBIC: case SPLINE: { if (ireg==0) ireg+=1; // need one more sample to the left if (ireg>(nflg-3)) ireg=nflg-3; // need two more samples to the right break; } } // Assign effective flag flgout[iflgout]=flreg[ireg]; /* cout << iflgout << " " << ireg << " " << flreg[ireg] << endl; */ } } else // We are interp/extrap-olating gaps absolutely flgout.set(false); } InterpolateArray1D<Double,Float>::InterpolationMethod CLPatchPanel::ftype(String& strtype) { if (strtype.contains("nearest")) return InterpolateArray1D<Double,Float>::nearestNeighbour; if (strtype.contains("linear")) return InterpolateArray1D<Double,Float>::linear; if (strtype.contains("cubic")) return InterpolateArray1D<Double,Float>::cubic; if (strtype.contains("spline")) return InterpolateArray1D<Double,Float>::spline; // cout << "Using linear for freq interpolation as last resort." << endl; return InterpolateArray1D<Double,Float>::linear; } } //# NAMESPACE CASA - END