Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
generate_distortion_map.C
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file generate_distortion_map.C
1 
2 
3 #include "AnnularFieldSim.h"
4 #include "TTree.h" //this prevents a lazy binding issue and/or is a magic spell.
5 #include "TCanvas.h" //this prevents a lazy binding issue and/or is a magic spell.
6 
7 // cppcheck-suppress unknownMacro
8 R__LOAD_LIBRARY(libfieldsim.so)
9 
10 char field_string[200];
11 char lookup_string[200];
12 
13 AnnularFieldSim *SetupDefaultSphenixTpc(bool twinMe=false, bool useSpacecharge=true);
14 AnnularFieldSim *SetupDigitalCurrentSphenixTpc(bool twinMe=false, bool useSpacecharge=true);
16 void SurveyFiles(TFileCollection* filelist);
17 
18 
19 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){
20  printf("generating single distortion map. Caution: This is vastly less efficient than re-using the tpc model once it is set up\n");
21 
22  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.
23  TString gainHistName[2]={"hIbfGain_posz","hIbfGFain_negz"};
24 
25  //and some parameters of the files we're loading:
26  bool usesChargeDensity=false; //true if source hists contain charge density per bin. False if hists are charge per bin.
27  float tpc_chargescale=1.6e-19;//Coulombs per bin unit.
28  float spacecharge_cm_per_axis_unit=0.1;//cm per histogram axis unit, matching the MDC2 sample from Evgeny.
29 
30  //file names we'll be filling as we go:
31  TFile *infile, *gainfile;
32 
33  TString sourcefilename=inputname;
34  TString outputfilename=outputname;
35 
36  //now build the time-consuming part:
37  AnnularFieldSim *tpc;
38  tpc=SetupDefaultSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
39 
40  //and the location to plot the fieldslices about:
41  TVector3 pos=0.5*(tpc->GetOuterEdge()+tpc->GetInnerEdge());;
42  pos.SetPhi(3.14159);
43 
44  infile=TFile::Open(sourcefilename.Data(),"READ");
45 
46 
47  //the total charge is prim + IBF
48  //if we are doing ADCs, though, we only read the one.
49  TH3* hCharge=(TH3*)(infile->Get(ibfName));
50  if (!isAdc){
51  hCharge->Add((TH3*)(infile->Get(primName)));
52  }
53  //hCharge->Scale(70);//Scaleing the histogram spacecharge by 100 times
54 
55  TString chargestring;
56 
57  //load the spacecharge into the distortion map generator:
58  // void load_spacecharge(TH3F *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity);
59  if (!isAdc){
60  chargestring=Form("%s:(%s+%s)",inputname,ibfName,primName);
61  tpc->load_spacecharge(hCharge,0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity, chargestring.Data());
62  if (hasTwin) tpc->twin->load_spacecharge(hCharge,0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
63  }
64  if (isAdc){ //load digital current using the scaling:
65  gainfile=TFile::Open(gainName,"READ");
66  TH2* hGain[2];
67  hGain[0]=(TH2*)(gainfile->Get(gainHistName[0]));
68  chargestring=Form("%s:(dc:%s g:%s:%s)",inputname,ibfName,gainName,gainHistName[0].Data());
69  tpc->load_digital_current(hCharge,hGain[0],tpc_chargescale,spacecharge_cm_per_axis_unit,chargestring.Data());
70  if (hasTwin) {
71  hGain[1]=(TH2*)(gainfile->Get(gainHistName[1]));
72  tpc->twin->load_digital_current(hCharge,hGain[1],tpc_chargescale,spacecharge_cm_per_axis_unit,chargestring.Data());
73  }
74  }
75  //build the electric fieldmap from the chargemap
76  tpc->populate_fieldmap();
77  if (hasTwin) tpc->twin->populate_fieldmap();
78 
79  //build the distortion maps from the fieldmaps and save it to the output filename.
80  if (scanSteps){
81  for(int i=0;i<10;i++){
82  TString study_filestring=Form("%s.steps%d.hist.root","study_file_changinginterval",50*(i+1));
83  tpc->GenerateSeparateDistortionMaps(study_filestring,50*(i+1),1,1,1,1,true);
84  //tpc->GenerateSeparateDistortionMaps(outputfilename.Data(),1,1,1,1,false);
85 }
86 }
87  else{
88  tpc->GenerateSeparateDistortionMaps(outputfilename.Data(),450,1,1,1,1,false);
89 }
90 
91  printf("distortions mapped.\n");
92  tpc->PlotFieldSlices(outputfilename.Data(),pos, 'E'); //plot the electric field
93  tpc->PlotFieldSlices(outputfilename.Data(),pos,'B'); //plot the magnetic field
94  printf("fieldslices plotted.\n");
95 
96  infile->Close();
97  printf("input closed. All done. Anything else is root's problem.\n");
98 
99  return;
100 
101 }
102 
103 
104 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){
105 
106 
107  int maxmaps=10;
108  int maxmapsperfile=2;
109 
110 
111  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.
112  //bool hasSpacecharge=true;
113 
114  //and some parameters of the files we're loading:
115  bool usesChargeDensity=false; //true if source hists contain charge density per bin. False if hists are charge per bin.
116  float tpc_chargescale=1.6e-19;//Coulombs per bin unit.
117  float spacecharge_cm_per_axis_unit=0.1;//cm per histogram axis unit, matching the MDC2 sample from Evgeny.
118 
119  //file names we'll be filling as we go:
120  TFile *infile;
121 
122  TString sourcefilename;
123  TString outputfilename;
124 
125 
126  //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.
127  TFileCollection *filelist=new TFileCollection();
128  filelist->Add(inputpattern);
129  filelist->Print();
130  printf("Using pattern \"%s\", found %d files to read, eg : %s\n",inputpattern,filelist->GetList()->GetEntries(),((TFileInfo*)(filelist->GetList()->At(0)))->GetCurrentUrl()->GetUrl());//Title());//Print();
131 
132 
133  // SurveyFiles( filelist);
134 
135  //now build the time-consuming part:
136  //AnnularFieldSim *tpc=SetupDefaultSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
137  AnnularFieldSim *tpc;
138 
139 
140  //previously I flagged digital current and used a different rossegger lookup table.
141  //now I just resample the histogram in that event. There is a ~hard problem there to get right:
142  //in each direction, if the source hist is binned more finely than the dest hist, I need to sum the total charge
143  //if the source hist is binned more coarsely, I need to interpolate the charge density and multiply by the dest cell volume
144  //but really, I need to differentiate between the source geometry and field point geometry. task for another time...
145  // if (isDigitalCurrent){
146  // tpc=SetupDigitalCurrentSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
147  // }else {
148  tpc=SetupDefaultSphenixTpc(hasTwin,hasSpacecharge);//loads the lookup, fields, etc.
149  // }
150  //and the location to plot the fieldslices about:
151  TVector3 pos=0.5*(tpc->GetOuterEdge()+tpc->GetInnerEdge());;
152  pos.SetPhi(3.14159);
153 
154 
155 
156 
157 
158  double totalQ=0;
159 
160  for (int i=0;i<filelist->GetNFiles();i++){
161  //for each file, find all histograms in that file.
162  sourcefilename=((TFileInfo*)(filelist->GetList()->At(i)))->GetCurrentUrl()->GetFile();//gross
163  infile=TFile::Open(sourcefilename.Data(),"READ");
164  TList *keys=infile->GetListOfKeys();
165  keys->Print();
166  int nKeys=infile->GetNkeys();
167 
168  for (int j=0;j<nKeys;j++){
169  TObject *tobj=infile->Get(keys->At(j)->GetName());
170  //if this isn't a 3d histogram, skip it:
171  bool isHist=tobj->InheritsFrom("TH3");
172  if (!isHist) continue;
173  TString objname=tobj->GetName();
174  if (objname.Contains("IBF")) continue; //this is an IBF-only map we don't want.
175  //assume this histogram is a charge map.
176  //load just the averages:
177  if (hasSpacecharge){
178  if (isDigitalCurrent){
179  tpc->load_and_resample_spacecharge(8,36,40,sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
180  if (hasTwin) tpc->twin->load_and_resample_spacecharge(8,36,40,sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
181 
182  }
183 
184  //load the spacecharge into the distortion map generator:
185  tpc->load_spacecharge(sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
186  if (hasTwin) tpc->twin->load_spacecharge(sourcefilename.Data(),tobj->GetName(),0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
187 
188  //or load just the average:
189  //tpc->load_spacecharge(sourcefilename.Data(),"h_Charge_0",0,tpc_chargescale,spacecharge_cm_per_axis_unit, usesChargeDensity);
190  //printf("Sanity check: Q has %d elements and dim=%d\n",tpc->q->Length(), tpc->q->dim);
191  //totalQ=0;
192  //for (int k=0;k<tpc->q->Length();k++){
193  // totalQ+=*(tpc->q->GetFlat(k));
194  //}
195  printf("Sanity check: Total Q in reported region is %E C\n",totalQ);
196  }
197  tpc->populate_fieldmap();
198  if (hasTwin) tpc->twin->populate_fieldmap();
199  if (sourcefilename.Contains("verage")){ //this is an IBF map we don't want.
200  outputfilename=Form("%s.average.%s.%s",outputfilebase,field_string,lookup_string);
201  } else {
202  outputfilename=Form("%s.file%d.%s.%s.%s",outputfilebase,i,tobj->GetName(),field_string,lookup_string);
203  }
204  printf("%s file has %s hist. field=%s, lookup=%s. no scaling.\n",
205  sourcefilename.Data(),tobj->GetName(),field_string,lookup_string);
206 
207  //TestSpotDistortion(tpc);
208 
209  //tpc->GenerateDistortionMaps(outputfilename,2,2,2,1,true);
210 
211 
212  tpc->GenerateSeparateDistortionMaps(outputfilename,2,2,2,1,true,nSteps);
213 
214  printf("distortions mapped.\n");
215  tpc->PlotFieldSlices(outputfilename.Data(),pos);
216  tpc->PlotFieldSlices(outputfilename.Data(),pos,'B');
217  printf("fieldslices plotted.\n");
218  printf("obj %d: getname: %s inherits from TH3D:%d \n",j,tobj->GetName(),tobj->InheritsFrom("TH3"));
219  //break; //rcc temp -- uncomment this to process one hist per file.
220  if (i>maxmaps) return;
221  }
222  infile->Close();
223  }
224 
225  return;
226 
227 }
228 
230  TVector3 dummy(20.9034,-2.3553,-103.4712);
231  float dummydest=-103.4752;
232  t->twin->GetStepDistortion(dummydest,dummy,true,false);
233  dummy.SetZ(dummy.Z()*-1);
234  t->GetStepDistortion(-dummydest,dummy,true,false);
235  return;
236 }
237 
238 AnnularFieldSim *SetupDefaultSphenixTpc(bool twinMe, bool useSpacecharge){
239  //step1: specify the sPHENIX space charge model parameters
240  const float tpc_rmin=20.0;
241  const float tpc_rmax=78.0;
242  float tpc_deltar=tpc_rmax-tpc_rmin;
243  const float tpc_z=105.5;
244  const float tpc_cmVolt=-400*tpc_z; //V =V_CM-V_RDO -- volts per cm times the length of the drift volume.
245  //const float tpc_magField=0.5;//T -- The old value used in carlos's studies.
246  //const float tpc_driftVel=4.0*1e6;//cm per s -- used in carlos's studies
247  const float tpc_driftVel=8.0*1e6;//cm per s -- 2019 nominal value
248  const float tpc_magField=1.4;//T -- 2019 nominal value
249  const char detgeoname[]="sphenix";
250 
251 
252  //step 2: specify the parameters of the field simulation. Larger numbers of
253  // bins will rapidly increase the memory footprint and compute times. There
254  // are some ways to mitigate this by setting a small region of interest, or a
255  // more parsimonious lookup strategy, specified when AnnularFieldSim() is
256  // actually constructed below.
257  int nr=26;//10;//24;//159;//159 nominal
258  int nr_roi_min=0;
259  int nr_roi=nr;//10;
260  int nr_roi_max=nr_roi_min+nr_roi;
261  int nphi=40;//38;//360;//360 nominal
262  int nphi_roi_min=0;
263  int nphi_roi=nphi;//38;
264  int nphi_roi_max=nphi_roi_min+nphi_roi;
265  int nz=40;//62;//62 nominal
266  int nz_roi_min=0;
267  int nz_roi=nz;
268  int nz_roi_max=nz_roi_min+nz_roi;
269 
270  bool realB=true;
271  bool realE=true;
272 
273 
274 
275  //step 3: create the fieldsim object. different choices of the last few arguments will change how it
276  // builds the lookup table spatially, and how it loads the spacecharge. The various start-up steps
277  // are exposed here so they can be timed in the macro.
278 
279  AnnularFieldSim *tpc;
280  if (useSpacecharge){
281  tpc= new AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
282  nr, nr_roi_min,nr_roi_max,1,2,
283  nphi,nphi_roi_min, nphi_roi_max,1,2,
284  nz, nz_roi_min, nz_roi_max,1,2,
286  }else{
287  tpc= new AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
288  nr, nr_roi_min,nr_roi_max,1,2,
289  nphi,nphi_roi_min, nphi_roi_max,1,2,
290  nz, nz_roi_min, nz_roi_max,1,2,
292  }
293 
294  tpc->UpdateEveryN(10);//show reports every 10%.
295 
296  //load the field maps, either flat or actual maps
297  tpc->setFlatFields(tpc_magField,tpc_cmVolt/tpc_z);
298  sprintf(field_string,"flat_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
299 
300  if (realE){
301  tpc->loadEfield("/sphenix/user/rcorliss/field/externalEfield.ttree.root","fTree");
302  sprintf(field_string,"realE_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
303  }
304  if (realB){
305  tpc->load3dBfield("/sphenix/user/rcorliss/field/sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5);
306  //tpc->loadBfield("sPHENIX.2d.root","fieldmap");
307  sprintf(field_string,"realB_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
308  }
309  if (realE && realB){
310  sprintf(field_string,"real_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
311  }
312  printf("set fields.\n");
313 
314 
315 
316 
317  //load the greens functions:
318  sprintf(lookup_string,"ross_phi1_%s_phislice_lookup_r%dxp%dxz%d",detgeoname,nr,nphi,nz);
319  char lookupFilename[300];
320  //sprintf(lookupFilename,"%s.root",lookup_string);
321  sprintf(lookupFilename,"/sphenix/user/rcorliss/rossegger/%s.root",lookup_string); //hardcoded for racf
322  TFile *fileptr=TFile::Open(lookupFilename,"READ");
323 
324  if (!fileptr){ //generate the lookuptable
325  //to use the full rossegger terms instead of trivial free-space greens functions, uncomment the line below:
326  tpc->load_rossegger();
327  printf("loaded rossegger greens functions.\n");
328  tpc->populate_lookup();
329  tpc->save_phislice_lookup(lookupFilename);
330  } else{ //load it from a file
331  fileptr->Close();
332  tpc->load_phislice_lookup(lookupFilename);
333  }
334 
335  printf("populated lookup.\n");
336 
337 
338 
339 
340 
341 
342 
343  //make our twin:
344  if(twinMe){
345  AnnularFieldSim *twin;
346  if (useSpacecharge){
347  twin= new AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
348  nr, nr_roi_min,nr_roi_max,1,2,
349  nphi,nphi_roi_min, nphi_roi_max,1,2,
350  nz, nz_roi_min, nz_roi_max,1,2,
352  } else{
353  twin= new AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
354  nr, nr_roi_min,nr_roi_max,1,2,
355  nphi,nphi_roi_min, nphi_roi_max,1,2,
356  nz, nz_roi_min, nz_roi_max,1,2,
358  } twin->UpdateEveryN(10);//show reports every 10%.
359 
360  //same magnetic field, opposite electric field
361  twin->setFlatFields(tpc_magField,-tpc_cmVolt/tpc_z);
362  //sprintf(field_string,"flat_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
363  //twin->loadBfield("sPHENIX.2d.root","fieldmap");
364  twin->load3dBfield("/sphenix/user/rcorliss/rossegger/sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5);
365  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.
366 
367 
368 
369 
370  //borrow the greens functions:
371  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.
372  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.
373 
374  tpc->set_twin(twin);
375  }
376 
377  return tpc;
378 }
379 
380 
381 AnnularFieldSim *SetupDigitalCurrentSphenixTpc(bool twinMe, bool useSpacecharge){
382  //step1: specify the sPHENIX space charge model parameters
383  const float tpc_rmin=20.0;
384  const float tpc_rmax=78.0;
385  float tpc_deltar=tpc_rmax-tpc_rmin;
386  const float tpc_z=105.5;
387  const float tpc_cmVolt=-400*tpc_z; //V =V_CM-V_RDO -- volts per cm times the length of the drift volume.
388  //const float tpc_magField=0.5;//T -- The old value used in carlos's studies.
389  //const float tpc_driftVel=4.0*1e6;//cm per s -- used in carlos's studies
390  const float tpc_driftVel=8.0*1e6;//cm per s -- 2019 nominal value
391  const float tpc_magField=1.4;//T -- 2019 nominal value
392  const char detgeoname[]="sphenix";
393 
394  //step 2: specify the parameters of the field simulation. Larger numbers of
395  // bins will rapidly increase the memory footprint and compute times. There
396  // are some ways to mitigate this by setting a small region of interest, or a
397  // more parsimonious lookup strategy, specified when AnnularFieldSim() is
398  // actually constructed below.
399 
400  int nr=8;//10;//24;//159;//159 nominal
401  int nr_roi_min=0;
402  int nr_roi=nr;//10;
403  int nr_roi_max=nr_roi_min+nr_roi;
404  int nphi=3*12;//38;//360;//360 nominal
405  int nphi_roi_min=0;
406  int nphi_roi=nphi;//38;
407  int nphi_roi_max=nphi_roi_min+nphi_roi;
408  int nz=40;//62;//62 nominal
409  int nz_roi_min=0;
410  int nz_roi=nz;
411  int nz_roi_max=nz_roi_min+nz_roi;
412 
413  bool realB=true;
414  bool realE=true;
415 
416 
417  //step 3: create the fieldsim object. different choices of the last few arguments will change how it
418  // builds the lookup table spatially, and how it loads the spacecharge. The various start-up steps
419  // are exposed here so they can be timed in the macro.
420  AnnularFieldSim *tpc;
421  if (useSpacecharge){
422  tpc= new AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
423  nr, nr_roi_min,nr_roi_max,1,2,
424  nphi,nphi_roi_min, nphi_roi_max,1,2,
425  nz, nz_roi_min, nz_roi_max,1,2,
427  }else{
428  tpc= new AnnularFieldSim(tpc_rmin,tpc_rmax,tpc_z,
429  nr, nr_roi_min,nr_roi_max,1,2,
430  nphi,nphi_roi_min, nphi_roi_max,1,2,
431  nz, nz_roi_min, nz_roi_max,1,2,
433  }
434 
435  tpc->UpdateEveryN(10);//show reports every 10%.
436 
437  //load the field maps, either flat or actual maps
438  tpc->setFlatFields(tpc_magField,tpc_cmVolt/tpc_z);
439  sprintf(field_string,"flat_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
440 
441  if (realE){
442  tpc->loadEfield("externalEfield.ttree.root","fTree");
443  sprintf(field_string,"realE_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
444  }
445  if (realB){
446  tpc->load3dBfield("sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5);
447  //tpc->loadBfield("sPHENIX.2d.root","fieldmap");
448  sprintf(field_string,"realB_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
449  }
450  if (realE && realB){
451  sprintf(field_string,"real_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
452  }
453  printf("set fields.\n");
454 
455 
456 
457 
458  //load the greens functions:
459  sprintf(lookup_string,"ross_phi1_%s_phislice_lookup_r%dxp%dxz%d",detgeoname,nr,nphi,nz);
460  char lookupFilename[200];
461  sprintf(lookupFilename,"%s.root",lookup_string);
462  TFile *fileptr=TFile::Open(lookupFilename,"READ");
463 
464  if (!fileptr){ //generate the lookuptable
465  //to use the full rossegger terms instead of trivial free-space greens functions, uncomment the line below:
466  tpc->load_rossegger();
467  printf("loaded rossegger greens functions.\n");
468  tpc->populate_lookup();
469  tpc->save_phislice_lookup(lookupFilename);
470  } else{ //load it from a file
471  fileptr->Close();
472  tpc->load_phislice_lookup(lookupFilename);
473  }
474 
475  printf("populated lookup.\n");
476 
477 
478 
479 
480 
481 
482 
483  //make our twin:
484  if(twinMe){
485  AnnularFieldSim *twin;
486  if (useSpacecharge){
487  twin= new AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
488  nr, nr_roi_min,nr_roi_max,1,2,
489  nphi,nphi_roi_min, nphi_roi_max,1,2,
490  nz, nz_roi_min, nz_roi_max,1,2,
492  } else{
493  twin= new AnnularFieldSim(tpc_rmin,tpc_rmax,-tpc_z,
494  nr, nr_roi_min,nr_roi_max,1,2,
495  nphi,nphi_roi_min, nphi_roi_max,1,2,
496  nz, nz_roi_min, nz_roi_max,1,2,
498  } twin->UpdateEveryN(10);//show reports every 10%.
499 
500  //same magnetic field, opposite electric field
501  twin->setFlatFields(tpc_magField,-tpc_cmVolt/tpc_z);
502  //sprintf(field_string,"flat_B%2.1f_E%2.1f",tpc_magField,tpc_cmVolt/tpc_z);
503  //twin->loadBfield("sPHENIX.2d.root","fieldmap");
504  twin->load3dBfield("sphenix3dmaprhophiz.root","fieldmap",1,-1.4/1.5);
505  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.
506 
507 
508 
509 
510  //borrow the greens functions:
511  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.
512  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.
513 
514  tpc->set_twin(twin);
515  }
516 
517  return tpc;
518 }
519 
520 void SurveyFiles(TFileCollection *filelist){
521  TFile *infile;
522 
523  TString sourcefilename;
524  TString outputfilename;
525  //run a check of the files we'll be looking at:
526  float integral_sum=0;
527  int nhists=0;
528  for (int i=0;i<filelist->GetNFiles();i++){
529  //for each file, find all histograms in that file.
530  sourcefilename=((TFileInfo*)(filelist->GetList()->At(i)))->GetCurrentUrl()->GetFile();//gross
531  printf("file %d: %s\n", i, sourcefilename.Data());
532  infile=TFile::Open(sourcefilename.Data(),"READ");
533  TList *keys=infile->GetListOfKeys();
534  //keys->Print();
535  int nKeys=infile->GetNkeys();
536 
537  for (int j=0;j<nKeys;j++){
538  TObject *tobj=infile->Get(keys->At(j)->GetName());
539  //if this isn't a 3d histogram, skip it:
540  bool isHist=tobj->InheritsFrom("TH3");
541  if (!isHist) continue;
542  TString objname=tobj->GetName();
543  printf(" hist %s ",objname.Data());
544  if (objname.Contains("IBF")) {
545  printf(" is IBF only.\n");
546  continue; //this is an IBF map we don't want.
547  }
548  float integral=((TH3D*)tobj)->Integral();
549  integral_sum+=integral;
550  nhists+=1;
551  printf(" will be used. Total Q=%3.3E (ave=%3.3E)\n",integral,integral_sum/nhists);
552  //assume this histogram is a charge map.
553  //load just the averages:
554 
555  //break; //rcc temp -- uncomment this to process one hist per file.
556  //if (i>maxmaps) return;
557  }
558  infile->Close();
559  }
560  return;
561 }