Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-18 09:17:53

0001 
0002 
0003 #include "AnnularFieldSim.h"
0004 
0005 #include <Rtypes.h>
0006 #include <TCanvas.h>
0007 #include <TFile.h>
0008 #include <TFileCollection.h>
0009 #include <TFileInfo.h>
0010 #include <TH3.h>
0011 #include <THashList.h>
0012 #include <TTree.h>
0013 
0014 #include <format>
0015 #include <string>
0016 
0017 R__LOAD_LIBRARY(libfieldsim.so)
0018 
0019 std::string field_string;
0020 std::string lookup_string;
0021 
0022 AnnularFieldSim *SetupDefaultSphenixTpc(bool twinMe=false, bool useSpacecharge=true, float xshift=0, float yshift=0, float zshift=0);
0023 AnnularFieldSim *SetupDigitalCurrentSphenixTpc(bool twinMe=false, bool useSpacecharge=true);
0024 void TestSpotDistortion(AnnularFieldSim *t);
0025 void SurveyFiles(TFileCollection* filelist);
0026 
0027   
0028 void generate_distortion_map(const char *inputname, const char* gainName, const char *outputname, const char *ibfName, const char *primName, bool hasSpacecharge=true, bool isAdc=false, int  /*nSteps*/=500, bool scanSteps=false, float xshift=0, float yshift=0, float zshift=0){
0029   std::cout << "generating single distortion map.  Caution:  This is vastly less efficient than re-using the tpc model once it is set up" << std::endl;
0030  
0031   bool hasTwin=true; //this flag prompts the code to build both a positive-half and a negative-half for the TPC, reusing as much of the calculations as possible.  It is more efficient to 'twin' one half of the TPC than to recalculate/store the greens functions for both.
0032   TString gainHistName[2]={"hIbfGain_posz","hIbfGFain_negz"};
0033 
0034   //and some parameters of the files we're loading:
0035   bool usesChargeDensity=false; //true if source hists contain charge density per bin.  False if hists are charge per bin.
0036   float tpc_chargescale=1.6e-19;//Coulombs per bin unit.
0037   float spacecharge_cm_per_axis_unit=0.1;//cm per histogram axis unit (mm), matching the MDC2 sample from Evgeny.
0038   
0039   //file names we'll be filling as we go:
0040   TFile *infile;
0041   TFile *gainfile;
0042  
0043   TString sourcefilename=inputname;
0044   std::string outputfilename=outputname;
0045 
0046   //now build the time-consuming part:
0047   AnnularFieldSim *tpc;
0048     tpc=SetupDefaultSphenixTpc(hasTwin,hasSpacecharge, xshift, yshift, zshift);//loads the lookup, fields, etc.
0049  
0050   //and the location to plot the fieldslices about:
0051  TVector3 pos=0.5*(tpc->GetOuterEdge()+tpc->GetInnerEdge());;
0052   pos.SetPhi(3.14159);
0053   
0054   infile=TFile::Open(sourcefilename.Data(),"READ");
0055 
0056 
0057   //the total charge is prim + IBF
0058   //if we are doing ADCs, though, we only read the one.
0059   TH3* hCharge=(TH3*)(infile->Get(ibfName));
0060   if (!isAdc){
0061     hCharge->Add((TH3*)(infile->Get(primName)));
0062   }   
0063     //hCharge->Scale(70);//Scaleing the histogram spacecharge by 100 times
0064 
0065   std::string chargestring;
0066            
0067   //load the spacecharge into the distortion map generator:
0068   //  void load_spacecharge(TH3F *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity);
0069   if (!isAdc){
0070     chargestring=std::format("{}:({}+{})",inputname,ibfName,primName);
0071     tpc->load_spacecharge(hCharge,0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity, chargestring);
0072     if (hasTwin) { tpc->twin->load_spacecharge(hCharge,0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
0073 }
0074   }
0075   if (isAdc){ //load digital current using the scaling:
0076     gainfile=TFile::Open(gainName,"READ");
0077     TH2* hGain[2];
0078     hGain[0]=(TH2*)(gainfile->Get(gainHistName[0]));
0079     chargestring=std::format("{}:(dc:{} g:{}:{})",inputname,ibfName,gainName,gainHistName[0].Data());
0080     tpc->load_digital_current(hCharge,hGain[0],tpc_chargescale,spacecharge_cm_per_axis_unit,chargestring);
0081     if (hasTwin) {
0082       hGain[1]=(TH2*)(gainfile->Get(gainHistName[1]));
0083       tpc->twin->load_digital_current(hCharge,hGain[1],tpc_chargescale,spacecharge_cm_per_axis_unit,chargestring);
0084     }
0085   }
0086   //build the electric fieldmap from the chargemap
0087   tpc->populate_fieldmap();
0088   if (hasTwin) {  tpc->twin->populate_fieldmap();
0089 }
0090 
0091   //build the distortion maps from the fieldmaps and save it to the output filename.
0092   if (scanSteps){
0093   for(int i=0;i<10;i++){
0094     std::string study_filestring=std::format("study_file_changinginterval.steps{}.hist.root",50*(i+1));
0095   tpc->GenerateSeparateDistortionMaps(study_filestring,50*(i+1),1,1,1,1,true);
0096   //tpc->GenerateSeparateDistortionMaps(outputfilename.Data(),1,1,1,1,false);
0097 }
0098 }
0099   else{
0100    tpc->GenerateSeparateDistortionMaps(outputfilename,450,1,1,1,1,false);
0101 }
0102 
0103   std::cout << "distortions mapped." << std::endl;
0104   tpc->PlotFieldSlices(outputfilename,pos, 'E'); //plot the electric field
0105   tpc->PlotFieldSlices(outputfilename,pos,'B'); //plot the magnetic field
0106   std::cout << "fieldslices plotted." << std::endl;
0107   
0108   infile->Close();
0109   std::cout << "input closed.  All done.  Anything else is root's problem." << std::endl;
0110 
0111   return;
0112   
0113 }
0114 
0115   
0116 void generate_distortion_map(const char * inputpattern="./evgeny_apr/Smooth*.root", const char *outputfilebase="./apr07_maps/apr07", bool hasSpacecharge=true, bool isDigitalCurrent=false, int nSteps=500){
0117   
0118 
0119   int maxmaps=10;
0120 //  int maxmapsperfile=2;
0121 
0122   
0123   bool hasTwin=true; //this flag prompts the code to build both a positive-half and a negative-half for the TPC, reusing as much of the calculations as possible.  It is more efficient to 'twin' one half of the TPC than to recalculate/store the greens functions for both.
0124   //bool hasSpacecharge=true;
0125 
0126   //and some parameters of the files we're loading:
0127   bool usesChargeDensity=false; //true if source hists contain charge density per bin.  False if hists are charge per bin.
0128   float tpc_chargescale=1.6e-19;//Coulombs per bin unit.
0129   float spacecharge_cm_per_axis_unit=0.1;//cm per histogram axis unit, matching the MDC2 sample from Evgeny.
0130   
0131   //file names we'll be filling as we go:
0132   TFile *infile;
0133  
0134   TString sourcefilename;
0135   std::string outputfilename;
0136 
0137   
0138   //find all files that match the input string (we don't need this yet, but doing it before building the fieldsim saves time if there's an empty directory or something.
0139   TFileCollection *filelist=new TFileCollection();
0140   filelist->Add(inputpattern);
0141   filelist->Print();
0142   std::cout << "Using pattern \"" << inputpattern << "\", found " <<  filelist->GetList()->GetEntries()
0143         << " files to read, eg : " << ((TFileInfo*)(filelist->GetList()->At(0)))->GetCurrentUrl()->GetUrl() << std::endl;
0144 
0145 
0146   // SurveyFiles( filelist);
0147 
0148   //now build the time-consuming part:
0149   //AnnularFieldSim *tpc=SetupDefaultSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
0150   AnnularFieldSim *tpc;
0151 
0152 
0153   //previously I flagged digital current and used a different rossegger lookup table.
0154   //now I just resample the histogram in that event.  There is a ~hard problem there to get right:
0155   //in each direction, if the source hist is binned more finely than the dest hist, I need to sum the total charge
0156   //if the source hist is binned more coarsely, I need to interpolate the charge density and multiply by the dest cell volume
0157   //but really, I need to differentiate between the source geometry and field point geometry.  task for another time...
0158   // if (isDigitalCurrent){
0159   //    tpc=SetupDigitalCurrentSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
0160   //  }else {
0161     tpc=SetupDefaultSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
0162     //  }
0163   //and the location to plot the fieldslices about:
0164  TVector3 pos=0.5*(tpc->GetOuterEdge()+tpc->GetInnerEdge());;
0165   pos.SetPhi(3.14159);
0166 
0167 
0168   
0169 
0170 
0171   double totalQ=0;
0172   
0173   for (int i=0;i<filelist->GetNFiles();i++){
0174    //for each file, find all histograms in that file.
0175     sourcefilename=((TFileInfo*)(filelist->GetList()->At(i)))->GetCurrentUrl()->GetFile();//gross
0176     infile=TFile::Open(sourcefilename.Data(),"READ");
0177     TList *keys=infile->GetListOfKeys();
0178     keys->Print();
0179     int nKeys=infile->GetNkeys();
0180 
0181     for (int j=0;j<nKeys;j++){
0182       TObject *tobj=infile->Get(keys->At(j)->GetName());
0183       //if this isn't a 3d histogram, skip it:
0184       bool isHist=tobj->InheritsFrom("TH3");
0185       if (!isHist) { continue;
0186 }
0187       TString objname=tobj->GetName();
0188       if (objname.Contains("IBF")) { continue; //this is an IBF-only map we don't want.
0189 }
0190       //assume this histogram is a charge map.
0191       //load just the averages:
0192       if (hasSpacecharge){
0193     if (isDigitalCurrent){
0194       tpc->load_and_resample_spacecharge(8,36,40,sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
0195       if (hasTwin) { tpc->twin->load_and_resample_spacecharge(8,36,40,sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
0196 }
0197 
0198     }
0199 
0200     //load the spacecharge into the distortion map generator:
0201     tpc->load_spacecharge(sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
0202     if (hasTwin)
0203     {
0204       tpc->twin->load_spacecharge(sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
0205     }
0206 
0207     //or load just the average:
0208     //tpc->load_spacecharge(sourcefilename.Data(),"h_Charge_0",0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
0209     //printf("Sanity check:  Q has %d elements and dim=%d\n",tpc->q->Length(), tpc->q->dim);
0210     //totalQ=0;
0211     //for (int k=0;k<tpc->q->Length();k++){
0212     //  totalQ+=*(tpc->q->GetFlat(k));
0213     //}
0214     std::cout << std::format("Sanity check:  Total Q in reported region is {:E} C",totalQ) << std::endl;
0215       }
0216       tpc->populate_fieldmap();
0217       if (hasTwin)
0218       {
0219     tpc->twin->populate_fieldmap();
0220       }
0221       if (sourcefilename.Contains("verage")){ //this is an IBF map we don't want.
0222     outputfilename=std::format("{}.average.{}.{}",outputfilebase,field_string,lookup_string);
0223       } else {
0224     outputfilename=std::format("{}.file{}.{}.{}.{}",outputfilebase,i,tobj->GetName(),field_string,lookup_string);
0225       }
0226       std::cout << sourcefilename.Data() << " file has " << tobj->GetName() << " hist.  field="
0227         << field_string << ", lookup=" << lookup_string << ". no scaling" << std::endl;
0228 
0229       //TestSpotDistortion(tpc);
0230  
0231       //tpc->GenerateDistortionMaps(outputfilename,2,2,2,1,true);
0232 
0233 
0234       tpc->GenerateSeparateDistortionMaps(outputfilename,2,2,2,1,nSteps,true);
0235 
0236       std::cout << "distortions mapped." << std::endl;
0237       tpc->PlotFieldSlices(outputfilename,pos);
0238       tpc->PlotFieldSlices(outputfilename,pos,'B');
0239       std::cout << "fieldslices plotted." << std::endl;     
0240       std::cout << "obj " << j << ": getname: " << tobj->GetName() << " inherits from TH3D:"
0241         << tobj->InheritsFrom("TH3") << std::endl;
0242 
0243       //break; //rcc temp -- uncomment this to process one hist per file.
0244       if (i>maxmaps)
0245       {
0246     return;
0247       }
0248     }
0249       infile->Close();
0250   }
0251 
0252   return;
0253   
0254 }
0255 
0256 void TestSpotDistortion(AnnularFieldSim *t){
0257        TVector3 dummy(20.9034,-2.3553,-103.4712);
0258       float dummydest=-103.4752;
0259       t->twin->GetStepDistortion(dummydest,dummy,true,false);
0260       dummy.SetZ(dummy.Z()*-1);
0261       t->GetStepDistortion(-dummydest,dummy,true,false);
0262       return;
0263 }
0264 
0265 AnnularFieldSim *SetupDefaultSphenixTpc(bool twinMe, bool useSpacecharge, float xshift, float yshift, float zshift){
0266   //step1:  specify the sPHENIX space charge model parameters
0267   const float tpc_rmin=20.0;
0268   const float tpc_rmax=78.0;
0269 //  float tpc_deltar=tpc_rmax-tpc_rmin;
0270   const float tpc_z=105.5;
0271   const float tpc_cmVolt=-400*tpc_z; //V =V_CM-V_RDO -- volts per cm times the length of the drift volume.
0272   //const float tpc_magField=0.5;//T -- The old value used in carlos's studies.
0273   //const float tpc_driftVel=4.0*1e6;//cm per s  -- used in carlos's studies
0274   const float tpc_driftVel=8.0*1e6;//cm per s  -- 2019 nominal value
0275   const float tpc_magField=1.4;//T -- 2019 nominal value
0276   const char detgeoname[]="sphenix";
0277 
0278 
0279   //step 2: specify the parameters of the field simulation.  Larger numbers of
0280   // bins will rapidly increase the memory footprint and compute times.  There
0281   // are some ways to mitigate this by setting a small region of interest, or a
0282   // more parsimonious lookup strategy, specified when AnnularFieldSim() is
0283   // actually constructed below.
0284   int nr=26;//10;//24;//159;//159 nominal
0285   int nr_roi_min=0;
0286   int nr_roi=nr;//10;
0287   int nr_roi_max=nr_roi_min+nr_roi;
0288   int nphi=40;//38;//360;//360 nominal
0289   int nphi_roi_min=0;
0290   int nphi_roi=nphi;//38;
0291   int nphi_roi_max=nphi_roi_min+nphi_roi;
0292   int nz=40;//62;//62 nominal
0293   int nz_roi_min=0;
0294   int nz_roi=nz;
0295   int nz_roi_max=nz_roi_min+nz_roi;
0296 
0297   bool realB=true;
0298   bool realE=true;
0299   
0300 
0301   
0302   //step 3:  create the fieldsim object.  different choices of the last few arguments will change how it
0303   // builds the lookup table spatially, and how it loads the spacecharge.  The various start-up steps
0304   // are exposed here so they can be timed in the macro.
0305   
0306   AnnularFieldSim *tpc;
0307   if (useSpacecharge){
0308     tpc=    new  AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
0309              nr, nr_roi_min,nr_roi_max,1,2,
0310              nphi,nphi_roi_min, nphi_roi_max,1,2,
0311              nz, nz_roi_min, nz_roi_max,1,2,
0312                  tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::FromFile);
0313   }else{
0314     tpc=    new  AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
0315              nr, nr_roi_min,nr_roi_max,1,2,
0316              nphi,nphi_roi_min, nphi_roi_max,1,2,
0317              nz, nz_roi_min, nz_roi_max,1,2,
0318                  tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::NoSpacecharge);
0319   }
0320     
0321   tpc->UpdateEveryN(10);//show reports every 10%.
0322 
0323     //load the field maps, either flat or actual maps
0324   tpc->setFlatFields(tpc_magField,tpc_cmVolt/tpc_z);
0325   field_string = std::format("flat_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0326 
0327   if (realE){
0328     tpc->loadEfield("/sphenix/user/rcorliss/field/externalEfield.ttree.root","fTree");
0329     field_string = std::format("realE_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0330   }
0331    if (realB){
0332     tpc->load3dBfield("/sphenix/user/rcorliss/field/sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5, xshift, yshift,zshift);
0333         //tpc->loadBfield("sPHENIX.2d.root","fieldmap");
0334     field_string = std::format("realB_B{:.1f}_E{:.1f}_x{:.1f}_y{:.1f}_z{:.1f}",tpc_magField,tpc_cmVolt/tpc_z,xshift, yshift,zshift);
0335   } 
0336   if (realE && realB){
0337     field_string = std::format("real_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0338   }
0339   std::cout << "set fields." << std::endl;
0340 
0341 
0342 
0343 
0344   //load the greens functions:
0345   lookup_string = std::format("ross_phi1_{}_phislice_lookup_r{}xp{}xz{}",detgeoname,nr,nphi,nz);
0346   //sprintf(lookupFilename,"%s.root",lookup_string);
0347   std::string lookupFilename = std::format("/sphenix/user/rcorliss/rossegger/{}.root",lookup_string); //hardcoded for racf
0348   TFile *fileptr=TFile::Open(lookupFilename.c_str(),"READ");
0349 
0350   if (!fileptr){ //generate the lookuptable
0351     //to use the full rossegger terms instead of trivial free-space greens functions, uncomment the line below:
0352     tpc->load_rossegger();
0353     std::cout << "loaded rossegger greens functions." << std::endl;
0354     tpc->populate_lookup();
0355     tpc->save_phislice_lookup(lookupFilename);
0356   } else{ //load it from a file
0357     fileptr->Close();
0358     tpc->load_phislice_lookup(lookupFilename);
0359   }
0360 
0361   std::cout << "populated lookup." << std::endl;
0362 
0363 
0364 
0365 
0366 
0367 
0368     
0369   //make our twin:
0370   if(twinMe){
0371     AnnularFieldSim *twin;
0372       if (useSpacecharge){
0373     twin=      new  AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
0374                nr, nr_roi_min,nr_roi_max,1,2,
0375                nphi,nphi_roi_min, nphi_roi_max,1,2,
0376                nz, nz_roi_min, nz_roi_max,1,2,
0377                tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::FromFile);
0378       } else{
0379     twin=      new  AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
0380                nr, nr_roi_min,nr_roi_max,1,2,
0381                nphi,nphi_roi_min, nphi_roi_max,1,2,
0382                nz, nz_roi_min, nz_roi_max,1,2,
0383                tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::NoSpacecharge);
0384       }    twin->UpdateEveryN(10);//show reports every 10%.
0385 
0386     //same magnetic field, opposite electric field
0387     twin->setFlatFields(tpc_magField,-tpc_cmVolt/tpc_z);
0388     //sprintf(field_string,"flat_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
0389     //twin->loadBfield("sPHENIX.2d.root","fieldmap");
0390     twin->load3dBfield("/sphenix/user/rcorliss/rossegger/sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5, xshift, yshift, zshift);
0391     twin->loadEfield("/sphenix/user/rcorliss/rossegger/externalEfield.ttree.root","fTree",-1);//final '-1' tells it to flip z and the field z coordinate. r coordinate doesn't change, and we assume phi won't either, though the latter is less true.
0392 
0393 
0394 
0395 
0396     //borrow the greens functions:
0397     twin->borrow_rossegger(tpc->green,tpc_z);//use the original's green's functions, shift our internal coordinates by tpc_z when querying those functions.
0398     twin->borrow_epartial_from(tpc,tpc_z);//use the original's epartial.  Note that those values ought to be symmetric about z, and since our boundary conditions are translated along with our coordinates, they're completely unchanged.
0399 
0400     tpc->set_twin(twin);
0401   }
0402 
0403   return tpc;
0404 }
0405   
0406 
0407 AnnularFieldSim *SetupDigitalCurrentSphenixTpc(bool twinMe, bool useSpacecharge){
0408   //step1:  specify the sPHENIX space charge model parameters
0409   const float tpc_rmin=20.0;
0410   const float tpc_rmax=78.0;
0411 //  float tpc_deltar=tpc_rmax-tpc_rmin;
0412   const float tpc_z=105.5;
0413   const float tpc_cmVolt=-400*tpc_z; //V =V_CM-V_RDO -- volts per cm times the length of the drift volume.
0414   //const float tpc_magField=0.5;//T -- The old value used in carlos's studies.
0415   //const float tpc_driftVel=4.0*1e6;//cm per s  -- used in carlos's studies
0416   const float tpc_driftVel=8.0*1e6;//cm per s  -- 2019 nominal value
0417   const float tpc_magField=1.4;//T -- 2019 nominal value
0418   const char detgeoname[]="sphenix";
0419   
0420   //step 2: specify the parameters of the field simulation.  Larger numbers of
0421   // bins will rapidly increase the memory footprint and compute times.  There
0422   // are some ways to mitigate this by setting a small region of interest, or a
0423   // more parsimonious lookup strategy, specified when AnnularFieldSim() is
0424   // actually constructed below.
0425   
0426   int nr=8;//10;//24;//159;//159 nominal
0427   int nr_roi_min=0;
0428   int nr_roi=nr;//10;
0429   int nr_roi_max=nr_roi_min+nr_roi;
0430   int nphi=3*12;//38;//360;//360 nominal
0431   int nphi_roi_min=0;
0432   int nphi_roi=nphi;//38;
0433   int nphi_roi_max=nphi_roi_min+nphi_roi;
0434   int nz=40;//62;//62 nominal
0435   int nz_roi_min=0;
0436   int nz_roi=nz;
0437   int nz_roi_max=nz_roi_min+nz_roi;
0438 
0439   bool realB=true;
0440   bool realE=true;
0441   
0442 
0443   //step 3:  create the fieldsim object.  different choices of the last few arguments will change how it
0444   // builds the lookup table spatially, and how it loads the spacecharge.  The various start-up steps
0445   // are exposed here so they can be timed in the macro.
0446   AnnularFieldSim *tpc;
0447   if (useSpacecharge){
0448     tpc=    new  AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
0449              nr, nr_roi_min,nr_roi_max,1,2,
0450              nphi,nphi_roi_min, nphi_roi_max,1,2,
0451              nz, nz_roi_min, nz_roi_max,1,2,
0452                  tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::FromFile);
0453   }else{
0454     tpc=    new  AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
0455              nr, nr_roi_min,nr_roi_max,1,2,
0456              nphi,nphi_roi_min, nphi_roi_max,1,2,
0457              nz, nz_roi_min, nz_roi_max,1,2,
0458                  tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::NoSpacecharge);
0459   }
0460     
0461   tpc->UpdateEveryN(10);//show reports every 10%.
0462 
0463     //load the field maps, either flat or actual maps
0464   tpc->setFlatFields(tpc_magField,tpc_cmVolt/tpc_z);
0465   field_string = std::format("flat_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0466 
0467   if (realE){
0468     tpc->loadEfield("externalEfield.ttree.root","fTree");
0469     field_string = std::format("realE_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0470   }
0471    if (realB){
0472     tpc->load3dBfield("sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5);
0473         //tpc->loadBfield("sPHENIX.2d.root","fieldmap");
0474     field_string = std::format("realB_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0475   } 
0476   if (realE && realB){
0477     field_string = std::format("real_B{:.1f}_E{:.1f}",tpc_magField,tpc_cmVolt/tpc_z);
0478   }
0479   std::cout << "set fields." << std::endl;
0480 
0481 
0482 
0483 
0484   //load the greens functions:
0485   lookup_string = std::format("ross_phi1_{}_phislice_lookup_r{}xp{}xz{}",detgeoname,nr,nphi,nz);
0486   std::string lookupFilename = lookup_string + ".root";
0487   TFile *fileptr=TFile::Open(lookupFilename.c_str(),"READ");
0488 
0489   if (!fileptr){ //generate the lookuptable
0490     //to use the full rossegger terms instead of trivial free-space greens functions, uncomment the line below:
0491     tpc->load_rossegger();
0492     std::cout << "loaded rossegger greens functions." << std::endl;
0493     tpc->populate_lookup();
0494     tpc->save_phislice_lookup(lookupFilename);
0495   } else{ //load it from a file
0496     fileptr->Close();
0497     tpc->load_phislice_lookup(lookupFilename);
0498   }
0499 
0500   std::cout << "populated lookup." << std::endl;
0501 
0502 
0503 
0504 
0505 
0506 
0507     
0508   //make our twin:
0509   if(twinMe){
0510     AnnularFieldSim *twin;
0511       if (useSpacecharge){
0512     twin=      new  AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
0513                nr, nr_roi_min,nr_roi_max,1,2,
0514                nphi,nphi_roi_min, nphi_roi_max,1,2,
0515                nz, nz_roi_min, nz_roi_max,1,2,
0516                tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::FromFile);
0517       } else{
0518     twin=      new  AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
0519                nr, nr_roi_min,nr_roi_max,1,2,
0520                nphi,nphi_roi_min, nphi_roi_max,1,2,
0521                nz, nz_roi_min, nz_roi_max,1,2,
0522                tpc_driftVel, AnnularFieldSim::PhiSlice, AnnularFieldSim::NoSpacecharge);
0523       }    twin->UpdateEveryN(10);//show reports every 10%.
0524 
0525     //same magnetic field, opposite electric field
0526     twin->setFlatFields(tpc_magField,-tpc_cmVolt/tpc_z);
0527     //sprintf(field_string,"flat_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
0528     //twin->loadBfield("sPHENIX.2d.root","fieldmap");
0529     twin->load3dBfield("sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5);
0530     twin->loadEfield("externalEfield.ttree.root","fTree",-1);//final '-1' tells it to flip z and the field z coordinate. r coordinate doesn't change, and we assume phi won't either, though the latter is less true.
0531 
0532 
0533 
0534 
0535     //borrow the greens functions:
0536     twin->borrow_rossegger(tpc->green,tpc_z);//use the original's green's functions, shift our internal coordinates by tpc_z when querying those functions.
0537     twin->borrow_epartial_from(tpc,tpc_z);//use the original's epartial.  Note that those values ought to be symmetric about z, and since our boundary conditions are translated along with our coordinates, they're completely unchanged.
0538 
0539     tpc->set_twin(twin);
0540   }
0541 
0542   return tpc;
0543 }
0544   
0545 void  SurveyFiles(TFileCollection *filelist){
0546    TFile *infile;
0547  
0548   TString sourcefilename;
0549   TString outputfilename;
0550  //run a check of the files we'll be looking at:
0551   float integral_sum=0;
0552   int nhists=0;
0553   for (int i=0;i<filelist->GetNFiles();i++){
0554    //for each file, find all histograms in that file.
0555     sourcefilename=((TFileInfo*)(filelist->GetList()->At(i)))->GetCurrentUrl()->GetFile();//gross
0556     std::cout << "file " << i << ": " << sourcefilename << std::endl;
0557     infile=TFile::Open(sourcefilename.Data(),"READ");
0558     TList *keys=infile->GetListOfKeys();
0559     //keys->Print();
0560     int nKeys=infile->GetNkeys();
0561 
0562     for (int j=0;j<nKeys;j++){
0563       TObject *tobj=infile->Get(keys->At(j)->GetName());
0564       //if this isn't a 3d histogram, skip it:
0565       bool isHist=tobj->InheritsFrom("TH3");
0566       if (!isHist) { continue;
0567 }
0568       TString objname=tobj->GetName();
0569       std::cout << " hist " << objname << " ";
0570       if (objname.Contains("IBF")) {
0571     std::cout << " is IBF only." << std::endl;
0572     continue; //this is an IBF map we don't want.
0573       }
0574       float integral=((TH3D*)tobj)->Integral();
0575       integral_sum+=integral;
0576       nhists+=1;
0577       std::cout << std::format(" will be used.  Total Q={:.3E} (ave={:.3E})",integral,integral_sum/nhists) << std::endl;
0578       //assume this histogram is a charge map.
0579       //load just the averages:
0580  
0581       //break; //rcc temp -- uncomment this to process one hist per file.
0582       //if (i>maxmaps) return;
0583     }
0584       infile->Close();
0585   }
0586   return;
0587 }