ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AnnularFieldSim.h
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file AnnularFieldSim.h
1 #include "Rossegger.h"
2 
3 #include <TObject.h> // for TObject
4 #include <TVector3.h>
5 
6 #include <cmath> // for NAN, abs
7 #include <cstdio> // for printf
8 #include <cstdlib> // for malloc
9 #include <string> // for string
10 
11 #include <cassert>
12 
13 template <class T>
14 class MultiArray;
15 class AnalyticFieldModel;
16 class TH3F;
17 class TTree;
18 
20 {
21  private:
22  //units:
23  const float cm = 1; //centimeters -- if you change this, check that all the loading functions are properly agnostic.
24  const float m = 100 * cm; //meters.
25  const float mm = cm / 10;
26  const float um = mm / 1e3;
27 
28  const float C = 1; //Coulombs
29  const float nC = C / 1e9;
30  const float fC = C / 1e15;
31 
32  const float s = 1; //seconds
33  const float us = s / 1e6;
34  const float ns = s / 1e9;
35 
36  const float V = 1; //volts
37 
38  const float Tesla = V * s / m / m; //Tesla=Vs/m^2
39  const float kGauss = Tesla / 10; //kGauss
40 
41  const float eps0 = 8.854e-12 * (C / V) / m; //Farads(=Coulombs/Volts) per meter
42  const float epsinv = 1 / eps0; //Vcm/C
43  const float k_perm = 1 / (4 * 3.1416 * eps0); //implied units of V*cm/C because we're doing unitful work here.
44 
45  public:
47  {
52  }; //note that 'OnLowEdge' is qualitatively different from 'OnHighEdge'. Low means there is a non-zero distance between the point and the edge of the bin. High applies even if that distance is exactly zero.
54  {
60  };
61  //Full3D = uses (nr x nphi x nz)^2 lookup table
62  //Hybrid = uses (nr x nphi x nz) x (nr_local x nphi_local x nz_local) + (nr_low x nphi_low x nz_low)^2 set of tables
63  //PhiSlice = uses (nr x 1 x nz) x (nr x nphi x nz) lookup table exploiting phi symmetry.
64  //Analytic = doesn't use lookup tables -- no memory footprint, uses analytic E field at center of each bin.
65  // Note that this is not the same as analytic propagation, which checks the analytic field integrals in each step.
66  //NoLookup = Don't build any structures -- effectively ignores any calculated spacecharge field
68  {
72  }; //load from file, load from AnalyticFieldModel, or set to zero.
73  //note that if we set to Zero, we skip the lookup step.
74 
75  //debug items
76  //
81 
83 
84  //the other half of the detector:
85  AnnularFieldSim *twin = nullptr;
86  bool hasTwin = false;
87 
88  //constants of motion, dimensions, etc:
89  //
90  TVector3 zero_vector; //a shorthand way to return a vectorial zero.
91  //static constexpr float k=8.987e13;//=1/(4*pi*eps0) in N*cm^2/C^2 in a vacuum. N*cm^2/C units, so that we supply space charge in coulomb units.
92  //static constexpr float k_perm=8.987e11;//=1/(4*pi*eps0) in (V*cm)/C in a vacuum. so that we supply space charge in Coulombs, distance in cm, and fields in V/cm
93 
94  //gas constants:
95  double vdrift = NAN; //gas drift speed in cm/s
96  double langevin_T1 = NAN;
97  double langevin_T2 = NAN; //gas tensor drift terms.
98  double omegatau_nominal = NAN; //nominal omegatau value, derived from vdrift and field strengths.
99  //double vprime; //first derivative of drift velocity at specific E
100  //double vprime2; //second derivative of drift velocity at specific E
101 
102  //field constants:
103  std::string fieldstring;
104  std::string Bfieldname;
105  std::string Efieldname;
106  // char fieldstring[300],Bfieldname[100],Efieldname[100];
107  std::string chargefilename;
108  char chargestring[300] = {0}; //, chargefilename[100];
109  float Enominal = NAN; //magnitude of the nominal field on which drift speed is based, in V/cm.
110  float Bnominal; //magnitude of the nominal magnetic field on which drift speed is based, in Tesla.
111 
112  //physical dimensions
113  float phispan; //angular span of the area in the phi direction, since TVector3 is too smart.
114  float rmin, rmax; //inner and outer radii of the annulus
115  float zmin, zmax; //lower and upper edges of the coordinate system in z (not fully implemented yet)
116  //float phimin, phimax;//not implemented at all yet.
117  TVector3 dim; //dimensions of simulated region, in cm
118  Rossegger *green; //stand-alone class to compute greens functions.
119  float green_shift; //how far to offset our position in z when querying our green's functions.
120 
121  //variables related to the whole-volume tiling:
122  //
123  int nr, nphi, nz; //number of fundamental bins (f-bins) in each direction = dimensions of 3D array covering entire volume
124  TVector3 step; //size of an f-bin in each direction
125  LookupCase lookupCase; //which lookup system to instantiate and use.
126  ChargeCase chargeCase; //which charge model to use
127  int truncation_length; //distance in cells (full 3D metric in units of bins)
128 
129  //variables related to the region of interest:
130  //
131  int rmin_roi, phimin_roi, zmin_roi; //lower edge of our region of interest, measured in f-bins
132  int rmax_roi, phimax_roi, zmax_roi; //excluded upper edge of our region of interest, measured in f-bins
133  int nr_roi, nphi_roi, nz_roi; //dimensions of our roi in f-bins
134 
135  //variables related to the high-res behavior:
136  //
137  int nr_high = -1;
138  int nphi_high = -1;
139  int nz_high = -1; //dimensions, in f-bins of neighborhood of a f-bin in which we calculate the field in full resolution
140 
141  //variables related to the low-res behavior:
142  //
143  int r_spacing = -1;
144  int phi_spacing = -1;
145  int z_spacing = -1; //number of f-bins, in each direction, to gang together to make a single low-resolution bin (l-bin)
146  int nr_low = -1;
147  int nphi_low = -1;
148  int nz_low = -1; //dimensions, in l-bins, of the entire volume
149  int rmin_roi_low = -1;
150  int phimin_roi_low = -1;
151  int zmin_roi_low = -1; //lowest l-bin that is at least partly in our region of interest
152  int rmax_roi_low = -1;
153  int phimax_roi_low = -1;
154  int zmax_roi_low = -1; //excluded upper edge l-bin of our region of interest
155  int nr_roi_low = -1;
156  int nphi_roi_low = -1;
157  int nz_roi_low = -1; //dimensions of our roi in l-bins
158 
159  //3- and 6-dimensional arrays to handle bin and bin-to-bin data
160  //
161  MultiArray<TVector3> *Efield; //total electric field in each f-bin in the roi for given configuration of charge AND external field.
162  MultiArray<TVector3> *Epartial_highres; //electric field in each f-bin in the roi from charge in a given f-bin or summed bin in the high res region.
163  MultiArray<TVector3> *Epartial_lowres; //electric field in each l-bin in the roi from charge in a given l-bin anywhere in the volume.
164  MultiArray<TVector3> *Epartial; //electric field for the old brute-force model.
165  MultiArray<TVector3> *Epartial_phislice; //electric field in a 2D phi-slice from the full 3D region.
166  MultiArray<TVector3> *Eexternal; //externally applied electric field in each f-bin in the roi
167  MultiArray<TVector3> *Bfield; //magnetic field in each f-bin in the roi
168  MultiArray<double> *q; //space charge in each f-bin in the whole volume
169  MultiArray<double> *q_local; //temporary holder of space charge in each f-bin and summed bin of the high-res region.
170  MultiArray<double> *q_lowres; //space charge in each l-bin. = sums over sets of f-bins.
171 
172  public:
173  //constructors with history for backwards compatibility
174  AnnularFieldSim(float rmin, float rmax, float dz, int r, int phi, int z, float vdr); //abbr. constructor with roi=full region
175  AnnularFieldSim(float rin, float rout, float dz,
176  int r, int roi_r0, int roi_r1,
177  int phi, int roi_phi0, int roi_phi1,
178  int z, int roi_z0, int roi_z1,
179  float vdr, LookupCase in_lookupCase = PhiSlice);
180  AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
181  int r, int roi_r0, int roi_r1, int in_rLowSpacing, int in_rHighSize,
182  int phi, int roi_phi0, int roi_phi1, int in_phiLowSpacing, int in_phiHighSize,
183  int z, int roi_z0, int roi_z1, int in_zLowSpacing, int in_zHighSize,
184  float vdr, LookupCase in_lookupCase);
185  AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
186  int r, int roi_r0, int roi_r1, int in_rLowSpacing, int in_rHighSize,
187  int phi, int roi_phi0, int roi_phi1, int in_phiLowSpacing, int in_phiHighSize,
188  int z, int roi_z0, int roi_z1, int in_zLowSpacing, int in_zHighSize,
189  float vdr, LookupCase in_lookupCase, ChargeCase in_chargeCase);
190 
191  //debug functions:
192  void UpdateEveryN(int n)
193  {
194  debug_npercent = n;
196  return;
197  };
198  bool debugFlag()
199  {
201  {
202  debug_printCounter = 0;
203  return true;
204  }
205  return false;
206  };
207  void SetDistortionScaleRPZ(float a, float b, float c)
208  {
209  debug_distortionScale.SetXYZ(a, b, c);
210  return;
211  };
213  {
215  return;
216  }
217 
218  //getters for internal states:
219  const char *GetLookupString();
220  const char *GetGasString();
221  const char *GetFieldString();
222  const char *GetChargeString() { return chargestring; };
223  float GetNominalB() { return Bnominal; };
224  float GetNominalE() { return Enominal; };
225  float GetChargeAt(TVector3 pos);
226  TVector3 GetFieldAt(TVector3 pos);
227  TVector3 GetBFieldAt(TVector3 pos);
228  TVector3 GetFieldStep() { return step; };
229  int GetFieldStepsR() { return nr_roi; };
230  int GetFieldStepsPhi() { return nphi_roi; };
231  int GetFieldStepsZ() { return nz_roi; };
232  TVector3 GetInnerEdge() { return TVector3(rmin, 0, zmin); };
233  TVector3 GetOuterEdge() { return TVector3(rmax, 0, zmax); };
234 
235  //file-writing functions for complex mapping questions:
236  void GenerateDistortionMaps(const char *filebase, int r_subsamples = 1, int p_subsamples = 1, int z_subsamples = 1, int z_substeps = 1, bool andCartesian = false);
237  void GenerateSeparateDistortionMaps(const char *filebase, int r_subsamples = 1, int p_subsamples = 1, int z_subsamples = 1, int z_substeps = 1, bool andCartesian = false);
238  void PlotFieldSlices(const std::string &filebase, TVector3 pos, char which = 'E');
239 
240  void load_spacecharge(const std::string &filename, const std::string &histname, float zoffset = 0, float chargescale = 1, float cmscale = 1, bool isChargeDensity = true);
241  void load_spacecharge(TH3F *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity);
242 
243  void load_and_resample_spacecharge(int new_nphi, int new_nr, int new_nz, const std::string &filename, const std::string &histname, float zoffset, float chargescale, float cmscale, bool isChargeDensity);
244 
245  void load_and_resample_spacecharge(int new_nphi, int new_nr, int new_nz, TH3F *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity);
246 
247  void load_analytic_spacecharge(float scalefactor);
248  void add_testcharge(float r, float phi, float z, float coulombs);
249 
250  void setNominalB(float x)
251  {
252  Bnominal = x;
253  UpdateOmegaTau();
254  return;
255  };
256  void setNominalE(float x)
257  {
258  Enominal = x;
259  UpdateOmegaTau();
260  return;
261  };
262  void setFlatFields(float B, float E);
263  void loadEfield(const std::string &filename, const std::string &treename, int zsign = 1);
264  void loadBfield(const std::string &filename, const std::string &treename);
265  void load3dBfield(const std::string &filename, const std::string &treename, int zsign = 1, float scale = 1.0);
266 
267  void loadField(MultiArray<TVector3> **field, TTree *source, float *rptr, float *phiptr, float *zptr, float *frptr, float *fphiptr, float *fzptr, float fieldunit, int zsign);
268 
269  void load_rossegger(double epsilon = 1E-4)
270  {
271  green = new Rossegger(rmin, rmax, zmax, epsilon);
272  return;
273  };
274  void borrow_rossegger(Rossegger *ross, float zshift)
275  {
276  green = ross;
277  green_shift = zshift;
278  return;
279  }; //get an already-existing rossegger table instead of loading it ourselves.
280  void borrow_epartial_from(AnnularFieldSim *sim, float zshift)
281  {
283  green_shift = zshift;
284  return;
285  }; //get an already-existing rossegger table instead of loading it ourselves.
287  {
288  twin = sim;
289  hasTwin = true;
290  return;
291  }; //define a twin to handle the negative-z drifting. If asked to drift something out of range in z, if the twin flag is set we will ask the twin to do the drifting. Note that the twin does not get linked back in to this side. It is only the follower.
292 
293  TVector3 calc_unit_field(TVector3 at, TVector3 from);
294  TVector3 analyticFieldIntegral(float zdest, TVector3 start) { return analyticFieldIntegral(zdest, start, Efield); };
295 
296  TVector3 analyticFieldIntegral(float zdest, TVector3 start, MultiArray<TVector3> *field);
297  TVector3 interpolatedFieldIntegral(float zdest, TVector3 start) { return interpolatedFieldIntegral(zdest, start, Efield); };
298  TVector3 interpolatedFieldIntegral(float zdest, TVector3 start, MultiArray<TVector3> *field);
299  double FilterPhiPos(double phi); //puts phi in 0<phi<2pi
300  int FilterPhiIndex(int phi, int range); //puts phi in bin range 0<phi<range. defaults to using nphi for range.
301 
302  TVector3 GetCellCenter(int r, int phi, int z);
303  TVector3 GetRoiCellCenter(int r, int phi, int z);
304  TVector3 GetGroupCellCenter(int r0, int r1, int phi0, int phi1, int z0, int z1);
305  TVector3 GetWeightedCellCenter(int r, int phi, int z);
306  TVector3 fieldIntegral(float zdest, TVector3 start, MultiArray<TVector3> *field);
307  void populate_fieldmap();
308  //now handled by setting 'analytic' lookup: void populate_analytic_fieldmap();
309  void populate_lookup();
310  void populate_full3d_lookup();
312  void populate_lowres_lookup();
314 
315  void load_phislice_lookup(const char *sourcefile);
316  void save_phislice_lookup(const char *destfile);
317 
318  TVector3 sum_field_at(int r, int phi, int z);
319  TVector3 sum_full3d_field_at(int r, int phi, int z);
320  TVector3 sum_local_field_at(int r, int phi, int z);
321  TVector3 sum_nonlocal_field_at(int r, int phi, int z);
322  TVector3 sum_phislice_field_at(int r, int phi, int z);
323  TVector3 swimToInAnalyticSteps(float zdest, TVector3 start, int steps, int *goodToStep);
324  TVector3 swimToInSteps(float zdest, TVector3 start, int steps, bool interpolate, int *goodToStep);
325  TVector3 swimTo(float zdest, TVector3 start, bool interpolate = true, bool useAnalytic = false);
326  TVector3 GetStepDistortion(float zdest, TVector3 start, bool interpolate = true, bool useAnalytic = false);
327  TVector3 GetTotalDistortion(float zdest, TVector3 start, int nsteps, bool interpolate = true, int *goodToStep = 0);
328 
329  private:
333  int GetRindex(float pos);
334  int GetPhiIndex(float pos);
335  int GetZindex(float pos);
336 
338  {
340  return;
341  }; //various constants to match internal representation to the familiar formula. Adding in these factors suggests I should switch to a unitful calculation throughout...
342 };
343 
344 #ifndef MULTIARRAY
345 #define MULTIARRAY
346 template <class T>
347 class MultiArray : public TObject
348 {
349  //class to hold an up-to-six dimensional array of whatever T is. Any indices not used are flattened. This should probably be replaced with sets of TH3s...
350  public:
351  static const int MAX_DIM = 6;
352  int dim;
353  int n[6];
354  int length;
356 
357  MultiArray(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0)
358  {
359  int n_[6];
360  for (int i = 0; i < MAX_DIM; i++)
361  n[i] = 0;
362  n_[0] = a;
363  n_[1] = b;
364  n_[2] = c;
365  n_[3] = d;
366  n_[4] = e;
367  n_[5] = f;
368  length = 1;
369  dim = MAX_DIM;
370  for (int i = 0; i < dim; i++)
371  {
372  if (n_[i] < 1)
373  {
374  dim = i;
375  break;
376  }
377  n[i] = n_[i];
378  length *= n[i];
379  }
380  field = static_cast<T *>(malloc(length * sizeof(T)));
381  //field=(T)( malloc(length*sizeof(T) ));
382  //for (int i=0;i<length;i++) field[i].SetXYZ(0,0,0);
383  }
385  {
386  free(field);
387  }
388 
389  void Add(int a, int b, int c, T in)
390  {
391  Add(a, b, c, 0, 0, 0, in);
392  return;
393  };
394  void Add(int a, int b, int c, int d, int e, int f, T in)
395  {
396  int n_[6];
397  n_[0] = a;
398  n_[1] = b;
399  n_[2] = c;
400  n_[3] = d;
401  n_[4] = e;
402  n_[5] = f;
403  int index = n_[0];
404  for (int i = 1; i < dim; i++)
405  {
406  index = (index * n[i]) + n_[i];
407  }
408  field[index] = field[index] + in;
409  return;
410  }
411 
412  T Get(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0)
413  {
414  int n_[6];
415  n_[0] = a;
416  n_[1] = b;
417  n_[2] = c;
418  n_[3] = d;
419  n_[4] = e;
420  n_[5] = f;
421  int index = 0;
422  for (int i = 0; i < dim; i++)
423  {
424  if (n[i] <= n_[i] || n_[i] < 0)
425  { //check bounds
426  printf("asking for el %d %d %d %d %d %d. %dth element is outside of bounds 0<x<%d\n", n_[0], n_[1], n_[2], n_[3], n_[4], n_[5], n_[i], n[i]);
427  assert(false);
428  }
429  index = (index * n[i]) + n_[i];
430  }
431  return field[index];
432  }
433  T *GetPtr(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0)
434  { //faster for repeated access.
435  int n_[6];
436  n_[0] = a;
437  n_[1] = b;
438  n_[2] = c;
439  n_[3] = d;
440  n_[4] = e;
441  n_[5] = f;
442  int index = n_[0];
443  for (int i = 1; i < dim; i++)
444  {
445  index = (index * n[i]) + n_[i];
446  }
447  return &(field[index]);
448  }
449 
450  T *GetFlat(int a = 0)
451  {
452  if (a >= length) assert(false); //check bounds
453  return &(field[a]);
454  }
455 
456  int Length()
457  {
458  return length;
459  }
460 
461  void Set(int a, int b, int c, T in)
462  {
463  Set(a, b, c, 0, 0, 0, in);
464  return;
465  };
466  void Set(int a, int b, int c, int d, int e, int f, T in)
467  {
468  int n_[6];
469  n_[0] = a;
470  n_[1] = b;
471  n_[2] = c;
472  n_[3] = d;
473  n_[4] = e;
474  n_[5] = f;
475  int index = n_[0];
476  for (int i = 1; i < dim; i++)
477  {
478  index = (index * n[i]) + n_[i];
479  }
480  field[index] = in;
481  return;
482  }
483 };
484 #endif //MULTIARRAY