Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 #include "ChargeMapReader.h"
0002 
0003 #include "MultiArray.h"
0004 
0005 #include <TAxis.h>
0006 #include <TFile.h>
0007 #include <TH2.h>
0008 #include <TH3.h>
0009 
0010 #include <cassert>
0011 #include <cmath>
0012 #include <format>
0013 #include <iostream>
0014 
0015 #define DEBUG false
0016 
0017 ChargeMapReader::ChargeMapReader()
0018   : ChargeMapReader(20, 20.0, 78.0, 20, 0, 2 * M_PI, 40, -105.5, 105.5)
0019 {
0020   std::cout << "made a new ChargeMapReader with default values -- cascading to next constructor" << std::endl;
0021   return;
0022 }
0023 
0024 ChargeMapReader::ChargeMapReader(int _nr, float _rmin, float _rmax, int _nphi, float _phimin, float _phimax, int _nz, float _zmin, float _zmax)
0025 {
0026   std::cout << std::format("made a new ChargeMapReader with defined values:\n {} {:.2f} {:.2f}\n {} {:.2f} {:.2f}\n {} {:.2f} {:.2f}", _nr, _rmin, _rmax, _nphi, _phimin, _phimax, _nz, _zmin, _zmax) << std::endl;
0027   SetOutputParameters(_nr, _rmin, _rmax, _nphi, _phimin, _phimax, _nz, _zmin, _zmax);
0028   return;
0029 }
0030 
0031 bool ChargeMapReader::CanInterpolateAt(float r, float phi, float z)
0032 {
0033   return CanInterpolateAt(phi, r, z, hChargeDensity);
0034 }
0035 
0036 // a convenient method to check whether it's safe to interpolate for a particular histogram.
0037 bool ChargeMapReader::CanInterpolateAt(float x, float y, float z, TH3* h)
0038 {
0039   if (h == nullptr)
0040   {
0041     return false;
0042   }
0043   float pos[3] = {x, y, z};
0044   // todo: is it worth keeping these values somewhere for ease of access?
0045   TAxis* ax[3] = {nullptr, nullptr, nullptr};
0046   ax[0] = h->GetXaxis();
0047   ax[1] = h->GetYaxis();
0048   ax[2] = h->GetZaxis();
0049 
0050   int nbins[3];
0051   for (int i = 0; i < 3; i++)
0052   {
0053     nbins[i] = ax[i]->GetNbins();  // number of bins, not counting under and overflow.
0054     if (nbins[i] == 1)
0055     {
0056       return false;  // if there's only one non over/under bin, then no place is safe to interpolate.
0057     }
0058   }
0059 
0060   //   0     1     2   ...   n-1    n    n+1
0061   // under|first|   ..|  ..  |  .. |last| over
0062   for (int i = 0; i < 3; i++)
0063   {  // check each axis:
0064     int axbin = ax[i]->FindBin(pos[i]);
0065 
0066     if (axbin < 1 || axbin > nbins[i])
0067     {
0068       return false;  // before the first bin, or after the last bin
0069     }
0070     if (axbin > 1 && axbin < nbins[i])
0071     {
0072       continue;  // if we're in a middle bin, we're fine on this axis.
0073     }
0074 
0075     // now we need to check if we're in the safe parts of the first and last bins:
0076     float low = ax[i]->GetBinLowEdge(axbin);
0077     float high = ax[i]->GetBinUpEdge(axbin);
0078     float binmid = 0.5 * (high + low);
0079 
0080     if (axbin == 1 && pos[i] <= binmid)
0081     {
0082       return false;  // we're in the first bin, but below the midpoint, so we would interpolate out of bounds
0083     }
0084     if (axbin == nbins[i] && pos[i] >= binmid)
0085     {
0086       return false;  // we're in the last bin, but above the midpoint, so we would interpolate out of bounds
0087     }
0088   }
0089 
0090   // if we get here, we are in the okay-range of all three axes.
0091   return true;
0092 }
0093 
0094 void ChargeMapReader::FillChargeHistogram(TH3* h)
0095 {
0096   // fills the provided histogram with the data from the internal representation.
0097 
0098   //   0     1     2     ...   n-1
0099   // first|   ..|  ..  |  .. |last
0100   float dphi;
0101   float dr;
0102   float dz;  // bin widths in each dimension.  Got too confusing to make these an array.
0103   if (DEBUG)
0104   {
0105     std::cout << "filling chargehistogram" << std::endl;
0106   }
0107 
0108   dr = binWidth[0];
0109   dphi = binWidth[1];
0110   dz = binWidth[2];
0111 
0112   float phimid;
0113   float zmid;  // midpoints at each step.
0114   int i[3];
0115   for (i[0] = 0; i[0] < nBins[0]; i[0]++)
0116   {  // r
0117     float rmid = lowerBound[0] + (i[0] + 0.5) * dr;
0118     for (i[1] = 0; i[1] < nBins[1]; i[1]++)
0119     {  // phi
0120       phimid = lowerBound[1] + (i[1] + 0.5) * dphi;
0121       for (i[2] = 0; i[2] < nBins[2]; i[2]++)
0122       {  // z
0123         zmid = lowerBound[2] + (i[2] + 0.5) * dz;
0124         h->Fill(phimid, rmid, zmid, charge->Get(i[0], i[1], i[2]));
0125       }  // z
0126     }    // phi
0127   }      // r
0128   return;
0129 }
0130 
0131 void ChargeMapReader::RegenerateCharge()
0132 {
0133   // Builds the charge 3D array (internal representation) from the internal charge density map.
0134   // either the density map has changed, or the binning of the output has changed (hopefully not the latter, because that's a very unusual thing to change mid-run.
0135   // we want to rebuild the charge per bin of our output representation in any case.  Generally, we will interpolate from the charge density that we know we have, but we need to be careful not to ask to interpolate in regions where that is not allowed.
0136   if (DEBUG)
0137   {
0138     std::cout << std::format("regenerating charge array contents with axis scale={:.2E} and charge scale={:.2E}", inputAxisScale, inputChargeScale) << std::endl;
0139   }
0140   if (hChargeDensity == nullptr)
0141   {
0142     // we don't have charge information, so set everything to zeroes
0143     std::cout << "no charge data found.  Setting all charges to 0.0" << std::endl;
0144     charge->SetAll(0);  // otherwise set the array to all zeroes.
0145     return;
0146   }
0147 
0148   //   0     1     2     ...   n-1
0149   // first|   ..|  ..  |  .. |last
0150   float dphi;
0151   float dr;
0152   float dz;  // internal rep bin widths in each dimension.  Got too confusing to make these an array.
0153   dr = binWidth[0];
0154   dphi = binWidth[1];
0155   dz = binWidth[2];
0156   float dzhist;
0157   float drhist;
0158   dzhist = dz / inputAxisScale;
0159   drhist = dr / inputAxisScale;
0160 
0161   float phimid;
0162   float zmid;  // position of the center of each fixed-width array bin, in the input histogram units
0163   // note that since we computed density using the hist units, we must use those units for the volume term again here.
0164   int i[3];
0165   for (i[0] = 0; i[0] < nBins[0]; i[0]++)
0166   {  // r
0167     float rmid = (lowerBound[0] + (i[0] + 0.5) * dr) / inputAxisScale;
0168     // float rlow = (lowerBound[0] + dr * i[0]) / inputAxisScale;
0169     float histBinVolume = dzhist * dphi * rmid * drhist;  // volume of our output bin in histogram units.
0170     // note that since we have equal bin widths, the volume term depends only on r.
0171     //  Q_bin=Density_ion_interp*bin_volume*(coulomb/ion)
0172     float scaleFactor = histBinVolume * inputChargeScale;  // hence the total scale factor is the volume term times the charge scale factor
0173     for (i[1] = 0; i[1] < nBins[1]; i[1]++)
0174     {  // phi
0175       phimid = lowerBound[1] + (i[1] + 0.5) * dphi;
0176       for (i[2] = 0; i[2] < nBins[2]; i[2]++)
0177       {  // z
0178         zmid = (lowerBound[2] + (i[2] + 0.5) * dz) / inputAxisScale;
0179         float q = 0;
0180         if (CanInterpolateAt(phimid, rmid, zmid, hChargeDensity))
0181         {  // interpolate if we can
0182           if (false)
0183           {  // deep debug statements.
0184             std::cout << std::format("function said we could interpolate at (r,phi,z)=({:.2f}, {:.2f},{:.2f}), bounds are:", rmid, phimid, zmid);
0185             std::cout << std::format("  r: {:.2f} < {:.2f} < {:.2f}", hChargeDensity->GetYaxis()->GetXmin(), rmid, (hChargeDensity->GetYaxis()->GetXmax()));
0186             std::cout << std::format("  p: {:.2f} < {:.2f} < {:.2f}", hChargeDensity->GetXaxis()->GetXmin(), phimid, hChargeDensity->GetXaxis()->GetXmax());
0187             std::cout << std::format("  z: {:.2f} < {:.2f} < {:.2f}", hChargeDensity->GetZaxis()->GetXmin(), zmid, hChargeDensity->GetZaxis()->GetXmax()) << std::endl;
0188           }
0189           q = scaleFactor * hChargeDensity->Interpolate(phimid, rmid, zmid);
0190         }
0191         else
0192         {  // otherwise, just take the central value and assume it's flat.  Better than a zero.
0193           q = scaleFactor * hChargeDensity->GetBinContent(hChargeDensity->FindBin(phimid, rmid, zmid));
0194         }
0195         if (false)
0196         {  // deep debug statements.
0197           int global = hSourceCharge->FindBin(phimid, rmid, zmid);
0198           if (CanInterpolateAt(phimid, rmid, zmid, hChargeDensity))
0199           {
0200             std::cout << std::format("density debug report (interp) (r,phi,z)=({:.2f}, {:.2f},{:.2f}), glob={}, q_dens={:E}", rmid, phimid, zmid, global, q);
0201             std::cout << std::format(", density={:E}, vol={:E}", (hChargeDensity->Interpolate(phimid, rmid, zmid)), histBinVolume);
0202             std::cout << std::format(", q_bin={:E}, q_bin_coul={:E}", (hSourceCharge->GetBinContent(hSourceCharge->FindBin(phimid, rmid, zmid))), (hSourceCharge->GetBinContent(hSourceCharge->FindBin(phimid, rmid, zmid)) * inputChargeScale));
0203             std::cout << std::format(", q_interp={:E}, q_bin_coul/vol={:E}", (hSourceCharge->Interpolate(phimid, rmid, zmid)), (hSourceCharge->GetBinContent(hSourceCharge->FindBin(phimid, rmid, zmid)) / histBinVolume)) << std::endl;
0204           }
0205           else
0206           {
0207             std::cout << std::format("density debug report (getbin) (r,phi,z)=({:.2f}, {:.2f},{:.2f}), glob={}, q_dens={:E}", rmid, phimid, zmid, global, q);
0208             std::cout << std::format(", density={:E}, vol={:E}", hChargeDensity->GetBinContent(hChargeDensity->FindBin(phimid, rmid, zmid)), histBinVolume);
0209             std::cout << std::format(", q_bin={:E}, q_bin_ions={:E}", hSourceCharge->GetBinContent(hSourceCharge->FindBin(phimid, rmid, zmid)), (hSourceCharge->GetBinContent(hSourceCharge->FindBin(phimid, rmid, zmid)) / inputChargeScale));
0210             std::cout << std::format(", q_bin_ions/vol={:E}", (hSourceCharge->GetBinContent(hSourceCharge->FindBin(phimid, rmid, zmid)) / scaleFactor)) << std::endl;
0211           }
0212         }
0213         charge->Set(i[0], i[1], i[2], q);
0214 
0215       }  // z
0216     }    // phi
0217   }      // r
0218 
0219   if (DEBUG)
0220   {
0221     std::cout << "done regenerating charge array contents" << std::endl;
0222   }
0223 
0224   return;
0225 }
0226 
0227 void ChargeMapReader::RegenerateDensity()
0228 {
0229   // assume the input map has changed, so we need to rebuild our internal representation of the density.
0230   // this is done by cloning the SourceCharge histogram and dividing each bin in it by its volume
0231   if (DEBUG)
0232   {
0233     std::cout << "regenerating density histogram" << std::endl;
0234   }
0235 
0236   // if we have one already, delete it.
0237   if (hChargeDensity != nullptr)
0238   {
0239     if (DEBUG)
0240     {
0241       std::cout << "deleting old density histogram" << std::endl;
0242     }
0243     delete hChargeDensity;
0244   }
0245   if (hSourceCharge == nullptr)
0246   {
0247     // the source data doesn't exist, so we will fail if we try to clone
0248     std::cout << "no source charge data file is open, or the histogram was not found." << std::endl;
0249     return;
0250   }
0251 
0252   // clone this from the source histogram, which we assume is open.
0253   hChargeDensity = static_cast<TH3*>(hSourceCharge->Clone("hChargeDensity"));
0254   hChargeDensity->Reset();
0255 
0256   // then go through it, bin by bin, and replace each bin content with the corresponding density, so we can interpolate correctly.
0257   // TODO:  Does this mean we once again need 'guard' bins?  Gross.
0258 
0259   TAxis* ax[3] = {nullptr, nullptr, nullptr};
0260   ax[0] = hChargeDensity->GetXaxis();
0261   ax[1] = hChargeDensity->GetYaxis();
0262   ax[2] = hChargeDensity->GetZaxis();
0263 
0264   int nbins[3];
0265   for (int i = 0; i < 3; i++)
0266   {
0267     nbins[i] = ax[i]->GetNbins();  // number of bins, not counting under and overflow.
0268   }
0269 
0270   //   0     1     2   ...   n-1    n    n+1
0271   // under|first|   ..|  ..  |  .. |last| over
0272   int i[3];
0273   float low[3];
0274   float high[3];
0275   float dr;
0276   float dz;  // bin widths in each dimension.  Got too confusing to make these an array.
0277   // note that all of this is done in the native units of the source data, specifically, the volume element is in hist units, not our internal units.
0278 
0279   for (i[0] = 1; i[0] <= nbins[0]; i[0]++)
0280   {  // phi
0281     int a = 0;
0282     low[a] = ax[a]->GetBinLowEdge(i[a]);
0283     high[a] = ax[a]->GetBinUpEdge(i[a]);
0284     float dphi = high[a] - low[a];
0285     for (i[1] = 1; i[1] <= nbins[1]; i[1]++)
0286     {  // r
0287       a = 1;
0288       low[a] = ax[a]->GetBinLowEdge(i[a]);
0289       high[a] = ax[a]->GetBinUpEdge(i[a]);
0290       dr = high[a] - low[a];
0291       float rphiterm = dphi * (low[1] + 0.5 * dr) * dr;
0292       for (i[2] = 1; i[2] <= nbins[2]; i[2]++)
0293       {  // z
0294         a = 2;
0295         low[a] = ax[a]->GetBinLowEdge(i[a]);
0296         high[a] = ax[a]->GetBinUpEdge(i[a]);
0297         dz = high[a] - low[a];
0298         // float volume=dz*dphi*(low[1]+0.5*dr)*dr;
0299         float volume = dz * rphiterm;
0300         int globalBin = hSourceCharge->GetBin(i[0], i[1], i[2]);
0301         float q = hSourceCharge->GetBinContent(globalBin);
0302         hChargeDensity->SetBinContent(globalBin, q / volume);
0303         if (false)
0304         {  // deep debug statements.
0305           std::cout << std::format("iprz=({},{},{}),glob={}", i[0], i[1], i[2], globalBin);
0306           std::cout << std::format("edges=[{:.2f},{:.2f}],[{:.1f},{:.1f}],[{:.1f},%f.1],", low[0], high[0], low[1], high[1], low[2], high[2]);
0307           std::cout << std::format("\tq={:E},vol={:E},dens={:E}", q, volume, hChargeDensity->GetBinContent(globalBin)) << std::endl;
0308         }
0309       }
0310     }
0311   }
0312   if (DEBUG)
0313   {
0314     std::cout << "done regenerating density histogram" << std::endl;
0315   }
0316 
0317   return;
0318 }
0319 
0320 bool ChargeMapReader::ReadSourceCharge(const std::string& filename, const std::string& histname, float axisScale, float contentScale)
0321 {
0322   // load the charge-per-bin data from the specified file.
0323   inputAxisScale = axisScale;
0324   inputChargeScale = contentScale;
0325   TFile* inputFile = TFile::Open(filename.c_str(), "READ");
0326   inputFile->GetObject(histname.c_str(), hSourceCharge);
0327   if (hSourceCharge == nullptr)
0328   {
0329     return false;
0330   }
0331   RegenerateDensity();
0332   RegenerateCharge();
0333   inputFile->Close();
0334   // after this, the source histogram doesn't exist anymore.
0335   return true;
0336 }
0337 
0338 bool ChargeMapReader::ReadSourceCharge(TH3* sourceHist, float axisScale, float contentScale)
0339 {
0340   if (DEBUG)
0341   {
0342     std::cout << "reading charge from " << sourceHist->GetName() << std::endl;
0343   }
0344   inputAxisScale = axisScale;
0345   inputChargeScale = contentScale;
0346   hSourceCharge = sourceHist;  // note that this means we don't own this histogram!
0347   if (hSourceCharge == nullptr)
0348   {
0349     return false;
0350   }
0351   RegenerateDensity();
0352   RegenerateCharge();
0353 
0354   if (DEBUG)
0355   {
0356     std::cout << "done reading charge from " << sourceHist->GetName() << std::endl;
0357   }
0358 
0359   return true;
0360 }
0361 
0362 bool ChargeMapReader::ReadSourceAdc(const std::string& adcfilename, const std::string& adchistname, const std::string& ibfgainfilename, const std::string& ibfgainhistname, float axisScale, float contentScale)
0363 {
0364   // load the adc-per-bin data from the specified file.
0365   TFile* adcInputFile = TFile::Open(adcfilename.c_str(), "READ");
0366   TH3* hAdc = nullptr;
0367   adcInputFile->GetObject(adchistname.c_str(), hAdc);
0368   if (hSourceCharge == nullptr)
0369   {
0370     std::cout << "Source Charge hist or file " << adcfilename << " not found!" << std::endl;
0371     return false;
0372   }
0373   // load the conversion factor from adc sum to ibf
0374   TFile* ibfGainInputFile = TFile::Open(ibfgainfilename.c_str(), "READ");
0375   TH2* hIbfGain = nullptr;
0376   ibfGainInputFile->GetObject(ibfgainhistname.c_str(), hIbfGain);
0377   if (hIbfGain == nullptr)
0378   {
0379     std::cout << "IBF Gain hist or file " << ibfgainfilename << " not found!" << std::endl;
0380     return false;
0381   }
0382 
0383   // and then hand the task off to the next function
0384   bool ret = ReadSourceAdc(hAdc, hIbfGain, axisScale, contentScale);
0385   adcInputFile->Close();
0386   // after this, the source histograms don't exist anymore.
0387   return ret;
0388 }
0389 
0390 bool ChargeMapReader::ReadSourceAdc(TH3* adcHist, TH2* gainHist, float axisScale, float contentScale)
0391 {
0392   if (DEBUG)
0393   {
0394     std::cout << "reading ADCs from " << adcHist->GetName() << ", ibfGain from "
0395               << gainHist->GetName() << std::endl;
0396   }
0397   inputAxisScale = axisScale;
0398   inputChargeScale = contentScale;
0399   hSourceCharge = adcHist;  // note that this means we don't own this histogram!  It may disappear.
0400   if (hSourceCharge == nullptr)
0401   {
0402     return false;
0403   }
0404 
0405   // scale the voxels of the sourcecharge by the gainhist data.
0406 
0407   TAxis* ax[3] = {nullptr, nullptr, nullptr};
0408   ax[0] = hSourceCharge->GetXaxis();
0409   ax[1] = hSourceCharge->GetYaxis();
0410   ax[2] = hSourceCharge->GetZaxis();
0411 
0412   int nbins[3];
0413   for (int i = 0; i < 3; i++)
0414   {
0415     nbins[i] = ax[i]->GetNbins();  // number of bins, not counting under and overflow.
0416   }
0417 
0418   //   0     1     2   ...   n-1    n    n+1
0419   // under|first|   ..|  ..  |  .. |last| over
0420   int i[3];
0421 
0422   for (i[0] = 1; i[0] <= nbins[0]; i[0]++)
0423   {  // phi
0424     for (i[1] = 1; i[1] <= nbins[1]; i[1]++)
0425     {  // r
0426       int globalBin2D = gainHist->GetBin(i[0], i[1]);
0427       float scalefactor = gainHist->GetBinContent(globalBin2D);
0428       for (i[2] = 1; i[2] <= nbins[2]; i[2]++)
0429       {  // z
0430         int globalBin = hSourceCharge->GetBin(i[0], i[1], i[2]);
0431         float q = hSourceCharge->GetBinContent(globalBin);
0432         hSourceCharge->SetBinContent(globalBin, q * scalefactor);
0433         if (false)
0434         {  // deep debug statements.
0435           std::cout << std::format("applying gain to adc input:  iprz=({},{},{}), glob3d={}, glob2d={}, adc=%f, scale=%f", i[0], i[1], i[2], globalBin, globalBin2D, q, scalefactor) << std::endl;
0436         }
0437       }  // z
0438     }    // r
0439   }      // phi
0440   if (DEBUG)
0441   {
0442     std::cout << "done converting adc hist to ions" << std::endl;
0443   }
0444 
0445   RegenerateDensity();
0446   RegenerateCharge();
0447 
0448   if (DEBUG)
0449   {
0450     std::cout << "done converting ADCs from " << adcHist->GetName() << std::endl;
0451   }
0452 
0453   return true;
0454 }
0455 
0456 bool ChargeMapReader::SetOutputParameters(int _nr, float _rmin, float _rmax, int _nphi, float _phimin, float _phimax, int _nz, float _zmin, float _zmax)
0457 {
0458   // change all the parameters of our output array and rebuild the array from scratch.
0459   if (!(_rmax > _rmin) || !(_phimax > _phimin) || !(_zmax > _zmin))
0460   {
0461     return false;  // the bounds are not well-ordered.
0462   }
0463   if (_nr < 1 || _nphi < 1 || _nz < 1)
0464   {
0465     return false;  // must be at least one bin wide.
0466   }
0467 
0468   nBins[0] = _nr;
0469   nBins[1] = _nphi;
0470   nBins[2] = _nz;
0471   lowerBound[0] = _rmin;
0472   lowerBound[1] = _phimin;
0473   lowerBound[2] = _zmin;
0474   upperBound[0] = _rmax;
0475   upperBound[1] = _phimax;
0476   upperBound[2] = _zmax;
0477 
0478   for (int i = 0; i < 3; i++)
0479   {
0480     binWidth[i] = (upperBound[i] - lowerBound[i]) / (1.0 * nBins[i]);
0481   }
0482 
0483   // if the array exists, delete it.
0484   if (charge != nullptr)
0485   {
0486     if (DEBUG)
0487     {
0488       std::cout << "charge array existed.  deleting" << std::endl;
0489     }
0490     delete charge;
0491     charge = nullptr;
0492   }
0493   if (DEBUG)
0494   {
0495     std::cout << "building new charge array" << std::endl;
0496   }
0497   if (DEBUG)
0498   {
0499     std::cout << "should have " << (nBins[0] * nBins[1] * nBins[2])
0500               << " elements" << std::endl;
0501   }
0502 
0503   charge = new MultiArray<float>(nBins[0], nBins[1], nBins[2]);
0504 
0505   if (hChargeDensity != nullptr)
0506   {
0507     if (DEBUG)
0508     {
0509       std::cout << "charge density data exists, regenerating charge" << std::endl;
0510     }
0511     RegenerateCharge();  // fill the array with the charge data if available
0512   }
0513   else
0514   {
0515     charge->SetAll(0);  // otherwise set the array to all zeroes.
0516   }
0517   if (DEBUG)
0518   {
0519     std::cout << "finished building array" << std::endl;
0520   }
0521 
0522   return true;
0523 }
0524 
0525 bool ChargeMapReader::SetOutputBounds(float _rmin, float _rmax, float _phimin, float _phimax, float _zmin, float _zmax)
0526 {
0527   // change all the bounds of our output array and rebuild the array from scratch, leaving the original binning
0528 
0529   if (!(_rmax > _rmin) || !(_phimax > _phimin) || !(_zmax > _zmin))
0530   {
0531     return false;  // the bounds are not well-ordered.
0532   }
0533 
0534   lowerBound[0] = _rmin;
0535   lowerBound[1] = _phimin;
0536   lowerBound[2] = _zmin;
0537   upperBound[0] = _rmax;
0538   upperBound[1] = _phimax;
0539   upperBound[2] = _zmax;
0540 
0541   for (int i = 0; i < 3; i++)
0542   {
0543     binWidth[i] = (upperBound[i] - lowerBound[i]) / (1.0 * nBins[i]);
0544   }
0545 
0546   // if the array exists, delete it.
0547   if (charge != nullptr)
0548   {
0549     delete charge;
0550     charge = nullptr;
0551   }
0552   charge = new MultiArray<float>(nBins[0], nBins[1], nBins[2]);
0553 
0554   if (hChargeDensity != nullptr)
0555   {
0556     RegenerateCharge();  // fill the array with the charge data if available
0557   }
0558   else
0559   {
0560     charge->SetAll(0);  // otherwise set the array to all zeroes.
0561   }
0562   return true;
0563 
0564   return true;
0565 }
0566 
0567 bool ChargeMapReader::SetOutputBins(int _nr, int _nphi, int _nz)
0568 {
0569   // change the number of bins of our output array and rebuild the array from scratch, leaving the bounds alone.
0570   if (_nr < 1 || _nphi < 1 || _nz < 1)
0571   {
0572     return false;  // must be at least one bin wide.
0573   }
0574   nBins[0] = _nr;
0575   nBins[1] = _nphi;
0576   nBins[2] = _nz;
0577 
0578   for (int i = 0; i < 3; i++)
0579   {
0580     binWidth[i] = (upperBound[i] - lowerBound[i]) / (1.0 * nBins[i]);
0581   }
0582 
0583   // if the array exists, delete it.
0584   if (charge != nullptr)
0585   {
0586     delete charge;
0587     charge = nullptr;
0588   }
0589   charge = new MultiArray<float>(nBins[0], nBins[1], nBins[2]);
0590 
0591   if (hChargeDensity != nullptr)
0592   {
0593     RegenerateCharge();  // fill the array with the charge data if available
0594   }
0595   else
0596   {
0597     charge->SetAll(0);  // otherwise set the array to all zeroes.
0598   }
0599   return true;
0600 }
0601 
0602 void ChargeMapReader::AddChargeInBin(int r, int phi, int z, float q)
0603 {
0604   assert(r > 0 && r < nBins[0]);
0605   assert(phi > 0 && phi < nBins[1]);
0606   assert(z > 0 && z < nBins[2]);
0607   if (DEBUG)
0608   {
0609     std::cout << std::format("adding charge in array element {} {} {} to %.2E", r, phi, z, q) << std::endl;
0610   }
0611 
0612   charge->Add(r, phi, z, q);
0613   return;
0614 }
0615 
0616 void ChargeMapReader::AddChargeAtPosition(float r, float phi, float z, float q)
0617 {
0618   // bounds checking are handled by the binwise function, so no need to do so here:
0619   AddChargeInBin((r - lowerBound[0]) / binWidth[0], (phi - lowerBound[1]) / binWidth[1], (z - lowerBound[2]) / binWidth[2], q);
0620   return;
0621 }
0622 
0623 float ChargeMapReader::GetChargeInBin(int r, int phi, int z)
0624 {
0625   if (!(r >= 0 && r < nBins[0]))
0626   {
0627     std::cout << "requested rbin " << r << ", but bounds are 0 to "
0628               << nBins[0] << ". Failing." << std::endl;
0629     assert(r >= 0 && r < nBins[0]);
0630   }
0631   if (!(phi >= 0 && phi < nBins[1]))
0632   {
0633     std::cout << "requested phibin " << phi << ", but bounds are 0 to "
0634               << nBins[1] << ". Failing." << std::endl;
0635     assert(phi >= 0 && phi < nBins[1]);
0636   }
0637   if (!(z >= 0 && z < nBins[2]))
0638   {
0639     std::cout << "requested rbin " << z << ", but bounds are 0 to "
0640               << nBins[2] << ". Failing." << std::endl;
0641     assert(z >= 0 && z < nBins[2]);
0642   }
0643 
0644   if (DEBUG)
0645   {
0646     std::cout << "getting charge in array element " << r << " "
0647               << phi << " " << z << std::endl;
0648   }
0649 
0650   return charge->Get(r, phi, z);
0651 }
0652 
0653 float ChargeMapReader::GetChargeAtPosition(float r, float phi, float z)
0654 {
0655   // bounds checking are handled by the binwise function, so no need to do so here:
0656   return GetChargeInBin((r - lowerBound[0]) / binWidth[0], (phi - lowerBound[1]) / binWidth[1], (z - lowerBound[2]) / binWidth[2]);
0657 }
0658 
0659 void ChargeMapReader::SetChargeInBin(int r, int phi, int z, float q)
0660 {
0661   if (!(r >= 0 && r < nBins[0]))
0662   {
0663     std::cout << "requested rbin " << r << ", but bounds are to " << nBins[0]
0664               << ". Failing." << std::endl;
0665     assert(r >= 0 && r < nBins[0]);
0666   }
0667   if (!(phi >= 0 && phi < nBins[1]))
0668   {
0669     std::cout << "requested phibin " << phi << ", but bounds are 0 to "
0670               << nBins[1] << ". Failing." << std::endl;
0671     assert(phi >= 0 && phi < nBins[1]);
0672   }
0673   if (!(z >= 0 && z < nBins[2]))
0674   {
0675     std::cout << "requested rbin " << z << ", but bounds are 0 to "
0676               << nBins[2] << ". Failing." << std::endl;
0677     assert(z >= 0 && z < nBins[2]);
0678   }
0679   if (DEBUG)
0680   {
0681     std::cout << std::format("setting charge in array element {} {} {} to {:.2E}", r, phi, z, q) << std::endl;
0682   }
0683 
0684   charge->Set(r, phi, z, q);
0685   return;
0686 }
0687 
0688 void ChargeMapReader::SetChargeAtPosition(float r, float phi, float z, float q)
0689 {
0690   // bounds checking are handled by the binwise function, so no need to do so here:
0691   SetChargeInBin((r - lowerBound[0]) / binWidth[0], (phi - lowerBound[1]) / binWidth[1], (z - lowerBound[2]) / binWidth[2], q);
0692   return;
0693 }