Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 #include "Rossegger.h"
0002 
0003 #include <TVector3.h>
0004 
0005 #include <cmath>
0006 #include <limits>
0007 #include <string>
0008 
0009 class AnalyticFieldModel;
0010 class ChargeMapReader;
0011 class TH2;
0012 class TH3;
0013 class TTree;
0014 
0015 template <class T>
0016 class MultiArray;
0017 
0018 class AnnularFieldSim
0019 {
0020  public:
0021   enum BoundsCase
0022   {
0023     InBounds,
0024     OnHighEdge,
0025     OnLowEdge,
0026     OutOfBounds
0027   };  // 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.
0028   enum LookupCase
0029   {
0030     Full3D,
0031     HybridRes,
0032     PhiSlice,
0033     Analytic,
0034     NoLookup
0035   };
0036   // Full3D = uses (nr x nphi x nz)^2 lookup table
0037   // 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
0038   // PhiSlice = uses (nr x 1 x nz) x (nr x nphi x nz) lookup table exploiting phi symmetry.
0039   // Analytic = doesn't use lookup tables -- no memory footprint, uses analytic E field at center of each bin.
0040   //     Note that this is not the same as analytic propagation, which checks the analytic field integrals in each step.
0041   // NoLookup = Don't build any structures -- effectively ignores any calculated spacecharge field
0042   enum ChargeCase
0043   {
0044     FromFile,
0045     AnalyticSpacecharge,
0046     NoSpacecharge
0047   };  // load from file, load from AnalyticFieldModel, or set to zero.
0048   // note that if we set to Zero, we skip the lookup step.
0049 
0050   // constructors with history for backwards compatibility
0051   AnnularFieldSim(float rin, float rout, float dz, int r, int phi, int z, float vdr);  // abbr. constructor with roi=full region
0052   AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
0053                   int r, int roi_r0, int roi_r1,
0054                   int phi, int roi_phi0, int roi_phi1,
0055                   int z, int roi_z0, int roi_z1,
0056                   float vdr, LookupCase in_lookupCase = PhiSlice);
0057   AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
0058                   int r, int roi_r0, int roi_r1, int in_rLowSpacing, int in_rHighSize,
0059                   int phi, int roi_phi0, int roi_phi1, int in_phiLowSpacing, int in_phiHighSize,
0060                   int z, int roi_z0, int roi_z1, int in_zLowSpacing, int in_zHighSize,
0061                   float vdr, LookupCase in_lookupCase);
0062   AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
0063                   int r, int roi_r0, int roi_r1, int in_rLowSpacing, int in_rHighSize,
0064                   int phi, int roi_phi0, int roi_phi1, int in_phiLowSpacing, int in_phiHighSize,
0065                   int z, int roi_z0, int roi_z1, int in_zLowSpacing, int in_zHighSize,
0066                   float vdr, LookupCase in_lookupCase, ChargeCase in_chargeCase);
0067   //! delete copy ctor and assignment opertor (cppcheck)
0068   explicit AnnularFieldSim(const AnnularFieldSim &) = delete;
0069   AnnularFieldSim &operator=(const AnnularFieldSim &) = delete;
0070 
0071   // debug functions:
0072   void UpdateEveryN(int n)
0073   {
0074     debug_npercent = n;
0075     debug_printActionEveryN = 0;
0076     return;
0077   };
0078   bool debugFlag()
0079   {
0080     if (debug_printActionEveryN > 0 && debug_printCounter++ >= debug_printActionEveryN)
0081     {
0082       debug_printCounter = 0;
0083       return true;
0084     }
0085     return false;
0086   };
0087   void SetDistortionScaleRPZ(float a, float b, float c)
0088   {
0089     debug_distortionScale.SetXYZ(a, b, c);
0090     return;
0091   };
0092   void SetTruncationDistance(int x)
0093   {
0094     truncation_length = x;
0095     return;
0096   }
0097 
0098   // getters for internal states:
0099   std::string GetLookupString();
0100   std::string GetGasString();
0101   std::string GetFieldString();
0102   const std::string &GetChargeString() { return chargestring; };
0103   float GetNominalB() { return Bnominal; };
0104   float GetNominalE() { return Enominal; };
0105   float GetChargeAt(const TVector3 &pos);
0106   TVector3 GetLocalFieldComponents(const TVector3 &field, const TVector3 &pos, const TVector3 &origin);
0107   TVector3 GetFieldAt(const TVector3 &pos);
0108   TVector3 GetBFieldAt(const TVector3 &pos);
0109   TVector3 GetFieldStep() { return step; };
0110   int GetFieldStepsR() { return nr_roi; };
0111   int GetFieldStepsPhi() { return nphi_roi; };
0112   int GetFieldStepsZ() { return nz_roi; };
0113   TVector3 GetInnerEdge() { return TVector3(rmin, 0, zmin); };
0114   TVector3 GetOuterEdge() { return TVector3(rmax, 0, zmax); };
0115 
0116   // file-writing functions for complex mapping questions:
0117   void GenerateDistortionMaps(const std::string &filebase, int r_subsamples = 1, int p_subsamples = 1, int z_subsamples = 1, int z_substeps = 1, bool andCartesian = false);
0118 
0119   void GenerateSeparateDistortionMaps(const std::string &filebase, int nSteps = 500, int r_subsamples = 1, int p_subsamples = 1, int z_subsamples = 1, int z_substeps = 1, bool andCartesian = false);
0120 
0121   void PlotFieldSlices(const std::string &filebase, const TVector3 &pos, char which = 'E');
0122 
0123   void load_spacecharge(const std::string &filename, const std::string &histname, float zoffset = 0, float chargescale = 1, float cmscale = 1, bool isChargeDensity = true);
0124   void load_spacecharge(TH3 *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity, const std::string &inputchargestring = "");
0125 
0126   void load_digital_current(TH3 *hist, TH2 *gainHist, float chargescale, float cmscale, const std::string &inputchargestring);
0127 
0128   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);
0129 
0130   void load_and_resample_spacecharge(int new_nphi, int new_nr, int new_nz, TH3 *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity);
0131   void save_spacecharge(const std::string &filename);
0132   void load_analytic_spacecharge(float scalefactor);
0133   void add_testcharge(float r, float phi, float z, float coulombs);
0134 
0135   void setNominalB(float x)
0136   {
0137     Bnominal = x;
0138     UpdateOmegaTau();
0139     return;
0140   };
0141   void setNominalE(float x)
0142   {
0143     Enominal = x;
0144     UpdateOmegaTau();
0145     return;
0146   };
0147   void setFlatFields(float B, float E);
0148   void loadEfield(const std::string &filename, const std::string &treename, int zsign = 1);
0149   void loadBfield(const std::string &filename, const std::string &treename);
0150   void load3dBfield(const std::string &filename, const std::string &treename, int zsign = 1, float scale = 1.0, float xshift = 0, float yshift = 0, float zshift = 0);
0151 
0152   void loadField(MultiArray<TVector3> **field, TTree *source, const float *rptr, const float *phiptr, const float *zptr, const float *frptr, const float *fphiptr, const float *fzptr, float fieldunit, int zsign, float xshift = 0, float yshift = 0, float zshift = 0);
0153 
0154   void load_rossegger(double epsilon = 1E-4)
0155   {
0156     green = new Rossegger(rmin, rmax, zmax, epsilon);
0157     return;
0158   };
0159   void borrow_rossegger(Rossegger *ross, float zshift)
0160   {
0161     green = ross;
0162     green_shift = zshift;
0163     printf("AnnularFieldSim::borrow_rossegger:  borrowed Rossegger table with zshift %f\n", zshift);
0164     return;
0165   };  // get an already-existing rossegger table instead of loading it ourselves.
0166   void borrow_epartial_from(AnnularFieldSim *sim, float zshift)
0167   {
0168     Epartial_phislice = sim->Epartial_phislice;
0169     green_shift = zshift;
0170     printf("AnnularFieldSim::borrow_epartial_from:  borrowed Epartial_phislice table with zshift %f\n", zshift);
0171     return;
0172   };  // get an already-existing rossegger table instead of loading it ourselves.
0173   void set_twin(AnnularFieldSim *sim)
0174   {
0175     twin = sim;
0176     hasTwin = true;
0177     return;
0178   };  // 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.
0179 
0180   TVector3 calc_unit_field(TVector3 at, TVector3 from);
0181   TVector3 analyticFieldIntegral(float zdest, TVector3 start) { return analyticFieldIntegral(zdest, start, Efield); };
0182 
0183   TVector3 analyticFieldIntegral(float zdest, TVector3 start, MultiArray<TVector3> *field);
0184   TVector3 interpolatedFieldIntegral(float zdest, TVector3 start) { return interpolatedFieldIntegral(zdest, start, Efield); };
0185   TVector3 interpolatedFieldIntegral(float zdest, const TVector3 &start, MultiArray<TVector3> *field);
0186   double FilterPhiPos(double phi);               // puts phi in 0<phi<2pi
0187   int FilterPhiIndex(int phi, int range) const;  // puts phi in bin range 0<phi<range.  defaults to using nphi for range.
0188 
0189   TVector3 GetCellCenter(int r, int phi, int z);
0190   TVector3 GetRoiCellCenter(int r, int phi, int z);
0191   TVector3 GetGroupCellCenter(int r0, int r1, int phi0, int phi1, int z0, int z1);
0192   TVector3 GetWeightedCellCenter(int r, int phi, int z);
0193   TVector3 fieldIntegral(float zdest, const TVector3 &start, MultiArray<TVector3> *field);
0194   void populate_fieldmap();
0195   // now handled by setting 'analytic' lookup:  void populate_analytic_fieldmap();
0196   void populate_lookup();
0197   void populate_full3d_lookup();
0198   void populate_highres_lookup();
0199   void populate_lowres_lookup();
0200   void populate_phislice_lookup();
0201 
0202   void load_phislice_lookup(const std::string &sourcefile);
0203   void save_phislice_lookup(const std::string &destfile);
0204 
0205   Rossegger *green;   // stand-alone class to compute greens functions.
0206   float green_shift;  // how far to offset our position in z when querying our green's functions.
0207   AnnularFieldSim *twin = nullptr;
0208   bool hasTwin = false;
0209 
0210   TVector3 sum_field_at(int r, int phi, int z);
0211   TVector3 sum_full3d_field_at(int r, int phi, int z);
0212   TVector3 sum_local_field_at(int r, int phi, int z);
0213   TVector3 sum_nonlocal_field_at(int r, int phi, int z);
0214   TVector3 sum_phislice_field_at(int r, int phi, int z);
0215   TVector3 swimToInAnalyticSteps(float zdest, TVector3 start, int steps, int *goodToStep);
0216   TVector3 swimToInSteps(float zdest, const TVector3 &start, int steps, bool interpolate, int *goodToStep);
0217   TVector3 swimTo(float zdest, const TVector3 &start, bool interpolate = true, bool useAnalytic = false);
0218   TVector3 GetStepDistortion(float zdest, const TVector3 &start, bool interpolate = true, bool useAnalytic = false);
0219   TVector3 GetTotalDistortion(float zdest, const TVector3 &start, int nsteps, bool interpolate = true, int *goodToStep = 0, int *success = 0);
0220 
0221  private:
0222   BoundsCase GetRindexAndCheckBounds(float pos, int *r);
0223   BoundsCase GetPhiIndexAndCheckBounds(float pos, int *phi);
0224   BoundsCase GetZindexAndCheckBounds(float pos, int *z);
0225   int GetRindex(float pos);
0226   int GetPhiIndex(float pos);
0227   int GetZindex(float pos);
0228 
0229   void UpdateOmegaTau()
0230   {
0231     omegatau_nominal = -Bnominal * vdrift / std::abs(Enominal);
0232     return;
0233   };  // various constants to match internal representation to the familiar formula.  Adding in these factors suggests I should switch to a unitful calculation throughout...
0234   // units:
0235   const float cm = 1;        // centimeters -- if you change this, check that all the loading functions are properly agnostic.
0236   const float m = 100 * cm;  // meters.
0237   const float mm = cm / 10;
0238   const float um = mm / 1e3;
0239 
0240   const float C = 1;  // Coulombs
0241   const float nC = C / 1e9;
0242   const float fC = C / 1e15;
0243 
0244   const float s = 1;  // seconds
0245   const float us = s / 1e6;
0246   const float ns = s / 1e9;
0247 
0248   const float V = 1;  // volts
0249 
0250   const float Tesla = V * s / m / m;  // Tesla=Vs/m^2
0251   const float kGauss = Tesla / 10;    // kGauss
0252 
0253   const float eps0 = 8.854e-12 * (C / V) / m;    // Farads(=Coulombs/Volts) per meter
0254   const float epsinv = 1 / eps0;                 // Vcm/C
0255   const float k_perm = 1 / (4 * 3.1416 * eps0);  // implied units of V*cm/C because we're doing unitful work here.
0256   // debug items
0257   // bool
0258   bool RdeltaRswitch = false;
0259   int debug_printActionEveryN;
0260   int debug_npercent;
0261   int debug_printCounter;
0262   TVector3 debug_distortionScale;
0263 
0264   AnalyticFieldModel *aliceModel = nullptr;
0265 
0266   // the other half of the detector:
0267 
0268   // constants of motion, dimensions, etc:
0269   //
0270   TVector3 zero_vector;  // a shorthand way to return a vectorial zero.
0271   // 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.
0272   // 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
0273 
0274   // gas constants:
0275   double vdrift = std::numeric_limits<double>::quiet_NaN();  // gas drift speed in cm/s
0276   double langevin_T1 = std::numeric_limits<double>::quiet_NaN();
0277   double langevin_T2 = std::numeric_limits<double>::quiet_NaN();       // gas tensor drift terms.
0278   double omegatau_nominal = std::numeric_limits<double>::quiet_NaN();  // nominal omegatau value, derived from vdrift and field strengths.
0279   // double vprime; //first derivative of drift velocity at specific E
0280   // double vprime2; //second derivative of drift velocity at specific E
0281 
0282   // field constants:
0283   std::string fieldstring;
0284   std::string Bfieldname;
0285   std::string Efieldname;
0286   //  char fieldstring[300],Bfieldname[100],Efieldname[100];
0287   std::string chargesourcename;
0288   std::string chargestring;
0289   float Enominal = std::numeric_limits<double>::quiet_NaN();  // magnitude of the nominal field on which drift speed is based, in V/cm.
0290   float Bnominal;                                             // magnitude of the nominal magnetic field on which drift speed is based, in Tesla.
0291 
0292   // physical dimensions
0293   float phispan;     // angular span of the area in the phi direction, since TVector3 is too smart.
0294   float rmin, rmax;  // inner and outer radii of the annulus
0295   float zmin, zmax;  // lower and upper edges of the coordinate system in z (not fully implemented yet)
0296   // float phimin, phimax;//not implemented at all yet.
0297   TVector3 dim;  // dimensions of simulated region, in cm
0298 
0299   // variables related to the whole-volume tiling:
0300   //
0301   int nr, nphi, nz;       // number of fundamental bins (f-bins) in each direction = dimensions of 3D array covering entire volume
0302   TVector3 step;          // size of an f-bin in each direction
0303   LookupCase lookupCase;  // which lookup system to instantiate and use.
0304   ChargeCase chargeCase;  // which charge model to use
0305   int truncation_length;  // distance in cells (full 3D metric in units of bins)
0306 
0307   // variables related to the region of interest:
0308   //
0309   int rmin_roi, phimin_roi, zmin_roi;  // lower edge of our region of interest, measured in f-bins
0310   int rmax_roi, phimax_roi, zmax_roi;  // excluded upper edge of our region of interest, measured in f-bins
0311   int nr_roi, nphi_roi, nz_roi;        // dimensions of our roi in f-bins
0312 
0313   // variables related to the high-res behavior:
0314   //
0315   int nr_high{-1};
0316   int nphi_high{-1};
0317   int nz_high{-1};  // dimensions, in f-bins of neighborhood of a f-bin in which we calculate the field in full resolution
0318 
0319   // variables related to the low-res behavior:
0320   //
0321   int r_spacing{-1};
0322   int phi_spacing{-1};
0323   int z_spacing{-1};  // number of f-bins, in each direction, to gang together to make a single low-resolution bin (l-bin)
0324   int nr_low{-1};
0325   int nphi_low{-1};
0326   int nz_low{-1};  // dimensions, in l-bins, of the entire volume
0327   int rmin_roi_low{-1};
0328   int phimin_roi_low{-1};
0329   int zmin_roi_low{-1};  // lowest l-bin that is at least partly in our region of interest
0330   int rmax_roi_low{-1};
0331   int phimax_roi_low{-1};
0332   int zmax_roi_low{-1};  // excluded upper edge l-bin of our region of interest
0333   int nr_roi_low{-1};
0334   int nphi_roi_low{-1};
0335   int nz_roi_low{-1};  // dimensions of our roi in l-bins
0336 
0337   // 3- and 6-dimensional arrays to handle bin and bin-to-bin data
0338   //
0339   MultiArray<TVector3> *Efield;             // total electric field in each f-bin in the roi for given configuration of charge AND external field.
0340   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.
0341   MultiArray<TVector3> *Epartial_lowres;    // electric field in each l-bin in the roi from charge in a given l-bin anywhere in the volume.
0342   MultiArray<TVector3> *Epartial;           // electric field for the old brute-force model.
0343   MultiArray<TVector3> *Epartial_phislice;  // electric field in a 2D phi-slice from the full 3D region.
0344   MultiArray<TVector3> *Eexternal;          // externally applied electric field in each f-bin in the roi
0345   MultiArray<TVector3> *Bfield;             // magnetic field in each f-bin in the roi
0346 
0347   ChargeMapReader *q;            // //class to read and report charge.
0348                                  //  MultiArray<double> *q;                    //space charge in each f-bin in the whole volume
0349   MultiArray<double> *q_local;   // temporary holder of space charge in each f-bin and summed bin of the high-res region.
0350   MultiArray<double> *q_lowres;  // space charge in each l-bin. = sums over sets of f-bins.
0351   TH2 *hRdeltaRComponent{nullptr};
0352 };