File indexing completed on 2025-12-18 09:17:52
0001 #include "AnnularFieldSim.h"
0002
0003 #include "AnalyticFieldModel.h"
0004 #include "ChargeMapReader.h"
0005 #include "MultiArray.h" //for TH3 alternative
0006 #include "Rossegger.h"
0007
0008 #include <TCanvas.h>
0009 #include <TFile.h>
0010 #include <TH1.h>
0011 #include <TH2.h>
0012 #include <TH3.h>
0013 #include <TLatex.h>
0014 #include <TPad.h>
0015 #include <TStyle.h>
0016 #include <TTree.h>
0017 #include <TVector3.h>
0018
0019 #include <algorithm>
0020 #include <cassert> // for assert
0021 #include <cmath>
0022 #include <cstdint>
0023 #include <cstdlib>
0024 #include <format>
0025 #include <iostream>
0026
0027 #define ALMOST_ZERO 0.00001
0028
0029 AnnularFieldSim::AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
0030 int r, int roi_r0, int roi_r1, int , int ,
0031 int phi, int roi_phi0, int roi_phi1, int , int ,
0032 int z, int roi_z0, int roi_z1, int , int ,
0033 float vdr, LookupCase in_lookupCase, ChargeCase in_chargeCase)
0034 {
0035
0036 if (roi_r0 >= r || roi_r1 > r || roi_r0 >= roi_r1)
0037 {
0038 exit(1);
0039 }
0040 if (roi_phi0 >= phi || roi_phi1 > phi || roi_phi0 >= roi_phi1)
0041 {
0042 std::cout << "phi roi is out of range or spans the wrap-around. Please spare me that math." << std::endl;
0043 exit(1);
0044 }
0045 if (roi_z0 >= z || roi_z1 > z || roi_z0 >= roi_z1)
0046 {
0047 exit(1);
0048 }
0049
0050 hasTwin = false;
0051 green_shift = 0;
0052
0053 std::cout << std::format("AnnularFieldSim::AnnularFieldSim with ({}x{}x{}) grid", r, phi, z) << std::endl;
0054 std::cout << std::format("units m={:.2E}, cm={:.2E}, mm={:.2E}, um={:.2E},", m, cm, mm, um) << std::endl;
0055 std::cout << std::format("units s={:.2E}, us={:.2E}, ns={:.2E},", s, us, ns) << std::endl;
0056 std::cout << std::format("units C={:.2E}, nC={:.2E}, fC={:.2E}, ", C, nC, fC) << std::endl;
0057 std::cout << std::format("units Tesla={:.2E}, kGauss={:.2E}", Tesla, kGauss) << std::endl;
0058
0059
0060
0061 debug_printActionEveryN = -1;
0062 debug_printCounter = 0;
0063 debug_distortionScale.SetXYZ(0, 0, 0);
0064 debug_npercent = 5;
0065
0066
0067
0068
0069
0070 Enominal = 400 * V / cm;
0071 Bnominal = 1.4 * Tesla;
0072 vdrift = vdr * cm / s;
0073 langevin_T1 = 1.0;
0074 langevin_T2 = 1.0;
0075 omegatau_nominal = -10 * (Bnominal / kGauss) * (vdrift / (cm / us)) / (Enominal / (V / cm));
0076
0077 rmin = in_innerRadius * cm;
0078 rmax = in_outerRadius * cm;
0079 zmin = 0;
0080 zmax = in_outerZ * cm;
0081 if (zmin > zmax)
0082 {
0083 zmin = zmax;
0084 zmax = 0;
0085 }
0086 zero_vector.SetXYZ(0, 0, 0);
0087
0088
0089 dim.SetXYZ(1, 0, 0);
0090 dim.SetPerp(rmax - rmin);
0091 dim.SetPhi(0);
0092 phispan = 2 * M_PI;
0093 dim.SetZ(zmax - zmin);
0094
0095
0096
0097
0098 green = nullptr;
0099
0100
0101 nr = r;
0102 nphi = phi;
0103 nz = z;
0104 std::cout << std::format("AnnularFieldSim::AnnularFieldSim set variables nr={}, nphi={}, nz={}", nr, nphi, nz) << std::endl;
0105
0106 truncation_length = -1;
0107
0108
0109
0110 step.SetXYZ(1, 0, 0);
0111 step.SetPerp(dim.Perp() / nr);
0112 step.SetPhi(phispan / nphi);
0113 step.SetZ(dim.Z() / nz);
0114 std::cout << std::format("f-bin size: r={:.6f}, phi={:.6f}, z={:.6f}, wanted {:.6f},{:.6f}", step.Perp(), step.Phi(), (rmax - rmin) / nr, phispan / nphi, (zmax - zmin) / nz) << std::endl;
0115
0116
0117
0118
0119 q = new ChargeMapReader(nr, rmin, rmax, nphi, 0, phispan, nz, zmin, zmax);
0120 chargestring = "No spacecharge present.";
0121
0122
0123 rmin_roi = roi_r0;
0124 phimin_roi = roi_phi0;
0125 zmin_roi = roi_z0;
0126 rmax_roi = roi_r1;
0127 phimax_roi = roi_phi1;
0128 zmax_roi = roi_z1;
0129 std::cout << std::format("AnnularFieldSim::AnnularFieldSim set roi variables rmin={} phimin={} zmin={} rmax={} phimax={} zmax={}",
0130 rmin_roi, phimin_roi, zmin_roi, rmax_roi, phimax_roi, zmax_roi)
0131 << std::endl;
0132
0133 nr_roi = rmax_roi - rmin_roi;
0134 nphi_roi = phimax_roi - phimin_roi;
0135 nz_roi = zmax_roi - zmin_roi;
0136 std::cout << std::format("AnnularFieldSim::AnnularFieldSim calc'd roi variables nr={} nphi={} nz={}",
0137 nr_roi, nphi_roi, nz_roi)
0138 << std::endl;
0139
0140
0141 Efield = new MultiArray<TVector3>(nr_roi, nphi_roi, nz_roi);
0142 for (int i = 0; i < Efield->Length(); i++)
0143 {
0144 Efield->GetFlat(i)->SetXYZ(0, 0, 0);
0145 }
0146
0147
0148 Eexternal = new MultiArray<TVector3>(nr_roi, nphi_roi, nz_roi);
0149 for (int i = 0; i < Eexternal->Length(); i++)
0150 {
0151 Eexternal->GetFlat(i)->SetXYZ(0, 0, 0);
0152 }
0153
0154
0155 Bfield = new MultiArray<TVector3>(nr_roi, nphi_roi, nz_roi);
0156 for (int i = 0; i < Bfield->Length(); i++)
0157 {
0158 Bfield->GetFlat(i)->SetXYZ(0, 0, 0);
0159 }
0160
0161
0162 lookupCase = in_lookupCase;
0163 chargeCase = in_chargeCase;
0164 if (chargeCase == ChargeCase::NoSpacecharge)
0165 {
0166 lookupCase = LookupCase::NoLookup;
0167 }
0168
0169 if (lookupCase == Full3D)
0170 {
0171 std::cout << std::format("AnnularFieldSim::AnnularFieldSim building Epartial (full3D) with nr_roi={} nphi_roi={} nz_roi={} =~{:.2f}M TVector3 objects",
0172 nr_roi, nphi_roi, nz_roi, nr_roi * nphi_roi * nz_roi * nr * nphi * nz / 1.0e6)
0173 << std::endl;
0174
0175 Epartial = new MultiArray<TVector3>(nr_roi, nphi_roi, nz_roi, nr, nphi, nz);
0176 for (int i = 0; i < Epartial->Length(); i++)
0177 {
0178 Epartial->GetFlat(i)->SetXYZ(0, 0, 0);
0179 }
0180
0181 Epartial_highres = new MultiArray<TVector3>(1);
0182 Epartial_highres->GetFlat(0)->SetXYZ(0, 0, 0);
0183
0184 Epartial_lowres = new MultiArray<TVector3>(1);
0185 Epartial_lowres->GetFlat(0)->SetXYZ(0, 0, 0);
0186
0187 Epartial_phislice = new MultiArray<TVector3>(1);
0188 Epartial_phislice->GetFlat(0)->SetXYZ(0, 0, 0);
0189 q_lowres = new MultiArray<double>(1);
0190 *(q_lowres->GetFlat(0)) = 0;
0191 q_local = new MultiArray<double>(1);
0192 *(q_local->GetFlat(0)) = 0;
0193 }
0194 else if (lookupCase == HybridRes)
0195 {
0196 std::cout << "lookupCase==HybridRes" << std::endl;
0197
0198 Epartial = new MultiArray<TVector3>(1);
0199 Epartial->GetFlat(0)->SetXYZ(0, 0, 0);
0200
0201 Epartial_phislice = new MultiArray<TVector3>(1);
0202 Epartial_phislice->GetFlat(0)->SetXYZ(0, 0, 0);
0203 }
0204 else if (lookupCase == PhiSlice)
0205 {
0206 std::cout << "lookupCase==PhiSlice" << std::endl;
0207
0208 Epartial_phislice = new MultiArray<TVector3>(nr_roi, 1, nz_roi, nr, nphi, nz);
0209 for (int i = 0; i < Epartial_phislice->Length(); i++)
0210 {
0211 Epartial_phislice->GetFlat(i)->SetXYZ(0, 0, 0);
0212 }
0213
0214 Epartial = new MultiArray<TVector3>(1);
0215 Epartial->GetFlat(0)->SetXYZ(0, 0, 0);
0216 Epartial_highres = new MultiArray<TVector3>(1);
0217 Epartial_highres->GetFlat(0)->SetXYZ(0, 0, 0);
0218
0219 Epartial_lowres = new MultiArray<TVector3>(1);
0220 Epartial_lowres->GetFlat(0)->SetXYZ(0, 0, 0);
0221
0222 q_lowres = new MultiArray<double>(1);
0223 *(q_lowres->GetFlat(0)) = 0;
0224 q_local = new MultiArray<double>(1);
0225 *(q_local->GetFlat(0)) = 0;
0226 }
0227 else if (lookupCase == Analytic || lookupCase == NoLookup)
0228 {
0229 std::cout << "lookupCase==Analytic (or NoLookup)" << std::endl;
0230
0231
0232 Epartial_phislice = new MultiArray<TVector3>(1);
0233 Epartial_phislice->GetFlat(0)->SetXYZ(0, 0, 0);
0234
0235 Epartial = new MultiArray<TVector3>(1);
0236 Epartial->GetFlat(0)->SetXYZ(0, 0, 0);
0237
0238 Epartial_highres = new MultiArray<TVector3>(1);
0239 Epartial_highres->GetFlat(0)->SetXYZ(0, 0, 0);
0240
0241 Epartial_lowres = new MultiArray<TVector3>(1);
0242 Epartial_lowres->GetFlat(0)->SetXYZ(0, 0, 0);
0243
0244 q_lowres = new MultiArray<double>(1);
0245 *(q_lowres->GetFlat(0)) = 0;
0246 q_local = new MultiArray<double>(1);
0247 *(q_local->GetFlat(0)) = 0;
0248 }
0249 else
0250 {
0251 std::cout << "Ran into wrong lookupCase logic in constructor." << std::endl;
0252 exit(1);
0253 }
0254
0255 return;
0256 }
0257 AnnularFieldSim::AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
0258 int r, int roi_r0, int roi_r1, int in_rLowSpacing, int in_rHighSize,
0259 int phi, int roi_phi0, int roi_phi1, int in_phiLowSpacing, int in_phiHighSize,
0260 int z, int roi_z0, int roi_z1, int in_zLowSpacing, int in_zHighSize,
0261 float vdr, LookupCase in_lookupCase)
0262 : AnnularFieldSim(in_innerRadius, in_outerRadius, in_outerZ,
0263 r, roi_r0, roi_r1, in_rLowSpacing, in_rHighSize,
0264 phi, roi_phi0, roi_phi1, in_phiLowSpacing, in_phiHighSize,
0265 z, roi_z0, roi_z1, in_zLowSpacing, in_zHighSize,
0266 vdr, in_lookupCase, ChargeCase::FromFile)
0267 {
0268 std::cout << "AnnularFieldSim::OldConstructor building AnnularFieldSim with ChargeCase::FromFile" << std::endl;
0269 return;
0270 }
0271 AnnularFieldSim::AnnularFieldSim(float in_innerRadius, float in_outerRadius, float in_outerZ,
0272 int r, int roi_r0, int roi_r1,
0273 int phi, int roi_phi0, int roi_phi1,
0274 int z, int roi_z0, int roi_z1,
0275 float vdr, LookupCase in_lookupCase)
0276 : AnnularFieldSim(in_innerRadius, in_outerRadius, in_outerZ,
0277 r, roi_r0, roi_r1, 1, 3,
0278 phi, roi_phi0, roi_phi1, 1, 3,
0279 z, roi_z0, roi_z1, 1, 3,
0280 vdr, in_lookupCase, ChargeCase::FromFile)
0281 {
0282
0283
0284 std::cout << "AnnularFieldSim::OldConstructor building AnnularFieldSim with local_size=1 in all dimensions, lowres_spacing=1 in all dimensions" << std::endl;
0285 return;
0286 }
0287 AnnularFieldSim::AnnularFieldSim(float rin, float rout, float dz, int r, int phi, int z, float vdr)
0288 : AnnularFieldSim(rin, rout, dz,
0289 r, 0, r,
0290 phi, 0, phi,
0291 z, 0, z,
0292 vdr, LookupCase::PhiSlice)
0293 {
0294
0295 std::cout << "AnnularFieldSim::OldConstructor building AnnularFieldSim with roi=full in all dimensions" << std::endl;
0296 return;
0297 }
0298
0299 TVector3 AnnularFieldSim::calc_unit_field(TVector3 at, TVector3 from)
0300 {
0301
0302 int r_position;
0303 if (GetRindexAndCheckBounds(at.Perp(), &r_position) != InBounds)
0304 {
0305 std::cout << std::format("something's asking for 'at' with r={:.6f}, which is index={}",
0306 at.Perp(), r_position)
0307 << std::endl;
0308 exit(1);
0309 }
0310
0311
0312
0313
0314
0315 TVector3 field(0, 0, 0);
0316
0317
0318
0319
0320 if (green == nullptr)
0321 {
0322 TVector3 delr = at - from;
0323 field = delr;
0324 if (delr.Mag() < ALMOST_ZERO * ALMOST_ZERO)
0325 {
0326
0327
0328 }
0329 else
0330 {
0331 field.SetMag(k_perm * 1 / (delr * delr));
0332 }
0333
0334 }
0335 else
0336 {
0337 double atphi = FilterPhiPos(at.Phi());
0338 double fromphi = FilterPhiPos(from.Phi());
0339 double delphi = std::abs(at.DeltaPhi(from));
0340
0341 at.SetZ(at.Z() + green_shift);
0342 from.SetZ(from.Z() + green_shift);
0343 double Er = green->Er(at.Perp(), atphi, at.Z(), from.Perp(), fromphi, from.Z());
0344
0345 double Ephi = 0;
0346 if (delphi > ALMOST_ZERO)
0347 {
0348 Ephi = green->Ephi(at.Perp(), atphi, at.Z(), from.Perp(), fromphi, from.Z());
0349 }
0350 double Ez = green->Ez(at.Perp(), atphi, at.Z(), from.Perp(), fromphi, from.Z());
0351 field.SetXYZ(-Er, -Ephi, -Ez);
0352 field = field * epsinv;
0353 field.RotateZ(at.Phi());
0354
0355 }
0356 return field;
0357 }
0358
0359 TVector3 AnnularFieldSim::GetLocalFieldComponents(const TVector3 &field, const TVector3 &pos, const TVector3 &origin)
0360 {
0361
0362
0363
0364
0365
0366 TVector3 local_pos = pos - origin;
0367
0368 TVector3 radial_vector = local_pos;
0369 radial_vector.SetZ(0);
0370 if (radial_vector.Mag() == 0)
0371 {
0372
0373 radial_vector.SetXYZ(1, 0, 0);
0374 }
0375 else
0376 {
0377 radial_vector.SetMag(1);
0378 }
0379
0380 TVector3 azimuthal_vector = radial_vector;
0381 azimuthal_vector.SetXYZ(-radial_vector.Y(), radial_vector.X(), 0);
0382
0383 TVector3 local_field;
0384 local_field.SetXYZ(field.Dot(radial_vector), field.Dot(azimuthal_vector), field.Z());
0385 return local_field;
0386 }
0387
0388 double AnnularFieldSim::FilterPhiPos(double phi)
0389 {
0390
0391
0392 double p = phi;
0393 if (p >= 2 * M_PI)
0394 {
0395 p -= 2 * M_PI;
0396 }
0397 if (p < 0)
0398 {
0399 p += 2 * M_PI;
0400 }
0401 if (p >= 2 * M_PI || p < 0)
0402 {
0403 std::cout << std::format("AnnularFieldSim::FilterPhiPos asked to filter {:.6f}, which is more than range={:.6f} out of bounds. Check what called this.",
0404 phi, 2 * M_PI)
0405 << std::endl;
0406 exit(1);
0407 }
0408 return p;
0409 }
0410 int AnnularFieldSim::FilterPhiIndex(int phi, int range = -1) const
0411 {
0412 if (range < 0)
0413 {
0414 range = nphi;
0415 }
0416
0417
0418 int p = phi;
0419 if (p >= range)
0420 {
0421 p -= range;
0422 }
0423 if (p < 0)
0424 {
0425 p += range;
0426 }
0427 if (p >= range || p < 0)
0428 {
0429 std::cout << std::format("AnnularFieldSim::FilterPhiIndex asked to filter {}, which is more than range={} out of bounds. Check what called this.", phi, range) << std::endl;
0430 exit(1);
0431 }
0432 return p;
0433 }
0434
0435 int AnnularFieldSim::GetRindex(float pos)
0436 {
0437 float r0f = (pos - rmin) / step.Perp();
0438 int r0 = std::floor(r0f);
0439 return r0;
0440 }
0441
0442 int AnnularFieldSim::GetPhiIndex(float pos)
0443 {
0444 float p0f = (pos) / step.Phi();
0445 int phitemp = std::floor(p0f);
0446 int p0 = FilterPhiIndex(phitemp);
0447 return p0;
0448 }
0449
0450 int AnnularFieldSim::GetZindex(float pos)
0451 {
0452 float z0f = (pos - zmin) / step.Z();
0453 int z0 = std::floor(z0f);
0454 return z0;
0455 }
0456
0457 AnnularFieldSim::BoundsCase AnnularFieldSim::GetRindexAndCheckBounds(float pos, int *r)
0458 {
0459
0460
0461 float r0f = (pos - rmin) / step.Perp();
0462 int r0 = std::floor(r0f);
0463 *r = r0;
0464
0465 int r0lowered_slightly = std::floor(r0f - ALMOST_ZERO);
0466 int r0raised_slightly = std::floor(r0f + ALMOST_ZERO);
0467 if (r0lowered_slightly >= rmax_roi || r0raised_slightly < rmin_roi)
0468 {
0469 return OutOfBounds;
0470 }
0471
0472
0473 if (r0 >= rmax_roi)
0474 {
0475 return OnHighEdge;
0476 }
0477 if (r0 < rmin_roi)
0478 {
0479 return OnLowEdge;
0480 }
0481
0482 return InBounds;
0483 }
0484 AnnularFieldSim::BoundsCase AnnularFieldSim::GetPhiIndexAndCheckBounds(float pos, int *phi)
0485 {
0486
0487 float p0f = (pos) / step.Phi();
0488 int phitemp = std::floor(p0f);
0489 int p0 = FilterPhiIndex(phitemp);
0490 *phi = p0;
0491
0492 phitemp = std::floor(p0f - ALMOST_ZERO);
0493 int p0lowered_slightly = FilterPhiIndex(phitemp);
0494 phitemp = std::floor(p0f + ALMOST_ZERO);
0495 int p0raised_slightly = FilterPhiIndex(phitemp);
0496
0497
0498
0499
0500
0501 if ((p0lowered_slightly >= phimax_roi && p0 != 0) || (p0raised_slightly < phimin_roi && p0 != (nphi - 1)))
0502 {
0503 return OutOfBounds;
0504 }
0505
0506 if (p0 >= phimax_roi)
0507 {
0508 return OnHighEdge;
0509 }
0510 if (p0 < phimin_roi)
0511 {
0512 return OnLowEdge;
0513 }
0514
0515 return InBounds;
0516 }
0517
0518 AnnularFieldSim::BoundsCase AnnularFieldSim::GetZindexAndCheckBounds(float pos, int *z)
0519 {
0520
0521 float z0f = (pos - zmin) / step.Z();
0522 int z0 = std::floor(z0f);
0523 *z = z0;
0524
0525 int z0lowered_slightly = std::floor(z0f - ALMOST_ZERO);
0526 int z0raised_slightly = std::floor(z0f + ALMOST_ZERO);
0527
0528 if (z0lowered_slightly >= zmax_roi || z0raised_slightly < zmin_roi)
0529 {
0530 return OutOfBounds;
0531 }
0532
0533 if (z0 >= zmax_roi)
0534 {
0535 return OnHighEdge;
0536 }
0537 if (z0 < zmin_roi)
0538 {
0539 return OnLowEdge;
0540 }
0541
0542 return InBounds;
0543 }
0544
0545 TVector3 AnnularFieldSim::analyticFieldIntegral(float zdest, TVector3 start, MultiArray<TVector3> *field)
0546 {
0547
0548
0549
0550
0551
0552
0553 int r;
0554 int phi;
0555 bool rOkay = (GetRindexAndCheckBounds(start.Perp(), &r) == InBounds);
0556 bool phiOkay = (GetPhiIndexAndCheckBounds(FilterPhiPos(start.Phi()), &phi) == InBounds);
0557
0558
0559
0560
0561
0562
0563 if (!rOkay || !phiOkay)
0564 {
0565 std::cout << std::format("AnnularFieldSim::analyticFieldIntegral asked to integrate along (r={:.6f}, phi={:.6f}), index=({}, {}), which is out of bounds. returning starting position.",
0566 start.Perp(), start.Phi(), r, phi)
0567 << std::endl;
0568 return start;
0569 }
0570
0571 int dir = (start.Z() > zdest ? -1 : 1);
0572
0573 int zi;
0574 int zf;
0575 double startz;
0576 double endz;
0577 BoundsCase startBound;
0578 BoundsCase endBound;
0579
0580
0581 if (dir > 0)
0582 {
0583 startBound = GetZindexAndCheckBounds(start.Z(), &zi);
0584 endBound = GetZindexAndCheckBounds(zdest, &zf);
0585 startz = start.Z();
0586 endz = zdest;
0587 }
0588 else
0589 {
0590 endBound = GetZindexAndCheckBounds(start.Z(), &zf);
0591 startBound = GetZindexAndCheckBounds(zdest, &zi);
0592 startz = zdest;
0593 endz = start.Z();
0594 }
0595 bool startOkay = (startBound == InBounds);
0596 bool endOkay = (endBound == InBounds || endBound == OnHighEdge);
0597
0598 if (!startOkay || !endOkay)
0599 {
0600 std::cout << std::format("AnnularFieldSim::analyticFieldIntegral asked to integrate from z={:.6f} to {:.6f}, index={} to {}, which is out of bounds. returning starting position.",
0601 startz, endz, zi, zf)
0602 << std::endl;
0603 return start;
0604 }
0605
0606 start.SetZ(startz);
0607 TVector3 integral;
0608 if (field == Efield)
0609 {
0610 integral = aliceModel->Eint(endz, start) + Eexternal->Get(r - rmin_roi, phi - phimin_roi, zi - zmin_roi) * (endz - startz);
0611 return dir * integral;
0612 }
0613 if (field == Bfield)
0614 {
0615 return interpolatedFieldIntegral(zdest, start, Bfield);
0616 }
0617 return integral;
0618 }
0619
0620 TVector3 AnnularFieldSim::fieldIntegral(float zdest, const TVector3 &start, MultiArray<TVector3> *field)
0621 {
0622
0623
0624
0625 int r;
0626 int phi;
0627 bool rOkay = (GetRindexAndCheckBounds(start.Perp(), &r) == InBounds);
0628 bool phiOkay = (GetPhiIndexAndCheckBounds(FilterPhiPos(start.Phi()), &phi) == InBounds);
0629
0630 if (!rOkay || !phiOkay)
0631 {
0632 std::cout << std::format("AnnularFieldSim::fieldIntegral asked to integrate along (r={:.6f}, phi={:.6f}), index=({}, {}), which is out of bounds. returning starting position.",
0633 start.Perp(), start.Phi(), r, phi)
0634 << std::endl;
0635 return start;
0636 }
0637
0638 int dir = (start.Z() > zdest ? -1 : 1);
0639
0640 int zi;
0641 int zf;
0642 double startz;
0643 double endz;
0644 BoundsCase startBound;
0645 BoundsCase endBound;
0646
0647
0648 if (dir > 0)
0649 {
0650 startBound = GetZindexAndCheckBounds(start.Z(), &zi);
0651 endBound = GetZindexAndCheckBounds(zdest, &zf);
0652 startz = start.Z();
0653 endz = zdest;
0654 }
0655 else
0656 {
0657 endBound = GetZindexAndCheckBounds(start.Z(), &zf);
0658 startBound = GetZindexAndCheckBounds(zdest, &zi);
0659 startz = zdest;
0660 endz = start.Z();
0661 }
0662 bool startOkay = (startBound == InBounds);
0663 bool endOkay = (endBound == InBounds || endBound == OnHighEdge);
0664
0665 if (!startOkay || !endOkay)
0666 {
0667 std::cout << std::format("AnnularFieldSim::fieldIntegral asked to integrate from z={:.6f} to {:.6f}, index={} to {}, which is out of bounds. returning starting position.",
0668 startz, endz, zi, zf)
0669 << std::endl;
0670 return start;
0671 }
0672
0673 TVector3 fieldInt(0, 0, 0);
0674
0675 TVector3 tf;
0676 for (int i = zi; i < zf; i++)
0677 {
0678 tf = field->Get(r - rmin_roi, phi - phimin_roi, i - zmin_roi);
0679
0680 fieldInt += tf * step.Z();
0681 }
0682
0683
0684 fieldInt -= field->Get(r - rmin_roi, phi - phimin_roi, zi - zmin_roi) * (startz - zi * step.Z());
0685
0686
0687 if (endz / step.Z() - zf > ALMOST_ZERO)
0688 {
0689
0690
0691 fieldInt += field->Get(r - rmin_roi, phi - phimin_roi, zf - zmin_roi) * (endz - zf * step.Z());
0692 }
0693
0694 return dir * fieldInt;
0695 }
0696
0697 TVector3 AnnularFieldSim::GetCellCenter(int r, int phi, int z)
0698 {
0699
0700
0701 TVector3 c(1, 0, 0);
0702 c.SetPerp((r + 0.5) * step.Perp() + rmin);
0703 c.SetPhi((phi + 0.5) * step.Phi());
0704 c.SetZ((z + 0.5) * step.Z() + zmin);
0705
0706 return c;
0707 }
0708
0709 TVector3 AnnularFieldSim::GetRoiCellCenter(int r, int phi, int z)
0710 {
0711
0712
0713
0714 if (r > nr_roi || r < 0 || phi > nphi_roi || phi < 0 || z > nz_roi || z < 0)
0715 {
0716 return zero_vector;
0717 }
0718
0719 TVector3 c(1, 0, 0);
0720 c.SetPerp((r + rmin_roi + 0.5) * step.Perp() + rmin);
0721 c.SetPhi((phi + phimin_roi + 0.5) * step.Phi());
0722 c.SetZ((z + zmin_roi + 0.5) * step.Z() + zmin);
0723
0724 return c;
0725 }
0726
0727 TVector3 AnnularFieldSim::GetGroupCellCenter(int r0, int r1, int phi0, int phi1, int z0, int z1)
0728 {
0729
0730 float ravg = (r0 + r1) / 2.0 + 0.5;
0731 if (phi0 > phi1)
0732 {
0733 phi1 += nphi;
0734 }
0735 if (phi0 > phi1)
0736 {
0737 std::cout << std::format("phi1({})<=phi0({}) even after boosting phi1. check what called this!", phi1, phi0) << std::endl;
0738 exit(1);
0739 }
0740 float phiavg = (r0 + r1) / 2.0 + 0.5;
0741 if (phiavg >= nphi)
0742 {
0743 phiavg -= nphi;
0744 }
0745
0746 float zavg = (z0 + z1) / 2.0 + 0.5;
0747
0748 TVector3 c(1, 0, 0);
0749 c.SetPerp((ravg) *step.Perp() + rmin);
0750 c.SetPhi((phiavg) *step.Phi());
0751 c.SetZ((zavg) *step.Z() + zmin);
0752
0753 return c;
0754 }
0755
0756 TVector3 AnnularFieldSim::GetWeightedCellCenter(int r, int phi, int z)
0757 {
0758
0759
0760 TVector3 c(1, 0, 0);
0761
0762 float rin = r * step.Perp() + rmin;
0763 float rout = rin + step.Perp();
0764
0765 float rMid = (4 * sin(step.Phi() / 2) * (pow(rout, 3) - pow(rin, 3)) / (3 * step.Phi() * (pow(rout, 2) - pow(rin, 2))));
0766 c.SetPerp(rMid);
0767 c.SetPhi((phi + 0.5) * step.Phi());
0768 c.SetZ((z + 0.5) * step.Z());
0769
0770 return c;
0771 }
0772
0773 TVector3 AnnularFieldSim::interpolatedFieldIntegral(float zdest, const TVector3 &start, MultiArray<TVector3> *field)
0774 {
0775
0776
0777 float r0 = (start.Perp() - rmin) / step.Perp() - 0.5;
0778 int r0i = std::floor(r0);
0779 float r0d = r0 - r0i;
0780 int ri[4];
0781 ri[0] = ri[1] = r0i;
0782 ri[2] = ri[3] = r0i + 1;
0783 float rw[4];
0784 rw[0] = rw[1] = 1 - r0d;
0785 rw[2] = rw[3] = r0d;
0786
0787 bool skip[] = {false, false, false, false};
0788 if (ri[0] < rmin_roi)
0789 {
0790 skip[0] = true;
0791 skip[1] = true;
0792 rw[2] = rw[3] = 1;
0793 }
0794 if (ri[2] >= rmax_roi)
0795 {
0796 skip[2] = true;
0797 skip[3] = true;
0798 rw[0] = rw[1] = 1;
0799 }
0800
0801
0802 float p0 = (start.Phi()) / step.Phi() - 0.5;
0803 int p0i = std::floor(p0);
0804 float p0d = p0 - p0i;
0805 int pi[4];
0806 pi[0] = pi[2] = FilterPhiIndex(p0i);
0807 pi[1] = pi[3] = FilterPhiIndex(p0i + 1);
0808 float pw[4];
0809 pw[0] = pw[2] = 1 - p0d;
0810 pw[1] = pw[3] = p0d;
0811
0812
0813 if (pi[0] < phimin_roi || pi[0] >= phimax_roi)
0814 {
0815 skip[0] = true;
0816 skip[2] = true;
0817 pw[1] = pw[3] = 1;
0818 }
0819 if (pi[1] >= phimax_roi || pi[1] < phimin_roi)
0820 {
0821 skip[1] = true;
0822 skip[3] = true;
0823 pw[0] = pw[2] = 1;
0824 }
0825
0826 int dir = (start.Z() < zdest ? 1 : -1);
0827
0828 int zi;
0829 int zf;
0830 double startz;
0831 double endz;
0832 BoundsCase startBound;
0833 BoundsCase endBound;
0834
0835
0836 if (dir > 0)
0837 {
0838 startBound = GetZindexAndCheckBounds(start.Z(), &zi);
0839 endBound = GetZindexAndCheckBounds(zdest, &zf);
0840 startz = start.Z();
0841 endz = zdest;
0842 }
0843 else
0844 {
0845 endBound = GetZindexAndCheckBounds(start.Z(), &zf);
0846 startBound = GetZindexAndCheckBounds(zdest, &zi);
0847 startz = zdest;
0848 endz = start.Z();
0849 }
0850 bool startOkay = (startBound == InBounds || startBound == OnLowEdge);
0851 bool endOkay = (endBound == InBounds || endBound == OnHighEdge);
0852
0853 if (!startOkay || !endOkay)
0854 {
0855 std::cout << std::format("AnnularFieldSim::InterpolatedFieldIntegral asked to integrate from z={} to {}, index={} to {}, which is out of bounds. Returning zero.", startz, endz, zi, zf) << std::endl;
0856 ;
0857 return zero_vector;
0858 }
0859
0860 if (startBound == OnLowEdge)
0861 {
0862
0863 zi++;
0864 }
0865
0866 if (false)
0867 {
0868 bool isE = (field == Efield);
0869 bool isB = (field == Bfield);
0870 std::cout << std::format("interpFieldInt: isE={}, isB={}, start=({:.4f},{:.4f},{:.4f}) dest={:.4f}", isE, isB, start.X(), start.Y(), start.Z(), zdest) << std::endl;
0871
0872 std::cout << std::format("interpolating fieldInt for {} at r={}, phi={}", (field == Efield) ? "Efield" : "Bfield", r0, p0) << std::endl;
0873
0874 std::cout << std::format("direction={}, startz={:.4f}, endz={:.4f}, zi={}, zf={}", dir, startz, endz, zi, zf) << std::endl;
0875 for (int i = 0; i < 4; i++)
0876 {
0877 std::cout << std::format("skip[{}]={}\tw[i]={:.2f}, (pw={:.2f}, rw={:.2f})", i, skip[i], rw[i] * pw[i], pw[i], rw[i]) << std::endl;
0878 }
0879 }
0880
0881 TVector3 fieldInt(0, 0, 0);
0882 TVector3 partialInt;
0883
0884 for (int i = 0; i < 4; i++)
0885 {
0886 if (skip[i])
0887 {
0888
0889 continue;
0890 }
0891 partialInt.SetXYZ(0, 0, 0);
0892 for (int j = zi; j < zf; j++)
0893 {
0894
0895 partialInt += field->Get(ri[i] - rmin_roi, pi[i] - phimin_roi, j - zmin_roi) * step.Z();
0896 }
0897 if (startBound != OnLowEdge)
0898 {
0899 partialInt -= field->Get(ri[i] - rmin_roi, pi[i] - phimin_roi, zi - zmin_roi) * (startz - (zi * step.Z() + zmin));
0900
0901 }
0902 if ((endz - zmin) / step.Z() - zf > ALMOST_ZERO)
0903 {
0904 partialInt += field->Get(ri[i] - rmin_roi, pi[i] - phimin_roi, zf - zmin_roi) * (endz - (zf * step.Z() + zmin));
0905
0906 }
0907
0908
0909 fieldInt += rw[i] * pw[i] * partialInt;
0910 }
0911
0912 return dir * fieldInt;
0913 }
0914
0915 void AnnularFieldSim::load_analytic_spacecharge(float scalefactor = 1)
0916 {
0917
0918
0919
0920
0921
0922
0923
0924 double ifc_radius = 83.5 * cm;
0925 double ofc_radius = 254.5 * cm;
0926 double tpc_halfz = 250 * cm;
0927
0928 aliceModel = new AnalyticFieldModel(ifc_radius / cm, ofc_radius / cm, tpc_halfz / cm, scalefactor);
0929 double totalcharge = 0;
0930 double localcharge = 0;
0931
0932 TVector3 pos;
0933 for (int ifr = 0; ifr < nr; ifr++)
0934 {
0935 for (int ifphi = 0; ifphi < nphi; ifphi++)
0936 {
0937 for (int ifz = 0; ifz < nz; ifz++)
0938 {
0939 pos = GetCellCenter(ifr, ifphi, ifz);
0940 pos = pos * (1.0 / cm);
0941 double vol = step.Z() * step.Phi() * (2 * (ifr * step.Perp() + rmin) + step.Perp()) * step.Perp();
0942
0943 localcharge = vol * aliceModel->Rho(pos);
0944 totalcharge += localcharge;
0945
0946 q->AddChargeInBin(ifr, ifphi, ifz, localcharge);
0947 }
0948 }
0949 }
0950 std::cout << std::format("AnnularFieldSim::load_analytic_spacecharge: Total charge Q={:E} Coulombs", totalcharge) << std::endl;
0951
0952 if (lookupCase == HybridRes)
0953 {
0954
0955 for (int i = 0; i < q_lowres->Length(); i++)
0956 {
0957 *(q_lowres->GetFlat(i)) = 0;
0958 }
0959
0960
0961
0962 for (int ifr = 0; ifr < nr; ifr++)
0963 {
0964 int r_low = ifr / r_spacing;
0965 for (int ifphi = 0; ifphi < nphi; ifphi++)
0966 {
0967 int phi_low = ifphi / phi_spacing;
0968 for (int ifz = 0; ifz < nz; ifz++)
0969 {
0970 int z_low = ifz / z_spacing;
0971 q_lowres->Add(r_low, phi_low, z_low, q->GetChargeInBin(ifr, ifphi, ifz));
0972 }
0973 }
0974 }
0975 }
0976 return;
0977 }
0978
0979 void AnnularFieldSim::loadEfield(const std::string &filename, const std::string &treename, int zsign)
0980 {
0981
0982
0983 std::cout << std::format("AnnularFieldSim::loadEfield: loading E field from file {}, tree {} (sign={})", filename, treename, zsign) << std::endl;
0984 TFile fieldFile(filename.c_str(), "READ");
0985 TTree *fTree;
0986 fieldFile.GetObject(treename.c_str(), fTree);
0987 Efieldname = "E:" + filename + ":" + treename;
0988 float r;
0989 float z;
0990 float fr;
0991 float fz;
0992 float fphi;
0993 fTree->SetBranchAddress("r", &r);
0994 fTree->SetBranchAddress("er", &fr);
0995 fTree->SetBranchAddress("z", &z);
0996 fTree->SetBranchAddress("ez", &fz);
0997
0998 fphi = 0;
0999
1000
1001
1002 loadField(&Eexternal, fTree, &r, nullptr, &z, &fr, &fphi, &fz, V / cm, zsign);
1003 std::cout << std::format("AnnularFieldSim::loadEfield: finished loading E field from file {}, tree {} (sign={})", filename, treename, zsign) << std::endl;
1004 fieldFile.Close();
1005 return;
1006 }
1007 void AnnularFieldSim::loadBfield(const std::string &filename, const std::string &treename)
1008 {
1009
1010
1011 TFile fieldFile(filename.c_str(), "READ");
1012 TTree *fTree;
1013 fieldFile.GetObject(treename.c_str(), fTree);
1014 Bfieldname = "B:" + filename + ":" + treename;
1015 float r;
1016 float z;
1017 float fr;
1018 float fz;
1019 float fphi;
1020 fTree->SetBranchAddress("r", &r);
1021 fTree->SetBranchAddress("br", &fr);
1022 fTree->SetBranchAddress("z", &z);
1023 fTree->SetBranchAddress("bz", &fz);
1024
1025 fphi = 0;
1026
1027
1028
1029 loadField(&Bfield, fTree, &r, nullptr, &z, &fr, &fphi, &fz, Tesla, 1);
1030 std::cout << std::format("AnnularFieldSim::loadBfield: finished loading B field from file {}, tree {}", filename, treename) << std::endl;
1031 ;
1032 fieldFile.Close();
1033
1034 return;
1035 }
1036
1037 void AnnularFieldSim::load3dBfield(const std::string &filename, const std::string &treename, int zsign, float scale, float xshift, float yshift, float zshift)
1038 {
1039
1040
1041 std::cout << std::format("AnnularFieldSim::load3dBfield: loading B field from file {}, tree {} (sign={})", filename, treename, zsign) << std::endl;
1042
1043 TFile fieldFile(filename.c_str(), "READ");
1044 TTree *fTree;
1045 fieldFile.GetObject(treename.c_str(), fTree);
1046 Bfieldname = "B(3D):" + filename + ":" + treename + " Scale =" + std::format("{:.2f}", scale);
1047 float r;
1048 float z;
1049 float phi;
1050 float fr;
1051 float fz;
1052 float fphi;
1053 fTree->SetBranchAddress("rho", &r);
1054 fTree->SetBranchAddress("brho", &fr);
1055 fTree->SetBranchAddress("z", &z);
1056 fTree->SetBranchAddress("bz", &fz);
1057 fTree->SetBranchAddress("phi", &phi);
1058 fTree->SetBranchAddress("bphi", &fphi);
1059 loadField(&Bfield, fTree, &r, &phi, &z, &fr, &fphi, &fz, Tesla * scale, zsign, xshift, yshift, zshift);
1060 std::cout << std::format("AnnularFieldSim::load3dBfield: finished loading B field from file {}, tree {} (sign={}) with scale {}\n", filename, treename, zsign, scale) << std::endl;
1061 fieldFile.Close();
1062 return;
1063 }
1064
1065 void AnnularFieldSim::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, float yshift, float zshift)
1066 {
1067
1068
1069
1070 TVector3 origin = TVector3(xshift, yshift, zshift);
1071 bool phiSymmetry = (phiptr == nullptr);
1072
1073 std::cout << std::format("AnnularFieldSim::loadField: loading field from {}, detector origin at ({},{},{}) in the field map, field unit {}, zsign {}, handling={}", source->GetName(), origin.X(), origin.Y(), origin.Z(), fieldunit, zsign, (phiSymmetry ? "phi-symmetric" : "not phi-symmetric")) << std::endl;
1074
1075 int lowres_factor = 3;
1076
1077
1078
1079
1080
1081
1082
1083 int nlowbins_phi = nphi / lowres_factor + 1;
1084 int nlowbins_r = nr / lowres_factor + 1;
1085 int nlowbins_z = nz / lowres_factor + 1;
1086 float phi_lowres_step = M_PI * 2.0 / nlowbins_phi;
1087 float r_lowres_step = (rmax - rmin) / nlowbins_r;
1088 float z_lowres_step = (zmax - zmin) / nlowbins_z;
1089
1090 std::cout << std::format("loading field from {}<z<{}", zmin, zmax) << std::endl;
1091 TH3F *htEntries = new TH3F("htentries", "num of entries in the field loading", nphi, 0, M_PI * 2.0, nr, rmin, rmax, nz, zmin, zmax);
1092 TH3F *htSum[3];
1093 TH3F *htEntriesLow = new TH3F("htentrieslow", "num of lowres entries in the field loading", nlowbins_phi + 2, -phi_lowres_step, M_PI * 2.0 + phi_lowres_step, nlowbins_r + 2, rmin - r_lowres_step, rmax + r_lowres_step, nlowbins_z + 2, zmin - z_lowres_step, zmax + z_lowres_step);
1094 TH3F *htSumLow[3];
1095 std::cout << std::format("AnnularFieldSim::loadField: hires has {} phi bins from {} to {}, {} r bins from {} to {}, and {} z bins from {} to {}",
1096 htEntries->GetNbinsX(), htEntries->GetXaxis()->GetXmin(), htEntries->GetXaxis()->GetXmax(),
1097 htEntries->GetNbinsY(), htEntries->GetYaxis()->GetXmin(), htEntries->GetYaxis()->GetXmax(),
1098 htEntries->GetNbinsZ(), htEntries->GetZaxis()->GetXmin(), htEntries->GetZaxis()->GetXmax())
1099 << std::endl;
1100 std::cout << std::format("AnnularFieldSim::loadField: lowres has {} phi bins from {} to {}, {} r bins from {} to {}, and {} z bins from {} to {}",
1101 htEntriesLow->GetNbinsX(), htEntriesLow->GetXaxis()->GetXmin(), htEntriesLow->GetXaxis()->GetXmax(),
1102 htEntriesLow->GetNbinsY(), htEntriesLow->GetYaxis()->GetXmin(), htEntriesLow->GetYaxis()->GetXmax(),
1103 htEntriesLow->GetNbinsZ(), htEntriesLow->GetZaxis()->GetXmin(), htEntriesLow->GetZaxis()->GetXmax())
1104 << std::endl;
1105
1106 std::string axis[]{"r", "p", "z"};
1107 for (int i = 0; i < 3; i++)
1108 {
1109
1110 htSum[i] = new TH3F(std::string("htsum" + std::to_string(i)).c_str(), std::string("sum of " + axis[i] + "-axis entries in the field loading").c_str(), htEntries->GetNbinsX(), htEntries->GetXaxis()->GetXmin(), htEntries->GetXaxis()->GetXmax(), htEntries->GetNbinsY(), htEntries->GetYaxis()->GetXmin(), htEntries->GetYaxis()->GetXmax(), htEntries->GetNbinsZ(), htEntries->GetZaxis()->GetXmin(), htEntries->GetZaxis()->GetXmax());
1111
1112 htSumLow[i] = new TH3F(std::string("htsumlow" + std::to_string(i)).c_str(), std::string("sum of low " + axis[i] + "-axis entries in the field loading").c_str(), htEntriesLow->GetNbinsX(), htEntriesLow->GetXaxis()->GetXmin(), htEntriesLow->GetXaxis()->GetXmax(), htEntriesLow->GetNbinsY(), htEntriesLow->GetYaxis()->GetXmin(), htEntriesLow->GetYaxis()->GetXmax(), htEntriesLow->GetNbinsZ(), htEntriesLow->GetZaxis()->GetXmin(), htEntriesLow->GetZaxis()->GetXmax());
1113 }
1114
1115
1116 std::vector<float> phiCoords(nphi);
1117 int nPhiCoords = 1;
1118 if (phiSymmetry)
1119 {
1120
1121
1122 for (int j = 0; j < nphi; j++)
1123 {
1124 float phi0 = (j + 0.5) * step.Phi();
1125 phiCoords[j] = phi0;
1126 std::cout << std::format("{:.2f} ", phi0) << std::endl;
1127 }
1128 std::cout << std::endl;
1129 nPhiCoords = nphi;
1130 }
1131
1132
1133 int nEntries = source->GetEntries();
1134 for (int i = 0; i < nEntries; i++)
1135 {
1136
1137
1138 int rem = i % (source->GetEntries() / 10);
1139 int quo = i / (source->GetEntries() / 10);
1140 if (rem == 0 && quo > 0)
1141 {
1142 std::cout << std::format("loadField: {}0%", quo) << std::endl;
1143 }
1144
1145 source->GetEntry(i);
1146 float zval = *zptr * zsign;
1147
1148 float rval = *rptr;
1149 float phival;
1150
1151 float fzval = *fzptr * fieldunit * zsign;
1152 float fphival = *fphiptr * fieldunit * zsign;
1153 float frval = *frptr * fieldunit;
1154 TVector3 rphizField(frval, fphival, fzval);
1155
1156
1157
1158 if (!phiSymmetry)
1159 {
1160 assert(phiptr);
1161 phiCoords[0] = *phiptr;
1162 }
1163 for (int j = 0; j < nPhiCoords; j++)
1164 {
1165 phival = phiCoords[j];
1166
1167
1168 TVector3 fieldMapPos(rval, 0, zval);
1169 fieldMapPos.SetPhi(phival);
1170
1171
1172 TVector3 globalField = rphizField;
1173 globalField.RotateZ(phival);
1174
1175
1176 TVector3 tpcPos = fieldMapPos - origin;
1177
1178 TVector3 tpcField = GetLocalFieldComponents(globalField, fieldMapPos, origin);
1179
1180
1181 rval = tpcPos.Perp();
1182 phival = FilterPhiPos(tpcPos.Phi());
1183 zval = tpcPos.Z();
1184
1185
1186 frval = tpcField.X();
1187 fphival = tpcField.Y();
1188 fzval = tpcField.Z();
1189
1190 htEntries->Fill(phival, rval, zval);
1191 htSum[0]->Fill(phival, rval, zval, frval);
1192 htSum[1]->Fill(phival, rval, zval, fphival);
1193 htSum[2]->Fill(phival, rval, zval, fzval);
1194 htEntriesLow->Fill(phival, rval, zval);
1195 htSumLow[0]->Fill(phival, rval, zval, frval);
1196 htSumLow[1]->Fill(phival, rval, zval, fphival);
1197 htSumLow[2]->Fill(phival, rval, zval, fzval);
1198 }
1199 }
1200
1201 int nemptybins = 0;
1202 for (int i = 0; i < nphi; i++)
1203 {
1204 for (int j = 0; j < nr; j++)
1205 {
1206 for (int k = 0; k < nz; k++)
1207 {
1208 TVector3 cellcenter = GetCellCenter(j, i, k);
1209 int bin = htEntries->FindBin(FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z());
1210 TVector3 fieldvec(htSum[0]->GetBinContent(bin), htSum[1]->GetBinContent(bin), htSum[2]->GetBinContent(bin));
1211 if (htEntries->GetBinContent(bin) < 0.99)
1212 {
1213
1214 nemptybins++;
1215 }
1216 else
1217 {
1218 fieldvec = fieldvec * (1.0 / htEntries->GetBinContent(bin));
1219 }
1220
1221
1222
1223 fieldvec.RotateZ(FilterPhiPos(cellcenter.Phi()));
1224 (*field)->Set(j, i, k, fieldvec);
1225 }
1226 }
1227 }
1228 if (nemptybins > 0)
1229 {
1230 std::cout << std::format("found {} empty bins when constructing {}. Filling with lower resolution.", nemptybins, ((*field == Bfield) ? "Bfield" : "Eexternal")) << std::endl;
1231
1232
1233 for (int j = 0; j < htEntriesLow->GetNbinsY(); j++)
1234 {
1235 for (int k = 0; k < htEntriesLow->GetNbinsZ(); k++)
1236 {
1237 int lowbin = htEntriesLow->GetBin(1, j, k);
1238 int highbin = htEntriesLow->GetBin(htEntriesLow->GetNbinsX() - 1, j, k);
1239 htEntriesLow->SetBinContent(lowbin,
1240 htEntriesLow->GetBinContent(htEntriesLow->GetBin(htEntriesLow->GetNbinsX() - 2, j, k)));
1241
1242 for (auto &i : htSumLow)
1243 {
1244 i->SetBinContent(lowbin,
1245 i->GetBinContent(i->GetBin(htEntriesLow->GetNbinsX() - 2, j, k)));
1246 }
1247
1248 htEntriesLow->SetBinContent(highbin,
1249 htEntriesLow->GetBinContent(htEntriesLow->GetBin(2, j, k)));
1250 for (auto &i : htSumLow)
1251 {
1252 i->SetBinContent(highbin,
1253 i->GetBinContent(i->GetBin(2, j, k)));
1254 }
1255 }
1256 }
1257
1258
1259 for (int i = 0; i < nphi; i++)
1260 {
1261 for (int j = 0; j < nr; j++)
1262 {
1263 for (int k = 0; k < nz; k++)
1264 {
1265 TVector3 cellcenter = GetCellCenter(j, i, k);
1266 int bin = htEntries->FindBin(FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z());
1267 if (htEntries->GetBinContent(bin) == 0)
1268 {
1269 if (false)
1270 {
1271 std::cout << std::format("Filling coordinates p{},r{},z{}, (cell p{} r{} z{}) with lowres field", FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z(), i, j, k) << std::endl;
1272 std::cout << std::format(" sanity: htEntries->FindBins(p{:.2f},r{:.2f},z{:.2f})=(p{},r{},z{})={}, content={}", FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z(),
1273 htEntries->GetXaxis()->FindBin(FilterPhiPos(cellcenter.Phi())),
1274 htEntries->GetYaxis()->FindBin(cellcenter.Perp()),
1275 htEntries->GetZaxis()->FindBin(cellcenter.Z()), bin, htEntries->GetBinContent(bin))
1276 << std::endl;
1277
1278 TVector3 globalPos = cellcenter + origin;
1279
1280 float tr_low = htEntries->GetYaxis()->GetBinLowEdge(htEntries->GetYaxis()->FindBin(cellcenter.Perp()));
1281 float tr_high = htEntries->GetYaxis()->GetBinUpEdge(htEntries->GetYaxis()->FindBin(cellcenter.Perp()));
1282 float tz_low = htEntries->GetZaxis()->GetBinLowEdge(htEntries->GetZaxis()->FindBin(cellcenter.Z()));
1283 float tz_high = htEntries->GetZaxis()->GetBinUpEdge(htEntries->GetZaxis()->FindBin(cellcenter.Z()));
1284 float tphi_low = htEntries->GetXaxis()->GetBinLowEdge(htEntries->GetXaxis()->FindBin(FilterPhiPos(cellcenter.Phi())));
1285 float tphi_high = htEntries->GetXaxis()->GetBinUpEdge(htEntries->GetXaxis()->FindBin(FilterPhiPos(cellcenter.Phi())));
1286 TVector3 tlow(tr_low, 0, tz_low);
1287 tlow.RotateZ(tphi_low);
1288 TVector3 thigh(tr_high, 0, tz_high);
1289 thigh.RotateZ(tphi_high);
1290 tlow = tlow + origin;
1291 thigh = thigh + origin;
1292 if (phiSymmetry)
1293 {
1294 std::cout << std::format("this corresponds to (r,phi,z)=({},{},{}) in the input map coordinates, which has bounds roughly (r>{:.1f} && r<{:.1f} && z> {:.1f} && z<{:.1f})\n", globalPos.Perp(), globalPos.Phi(), globalPos.Z(), tlow.Perp(), thigh.Perp(), tlow.Z(), thigh.Z()) << std::endl;
1295 }
1296 else
1297 {
1298 std::cout << std::format("this corresponds to (r,phi,z)=({},{},{}) in the input map coordinates, which has bounds roughly (r>{:.1f} && r<{:.1f} && phi>{:.1f} && phi<{:.1f} && z> {:.1f} && z<{:.1f})\n", globalPos.Perp(), globalPos.Phi(), globalPos.Z(), tlow.Perp(), thigh.Perp(), tlow.Phi(), thigh.Phi(), tlow.Z(), thigh.Z()) << std::endl;
1299 }
1300 }
1301
1302 TVector3 fieldvec(htSumLow[0]->Interpolate(FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z()),
1303 htSumLow[1]->Interpolate(FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z()),
1304 htSumLow[2]->Interpolate(FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z()));
1305 int lowbin = htEntriesLow->FindBin(FilterPhiPos(cellcenter.Phi()), cellcenter.Perp(), cellcenter.Z());
1306
1307
1308 if (htEntriesLow->GetBinContent(lowbin) < 0.99)
1309 {
1310 std::cout << std::format("not enough entries in source to fill fieldmap, even using the lower res fall-back. Value near r={:.2f}, phi={:.2f}, z={:.2f} is {}, (with range of {:.3f},{:.3f},{:.3f}) Pick lower granularity!",
1311 cellcenter.Perp(), FilterPhiPos(cellcenter.Phi()), cellcenter.Z(),
1312 htEntriesLow->GetBinContent(lowbin), r_lowres_step, phi_lowres_step, z_lowres_step)
1313 << std::endl;
1314 std::cout << "Saving fieldmaps to debug.hist.root" << std::endl;
1315 TFile *debugfile = new TFile("debug.hist.root", "RECREATE");
1316 htEntries->Write();
1317 htEntriesLow->Write();
1318 for (int ii = 0; ii < 3; ii++)
1319 {
1320 htSum[ii]->Write();
1321 htSumLow[ii]->Write();
1322 }
1323 debugfile->Close();
1324 exit(1);
1325 }
1326 else
1327 {
1328 fieldvec = fieldvec * (1.0 / htEntriesLow->GetBinContent(lowbin));
1329 }
1330
1331 fieldvec.RotateZ(FilterPhiPos(cellcenter.Phi()));
1332 (*field)->Set(j, i, k, fieldvec);
1333 }
1334 }
1335 }
1336 }
1337 }
1338
1339 return;
1340 }
1341
1342 void AnnularFieldSim::load_spacecharge(const std::string &filename, const std::string &histname, float zoffset, float chargescale, float cmscale, bool isChargeDensity)
1343 {
1344 TFile *f = TFile::Open(filename.c_str());
1345 TH3 *scmap;
1346 f->GetObject(histname.c_str(), scmap);
1347 std::cout << "Loading spacecharge from '" << filename
1348 << "'. Seeking histname '" << histname << "'" << std::endl;
1349 chargesourcename = filename + ":" + histname;
1350 load_spacecharge(scmap, zoffset, chargescale, cmscale, isChargeDensity, chargesourcename);
1351 f->Close();
1352 return;
1353 }
1354
1355 void AnnularFieldSim::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)
1356 {
1357 TFile *f = TFile::Open(filename.c_str());
1358 TH3 *scmap;
1359 f->GetObject(histname.c_str(), scmap);
1360 std::cout << "Resampling spacecharge from '" << filename
1361 << "'. Seeking histname '" << histname << "'" << std::endl;
1362 chargesourcename = filename + ":" + histname;
1363 load_and_resample_spacecharge(new_nphi, new_nr, new_nz, scmap, zoffset, chargescale, cmscale, isChargeDensity);
1364 f->Close();
1365 return;
1366 }
1367
1368 void AnnularFieldSim::load_and_resample_spacecharge(int new_nphi, int new_nr, int new_nz, TH3 *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity)
1369 {
1370
1371
1372
1373
1374
1375
1376
1377 float hrmin = hist->GetYaxis()->GetXmin();
1378 float hrmax = hist->GetYaxis()->GetXmax();
1379 float hphimin = hist->GetXaxis()->GetXmin();
1380 float hphimax = hist->GetXaxis()->GetXmax();
1381 float hzmin = hist->GetZaxis()->GetXmin();
1382 float hzmax = hist->GetZaxis()->GetXmax();
1383
1384
1385 int hrn = hist->GetNbinsY();
1386 int hphin = hist->GetNbinsX();
1387 int hzn = hist->GetNbinsZ();
1388 std::cout << std::format("From histogram, native r dimensions: {}<r<{}, hrn (Y axis)={}", hrmin, hrmax, hrn) << std::endl;
1389
1390 std::cout << std::format("From histogram, native phi dimensions: {}<phi<{}, hrn (X axis)={}", hphimin, hphimax, hphin) << std::endl;
1391
1392 std::cout << std::format("From histogram, native z dimensions: {}<z<{}, hrn (Z axis)={}", hzmin, hzmax, hzn) << std::endl;
1393
1394
1395 float hrstep = (hrmax - hrmin) / hrn;
1396 float hphistep = (hphimax - hphimin) / hphin;
1397 float hzstep = (hzmax - hzmin) / hzn;
1398 float halfrstep = hrstep * 0.5;
1399 float halfphistep = hphistep * 0.5;
1400 float halfzstep = hzstep * 0.5;
1401
1402
1403
1404
1405 if (!isChargeDensity)
1406 {
1407 for (int i = 0; i < hphin; i++)
1408 {
1409 float phi = hphimin + hphistep * i;
1410 for (int j = 0; j < hrn; j++)
1411 {
1412 float r = hrmin + hrstep * j;
1413 for (int k = 0; k < hzn; k++)
1414 {
1415 float z = hzmin + hzstep * k;
1416 int bin = hist->FindBin(phi + halfphistep, r + halfrstep, z + halfzstep);
1417 double vol = hzstep * hphistep * (r + halfrstep) * hrstep;
1418 hist->SetBinContent(bin, hist->GetBinContent(bin) / vol);
1419 }
1420 }
1421 }
1422 }
1423
1424 TH3F *resampled = new TH3F("resampled", "resampled charge", new_nphi, hphimin, hphimax, new_nr, hrmin, hrmax, new_nz, hzmin, hzmax);
1425 float new_phistep = (hphimax - hphimin) / new_nphi;
1426 float new_rstep = (hrmax - hrmin) / new_nr;
1427 float new_zstep = (hzmax - hzmin) / new_nz;
1428
1429
1430 for (int i = 0; i < new_nphi; i++)
1431 {
1432 float phi = hphimin + new_phistep * (i + 0.5);
1433 float hphi = (phi - hphimin) / hphistep;
1434 bool phisafe = ((hphi - 0.75) > 0) && ((hphi + 0.75) < hphin);
1435 for (int j = 0; j < new_nr; j++)
1436 {
1437 float r = hrmin + new_rstep * (j + 0.5);
1438 float hr = (r - hrmin) / hrstep;
1439 bool rsafe = ((hr - 0.5) > 0) && ((hr + 0.5) < hrn);
1440 for (int k = 0; k < new_nz; k++)
1441 {
1442 float z = hzmin + new_zstep * (k + 0.5);
1443 float hz = (z - hzmin) / hzstep;
1444 bool zsafe = ((hz - 0.5) > 0) && ((hz + 0.5) < hzn);
1445
1446 if (phisafe && rsafe && zsafe)
1447 {
1448
1449 resampled->Fill(phi, r, z, hist->Interpolate(phi, r, z));
1450 }
1451 else
1452 {
1453 int bin = hist->FindBin(phi, r, z);
1454 resampled->Fill(phi, r, z, hist->GetBinContent(bin));
1455 }
1456 }
1457 }
1458 }
1459 std::cout << "here in load_and_resample_spacecharge, I am going to call load_spacecharge." << std::endl;
1460 load_spacecharge(resampled, zoffset, chargescale, cmscale, true);
1461 }
1462
1463 void AnnularFieldSim::load_spacecharge(TH3 *hist, float zoffset, float chargescale, float cmscale, bool isChargeDensity, const std::string &inputchargestring)
1464 {
1465
1466 if (std::abs(zoffset) > 0.001)
1467 {
1468 std::cout << std::format("nonzero zoffset given ({:E}) but new spacecharge loader can't deal with that. Failing.", zoffset) << std::endl;
1469
1470 assert(false);
1471 }
1472 if (isChargeDensity)
1473 {
1474 std::cout << "Input dataset is flagged as recording density not total charge, but new loader can't deal with that Failing.." << std::endl;
1475 assert(false);
1476 }
1477 q->ReadSourceCharge(hist, cmscale, chargescale);
1478
1479 chargestring = "SC loaded externally: " + inputchargestring + ".";
1480 return;
1481 }
1482
1483 void AnnularFieldSim::load_digital_current(TH3 *hist, TH2 *gainHist, float chargescale, float cmscale, const std::string &inputchargestring)
1484 {
1485 q->ReadSourceAdc(hist, gainHist, cmscale, chargescale);
1486
1487 chargestring = "SC loaded externally: " + inputchargestring + ".";
1488 return;
1489 }
1490
1491 void AnnularFieldSim::save_spacecharge(const std::string &filename)
1492 {
1493
1494 TH3F *hsc = new TH3F("hInternalSpaceCharge", "Internal Charge Histogram;phi(rad);r(cm);z(cm)", nphi, 0, phispan, nr, rmin, rmax, nz, zmin, zmax);
1495 for (int i = 0; i < nphi; i++)
1496 {
1497 float phi = 0 + step.Phi() * (i + 0.5);
1498 for (int j = 0; j < nr; j++)
1499 {
1500 float r = rmin + step.Perp() * (j + 0.5);
1501 for (int k = 0; k < nz; k++)
1502 {
1503 float z = zmin + step.Z() * (k + 0.5);
1504 hsc->Fill(phi, r, z, q->GetChargeAtPosition(r, phi, z));
1505
1506 }
1507 }
1508 }
1509 hsc->SaveAs(filename.c_str());
1510 return;
1511 }
1512
1513 void AnnularFieldSim::add_testcharge(float r, float phi, float z, float coulombs)
1514 {
1515 q->AddChargeAtPosition(r, phi, z, coulombs * C);
1516 return;
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544 }
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567 void AnnularFieldSim::populate_fieldmap()
1568 {
1569
1570
1571 std::cout << std::format("in pop_fieldmap, n=({},{},{})\n", nr, nphi, nz) << std::endl;
1572
1573 std::cout << std::format("populating fieldmap for ({}x{}x{}) grid with ({}x{}x{}) source ", nr_roi, nphi_roi, nz_roi, nr, nphi, nz) << std::endl;
1574
1575 if (truncation_length > 0)
1576 {
1577 std::cout << std::format(" ==> truncating anything more than {} cells away", truncation_length) << std::endl;
1578 }
1579 unsigned long long totalelements = nr_roi;
1580 totalelements *= nphi_roi;
1581 totalelements *= nz_roi;
1582 unsigned long long percent = totalelements / 100 * debug_npercent;
1583 std::cout << std::format("total elements = {}", totalelements * nr * nphi * nz) << std::endl;
1584
1585 int el = 0;
1586
1587 TVector3 localF;
1588 for (int ir = rmin_roi; ir < rmax_roi; ir++)
1589 {
1590 for (int iphi = phimin_roi; iphi < phimax_roi; iphi++)
1591 {
1592 for (int iz = zmin_roi; iz < zmax_roi; iz++)
1593 {
1594 localF = sum_field_at(ir, iphi, iz);
1595 if (!(el % percent))
1596 {
1597 std::cout << std::format("populate_fieldmap {}%: ", static_cast<uint64_t>(debug_npercent) * el / percent);
1598
1599 std::cout << std::format("sum_field_at (ir={}, iphi={}, iz={}) gives ({:E},{:E},{:E})", ir, iphi, iz, localF.X(), localF.Y(), localF.Z()) << std::endl;
1600 }
1601 el++;
1602
1603 Efield->Set(ir - rmin_roi, iphi - phimin_roi, iz - zmin_roi, localF);
1604
1605
1606 }
1607 }
1608 }
1609 return;
1610 }
1611
1612 void AnnularFieldSim::populate_lookup()
1613 {
1614
1615
1616
1617
1618
1619 if (lookupCase == Full3D)
1620 {
1621 std::cout << "lookupCase==Full3D" << std::endl;
1622
1623 populate_full3d_lookup();
1624 }
1625 else if (lookupCase == HybridRes)
1626 {
1627 std::cout << "lookupCase==HybridRes" << std::endl;
1628 populate_highres_lookup();
1629 populate_lowres_lookup();
1630 }
1631 else if (lookupCase == PhiSlice)
1632 {
1633 std::cout << "Populating lookup: lookupCase==PhiSlice" << std::endl;
1634 populate_phislice_lookup();
1635 }
1636 else if (lookupCase == Analytic)
1637 {
1638 std::cout << "Populating lookup: lookupCase==Analytic ===> skipping!" << std::endl;
1639 }
1640 else if (lookupCase == NoLookup)
1641 {
1642 std::cout << "Populating lookup: lookupCase==NoLookup ===> skipping!" << std::endl;
1643 }
1644 else
1645 {
1646 exit(1);
1647 }
1648 return;
1649 }
1650
1651 void AnnularFieldSim::populate_full3d_lookup()
1652 {
1653
1654
1655
1656 std::cout << std::format("populating full lookup table for ({}x{}x{})x({}x{}x{}) grid",
1657 rmax_roi - rmin_roi, phimax_roi - phimin_roi, zmax_roi - zmin_roi, nr, nphi, nz)
1658 << std::endl;
1659 unsigned long long totalelements = (rmax_roi - rmin_roi);
1660 totalelements *= (phimax_roi - phimin_roi);
1661 totalelements *= (zmax_roi - zmin_roi);
1662 totalelements *= nr;
1663 totalelements *= nphi;
1664 totalelements *= nz;
1665 unsigned long long percent = totalelements / 100 * debug_npercent;
1666 std::cout << std::format("total elements = {}", totalelements) << std::endl;
1667 TVector3 at(1, 0, 0);
1668 TVector3 from(1, 0, 0);
1669 TVector3 zero(0, 0, 0);
1670
1671 int el = 0;
1672 for (int ifr = rmin_roi; ifr < rmax_roi; ifr++)
1673 {
1674 for (int ifphi = phimin_roi; ifphi < phimax_roi; ifphi++)
1675 {
1676 for (int ifz = zmin_roi; ifz < zmax_roi; ifz++)
1677 {
1678 at = GetCellCenter(ifr, ifphi, ifz);
1679 for (int ior = 0; ior < nr; ior++)
1680 {
1681 for (int iophi = 0; iophi < nphi; iophi++)
1682 {
1683 for (int ioz = 0; ioz < nz; ioz++)
1684 {
1685 el++;
1686 if (!(el % percent))
1687 {
1688 std::cout << std::format("populate_full3d_lookup {}%", static_cast<uint64_t>(debug_npercent) * el / percent) << std::endl;
1689 }
1690 from = GetCellCenter(ior, iophi, ioz);
1691
1692
1693
1694 if (ifr == ior && ifphi == iophi && ifz == ioz)
1695 {
1696 Epartial->Set(ifr - rmin_roi, ifphi - phimin_roi, ifz - zmin_roi, ior, iophi, ioz, zero);
1697 }
1698 else
1699 {
1700 Epartial->Set(ifr - rmin_roi, ifphi - phimin_roi, ifz - zmin_roi, ior, iophi, ioz, calc_unit_field(at, from));
1701 }
1702 }
1703 }
1704 }
1705 }
1706 }
1707 }
1708 return;
1709 }
1710
1711 void AnnularFieldSim::populate_highres_lookup()
1712 {
1713 TVector3 at(1, 0, 0);
1714 TVector3 from(1, 0, 0);
1715 TVector3 zero(0, 0, 0);
1716
1717
1718 int r_highres_dist = (nr_high - 1) / 2;
1719 int phi_highres_dist = (nphi_high - 1) / 2;
1720 int z_highres_dist = (nz_high - 1) / 2;
1721
1722
1723 static int nfbinsin[3][3][3];
1724 for (auto &i : nfbinsin)
1725 {
1726 for (auto &j : i)
1727 {
1728 for (int &k : j)
1729 {
1730 k = 0;
1731 }
1732 }
1733 }
1734
1735
1736
1737
1738 TVector3 currentf;
1739 TVector3 newf;
1740 for (int ifr = rmin_roi; ifr < rmax_roi; ifr++)
1741 {
1742 int r_parentlow = std::floor((ifr - r_highres_dist) / (r_spacing * 1.0));
1743 int r_parenthigh = std::floor((ifr + r_highres_dist) / (r_spacing * 1.0)) + 1;
1744 int r_startpoint = r_parentlow * r_spacing;
1745 int r_endpoint = r_parenthigh * r_spacing;
1746
1747 for (int ifphi = phimin_roi; ifphi < phimax_roi; ifphi++)
1748 {
1749 int phi_parentlow = std::floor(FilterPhiIndex(ifphi - phi_highres_dist) / (phi_spacing * 1.0));
1750 bool phi_parentlow_wrapped = (ifphi - phi_highres_dist < 0);
1751 int phi_startpoint = phi_parentlow * phi_spacing;
1752 if (phi_parentlow_wrapped)
1753 {
1754 phi_startpoint -= nphi;
1755 }
1756
1757 int phi_parenthigh = std::floor(FilterPhiIndex(ifphi + phi_highres_dist) / (phi_spacing * 1.0)) + 1;
1758 bool phi_parenthigh_wrapped = (ifphi + phi_highres_dist >= nphi);
1759 int phi_endpoint = phi_parenthigh * phi_spacing;
1760 if (phi_parenthigh_wrapped)
1761 {
1762 phi_endpoint += nphi;
1763 }
1764
1765 for (int ifz = zmin_roi; ifz < zmax_roi; ifz++)
1766 {
1767
1768
1769 int z_parentlow = std::floor((ifz - z_highres_dist) / (z_spacing * 1.0));
1770 int z_parenthigh = std::floor((ifz + z_highres_dist) / (z_spacing * 1.0)) + 1;
1771 int z_startpoint = z_parentlow * z_spacing;
1772 int z_endpoint = z_parenthigh * z_spacing;
1773
1774
1775 at = GetCellCenter(ifr, ifphi, ifz);
1776
1777
1778
1779
1780
1781
1782
1783 for (int ir = r_startpoint; ir < r_endpoint; ir++)
1784 {
1785
1786
1787 ir = std::max(ir, 0);
1788 if (ir >= nr)
1789 {
1790 break;
1791 }
1792
1793 int rbin = (ir - ifr) + r_highres_dist;
1794 int rcell = 1;
1795 if (rbin <= 0)
1796 {
1797 rbin = 0;
1798 rcell = 0;
1799 }
1800 if (rbin >= nr_high)
1801 {
1802 rbin = nr_high - 1;
1803 rcell = 2;
1804 }
1805
1806 for (int iphi = phi_startpoint; iphi < phi_endpoint; iphi++)
1807 {
1808
1809 int phiFilt = FilterPhiIndex(iphi);
1810 int phibin = (iphi - ifphi) + phi_highres_dist;
1811 int phicell = 1;
1812 if (phibin <= 0)
1813 {
1814 phibin = 0;
1815 phicell = 0;
1816 }
1817 if (phibin >= nphi_high)
1818 {
1819 phibin = nphi_high - 1;
1820 phicell = 2;
1821 }
1822 for (int iz = z_startpoint; iz < z_endpoint; iz++)
1823 {
1824 iz = std::max(iz, 0);
1825 if (iz >= nz)
1826 {
1827 break;
1828 }
1829 int zbin = (iz - ifz) + z_highres_dist;
1830 int zcell = 1;
1831 if (zbin <= 0)
1832 {
1833 zbin = 0;
1834 zcell = 0;
1835 }
1836 if (zbin >= nz_high)
1837 {
1838 zbin = nz_high - 1;
1839 zcell = 2;
1840 }
1841
1842 from = GetCellCenter(ir, phiFilt, iz);
1843
1844 nfbinsin[rcell][phicell][zcell]++;
1845 int nf = nfbinsin[rcell][phicell][zcell];
1846
1847 int ir_rel = ifr - rmin_roi;
1848 int iphi_rel = ifphi - phimin_roi;
1849 int iz_rel = ifz - zmin_roi;
1850 if (zcell != 1 || rcell != 1 || phicell != 1)
1851 {
1852
1853
1854 if (iphi_rel < 0)
1855 {
1856 std::cout << std::format("{}: Getting with phi={}", __LINE__, iphi_rel) << std::endl;
1857 }
1858 currentf = Epartial_highres->Get(ir_rel, iphi_rel, iz_rel, rbin, phibin, zbin);
1859
1860
1861 newf = (currentf * (nf - 1) + calc_unit_field(at, from)) * (1 / (nf * 1.0));
1862 Epartial_highres->Set(ir_rel, iphi_rel, iz_rel, rbin, phibin, zbin, newf);
1863 }
1864 else
1865 {
1866
1867
1868 if (ifr == rbin && ifphi == phibin && ifz == zbin)
1869 {
1870 Epartial_highres->Set(ir_rel, iphi_rel, iz_rel, rbin, phibin, zbin, zero);
1871 }
1872 else
1873 {
1874 newf = calc_unit_field(at, from);
1875 Epartial_highres->Set(ir_rel, iphi_rel, iz_rel, rbin, phibin, zbin, newf);
1876 }
1877 }
1878 }
1879 }
1880 }
1881 }
1882 }
1883 }
1884 return;
1885 }
1886
1887 void AnnularFieldSim::populate_lowres_lookup()
1888 {
1889 TVector3 at(1, 0, 0);
1890 TVector3 from(1, 0, 0);
1891 TVector3 zero(0, 0, 0);
1892 int fphi_low;
1893 int fphi_high;
1894 int fz_low;
1895 int fz_high;
1896 int r_low;
1897 int r_high;
1898 int phi_low;
1899 int phi_high;
1900 int z_low;
1901 int z_high;
1902
1903
1904 for (int ifr = rmin_roi_low; ifr < rmax_roi_low; ifr++)
1905 {
1906 int fr_low = ifr * r_spacing;
1907 int fr_high = fr_low + r_spacing - 1;
1908 if (fr_high >= nr)
1909 {
1910 fr_high = nr - 1;
1911 }
1912 for (int ifphi = phimin_roi_low; ifphi < phimax_roi_low; ifphi++)
1913 {
1914 fphi_low = ifphi * phi_spacing;
1915 fphi_high = fphi_low + phi_spacing - 1;
1916 if (fphi_high >= nphi)
1917 {
1918 fphi_high = nphi - 1;
1919 }
1920 for (int ifz = zmin_roi_low; ifz < zmax_roi_low; ifz++)
1921 {
1922 fz_low = ifz * z_spacing;
1923 fz_high = fz_low + z_spacing - 1;
1924 if (fz_high >= nz)
1925 {
1926 fz_high = nz - 1;
1927 }
1928 at = GetGroupCellCenter(fr_low, fr_high, fphi_low, fphi_high, fz_low, fz_high);
1929
1930
1931
1932 for (int ior = 0; ior < nr_low; ior++)
1933 {
1934 r_low = ior * r_spacing;
1935 r_high = r_low + r_spacing - 1;
1936 int ir_rel = ifr - rmin_roi_low;
1937
1938 if (r_high >= nr)
1939 {
1940 r_high = nr - 1;
1941 }
1942 for (int iophi = 0; iophi < nphi_low; iophi++)
1943 {
1944 phi_low = iophi * phi_spacing;
1945 phi_high = phi_low + phi_spacing - 1;
1946 if (phi_high >= nphi)
1947 {
1948 phi_high = nphi - 1;
1949 }
1950 int iphi_rel = ifphi - phimin_roi_low;
1951 for (int ioz = 0; ioz < nz_low; ioz++)
1952 {
1953 z_low = ioz * z_spacing;
1954 z_high = z_low + z_spacing - 1;
1955 if (z_high >= nz)
1956 {
1957 z_high = nz - 1;
1958 }
1959 int iz_rel = ifz - zmin_roi_low;
1960 from = GetGroupCellCenter(r_low, r_high, phi_low, phi_high, z_low, z_high);
1961
1962 if (ifr == ior && ifphi == iophi && ifz == ioz)
1963 {
1964 Epartial_lowres->Set(ir_rel, iphi_rel, iz_rel, ior, iophi, ioz, zero);
1965 }
1966 else
1967 {
1968 Epartial_lowres->Set(ir_rel, iphi_rel, iz_rel, ior, iophi, ioz, calc_unit_field(at, from));
1969 }
1970
1971
1972
1973
1974 }
1975 }
1976 }
1977 }
1978 }
1979 }
1980 return;
1981 }
1982
1983 void AnnularFieldSim::populate_phislice_lookup()
1984 {
1985
1986
1987
1988 std::cout << std::format("populating phislice lookup for ({}x{}x{})x({}x{}x{}) grid", nr_roi, 1, nz_roi, nr, nphi, nz) << std::endl;
1989 unsigned long long totalelements = nr;
1990 totalelements *= nphi;
1991 totalelements *= nz;
1992 totalelements *= nr_roi;
1993 totalelements *= nz_roi;
1994 unsigned long long percent = totalelements / 100 * debug_npercent;
1995 std::cout << std::format("total elements = {}", totalelements) << std::endl;
1996 TVector3 at(1, 0, 0);
1997 TVector3 from(1, 0, 0);
1998 TVector3 zero(0, 0, 0);
1999
2000 int el = 0;
2001 for (int ifr = rmin_roi; ifr < rmax_roi; ifr++)
2002 {
2003 for (int ifz = zmin_roi; ifz < zmax_roi; ifz++)
2004 {
2005 at = GetCellCenter(ifr, 0, ifz);
2006 for (int ior = 0; ior < nr; ior++)
2007 {
2008 for (int iophi = 0; iophi < nphi; iophi++)
2009 {
2010 for (int ioz = 0; ioz < nz; ioz++)
2011 {
2012 el++;
2013 from = GetCellCenter(ior, iophi, ioz);
2014
2015
2016 if (ifr == ior && 0 == iophi && ifz == ioz)
2017 {
2018 if (!(el % percent))
2019 {
2020 std::cout << std::format("populate_phislice_lookup {}%: ", static_cast<uint64_t>(debug_npercent) * el / percent);
2021
2022 std::cout << std::format("self-to-self is zero (ir={}, iphi={}, iz={}) to (or={}, ophi=0, oz={}) gives ({:E},{:E},{:E})",
2023 ior, iophi, ioz, ifr, ifz, zero.X(), zero.Y(), zero.Z())
2024 << std::endl;
2025 }
2026 Epartial_phislice->Set(ifr - rmin_roi, 0, ifz - zmin_roi, ior, iophi, ioz, zero);
2027 }
2028 else
2029 {
2030 TVector3 unitf = calc_unit_field(at, from);
2031 if (true)
2032 {
2033 if (!(el % percent))
2034 {
2035 std::cout << std::format("populate_phislice_lookup {}%: ", static_cast<uint64_t>(debug_npercent) * el / percent);
2036
2037 std::cout << std::format("calc_unit_field (ir={}, iphi={}, iz={}) to (or={}, ophi=0, oz={}) gives ({:E},{:E},{:E})",
2038 ior, iophi, ioz, ifr, ifz, unitf.X(), unitf.Y(), unitf.Z())
2039 << std::endl;
2040 }
2041 }
2042
2043 Epartial_phislice->Set(ifr - rmin_roi, 0, ifz - zmin_roi, ior, iophi, ioz, unitf);
2044 }
2045 }
2046 }
2047 }
2048 }
2049 }
2050 return;
2051 }
2052
2053 void AnnularFieldSim::load_phislice_lookup(const std::string &sourcefile)
2054 {
2055 std::cout << std::format("loading phislice lookup for ({}x{}x{})x({}x{}x{}) grid from {}",
2056 nr_roi, 1, nz_roi, nr, nphi, nz, sourcefile)
2057 << std::endl;
2058 unsigned long long totalelements = nr;
2059 totalelements *= nphi;
2060 totalelements *= nz;
2061 totalelements *= nr_roi;
2062 totalelements *= nz_roi;
2063 unsigned long long percent = totalelements / 100 * debug_npercent;
2064 std::cout << std::format("total elements = {}", totalelements) << std::endl;
2065
2066 TFile *input = TFile::Open(sourcefile.c_str(), "READ");
2067 TTree *tInfo;
2068 input->GetObject("info", tInfo);
2069 assert(tInfo);
2070
2071 float file_rmin;
2072 float file_rmax;
2073 float file_zmin;
2074 float file_zmax;
2075 int file_rmin_roi;
2076 int file_rmax_roi;
2077 int file_zmin_roi;
2078 int file_zmax_roi;
2079 int file_nr;
2080 int file_np;
2081 int file_nz;
2082 tInfo->SetBranchAddress("rmin", &file_rmin);
2083 tInfo->SetBranchAddress("rmax", &file_rmax);
2084 tInfo->SetBranchAddress("zmin", &file_zmin);
2085 tInfo->SetBranchAddress("zmax", &file_zmax);
2086 tInfo->SetBranchAddress("rmin_roi_index", &file_rmin_roi);
2087 tInfo->SetBranchAddress("rmax_roi_index", &file_rmax_roi);
2088 tInfo->SetBranchAddress("zmin_roi_index", &file_zmin_roi);
2089 tInfo->SetBranchAddress("zmax_roi_index", &file_zmax_roi);
2090 tInfo->SetBranchAddress("nr", &file_nr);
2091 tInfo->SetBranchAddress("nphi", &file_np);
2092 tInfo->SetBranchAddress("nz", &file_nz);
2093 tInfo->GetEntry(0);
2094
2095 std::cout << "param\tobj\tfile" << std::endl;
2096
2097 std::cout << std::format("nr\t{}\t{}", nr, file_nr) << std::endl;
2098 std::cout << std::format("np\t{}\t{}", nphi, file_np) << std::endl;
2099 std::cout << std::format("nz\t{}\t{}", nz, file_nz) << std::endl;
2100 std::cout << std::format("rmin\t{:.2f}\t{:.2f}", rmin, file_rmin) << std::endl;
2101 std::cout << std::format("rmax\t{:.2f}\t{:.2f}", rmax, file_rmax) << std::endl;
2102 std::cout << std::format("zmin\t{:.2f}\t{:.2f}", zmin, file_zmin) << std::endl;
2103 std::cout << std::format("zmax\t{:.2f}\t{:.2f}", zmax, file_zmax) << std::endl;
2104 std::cout << std::format("rmin_roi\t{}\t{}", rmin_roi, file_rmin_roi) << std::endl;
2105 std::cout << std::format("rmax_roi\t{}\t{}", rmax_roi, file_rmax_roi) << std::endl;
2106 std::cout << std::format("zmin_roi\t{}\t{}", zmin_roi, file_zmin_roi) << std::endl;
2107 std::cout << std::format("zmax_roi\t{}\t{}", zmax_roi, file_zmax_roi) << std::endl;
2108
2109 if (file_rmin != rmin || file_rmax != rmax ||
2110 file_zmin != zmin || file_zmax != zmax ||
2111 file_rmin_roi != rmin_roi || file_rmax_roi != rmax_roi ||
2112 file_zmin_roi != zmin_roi || file_zmax_roi != zmax_roi ||
2113 file_nr != nr || file_np != nphi || file_nz != nz)
2114 {
2115 std::cout << "file parameters do not match fieldsim parameters:" << std::endl;
2116
2117 exit(1);
2118 }
2119
2120 TTree *tLookup;
2121 input->GetObject("phislice", tLookup);
2122 assert(tLookup);
2123 int ior;
2124 int ifr;
2125 int iophi;
2126 int ioz;
2127 int ifz;
2128 TVector3 *unitf = nullptr;
2129 tLookup->SetBranchAddress("ir_source", &ior);
2130 tLookup->SetBranchAddress("ir_target", &ifr);
2131 tLookup->SetBranchAddress("ip_source", &iophi);
2132
2133 tLookup->SetBranchAddress("iz_source", &ioz);
2134 tLookup->SetBranchAddress("iz_target", &ifz);
2135 tLookup->SetBranchAddress("Evec", &unitf);
2136
2137 int el = 0;
2138 std::cout << std::format("{} has {} entries", sourcefile, tLookup->GetEntries());
2139 for (int i = 0; i < (int) totalelements; i++)
2140 {
2141 el++;
2142 tLookup->GetEntry(i);
2143
2144 Epartial_phislice->Set(ifr - rmin_roi, 0, ifz - zmin_roi, ior, iophi, ioz, (*unitf) * (-1.0) * (V / (cm * C)));
2145
2146 if (!(el % percent))
2147 {
2148 std::cout << std::format("load_phislice_lookup {}%: ", static_cast<uint64_t>(debug_npercent) * el / percent);
2149
2150 std::cout << std::format("field from (ir={}, iphi={}, iz={}) to (or={}, ophi=0, oz={}) is ({:E},{:E},{:E})",
2151 ior, iophi, ioz, ifr, ifz, unitf->X(), unitf->Y(), unitf->Z())
2152 << std::endl;
2153 }
2154 }
2155
2156 input->Close();
2157 return;
2158 }
2159
2160 void AnnularFieldSim::save_phislice_lookup(const std::string &destfile)
2161 {
2162 std::cout << std::format("saving phislice lookup for ({}x{}x{})x({}x{}x{}) grid to {}", nr_roi, 1, nz_roi, nr, nphi, nz, destfile) << std::endl;
2163 unsigned long long totalelements = nr;
2164 totalelements *= nphi;
2165 totalelements *= nz;
2166 totalelements *= nr_roi;
2167 totalelements *= nz_roi;
2168 unsigned long long percent = totalelements / 100 * debug_npercent;
2169 std::cout << std::format("total elements = {}", totalelements) << std::endl;
2170
2171 TFile *output = TFile::Open(destfile.c_str(), "RECREATE");
2172 output->cd();
2173
2174 TTree *tInfo = new TTree("info", "Information about Lookup Table");
2175 tInfo->Branch("rmin", &rmin);
2176 tInfo->Branch("rmax", &rmax);
2177 tInfo->Branch("zmin", &zmin);
2178 tInfo->Branch("zmax", &zmax);
2179 tInfo->Branch("rmin_roi_index", &rmin_roi);
2180 tInfo->Branch("rmax_roi_index", &rmax_roi);
2181 tInfo->Branch("zmin_roi_index", &zmin_roi);
2182 tInfo->Branch("zmax_roi_index", &zmax_roi);
2183 tInfo->Branch("nr", &nr);
2184 tInfo->Branch("nphi", &nphi);
2185 tInfo->Branch("nz", &nz);
2186 tInfo->Fill();
2187 std::cout << "info tree built." << std::endl;
2188 ;
2189
2190 TTree *tLookup = new TTree("phislice", "Phislice Lookup Table");
2191 int ior;
2192 int ifr;
2193 int iophi;
2194 int ioz;
2195 int ifz;
2196 TVector3 unitf;
2197 tLookup->Branch("ir_source", &ior);
2198 tLookup->Branch("ir_target", &ifr);
2199 tLookup->Branch("ip_source", &iophi);
2200
2201 tLookup->Branch("iz_source", &ioz);
2202 tLookup->Branch("iz_target", &ifz);
2203 tLookup->Branch("Evec", &unitf);
2204 std::cout << "lookup tree built." << std::endl;
2205 ;
2206
2207 int el = 0;
2208 for (ifr = rmin_roi; ifr < rmax_roi; ifr++)
2209 {
2210 for (ifz = zmin_roi; ifz < zmax_roi; ifz++)
2211 {
2212 for (ior = 0; ior < nr; ior++)
2213 {
2214 for (iophi = 0; iophi < nphi; iophi++)
2215 {
2216 for (ioz = 0; ioz < nz; ioz++)
2217 {
2218 el++;
2219 unitf = Epartial_phislice->Get(ifr - rmin_roi, 0, ifz - zmin_roi, ior, iophi, ioz) * (-1 / (V / (C * cm)));
2220 if (true)
2221 {
2222 if (!(el % percent))
2223 {
2224 std::cout << std::format("save_phislice_lookup {}%: ", static_cast<uint64_t>(debug_npercent) * el / percent);
2225
2226 std::cout << std::format("field from (ir={}, iphi={}, iz={}) to (or={}, ophi=0, oz={}) is ({:E},{:E},{:E})",
2227 ior, iophi, ioz, ifr, ifz, unitf.X(), unitf.Y(), unitf.Z())
2228 << std::endl;
2229 }
2230 }
2231
2232 tLookup->Fill();
2233 }
2234 }
2235 }
2236 }
2237 }
2238 output->cd();
2239 tInfo->Write();
2240 tLookup->Write();
2241
2242 output->Close();
2243 return;
2244 }
2245
2246 void AnnularFieldSim::setFlatFields(float B, float E)
2247 {
2248
2249 std::cout << std::format("AnnularFieldSim::setFlatFields(B={} Tesla, E={} V/cm)", B, E) << std::endl;
2250 std::cout << std::format("lengths: Eext={}, Bfie={}", Eexternal->Length(), Bfield->Length()) << std::endl;
2251 Efieldname = "E:Flat:" + std::to_string(E);
2252 Bfieldname = "B:Flat:" + std::to_string(B);
2253
2254 Enominal = E * (V / cm);
2255 Bnominal = B * Tesla;
2256 for (int i = 0; i < Eexternal->Length(); i++)
2257 {
2258 Eexternal->GetFlat(i)->SetXYZ(0, 0, Enominal);
2259 }
2260 for (int i = 0; i < Bfield->Length(); i++)
2261 {
2262 Bfield->GetFlat(i)->SetXYZ(0, 0, Bnominal);
2263 }
2264 UpdateOmegaTau();
2265 return;
2266 }
2267
2268 TVector3 AnnularFieldSim::sum_field_at(int r, int phi, int z)
2269 {
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282 TVector3 sum(0, 0, 0);
2283 if (lookupCase == Full3D)
2284 {
2285 sum += sum_full3d_field_at(r, phi, z);
2286 }
2287 else if (lookupCase == HybridRes)
2288 {
2289 sum += sum_local_field_at(r, phi, z);
2290 sum += sum_nonlocal_field_at(r, phi, z);
2291 }
2292 else if (lookupCase == PhiSlice)
2293 {
2294 sum += sum_phislice_field_at(r, phi, z);
2295 }
2296 else if (lookupCase == Analytic)
2297 {
2298 sum += aliceModel->E(GetCellCenter(r, phi, z));
2299 }
2300 else if (lookupCase == NoLookup)
2301 {
2302
2303 }
2304 sum += Eexternal->Get(r - rmin_roi, phi - phimin_roi, z - zmin_roi);
2305 if (debugFlag())
2306 {
2307 std::cout << std::format("summed field at ({},{},{})=({},{},{})", r, phi, z, sum.X(), sum.Y(), sum.Z()) << std::endl;
2308 }
2309
2310 return sum;
2311 }
2312
2313 TVector3 AnnularFieldSim::sum_full3d_field_at(int r, int phi, int z)
2314 {
2315
2316
2317
2318 TVector3 sum(0, 0, 0);
2319 float rdist;
2320 float phidist;
2321 float zdist;
2322 float remdist;
2323 for (int ir = 0; ir < nr; ir++)
2324 {
2325 if (truncation_length > 0)
2326 {
2327 rdist = std::abs(ir - r);
2328 remdist = std::sqrt(truncation_length * truncation_length - rdist * rdist);
2329 if (remdist < 0)
2330 {
2331 continue;
2332 }
2333 }
2334 for (int iphi = 0; iphi < nphi; iphi++)
2335 {
2336 if (truncation_length > 0)
2337 {
2338 phidist = std::min(std::abs(iphi - phi), std::abs(std::abs(iphi - phi) - nphi));
2339 remdist = std::sqrt(truncation_length * truncation_length - phidist * phidist);
2340 if (remdist < 0)
2341 {
2342 continue;
2343 }
2344 }
2345 for (int iz = 0; iz < nz; iz++)
2346 {
2347 if (truncation_length > 0)
2348 {
2349 zdist = std::abs(iz - z);
2350 remdist = std::sqrt(truncation_length * truncation_length - zdist * zdist);
2351 if (remdist < 0)
2352 {
2353 continue;
2354 }
2355 }
2356
2357 if (r == ir && phi == iphi && z == iz)
2358 {
2359 continue;
2360 }
2361 sum += Epartial->Get(r - rmin_roi, phi - phimin_roi, z - zmin_roi, ir, iphi, iz) * q->GetChargeInBin(ir, iphi, iz);
2362 }
2363 }
2364 }
2365
2366 return sum;
2367 }
2368
2369 TVector3 AnnularFieldSim::sum_local_field_at(int r, int phi, int z)
2370 {
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390 int r_highres_dist = (nr_high - 1) / 2;
2391 int phi_highres_dist = (nphi_high - 1) / 2;
2392 int z_highres_dist = (nz_high - 1) / 2;
2393
2394 int r_parentlow = std::floor((r - r_highres_dist) / (r_spacing * 1.0));
2395 int r_parenthigh = std::floor((r + r_highres_dist) / (r_spacing * 1.0)) + 1;
2396 int phi_parentlow = std::floor((phi - phi_highres_dist) / (phi_spacing * 1.0));
2397 int phi_parenthigh = std::floor((phi + phi_highres_dist) / (phi_spacing * 1.0)) + 1;
2398 int z_parentlow = std::floor((z - z_highres_dist) / (z_spacing * 1.0));
2399 int z_parenthigh = std::floor((z + z_highres_dist) / (z_spacing * 1.0)) + 1;
2400 std::cout << std::format("AnnularFieldSim::sum_local_field_at parents: rlow={}, philow={}, zlow={}, rhigh={}, phihigh={}, zhigh={}",
2401 r_parentlow, phi_parentlow, z_parentlow, r_parenthigh, phi_parenthigh, z_parenthigh)
2402 << std::endl;
2403
2404
2405 for (int i = 0; i < q_local->Length(); i++)
2406 {
2407 *(q_local->GetFlat(i)) = 0;
2408 }
2409
2410
2411 for (int ir = r_parentlow * r_spacing; ir < r_parenthigh * r_spacing; ir++)
2412 {
2413 ir = std::max(ir, 0);
2414 if (ir >= nr)
2415 {
2416 break;
2417 }
2418 int rbin = (ir - r) + r_highres_dist;
2419 rbin = std::max(rbin, 0);
2420 if (rbin >= nr_high)
2421 {
2422 rbin = nr_high - 1;
2423 }
2424 for (int iphi = phi_parentlow * phi_spacing; iphi < phi_parenthigh * phi_spacing; iphi++)
2425 {
2426
2427 int phiFilt = FilterPhiIndex(iphi);
2428 int phibin = (iphi - phi) + phi_highres_dist;
2429 phibin = std::max(phibin, 0);
2430 if (phibin >= nphi_high)
2431 {
2432 phibin = nphi_high - 1;
2433 }
2434 for (int iz = z_parentlow * z_spacing; iz < z_parenthigh * z_spacing; iz++)
2435 {
2436 iz = std::max(iz, 0);
2437 if (iz >= nz)
2438 {
2439 break;
2440 }
2441
2442
2443 int zbin = (iz - z) + z_highres_dist;
2444 zbin = std::max(zbin, 0);
2445 if (zbin >= nz_high)
2446 {
2447 zbin = nz_high - 1;
2448 }
2449
2450 q_local->Add(rbin, phibin, zbin, q->GetChargeInBin(ir, phiFilt, iz));
2451
2452 }
2453 }
2454 }
2455
2456
2457
2458
2459
2460 TVector3 sum(0, 0, 0);
2461
2462
2463
2464
2465
2466
2467
2468 for (int ir = 0; ir < nr_high; ir++)
2469 {
2470 for (int iphi = 0; iphi < nphi_high; iphi++)
2471 {
2472 for (int iz = 0; iz < nz_high; iz++)
2473 {
2474
2475 if (phi - phimin_roi < 0)
2476 {
2477 std::cout << std::format("{}: Getting with phi={}", __LINE__, (phi - phimin_roi)) << std::endl;
2478 }
2479 sum += Epartial_highres->Get(r - rmin_roi, phi - phimin_roi, z - zmin_roi, ir, iphi, iz) * q_local->Get(ir, iphi, iz);
2480 }
2481 }
2482 }
2483
2484 return sum;
2485 }
2486
2487 TVector3 AnnularFieldSim::sum_nonlocal_field_at(int r, int phi, int z)
2488 {
2489
2490
2491
2492
2493
2494 bool skip[] = {false, false, false, false, false, false, false, false};
2495
2496 float r0 = r / (1.0 * r_spacing) - 0.5 - rmin_roi_low;
2497 int r0i = std::floor(r0);
2498 float r0d = r0 - r0i;
2499
2500 int ri[2];
2501 ri[0] = r0i;
2502 ri[1] = r0i + 1;
2503 float rw[2];
2504 rw[0] = 1 - r0d;
2505 rw[1] = r0d;
2506
2507
2508 if (ri[0] < 0)
2509 {
2510 for (int i = 0; i < 8; i++)
2511 {
2512 if ((i / 4) % 2 == 0)
2513 {
2514 skip[i] = true;
2515 }
2516 }
2517 rw[1] = 1;
2518 }
2519 if (ri[1] >= nr_roi_low)
2520 {
2521 for (int i = 0; i < 8; i++)
2522 {
2523 if ((i / 4) % 2 == 1)
2524 {
2525 skip[i] = true;
2526 }
2527 }
2528 rw[0] = 1;
2529 }
2530
2531
2532
2533 float p0 = phi / (1.0 * phi_spacing) - 0.5 - phimin_roi_low;
2534
2535
2536 int p0i = std::floor(p0);
2537 float p0d = p0 - p0i;
2538 int pi[4];
2539
2540
2541
2542
2543 pi[0] = FilterPhiIndex(p0i, nphi_low);
2544 pi[1] = FilterPhiIndex(p0i + 1, nphi_low);
2545
2546
2547
2548 float pw[2];
2549 pw[0] = 1 - p0d;
2550 pw[1] = p0d;
2551
2552
2553
2554 if (pi[0] >= nphi_roi_low)
2555 {
2556 for (int i = 0; i < 8; i++)
2557 {
2558 if ((i / 2) % 2 == 0)
2559 {
2560 skip[i] = true;
2561 }
2562 }
2563 pw[1] = 1;
2564 }
2565 if (pi[1] >= nphi_roi_low)
2566 {
2567 for (int i = 0; i < 8; i++)
2568 {
2569 if ((i / 2) % 2 == 1)
2570 {
2571 skip[i] = true;
2572 }
2573 }
2574 pw[0] = 1;
2575 }
2576
2577
2578
2579 float z0 = z / (1.0 * z_spacing) - 0.5 - zmin_roi_low;
2580 int z0i = std::floor(z0);
2581 float z0d = z0 - z0i;
2582
2583 int zi[2];
2584 zi[0] = z0i;
2585 zi[1] = z0i + 1;
2586 float zw[2];
2587 zw[0] = 1 - z0d;
2588 zw[1] = z0d;
2589
2590 if (zi[0] < 0)
2591 {
2592 for (int i = 0; i < 8; i++)
2593 {
2594 if ((i) % 2 == 0)
2595 {
2596 skip[i] = true;
2597 }
2598 }
2599 zw[1] = 1;
2600 }
2601 if (zi[1] >= nz_roi_low)
2602 {
2603 for (int i = 0; i < 8; i++)
2604 {
2605 if ((i) % 2 == 1)
2606 {
2607 skip[i] = true;
2608 }
2609 }
2610 zw[0] = 1;
2611 }
2612
2613 TVector3 sum(0, 0, 0);
2614
2615
2616 int lBinEdge[2];
2617 int hRegionEdge[2];
2618 bool overlapsPhi;
2619 bool overlapsZ;
2620
2621 int r_highres_dist = (nr_high - 1) / 2;
2622 int phi_highres_dist = (nphi_high - 1) / 2;
2623 int z_highres_dist = (nz_high - 1) / 2;
2624
2625 for (int ir = 0; ir < nr_low; ir++)
2626 {
2627 lBinEdge[0] = ir * r_spacing;
2628 lBinEdge[1] = (ir + 1) * r_spacing - 1;
2629 hRegionEdge[0] = r - r_highres_dist;
2630 hRegionEdge[1] = r + r_highres_dist;
2631 bool overlapsR = (lBinEdge[0] <= hRegionEdge[1] && hRegionEdge[0] <= lBinEdge[1]);
2632 for (int iphi = 0; iphi < nphi_low; iphi++)
2633 {
2634 lBinEdge[0] = iphi * phi_spacing;
2635 lBinEdge[1] = (iphi + 1) * phi_spacing - 1;
2636 hRegionEdge[0] = phi - phi_highres_dist;
2637 hRegionEdge[1] = phi + phi_highres_dist;
2638 overlapsPhi = (lBinEdge[0] <= hRegionEdge[1] && hRegionEdge[0] <= lBinEdge[1]);
2639 for (int iz = 0; iz < nz_low; iz++)
2640 {
2641 lBinEdge[0] = iz * z_spacing;
2642 lBinEdge[1] = (iz + 1) * z_spacing - 1;
2643 hRegionEdge[0] = z - z_highres_dist;
2644 hRegionEdge[1] = z + z_highres_dist;
2645 overlapsZ = (lBinEdge[0] <= hRegionEdge[1] && hRegionEdge[0] <= lBinEdge[1]);
2646
2647
2648
2649 if (overlapsR && overlapsPhi && overlapsZ)
2650 {
2651
2652 continue;
2653 }
2654
2655
2656
2657 for (int i = 0; i < 8; i++)
2658 {
2659 if (skip[i])
2660 {
2661 continue;
2662 }
2663 if (ri[(i / 4) % 2] + rmin_roi_low == ir && pi[(i / 2) % 2] + phimin_roi_low == iphi && zi[(i) % 2] + zmin_roi_low == iz)
2664 {
2665 std::cout << std::format("considering an l-bins effect on itself, r={}, phi={}, z={} (matches i={}, not skipped), means we're not interpolating fairly", ir, iphi, iz, i) << std::endl;
2666 exit(1);
2667 }
2668
2669
2670 if (pi[(i / 2) % 2] < 0)
2671 {
2672 std::cout << std::format("{}: Getting with phi={}", __LINE__, (pi[(i / 2) % 2])) << std::endl;
2673 }
2674 sum += (Epartial_lowres->Get(ri[(i / 4) % 2], pi[(i / 2) % 2], zi[(i) % 2], ir, iphi, iz) * q_lowres->Get(ir, iphi, iz)) * zw[(i) % 2] * pw[(i / 2) % 2] * rw[(i / 4) % 2];
2675 }
2676 }
2677 }
2678 }
2679
2680 return sum;
2681 }
2682
2683 TVector3 AnnularFieldSim::sum_phislice_field_at(int r, int phi, int z)
2684 {
2685
2686
2687
2688 TVector3 pos = GetRoiCellCenter(r - rmin_roi, phi - phimin_roi, z - zmin_roi);
2689 TVector3 slicepos = GetRoiCellCenter(r - rmin_roi, 0, z - zmin_roi);
2690 float rotphi = pos.Phi() - slicepos.Phi();
2691
2692
2693
2694
2695
2696
2697
2698 TVector3 sum(0, 0, 0);
2699 TVector3 unrotatedField(0, 0, 0);
2700 TVector3 unitField(0, 0, 0);
2701 int phirel;
2702 for (int ir = 0; ir < nr; ir++)
2703 {
2704 for (int iphi = 0; iphi < nphi; iphi++)
2705 {
2706 for (int iz = 0; iz < nz; iz++)
2707 {
2708
2709 if (r == ir && phi == iphi && z == iz)
2710 {
2711 continue;
2712 }
2713 phirel = FilterPhiIndex(iphi - phi);
2714 unitField = Epartial_phislice->Get(r - rmin_roi, 0, z - zmin_roi, ir, phirel, iz);
2715 unitField.RotateZ(rotphi);
2716
2717 sum += unitField * q->GetChargeInBin(ir, iphi, iz);
2718 ;
2719
2720
2721
2722
2723
2724
2725
2726
2727 }
2728 }
2729 }
2730
2731 return sum;
2732 }
2733
2734 TVector3 AnnularFieldSim::swimToInAnalyticSteps(float zdest, TVector3 start, int steps = 1, int *goodToStep = nullptr)
2735 {
2736
2737 double zdist = (zdest - start.Z()) * cm;
2738 double zstep = zdist / steps;
2739 start = start * cm;
2740
2741 TVector3 ret = start;
2742 TVector3 accumulated_distortion(0, 0, 0);
2743 TVector3 accumulated_drift(0, 0, 0);
2744 TVector3 drift_step(0, 0, zstep);
2745
2746 int rt;
2747 int pt;
2748 int zt;
2749 BoundsCase zBound;
2750 for (int i = 0; i < steps; i++)
2751 {
2752 zBound = GetZindexAndCheckBounds(ret.Z(), &zt);
2753 if (zBound == OnLowEdge)
2754 {
2755
2756 ret.SetZ(ret.Z() + ALMOST_ZERO);
2757 }
2758 if (GetRindexAndCheckBounds(ret.Perp(), &rt) != InBounds || GetPhiIndexAndCheckBounds(FilterPhiPos(ret.Phi()), &pt) != InBounds || (zBound == OutOfBounds))
2759 {
2760 std::cout << std::format("AnnularFieldSim::swimToInAnalyticSteps at step {} asked to swim particle from ({},{},{}) (rphiz)=({}cm,{}rad,{}cm) which is outside the ROI.",
2761 i, ret.X() / cm, ret.Y() / cm, ret.Z() / cm, ret.Perp() / cm, ret.Phi(), ret.Z() / cm)
2762 << std::endl;
2763
2764 std::cout << std::format(" -- {} <= r < {} \t{} <= phi < {} \t{} <= z < {}",
2765 rmin_roi * step.Perp() + rmin, rmax_roi * step.Perp() + rmin,
2766 phimin_roi * step.Phi(), phimax_roi * step.Phi(),
2767 zmin_roi * step.Z(), zmax_roi * step.Z())
2768 << std::endl;
2769
2770 if (!(goodToStep == nullptr))
2771 {
2772 *goodToStep = i - 1;
2773 }
2774 return ret * (1.0 / cm);
2775 }
2776
2777
2778 accumulated_distortion += GetStepDistortion(ret.Z() + zstep, ret, true, true);
2779
2780 accumulated_drift += drift_step;
2781
2782
2783 ret = start + accumulated_distortion + accumulated_drift;
2784 }
2785
2786 return ret * (1.0 / cm);
2787 }
2788
2789 TVector3 AnnularFieldSim::swimToInSteps(float zdest, const TVector3 &start, int steps = 1, bool interpolate = false, int *goodToStep = nullptr)
2790 {
2791 int *success = nullptr;
2792 TVector3 straightline(start.X(), start.Y(), zdest);
2793 TVector3 distortion = GetTotalDistortion(zdest, start, steps, interpolate, goodToStep, success);
2794 return straightline + distortion;
2795 }
2796
2797
2798 TVector3 AnnularFieldSim::GetTotalDistortion(float zdest, const TVector3 &start, int steps, bool interpolate, int *goodToStep, int *success)
2799 {
2800
2801
2802
2803
2804
2805 int rt;
2806 int pt;
2807 int zt;
2808 BoundsCase zBound;
2809 zBound = GetZindexAndCheckBounds(zdest, &zt);
2810 bool rdrswitch = RdeltaRswitch;
2811 if (success)
2812 {
2813 *success = 0;
2814 }
2815 if (zBound == OutOfBounds)
2816 {
2817 if (hasTwin)
2818 {
2819 zBound = GetZindexAndCheckBounds(-zdest, &zt);
2820 if (zBound == InBounds || zBound == OnLowEdge)
2821 {
2822
2823 return twin->GetTotalDistortion(zdest, start, steps, interpolate, goodToStep, success);
2824 }
2825 }
2826
2827 std::cout << std::format("AnnularFieldSim::GetTotalDistortion starting at ({},{},{})=(r{},p{},z{}) asked to drift to z={}, which is outside the ROI. hasTwin= {}. Returning zero_vector.",
2828 start.X(), start.Y(), start.Z(), start.Perp(), FilterPhiPos(start.Phi()), start.Z(), zdest, static_cast<int>(hasTwin))
2829 << std::endl;
2830
2831 std::cout << std::format(" -- {} <= r < {} \t{} <= phi < {} \t{} <= z < {}",
2832 rmin_roi * step.Perp() + rmin, rmax_roi * step.Perp() + rmin,
2833 phimin_roi * step.Phi(), phimax_roi * step.Phi(),
2834 zmin_roi * step.Z(), zmax_roi * step.Z())
2835 << std::endl;
2836
2837 *goodToStep = 0;
2838 *success = 0;
2839 return zero_vector;
2840 }
2841 if (zBound == OnLowEdge)
2842 {
2843
2844 zdest += ALMOST_ZERO;
2845 }
2846 zBound = GetZindexAndCheckBounds(start.Z(), &zt);
2847 if (zBound == OutOfBounds)
2848 {
2849 std::cout << std::format("AnnularFieldSim::GetTotalDistortion starting at ({},{},{})=(r{},p{},z{}) asked to drift from z={}, which is outside the ROI. Returning zero_vector.",
2850 start.X(), start.Y(), start.Z(), start.Perp(), FilterPhiPos(start.Phi()), start.Z(), start.Z())
2851 << std::endl;
2852
2853 std::cout << std::format(" -- {} <= r < {} \t{} <= phi < {} \t{} <= z < {}",
2854 rmin_roi * step.Perp() + rmin, rmax_roi * step.Perp() + rmin,
2855 phimin_roi * step.Phi(), phimax_roi * step.Phi(),
2856 zmin_roi * step.Z(), zmax_roi * step.Z())
2857 << std::endl;
2858 *goodToStep = 0;
2859 *success = 0;
2860 return zero_vector;
2861 }
2862 if (zBound == OnLowEdge)
2863 {
2864
2865 zdest += ALMOST_ZERO;
2866 }
2867
2868
2869
2870 double zstep = (zmax - zmin) / steps;
2871
2872
2873
2874
2875 if ((zdest - start.Z()) < 0)
2876 {
2877 zstep = -zstep;
2878 }
2879 int integernumbersteps = (zdest - start.Z()) / zstep;
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890 TVector3 position = start;
2891 TVector3 accumulated_distortion(0, 0, 0);
2892 TVector3 accumulated_drift(0, 0, 0);
2893 TVector3 drift_step(0, 0, zstep);
2894
2895
2896
2897
2898
2899
2900
2901 accumulated_distortion += GetStepDistortion(zdest - (integernumbersteps * zstep), position, true, false);
2902
2903
2904
2905 position.SetX(start.X() + accumulated_distortion.X());
2906 position.SetY(start.Y() + accumulated_distortion.Y());
2907 position.SetZ(zdest - (integernumbersteps * zstep));
2908
2909 float StartR;
2910 float FinalR;
2911 float DeltaR;
2912 for (int i = 0; i < integernumbersteps; i++)
2913 {
2914 if (GetRindexAndCheckBounds(position.Perp(), &rt) != InBounds || GetPhiIndexAndCheckBounds(FilterPhiPos(position.Phi()), &pt) != InBounds || (zBound == OutOfBounds))
2915 {
2916
2917
2918
2919 if (!(goodToStep == nullptr))
2920 {
2921 *goodToStep = i - 1, *success = 0;
2922 }
2923
2924 return (accumulated_distortion);
2925 }
2926
2927 StartR = position.Perp();
2928
2929 accumulated_distortion += GetStepDistortion(position.Z() + zstep, position, true, false);
2930 position.SetX(start.X() + accumulated_distortion.X());
2931 position.SetY(start.Y() + accumulated_distortion.Y());
2932 FinalR = position.Perp();
2933
2934
2935
2936
2937 DeltaR = FinalR - StartR;
2938 if (rdrswitch)
2939 {
2940 hRdeltaRComponent->Fill(StartR, DeltaR);
2941 }
2942 position.SetZ(position.Z() + zstep);
2943 }
2944 if (GetRindexAndCheckBounds(position.Perp(), &rt) != InBounds || GetPhiIndexAndCheckBounds(FilterPhiPos(position.Phi()), &pt) != InBounds || (zBound == OutOfBounds))
2945 {
2946 *goodToStep = integernumbersteps - 1;
2947 *success = 0;
2948 return (accumulated_distortion);
2949 }
2950 *goodToStep = integernumbersteps;
2951 *success = 1;
2952 return accumulated_distortion;
2953 }
2954
2955 void AnnularFieldSim::PlotFieldSlices(const std::string &filebase, const TVector3 &pos, char which)
2956 {
2957 bool mapEfield = true;
2958 if (which == 'B')
2959 {
2960 mapEfield = false;
2961 }
2962 which = mapEfield ? 'E' : 'B';
2963 std::string units;
2964 if (mapEfield)
2965 {
2966 units = "V/cm";
2967 }
2968 else
2969 {
2970 units = "T";
2971 }
2972
2973 std::cout << std::format("plotting field slices for {} field, slicing at ({:.2f},{:.2f},{:.2f})...",
2974 which, pos.Perp(), FilterPhiPos(pos.Phi()), pos.Z())
2975 << std::endl;
2976
2977 std::cout << "file=" << filebase << std::endl;
2978
2979 std::string plotfilename = filebase + "." + which + "field_slices.pdf";
2980 TVector3 inner = GetInnerEdge();
2981 TVector3 outer = GetOuterEdge();
2982 TVector3 steplocal = GetFieldStep();
2983
2984 TCanvas *c;
2985
2986 TH2F *hEfield[3][3];
2987 TH2F *hCharge[3];
2988 TH1F *hEfieldComp[3][3];
2989 std::string axis[] = {"r", "p", "z", "r", "p", "z"};
2990 float axisval[] = {(float) pos.Perp(), (float) FilterPhiPos(pos.Phi()), (float) pos.Z(), (float) pos.Perp(), (float) FilterPhiPos(pos.Phi()), (float) pos.Z()};
2991 int axn[] = {nr_roi, nphi_roi, nz_roi, nr_roi, nphi_roi, nz_roi};
2992 float axtop[] = {(float) outer.Perp(), 2 * M_PI, (float) outer.Z(), (float) outer.Perp(), 2 * M_PI, (float) outer.Z()};
2993 float axbot[] = {(float) inner.Perp(), 0, (float) inner.Z(), (float) inner.Perp(), 0, (float) inner.Z()};
2994
2995
2996 if (hasTwin)
2997 {
2998
2999 axbot[2] = axbot[5] = (float) (twin->GetInnerEdge().Z());
3000 }
3001 std::cout << std::format("rpz bounds are {}<r{}\t {}<phi{}\t {}<z{}",
3002 axbot[0], axtop[0], axbot[1], axtop[1], axbot[2], axtop[2])
3003 << std::endl;
3004
3005 float axstep[6];
3006 for (int i = 0; i < 6; i++)
3007 {
3008 axstep[i] = (axtop[i] - axbot[i]) / (1.0 * axn[i]);
3009 }
3010 TVector3 field;
3011 TVector3 lpos;
3012
3013
3014 for (int ax = 0; ax < 3; ax++)
3015 {
3016
3017 hCharge[ax] = new TH2F(std::string("hCharge" + axis[ax]).c_str(),
3018 std::format("Spacecharge Distribution in the {}{} plane at {}={:.3f} (C/cm^3);{};{}",
3019 axis[ax + 1], axis[ax + 2], axis[ax], axisval[ax], axis[ax + 1], axis[ax + 2])
3020 .c_str(),
3021 axn[ax + 1], axbot[ax + 1], axtop[ax + 1],
3022 axn[ax + 2], axbot[ax + 2], axtop[ax + 2]);
3023 for (int i = 0; i < 3; i++)
3024 {
3025
3026 hEfield[ax][i] = new TH2F(std::format("h{}field{}_{}{}", which, axis[i], axis[ax + 1], axis[ax + 2]).c_str(),
3027 std::format("{} component of {} Field in the {}{} plane at {}={:.3f} ({}) ;{};{}",
3028 axis[i], which, axis[ax + 1], axis[ax + 2], axis[ax], axisval[ax], units, axis[ax + 1], axis[ax + 2])
3029 .c_str(),
3030
3031 axn[ax + 1], axbot[ax + 1], axtop[ax + 1],
3032 axn[ax + 2], axbot[ax + 2], axtop[ax + 2]);
3033 hEfieldComp[ax][i] = new TH1F(std::format("h{}fieldComp{}_{}{}", which, axis[i], axis[ax + 1], axis[ax + 2]).c_str(),
3034 std::format("Log Magnitude of {} component of {} Field in the {}{} plane at {}={:.3f};log10(mag)",
3035 axis[i], which, axis[ax + 1], axis[ax + 2], axis[ax], axisval[ax])
3036 .c_str(),
3037 200, -5, 5);
3038 }
3039 }
3040
3041 float rpz_coord[3];
3042 for (int ax = 0; ax < 3; ax++)
3043 {
3044 rpz_coord[ax] = axisval[ax] + axstep[ax] / 2;
3045 for (int i = 0; i < axn[ax + 1]; i++)
3046 {
3047 rpz_coord[(ax + 1) % 3] = axbot[ax + 1] + (i + 0.5) * axstep[ax + 1];
3048 for (int j = 0; j < axn[ax + 2]; j++)
3049 {
3050 rpz_coord[(ax + 2) % 3] = axbot[ax + 2] + (j + 0.5) * axstep[ax + 2];
3051 lpos.SetXYZ(rpz_coord[0], 0, rpz_coord[2]);
3052 lpos.SetPhi(rpz_coord[1]);
3053 if (false && ax == 0)
3054 {
3055 std::cout << std::format("sampling rpz=({},{},{})=({},{},{}) after conversion to xyz=({},{},{})",
3056 rpz_coord[0], rpz_coord[1], rpz_coord[2], lpos.Perp(), FilterPhiPos(lpos.Phi()), lpos.Z(),
3057 lpos.X(), lpos.Y(), lpos.Z())
3058 << std::endl;
3059 }
3060 if (mapEfield)
3061 {
3062
3063 field = GetFieldAt(lpos) * (1.0 * cm / V);
3064 }
3065 else
3066 {
3067 field = GetBFieldAt(lpos) * (1.0 / Tesla);
3068
3069 }
3070 field.RotateZ(-rpz_coord[1]);
3071
3072 hEfield[ax][0]->Fill(rpz_coord[(ax + 1) % 3], rpz_coord[(ax + 2) % 3], field.X());
3073 hEfield[ax][1]->Fill(rpz_coord[(ax + 1) % 3], rpz_coord[(ax + 2) % 3], field.Y());
3074 hEfield[ax][2]->Fill(rpz_coord[(ax + 1) % 3], rpz_coord[(ax + 2) % 3], field.Z());
3075 hCharge[ax]->Fill(rpz_coord[(ax + 1) % 3], rpz_coord[(ax + 2) % 3], GetChargeAt(lpos));
3076 hEfieldComp[ax][0]->Fill((std::abs(field.X())));
3077 hEfieldComp[ax][1]->Fill((std::abs(field.Y())));
3078 hEfieldComp[ax][2]->Fill((std::abs(field.Z())));
3079 }
3080 }
3081 }
3082
3083 c = new TCanvas("cfieldslices", "electric field", 1200, 800);
3084 c->Divide(4, 3);
3085 gStyle->SetOptStat();
3086 for (int ax = 0; ax < 3; ax++)
3087 {
3088 for (int i = 0; i < 3; i++)
3089 {
3090 c->cd(ax * 4 + i + 1);
3091 gPad->SetRightMargin(0.15);
3092 hEfield[ax][i]->SetStats(false);
3093 hEfield[ax][i]->Draw("colz");
3094
3095 gPad->Modified();
3096
3097 }
3098 c->cd(ax * 4 + 4);
3099 gPad->SetRightMargin(0.15);
3100 hCharge[ax]->SetStats(false);
3101 hCharge[ax]->Draw("colz");
3102
3103 if (false)
3104 {
3105
3106
3107 gPad->Modified();
3108 }
3109 }
3110 c->SaveAs(plotfilename.c_str());
3111 std::cout << "after plotting field slices..." << std::endl;
3112 std::cout << "file=" << filebase << std::endl;
3113
3114 return;
3115 }
3116
3117 void AnnularFieldSim::GenerateSeparateDistortionMaps(const std::string &filebase, int nSteps, int r_subsamples, int p_subsamples, int z_subsamples, int , bool andCartesian)
3118 {
3119
3120
3121
3122
3123 TVector3 steplocal(step.Perp() / r_subsamples, 0, step.Z() / z_subsamples);
3124 steplocal.SetPhi(step.Phi() / p_subsamples);
3125 float deltar = steplocal.Perp();
3126 float deltap = steplocal.Phi();
3127 float deltaz = steplocal.Z();
3128 bool rdrswitch = RdeltaRswitch;
3129 TVector3 stepzvec(0, 0, deltaz);
3130
3131
3132
3133 int nSides = 1;
3134 if (hasTwin)
3135 {
3136 nSides = 2;
3137 }
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147 TVector3 lowerEdge = GetRoiCellCenter(rmin_roi, phimin_roi, zmin_roi);
3148 TVector3 upperEdge = GetRoiCellCenter(rmax_roi - 1, phimax_roi - 1, zmax_roi - 1);
3149 int nph = nphi * p_subsamples + 2;
3150 int nrh = nr * r_subsamples + 2;
3151 int nzh = nz * z_subsamples + 2;
3152
3153 float rih = lowerEdge.Perp() - 0.5 * step.Perp() - steplocal.Perp();
3154 float rfh = upperEdge.Perp() + 0.5 * step.Perp() + steplocal.Perp();
3155 float pih = FilterPhiPos(lowerEdge.Phi()) - 0.5 * step.Phi() - steplocal.Phi();
3156 float pfh = FilterPhiPos(upperEdge.Phi()) + 0.5 * step.Phi() + steplocal.Phi();
3157 float zih = lowerEdge.Z() - 0.5 * step.Z() - steplocal.Z();
3158 float zfh = upperEdge.Z() + 0.5 * step.Z() + steplocal.Z();
3159 float z_readout = upperEdge.Z() + 0.5 * step.Z();
3160
3161 std::cout << "generating distortion map..." << std::endl;
3162 std::cout << "file=" << filebase << std::endl;
3163 std::cout << std::format("Phi: {} steps from {} to {} (field has {} steps)", nph, pih, pfh, GetFieldStepsPhi()) << std::endl;
3164 std::cout << std::format("R: {} steps from {} to {} (field has {} steps)", nrh, rih, rfh, GetFieldStepsR()) << std::endl;
3165 std::cout << std::format("Z: {} steps from {} to {} (field has {} steps)", nzh, zih, zfh, GetFieldStepsZ()) << std::endl;
3166 std::string distortionFilename = filebase + ".distortion_map.hist.root";
3167 std::string summaryFilename = filebase + "distortion_summary.pdf";
3168 std::string diffSummaryFilename = filebase + "differential_summary.pdf";
3169
3170 TFile *outf = TFile::Open(distortionFilename.c_str(), "RECREATE");
3171 outf->cd();
3172
3173
3174
3175 TH3F *hDistortionR = new TH3F("hDistortionR", "Per-z-bin Distortion in the R direction as a function of (phi,r,z) (centered in r,phi, z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3176 TH3F *hDistortionP = new TH3F("hDistortionP", "Per-z-bin Distortion in the RPhi direction as a function of (phi,r,z) (centered in r,phi, z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3177 TH3F *hDistortionZ = new TH3F("hDistortionZ", "Per-z-bin Distortion in the Z direction as a function of (phi,r,z) (centered in r,phi, z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3178 TH3F *hIntDistortionR = new TH3F("hIntDistortionR", "Integrated R Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3179 TH3F *hIntDistortionP = new TH3F("hIntDistortionP", "Integrated R Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3180 TH3F *hIntDistortionZ = new TH3F("hIntDistortionZ", "Integrated R Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3181
3182 TH3F *hIntDistortionX = new TH3F("hIntDistortionX", "Integrated X Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3183 TH3F *hIntDistortionY = new TH3F("hIntDistortionY", "Integrated Y Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3184
3185 const int nMapComponents = 6;
3186 TH3F *hSeparatedMapComponent[2][6];
3187 TH3F *hValidToStepComponent[2];
3188 TH3F *hReachesReadout[2];
3189 std::string side[2];
3190 side[0] = "soloz";
3191 if (hasTwin)
3192 {
3193 side[0] = "posz";
3194 side[1] = "negz";
3195 }
3196 std::string sepAxis[] = {"X", "Y", "Z", "R", "P", "RPhi"};
3197 float zlower;
3198 float zupper;
3199 for (int i = 0; i < nSides; i++)
3200 {
3201 if (i == 0)
3202 {
3203 zlower = std::min(zih, zfh);
3204 zupper = std::max(zih, zfh);
3205 }
3206 else
3207 {
3208 zlower = -1 * std::max(zih, zfh);
3209 zupper = -1 * std::min(zih, zfh);
3210 }
3211 for (int j = 0; j < nMapComponents; j++)
3212 {
3213 hSeparatedMapComponent[i][j] = new TH3F(std::string("hIntDistortion" + sepAxis[j] + "_" + side[i]).c_str(),
3214 std::string("Integrated" + sepAxis[j] + " Deflection drifting from (phi,r,z) to z=endcap;phi;r;z (" + side[i] + " side)").c_str(),
3215 nph, pih, pfh, nrh, rih, rfh, nzh, zlower, zupper);
3216 }
3217 hValidToStepComponent[i] = new TH3F(std::string("hValidToStep_" + side[i]).c_str(),
3218 std::string("Step before out of bounds drifting from (phi,r,z) to z=endcap;phi;r;z (" + side[i] + " side)").c_str(),
3219 nph, pih, pfh, nrh, rih, rfh, nzh, zlower, zupper);
3220 hReachesReadout[i] = new TH3F(std::string("hReachesReadout_" + side[i]).c_str(),
3221 std::string("Bool value for checking if successfuly drifting from (phi,r,z) to z=endcap and reaches the readout;phi;r;z (" + side[i] + " side)").c_str(),
3222 nph, pih, pfh, nrh, rih, rfh, nzh, zlower, zupper);
3223 }
3224 if (rdrswitch)
3225 {
3226 hRdeltaRComponent = new TH2F("hRdeltaR", "Bool value of input for a 2D R_versus_deltaR plot from Zstart to Zend;R;deltaR", nrh, rih, rfh, 10 * nrh, rih - 70, rfh + 170);
3227
3228 twin->hRdeltaRComponent = hRdeltaRComponent;
3229 }
3230
3231
3232
3233 TVector3 pos(((nrh / 2) + 0.5) * steplocal.Perp() + rih, 0, zmin + ((nz / 2) + 0.5) * step.Z());
3234 float posphi = ((nph / 2) + 0.5) * steplocal.Phi() + pih;
3235 pos.SetPhi(posphi);
3236
3237 int xi[3] = {(int) std::floor((pos.Perp() - rih) / steplocal.Perp()), (int) std::floor((posphi - pih) / steplocal.Phi()), (int) std::floor((pos.Z() - zih) / steplocal.Z())};
3238 if (!hasTwin)
3239 {
3240 std::cout << std::format("rpz slice indices= ({},{},{}) (no twin)", xi[0], xi[1], xi[2]) << std::endl;
3241 }
3242 int twinz = (-pos.Z() - zih) / steplocal.Z();
3243 if (hasTwin)
3244 {
3245 std::cout << std::format("rpz slice indices= ({},{},{}) twinz={}", xi[0], xi[1], xi[2], twinz) << std::endl;
3246 }
3247
3248 const std::string axname[] = {"r", "p", "z", "r", "p", "z"};
3249 int axn[] = {nrh, nph, nzh, nrh, nph, nzh};
3250 float axval[] = {(float) pos.Perp(), (float) pos.Phi(), (float) pos.Z(), (float) pos.Perp(), (float) pos.Phi(), (float) pos.Z()};
3251 float axbot[] = {rih, pih, zih, rih, pih, zih};
3252 float axtop[] = {rfh, pfh, zfh, rfh, pfh, zfh};
3253 TH2F *hIntDist[3][3];
3254 TH1F *hRDist[2][3];
3255 TH2F *hDiffDist[3][3];
3256 TH1F *hRDiffDist[2][3];
3257 for (int i = 0; i < 3; i++)
3258 {
3259
3260 for (int ax = 0; ax < 3; ax++)
3261 {
3262
3263 hDiffDist[ax][i] = new TH2F(std::string("hDiffDist" + axname[i] + "_" + axname[ax + 1] + axname[ax + 2]).c_str(),
3264 std::format("{} component of diff. distortion in {}{} plane at {}={:.3f};{};{}",
3265 axname[i], axname[ax + 1], axname[ax + 2], axname[ax], axval[ax], axname[ax + 1], axname[ax + 2])
3266 .c_str(),
3267 axn[ax + 1], axbot[ax + 1], axtop[ax + 1],
3268 axn[ax + 2], axbot[ax + 2], axtop[ax + 2]);
3269 hIntDist[ax][i] = new TH2F(std::string("hIntDist" + axname[i] + "_" + axname[ax + 1] + axname[ax + 2]).c_str(),
3270 std::format("{} component of int. distortion in {}{} plane at {}={:.3f};{};{}",
3271 axname[i], axname[ax + 1], axname[ax + 2], axname[ax], axval[ax], axname[ax + 1], axname[ax + 2])
3272 .c_str(),
3273 axn[ax + 1], axbot[ax + 1], axtop[ax + 1],
3274 axn[ax + 2], axbot[ax + 2], axtop[ax + 2]);
3275 }
3276 hRDist[0][i] = new TH1F(std::string("hRDist" + axname[i]).c_str(),
3277 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3278 axn[0], axbot[0], axtop[0]);
3279 hRDist[1][i] = new TH1F(std::string("hRDist" + axname[i] + "Neg").c_str(),
3280 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3281 axn[0], axbot[0], axtop[0]);
3282 hRDiffDist[0][i] = new TH1F(std::string("hRDiffDist" + axname[i]).c_str(),
3283 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3284 axn[0], axbot[0], axtop[0]);
3285 hRDiffDist[1][i] = new TH1F(std::string("hRDiffDist" + axname[i] + "Neg").c_str(),
3286 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3287 axn[0], axbot[0], axtop[0]);
3288 }
3289
3290 TVector3 inpart;
3291 TVector3 outpart;
3292 TVector3 diffdistort;
3293 TVector3 distort;
3294 int validToStep;
3295 int successCheck;
3296
3297
3298 float partR;
3299 float partP;
3300 float partZ;
3301 int ir;
3302 int ip;
3303 int iz;
3304 float distortR;
3305 float distortP;
3306 float distortZ;
3307 float distortX;
3308 float distortY;
3309 float diffdistR;
3310 float diffdistP;
3311 float diffdistZ;
3312 TTree *dTree = new TTree("dTree", "Distortion per step z");
3313 dTree->Branch("r", &partR);
3314 dTree->Branch("p", &partP);
3315 dTree->Branch("z", &partZ);
3316 dTree->Branch("ir", &ir);
3317 dTree->Branch("ip", &ip);
3318 dTree->Branch("iz", &iz);
3319 dTree->Branch("dr", &distortR);
3320 dTree->Branch("drp", &distortP);
3321 dTree->Branch("dz", &distortZ);
3322
3323 std::cout << std::format("generating separated distortion map with ({}x{}x{}) grid ", nrh, nph, nzh) << std::endl;
3324 unsigned long long totalelements = nrh;
3325 totalelements *= nph;
3326 totalelements *= nzh;
3327 if (hasTwin)
3328 {
3329 totalelements *= 2;
3330 }
3331
3332 unsigned long long percent = totalelements / 100;
3333 unsigned long long waypoint = percent * debug_npercent;
3334 std::cout << std::format("total elements = {}", totalelements) << std::endl;
3335
3336 int el = 0;
3337
3338
3339
3340
3341
3342
3343
3344
3345 inpart.SetXYZ(1, 0, 0);
3346 for (ir = 0; ir < nrh; ir++)
3347 {
3348 partR = (ir + 0.5) * deltar + rih;
3349 if (ir == 0)
3350 {
3351 inpart.SetPerp(partR + deltar);
3352 }
3353 else if (ir == nrh - 1)
3354 {
3355 inpart.SetPerp(partR - deltar);
3356 }
3357 else
3358 {
3359 inpart.SetPerp(partR);
3360 }
3361 for (ip = 0; ip < nph; ip++)
3362 {
3363 partP = (ip + 0.5) * deltap + pih;
3364 inpart.SetPhi(partP);
3365
3366 for (iz = 0; iz < nzh; iz++)
3367 {
3368 partZ = (iz) *deltaz + zih;
3369 if (iz == 0)
3370 {
3371 inpart.SetZ(partZ + deltaz);
3372 }
3373 else if (iz == nzh - 1)
3374 {
3375 inpart.SetZ(partZ - deltaz);
3376 }
3377 else
3378 {
3379 inpart.SetZ(partZ);
3380 }
3381 partZ += 0.5 * deltaz;
3382 for (int localside = 0; localside < nSides; localside++)
3383 {
3384 if (localside == 0)
3385 {
3386 diffdistort = zero_vector;
3387 distort = GetTotalDistortion(z_readout, inpart, nSteps, true, &validToStep, &successCheck);
3388 }
3389 else
3390 {
3391
3392
3393 partZ *= -1;
3394 inpart.SetZ(-1 * inpart.Z());
3395 diffdistort = zero_vector;
3396 distort = twin->GetTotalDistortion(-z_readout, inpart, nSteps, true, &validToStep, &successCheck);
3397 }
3398
3399 diffdistort.RotateZ(-inpart.Phi());
3400 diffdistP = diffdistort.Y();
3401 diffdistR = diffdistort.X();
3402 diffdistZ = diffdistort.Z();
3403
3404 distortX = distort.X();
3405 distortY = distort.Y();
3406 distort.RotateZ(-inpart.Phi());
3407 distortP = distort.Y();
3408 distortR = distort.X();
3409 distortZ = distort.Z();
3410
3411 float distComp[nMapComponents];
3412 distComp[0] = distortX;
3413 distComp[1] = distortY;
3414 distComp[2] = distortZ;
3415 distComp[3] = distortR;
3416 distComp[4] = distortP / partR;
3417 distComp[5] = distortP;
3418
3419 for (int c = 0; c < nMapComponents; c++)
3420 {
3421 hSeparatedMapComponent[localside][c]->Fill(partP, partR, partZ, distComp[c]);
3422 }
3423
3424 hValidToStepComponent[localside]->Fill(partP, partR, partZ, validToStep);
3425 hReachesReadout[localside]->Fill(partP, partR, partZ, successCheck);
3426
3427
3428
3429
3430
3431 hIntDistortionR->Fill(partP, partR, partZ, distortR);
3432 hIntDistortionP->Fill(partP, partR, partZ, distortP);
3433 hIntDistortionZ->Fill(partP, partR, partZ, distortZ);
3434
3435 if (andCartesian)
3436 {
3437 hIntDistortionX->Fill(partP, partR, partZ, distortX);
3438 hIntDistortionY->Fill(partP, partR, partZ, distortY);
3439 }
3440
3441
3442 if (ir == xi[0] && localside == 0)
3443 {
3444
3445 hIntDist[0][0]->Fill(partP, partZ, distortR);
3446 hIntDist[0][1]->Fill(partP, partZ, distortP);
3447 hIntDist[0][2]->Fill(partP, partZ, distortZ);
3448 hDiffDist[0][0]->Fill(partP, partZ, diffdistR);
3449 hDiffDist[0][1]->Fill(partP, partZ, diffdistP);
3450 hDiffDist[0][2]->Fill(partP, partZ, diffdistZ);
3451 }
3452 if (ip == xi[1] && localside == 0)
3453 {
3454
3455 hIntDist[1][0]->Fill(partZ, partR, distortR);
3456 hIntDist[1][1]->Fill(partZ, partR, distortP);
3457 hIntDist[1][2]->Fill(partZ, partR, distortZ);
3458 hDiffDist[1][0]->Fill(partZ, partR, diffdistR);
3459 hDiffDist[1][1]->Fill(partZ, partR, diffdistP);
3460 hDiffDist[1][2]->Fill(partZ, partR, diffdistZ);
3461
3462 if (iz == xi[2] && localside == 0)
3463 {
3464 hRDist[0][0]->Fill(partR, distortR);
3465 hRDist[0][1]->Fill(partR, distortP);
3466 hRDist[0][2]->Fill(partR, distortZ);
3467 hRDiffDist[0][0]->Fill(partR, diffdistR);
3468 hRDiffDist[0][1]->Fill(partR, diffdistP);
3469 hRDiffDist[0][2]->Fill(partR, diffdistZ);
3470 }
3471 if (hasTwin && iz == twinz && localside == 1)
3472 {
3473 hRDist[1][0]->Fill(partR, distortR);
3474 hRDist[1][1]->Fill(partR, distortP);
3475 hRDist[1][2]->Fill(partR, distortZ);
3476 hRDiffDist[1][0]->Fill(partR, diffdistR);
3477 hRDiffDist[1][1]->Fill(partR, diffdistP);
3478 hRDiffDist[1][2]->Fill(partR, diffdistZ);
3479 }
3480 }
3481 if (iz == xi[2] && localside == 0)
3482 {
3483
3484
3485 hIntDist[2][0]->Fill(partR, partP, distortR);
3486 hIntDist[2][1]->Fill(partR, partP, distortP);
3487 hIntDist[2][2]->Fill(partR, partP, distortZ);
3488 hDiffDist[2][0]->Fill(partR, partP, diffdistR);
3489 hDiffDist[2][1]->Fill(partR, partP, diffdistP);
3490 hDiffDist[2][2]->Fill(partR, partP, diffdistZ);
3491 }
3492
3493 if (!(el % waypoint))
3494 {
3495 std::cout << std::format("generating distortions {}%: ", static_cast<int>(el / percent));
3496 std::cout << std::format("distortion at (ir={}, ip={}, iz={}) is ({:E},{:E},{:E})", ir, ip, iz, distortR, distortP, distortZ) << std::endl;
3497 }
3498 el++;
3499 }
3500 }
3501 }
3502 }
3503 std::cout << "Completed distortion generation. Saving outputs..." << std::endl;
3504
3505 TCanvas *canvas = new TCanvas("cdistort", "distortion integrals", 1200, 800);
3506
3507 std::cout << "was able to make a tcanvas" << std::endl;
3508 canvas->cd();
3509 TPad *c = new TPad("cplots", "distortion integral plots", 0, 0.2, 1, 1);
3510 canvas->cd();
3511 TPad *textpad = new TPad("ctext", "distortion integral plots", 0, 0.0, 1, 0.2);
3512 std::cout << "was able to make some tpads" << std::endl;
3513
3514 c->Divide(4, 3);
3515 gStyle->SetOptStat();
3516 std::cout << "was able to interact with gStyle" << std::endl;
3517
3518 for (int i = 0; i < 3; i++)
3519 {
3520
3521 for (int ax = 0; ax < 3; ax++)
3522 {
3523 std::cout << std::format("looping over components i={} ax={}", i, ax) << std::endl;
3524
3525
3526 c->cd(i * 4 + ax + 1);
3527 gPad->SetRightMargin(0.15);
3528 hIntDist[ax][i]->SetStats(false);
3529 hIntDist[ax][i]->Draw("colz");
3530 }
3531
3532 std::cout << "drawing R profile " << i << std::endl;
3533
3534 c->cd(i * 4 + 4);
3535 hRDist[0][i]->SetStats(false);
3536 hRDist[0][i]->SetFillColor(kRed);
3537 hRDist[0][i]->Draw("hist");
3538 if (hasTwin)
3539 {
3540 std::cout << "drawing R profile twin " << i << std::endl;
3541 hRDist[1][i]->SetStats(false);
3542 hRDist[1][i]->SetLineColor(kBlue);
3543 hRDist[1][i]->Draw("hist,same");
3544 }
3545 }
3546 std::cout << "switching to textpad" << std::endl;
3547
3548 textpad->cd();
3549 float texpos = 0.9;
3550 float texshift = 0.12;
3551 TLatex *tex = new TLatex(0.0, texpos, "Fill Me In");
3552 std::cout << "built TLatex" << std::endl;
3553
3554 tex->SetTextSize(texshift * 0.8);
3555 tex->DrawLatex(0.05, texpos, GetFieldString().c_str());
3556 texpos -= texshift;
3557 tex->DrawLatex(0.05, texpos, GetChargeString().c_str());
3558 texpos -= texshift;
3559
3560 tex->DrawLatex(0.05, texpos, std::format("Drifting grid of (rp)=({} x {}) electrons with {} steps", nrh, nph, nSteps).c_str());
3561 texpos -= texshift;
3562 tex->DrawLatex(0.05, texpos, GetLookupString().c_str());
3563 texpos -= texshift;
3564 tex->DrawLatex(0.05, texpos, GetGasString().c_str());
3565 texpos -= texshift;
3566 if (debug_distortionScale.Mag() > 0)
3567 {
3568 tex->DrawLatex(0.05, texpos, std::format("Distortion scaled by (r,p,z)=({:.2f},{:.2f},{:.2f})", debug_distortionScale.X(), debug_distortionScale.Y(), debug_distortionScale.Z()).c_str());
3569 texpos -= texshift;
3570 }
3571 texpos = 0.9;
3572 std::cout << "cd'ing to canvas:" << std::endl;
3573
3574 canvas->cd();
3575 std::cout << "draw1" << std::endl;
3576
3577 c->Draw();
3578 canvas->cd();
3579 std::cout << "draw2" << std::endl;
3580 textpad->Draw();
3581 std::cout << "was able to complete drawing on both pads" << std::endl;
3582
3583 canvas->SaveAs(summaryFilename.c_str());
3584
3585
3586
3587
3588
3589
3590
3591 for (int i = 0; i < 3; i++)
3592 {
3593
3594 for (int ax = 0; ax < 3; ax++)
3595 {
3596
3597 c->cd(i * 4 + ax + 1);
3598 gPad->SetRightMargin(0.15);
3599 hDiffDist[ax][i]->SetStats(false);
3600 hDiffDist[ax][i]->Draw("colz");
3601 }
3602 c->cd(i * 4 + 4);
3603 hRDiffDist[0][i]->SetStats(false);
3604 hRDiffDist[0][i]->SetFillColor(kRed);
3605 hRDiffDist[0][i]->Draw("hist");
3606 if (hasTwin)
3607 {
3608 hRDiffDist[1][i]->SetStats(false);
3609 hRDiffDist[1][i]->SetLineColor(kBlue);
3610 hRDiffDist[1][i]->Draw("hist same");
3611 }
3612 }
3613 textpad->cd();
3614 texpos = 0.9;
3615 texshift = 0.12;
3616 tex->SetTextSize(texshift * 0.8);
3617 tex->DrawLatex(0.05, texpos, GetFieldString().c_str());
3618 texpos -= texshift;
3619 tex->DrawLatex(0.05, texpos, GetChargeString().c_str());
3620 texpos -= texshift;
3621
3622 tex->DrawLatex(0.05, texpos, std::format("Drifting grid of (rp)=({} x {}) electrons with {} steps", nrh, nph, nSteps).c_str());
3623 texpos -= texshift;
3624 tex->DrawLatex(0.05, texpos, GetLookupString().c_str());
3625 texpos -= texshift;
3626 tex->DrawLatex(0.05, texpos, GetGasString().c_str());
3627 texpos -= texshift;
3628 tex->DrawLatex(0.05, texpos, "Differential Plots");
3629 texpos -= texshift;
3630 if (debug_distortionScale.Mag() > 0)
3631 {
3632 tex->DrawLatex(0.05, texpos, std::format("Distortion scaled by (r,p,z)=({:.2f},{:.2f},{:.2f})", debug_distortionScale.X(), debug_distortionScale.Y(), debug_distortionScale.Z()).c_str());
3633 texpos -= texshift;
3634 }
3635 texpos = 0.9;
3636
3637 canvas->cd();
3638 c->Draw();
3639 canvas->cd();
3640 textpad->Draw();
3641 canvas->SaveAs(diffSummaryFilename.c_str());
3642
3643 std::cout << "saving map histograms to:" << distortionFilename << std::endl;
3644
3645 outf->cd();
3646 for (int i = 0; i < nSides; i++)
3647 {
3648 std::cout << "Saving side " << side[i] << std::endl;
3649 for (int j = 0; j < nMapComponents; j++)
3650 {
3651 hSeparatedMapComponent[i][j]->GetSumw2()->Set(0);
3652 hSeparatedMapComponent[i][j]->Write();
3653 }
3654 hValidToStepComponent[i]->GetSumw2()->Set(0);
3655 hValidToStepComponent[i]->Write();
3656
3657 hReachesReadout[i]->GetSumw2()->Set(0);
3658 hReachesReadout[i]->Write();
3659 }
3660 if (rdrswitch)
3661 {
3662 hRdeltaRComponent->GetSumw2()->Set(0);
3663 hRdeltaRComponent->Write();
3664 }
3665
3666 hDistortionR->GetSumw2()->Set(0);
3667 hDistortionP->GetSumw2()->Set(0);
3668 hDistortionZ->GetSumw2()->Set(0);
3669 hIntDistortionR->GetSumw2()->Set(0);
3670 hIntDistortionP->GetSumw2()->Set(0);
3671 hIntDistortionZ->GetSumw2()->Set(0);
3672 if (andCartesian)
3673 {
3674 hIntDistortionX->GetSumw2()->Set(0);
3675 hIntDistortionY->GetSumw2()->Set(0);
3676 }
3677 hDistortionR->Write();
3678 hDistortionP->Write();
3679 hDistortionZ->Write();
3680 hIntDistortionR->Write();
3681 hIntDistortionP->Write();
3682 hIntDistortionZ->Write();
3683 if (false && andCartesian)
3684 {
3685 hIntDistortionX->Write();
3686 hIntDistortionY->Write();
3687 }
3688 std::cout << "finished writing histograms" << std::endl;
3689
3690
3691 outf->Close();
3692
3693
3694 std::cout << "wrote separated map and summary to " << filebase << std::endl;
3695 std::cout << "map: " << distortionFilename << std::endl;
3696 std::cout << "summary: " << summaryFilename << std::endl;
3697
3698 return;
3699 }
3700
3701 void AnnularFieldSim::GenerateDistortionMaps(const std::string &filebase, int r_subsamples, int p_subsamples, int z_subsamples, int , bool andCartesian)
3702 {
3703
3704
3705
3706 std::cout << "WARNING: You called the version of the distortion generator that generates a unified map. Are you sure you meant to do this?" << std::endl;
3707
3708
3709 bool makeUnifiedMap = true;
3710
3711 TVector3 steplocal(step.Perp() / r_subsamples, 0, step.Z() / z_subsamples);
3712 steplocal.SetPhi(step.Phi() / p_subsamples);
3713 float deltar = steplocal.Perp();
3714 float deltap = steplocal.Phi();
3715 float deltaz = steplocal.Z();
3716 TVector3 stepzvec(0, 0, deltaz);
3717 int nSteps = 500;
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728 TVector3 lowerEdge = GetRoiCellCenter(rmin_roi, phimin_roi, zmin_roi);
3729 TVector3 upperEdge = GetRoiCellCenter(rmax_roi - 1, phimax_roi - 1, zmax_roi - 1);
3730 int nph = nphi * p_subsamples + 2;
3731 int nrh = nr * r_subsamples + 2;
3732 int nzh = nz * z_subsamples + 2;
3733 int successCheck;
3734
3735 if (hasTwin && makeUnifiedMap)
3736 {
3737 lowerEdge.SetZ(-1 * upperEdge.Z());
3738 nzh += nz * z_subsamples;
3739 }
3740
3741 float rih = lowerEdge.Perp() - 0.5 * step.Perp() - steplocal.Perp();
3742 float rfh = upperEdge.Perp() + 0.5 * step.Perp() + steplocal.Perp();
3743 float pih = FilterPhiPos(lowerEdge.Phi()) - 0.5 * step.Phi() - steplocal.Phi();
3744 float pfh = FilterPhiPos(upperEdge.Phi()) + 0.5 * step.Phi() + steplocal.Phi();
3745 float zih = lowerEdge.Z() - 0.5 * step.Z() - steplocal.Z();
3746 float zfh = upperEdge.Z() + 0.5 * step.Z() + steplocal.Z();
3747 float z_readout = upperEdge.Z() + 0.5 * step.Z();
3748
3749 std::cout << "generating distortion map..." << std::endl;
3750 std::cout << "file=" << filebase << std::endl;
3751 std::cout << std::format("Phi: {} steps from {} to {} (field has {} steps)", nph, pih, pfh, GetFieldStepsPhi()) << std::endl;
3752 std::cout << std::format("R: %d steps from %f to %f (field has %d steps)", nrh, rih, rfh, GetFieldStepsR()) << std::endl;
3753 std::cout << std::format("Z: %d steps from %f to %f (field has %d steps)", nzh, zih, zfh, GetFieldStepsZ()) << std::endl;
3754 std::string distortionFilename = filebase + ".distortion_summary.pdf";
3755 std::string summaryFilename = filebase + ".distortion_summary.pdf";
3756 std::string diffSummaryFilename = filebase + ".differential_summary.pdf";
3757
3758 TFile *outf = TFile::Open(distortionFilename.c_str(), "RECREATE");
3759 outf->cd();
3760
3761
3762
3763 TH3F *hDistortionR = new TH3F("hDistortionR", "Per-z-bin Distortion in the R direction as a function of (phi,r,z) (centered in r,phi, z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3764 TH3F *hDistortionP = new TH3F("hDistortionP", "Per-z-bin Distortion in the RPhi direction as a function of (phi,r,z) (centered in r,phi, z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3765 TH3F *hDistortionZ = new TH3F("hDistortionZ", "Per-z-bin Distortion in the Z direction as a function of (phi,r,z) (centered in r,phi, z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3766 TH3F *hIntDistortionR = new TH3F("hIntDistortionR", "Integrated R Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3767 TH3F *hIntDistortionP = new TH3F("hIntDistortionP", "Integrated R Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3768 TH3F *hIntDistortionZ = new TH3F("hIntDistortionZ", "Integrated R Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3769
3770 TH3F *hIntDistortionX = new TH3F("hIntDistortionX", "Integrated X Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3771 TH3F *hIntDistortionY = new TH3F("hIntDistortionY", "Integrated Y Distortion from (phi,r,z) to z=0 (centered in r,phi, and z);phi;r;z", nph, pih, pfh, nrh, rih, rfh, nzh, zih, zfh);
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786 TVector3 pos(((nrh / 2) + 0.5) * steplocal.Perp() + rih, 0, zmin + ((nz / 2) + 0.5) * step.Z());
3787 float posphi = ((nph / 2) + 0.5) * steplocal.Phi() + pih;
3788 pos.SetPhi(posphi);
3789
3790 int xi[3] = {(int) std::floor((pos.Perp() - rih) / steplocal.Perp()), (int) std::floor((posphi - pih) / steplocal.Phi()), (int) std::floor((pos.Z() - zih) / steplocal.Z())};
3791 if (!hasTwin)
3792 {
3793 std::cout << std::format("rpz slice indices= ({},{},{}) (no twin)", xi[0], xi[1], xi[2]) << std::endl;
3794 }
3795 int twinz = 0;
3796 if (hasTwin)
3797 {
3798 twinz = twin->GetZindex(-1 * pos.Z());
3799 std::cout << std::format("rpz slice indices= ({},{},{}) twinz={}", xi[0], xi[1], xi[2], twinz) << std::endl;
3800 }
3801
3802 const std::string axname[] = {"r", "p", "z", "r", "p", "z"};
3803 int axn[] = {nrh, nph, nzh, nrh, nph, nzh};
3804 float axval[] = {(float) pos.Perp(), (float) pos.Phi(), (float) pos.Z(), (float) pos.Perp(), (float) pos.Phi(), (float) pos.Z()};
3805 float axbot[] = {rih, pih, zih, rih, pih, zih};
3806 float axtop[] = {rfh, pfh, zfh, rfh, pfh, zfh};
3807 TH2F *hIntDist[3][3];
3808 TH1F *hRDist[2][3];
3809 TH2F *hDiffDist[3][3];
3810 TH1F *hRDiffDist[2][3];
3811 for (int i = 0; i < 3; i++)
3812 {
3813
3814 for (int ax = 0; ax < 3; ax++)
3815 {
3816
3817 hDiffDist[ax][i] = new TH2F(std::string("hDiffDist" + axname[i] + "_" + axname[ax + 1] + axname[ax + 2]).c_str(),
3818 std::format("{} component of diff. distortion in {}{} plane at {}={:.3f};{};{}", axname[i], axname[ax + 1], axname[ax + 2], axname[ax], axval[ax], axname[ax + 1], axname[ax + 2]).c_str(),
3819 axn[ax + 1], axbot[ax + 1], axtop[ax + 1],
3820 axn[ax + 2], axbot[ax + 2], axtop[ax + 2]);
3821 hIntDist[ax][i] = new TH2F(std::string("hDIntDist" + axname[i] + "_" + axname[ax + 1] + axname[ax + 2]).c_str(),
3822 std::format("{} component of int. distortion in {}{} plane at {}={:.3f};{};{}", axname[i], axname[ax + 1], axname[ax + 2], axname[ax], axval[ax], axname[ax + 1], axname[ax + 2]).c_str(),
3823 axn[ax + 1], axbot[ax + 1], axtop[ax + 1],
3824 axn[ax + 2], axbot[ax + 2], axtop[ax + 2]);
3825 }
3826 hRDist[0][i] = new TH1F(std::string("hRDist" + axname[i]).c_str(),
3827 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3828 axn[0], axbot[0], axtop[0]);
3829 hRDist[1][i] = new TH1F(std::string("hRDist" + axname[i] + "Neg").c_str(),
3830 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3831 axn[0], axbot[0], axtop[0]);
3832 hRDiffDist[0][i] = new TH1F(std::string("hRDist" + axname[i]).c_str(),
3833 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3834 axn[0], axbot[0], axtop[0]);
3835 hRDiffDist[1][i] = new TH1F(std::string("hRDist" + axname[i] + "Neg").c_str(),
3836 std::format("{} component of int. distortion vs r with {}={:.3f} and {}={:.3f};r(cm);#delta (cm)", axname[i], axname[1], axval[1], axname[2], axval[2]).c_str(),
3837 axn[0], axbot[0], axtop[0]);
3838 }
3839
3840 TVector3 inpart;
3841 TVector3 outpart;
3842 TVector3 distort;
3843 int validToStep;
3844
3845
3846 float partR;
3847 float partP;
3848 float partZ;
3849 int ir;
3850 int ip;
3851 int iz;
3852 float distortR;
3853 float distortP;
3854 float distortZ;
3855 float distortX;
3856 float distortY;
3857 float diffdistR;
3858 float diffdistP;
3859 float diffdistZ;
3860 TTree *dTree = new TTree("dTree", "Distortion per step z");
3861 dTree->Branch("r", &partR);
3862 dTree->Branch("p", &partP);
3863 dTree->Branch("z", &partZ);
3864 dTree->Branch("ir", &ir);
3865 dTree->Branch("ip", &ip);
3866 dTree->Branch("iz", &iz);
3867 dTree->Branch("dr", &distortR);
3868 dTree->Branch("drp", &distortP);
3869 dTree->Branch("dz", &distortZ);
3870
3871 std::cout << "generating distortion map with (" << nrh << "x" << nph << "x" << nzh << " grid" << std::endl;
3872 unsigned long long totalelements = nrh;
3873 totalelements *= nph;
3874 totalelements *= nzh;
3875 unsigned long long percent = totalelements / 100 * debug_npercent;
3876 std::cout << std::format("total elements = {}", totalelements) << std::endl;
3877
3878 int el = 0;
3879
3880
3881
3882
3883
3884
3885
3886
3887 inpart.SetXYZ(1, 0, 0);
3888 for (ir = 0; ir < nrh; ir++)
3889 {
3890 partR = (ir + 0.5) * deltar + rih;
3891 if (ir == 0)
3892 {
3893 inpart.SetPerp(partR + deltar);
3894 }
3895 else if (ir == nrh - 1)
3896 {
3897 inpart.SetPerp(partR - deltar);
3898 }
3899 else
3900 {
3901 inpart.SetPerp(partR);
3902 }
3903 for (ip = 0; ip < nph; ip++)
3904 {
3905 partP = (ip + 0.5) * deltap + pih;
3906 inpart.SetPhi(partP);
3907
3908 for (iz = 0; iz < nzh; iz++)
3909 {
3910 partZ = (iz) *deltaz + zih;
3911 if (iz == 0)
3912 {
3913 inpart.SetZ(partZ + deltaz);
3914 }
3915 else if (iz == nzh - 1)
3916 {
3917 inpart.SetZ(partZ - deltaz);
3918 }
3919 else
3920 {
3921 inpart.SetZ(partZ);
3922 }
3923 partZ += 0.5 * deltaz;
3924
3925
3926
3927
3928
3929 if (hasTwin && inpart.Z() < 0)
3930 {
3931 distort = twin->GetTotalDistortion(inpart.Z(), inpart + stepzvec, nSteps, true, &validToStep, &successCheck);
3932 }
3933 else
3934 {
3935 distort = GetTotalDistortion(inpart.Z() + deltaz, inpart, nSteps, true, &validToStep, &successCheck);
3936 }
3937 distort.RotateZ(-inpart.Phi());
3938 diffdistP = distort.Y();
3939 diffdistR = distort.X();
3940 diffdistZ = distort.Z();
3941 hDistortionR->Fill(partP, partR, partZ, diffdistR);
3942 hDistortionP->Fill(partP, partR, partZ, diffdistP);
3943 hDistortionZ->Fill(partP, partR, partZ, diffdistZ);
3944 dTree->Fill();
3945
3946
3947 if (hasTwin && makeUnifiedMap && inpart.Z() < 0)
3948 {
3949 distort = twin->GetTotalDistortion(-z_readout, inpart + stepzvec, nSteps, true, &validToStep, &successCheck);
3950 }
3951 else
3952 {
3953 distort = GetTotalDistortion(z_readout, inpart, nSteps, true, &validToStep, &successCheck);
3954 }
3955 distortX = distort.X();
3956 distortY = distort.Y();
3957 distort.RotateZ(-inpart.Phi());
3958 distortP = distort.Y();
3959 distortR = distort.X();
3960 distortZ = distort.Z();
3961
3962
3963
3964
3965
3966 hIntDistortionR->Fill(partP, partR, partZ, distortR);
3967 hIntDistortionP->Fill(partP, partR, partZ, distortP);
3968 hIntDistortionZ->Fill(partP, partR, partZ, distortZ);
3969
3970 if (andCartesian)
3971 {
3972 hIntDistortionX->Fill(partP, partR, partZ, distortX);
3973 hIntDistortionY->Fill(partP, partR, partZ, distortY);
3974 }
3975
3976
3977 if (ir == xi[0])
3978 {
3979
3980 hIntDist[0][0]->Fill(partP, partZ, distortR);
3981 hIntDist[0][1]->Fill(partP, partZ, distortP);
3982 hIntDist[0][2]->Fill(partP, partZ, distortZ);
3983 hDiffDist[0][0]->Fill(partP, partZ, diffdistR);
3984 hDiffDist[0][1]->Fill(partP, partZ, diffdistP);
3985 hDiffDist[0][2]->Fill(partP, partZ, diffdistZ);
3986 }
3987 if (ip == xi[1])
3988 {
3989
3990 hIntDist[1][0]->Fill(partZ, partR, distortR);
3991 hIntDist[1][1]->Fill(partZ, partR, distortP);
3992 hIntDist[1][2]->Fill(partZ, partR, distortZ);
3993 hDiffDist[1][0]->Fill(partZ, partR, diffdistR);
3994 hDiffDist[1][1]->Fill(partZ, partR, diffdistP);
3995 hDiffDist[1][2]->Fill(partZ, partR, diffdistZ);
3996
3997 if (iz == xi[2])
3998 {
3999 hRDist[0][0]->Fill(partR, distortR);
4000 hRDist[0][1]->Fill(partR, distortP);
4001 hRDist[0][2]->Fill(partR, distortZ);
4002 hRDiffDist[0][0]->Fill(partR, diffdistR);
4003 hRDiffDist[0][1]->Fill(partR, diffdistP);
4004 hRDiffDist[0][2]->Fill(partR, diffdistZ);
4005 }
4006 if (hasTwin && iz == twinz)
4007 {
4008 hRDist[1][0]->Fill(partR, distortR);
4009 hRDist[1][1]->Fill(partR, distortP);
4010 hRDist[1][2]->Fill(partR, distortZ);
4011 hRDiffDist[1][0]->Fill(partR, diffdistR);
4012 hRDiffDist[1][1]->Fill(partR, diffdistP);
4013 hRDiffDist[1][2]->Fill(partR, diffdistZ);
4014 }
4015 }
4016 if (iz == xi[2])
4017 {
4018
4019
4020 hIntDist[2][0]->Fill(partR, partP, distortR);
4021 hIntDist[2][1]->Fill(partR, partP, distortP);
4022 hIntDist[2][2]->Fill(partR, partP, distortZ);
4023 hDiffDist[2][0]->Fill(partR, partP, diffdistR);
4024 hDiffDist[2][1]->Fill(partR, partP, diffdistP);
4025 hDiffDist[2][2]->Fill(partR, partP, diffdistZ);
4026 }
4027
4028 if (!(el % percent))
4029 {
4030 std::cout << std::format("generating distortions {}%: ", static_cast<int>(debug_npercent * (el / percent)));
4031 std::cout << std::format("distortion at (ir={}, ip={}, iz={}) is ({:E},{:E},{:E})", ir, ip, iz, distortR, distortP, distortZ) << std::endl;
4032 }
4033 el++;
4034 }
4035 }
4036 }
4037
4038 TCanvas *canvas = new TCanvas("cdistort", "distortion integrals", 1200, 800);
4039
4040 canvas->cd();
4041 TPad *c = new TPad("cplots", "distortion integral plots", 0, 0.2, 1, 1);
4042 canvas->cd();
4043 TPad *textpad = new TPad("ctext", "distortion integral plots", 0, 0.0, 1, 0.2);
4044 c->Divide(4, 3);
4045 gStyle->SetOptStat();
4046 for (int i = 0; i < 3; i++)
4047 {
4048
4049 for (int ax = 0; ax < 3; ax++)
4050 {
4051
4052 c->cd(i * 4 + ax + 1);
4053 gPad->SetRightMargin(0.15);
4054 hIntDist[ax][i]->SetStats(false);
4055 hIntDist[ax][i]->Draw("colz");
4056 }
4057 c->cd(i * 4 + 4);
4058 hRDist[0][i]->SetStats(false);
4059 hRDist[0][i]->SetFillColor(kRed);
4060 hRDist[0][i]->Draw("hist");
4061 if (hasTwin)
4062 {
4063 hRDist[1][i]->SetStats(false);
4064 hRDist[1][i]->SetLineColor(kBlue);
4065 hRDist[1][i]->Draw("hist,same");
4066 }
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081 }
4082 textpad->cd();
4083 float texpos = 0.9;
4084 float texshift = 0.12;
4085 TLatex *tex = new TLatex(0.0, texpos, "Fill Me In");
4086 tex->SetTextSize(texshift * 0.8);
4087 tex->DrawLatex(0.05, texpos, GetFieldString().c_str());
4088 texpos -= texshift;
4089 tex->DrawLatex(0.05, texpos, GetChargeString().c_str());
4090 texpos -= texshift;
4091
4092 tex->DrawLatex(0.05, texpos, std::format("Drifting grid of (rp)=({} x {}) electrons with {} steps", nrh, nph, nSteps).c_str());
4093 texpos -= texshift;
4094 tex->DrawLatex(0.05, texpos, GetLookupString().c_str());
4095 texpos -= texshift;
4096 tex->DrawLatex(0.05, texpos, GetGasString().c_str());
4097 texpos -= texshift;
4098 if (debug_distortionScale.Mag() > 0)
4099 {
4100 tex->DrawLatex(0.05, texpos, std::format("Distortion scaled by (r,p,z)=({:.2f},{:.2f},{:.2f})", debug_distortionScale.X(), debug_distortionScale.Y(), debug_distortionScale.Z()).c_str());
4101 texpos -= texshift;
4102 }
4103 texpos = 0.9;
4104
4105 canvas->cd();
4106 c->Draw();
4107 canvas->cd();
4108 textpad->Draw();
4109 canvas->SaveAs(summaryFilename.c_str());
4110
4111
4112
4113
4114
4115
4116
4117 for (int i = 0; i < 3; i++)
4118 {
4119
4120 for (int ax = 0; ax < 3; ax++)
4121 {
4122
4123 c->cd(i * 4 + ax + 1);
4124 gPad->SetRightMargin(0.15);
4125 hDiffDist[ax][i]->SetStats(false);
4126 hDiffDist[ax][i]->Draw("colz");
4127 }
4128 c->cd(i * 4 + 4);
4129 hRDiffDist[0][i]->SetStats(false);
4130 hRDiffDist[0][i]->SetFillColor(kRed);
4131 hRDiffDist[0][i]->Draw("hist");
4132 if (hasTwin)
4133 {
4134 hRDiffDist[1][i]->SetStats(false);
4135 hRDiffDist[1][i]->SetLineColor(kBlue);
4136 hRDiffDist[1][i]->Draw("hist same");
4137 }
4138 }
4139 textpad->cd();
4140 texpos = 0.9;
4141 texshift = 0.12;
4142 tex->SetTextSize(texshift * 0.8);
4143 tex->DrawLatex(0.05, texpos, GetFieldString().c_str());
4144 texpos -= texshift;
4145 tex->DrawLatex(0.05, texpos, GetChargeString().c_str());
4146 texpos -= texshift;
4147
4148 tex->DrawLatex(0.05, texpos, std::format("Drifting grid of (rp)=({} x {}) electrons with {} steps", nrh, nph, nSteps).c_str());
4149 texpos -= texshift;
4150 tex->DrawLatex(0.05, texpos, GetLookupString().c_str());
4151 texpos -= texshift;
4152 tex->DrawLatex(0.05, texpos, GetGasString().c_str());
4153 texpos -= texshift;
4154 tex->DrawLatex(0.05, texpos, "Differential Plots");
4155 texpos -= texshift;
4156 if (debug_distortionScale.Mag() > 0)
4157 {
4158 tex->DrawLatex(0.05, texpos, std::format("Distortion scaled by (r,p,z)=({:.2f},{:.2f},{:.2f})", debug_distortionScale.X(), debug_distortionScale.Y(), debug_distortionScale.Z()).c_str());
4159 texpos -= texshift;
4160 }
4161 texpos = 0.9;
4162
4163 canvas->cd();
4164 c->Draw();
4165 canvas->cd();
4166 textpad->Draw();
4167 canvas->SaveAs(diffSummaryFilename.c_str());
4168
4169
4170
4171 outf->cd();
4172
4173 hDistortionR->Write();
4174 hDistortionP->Write();
4175 hDistortionZ->Write();
4176 hIntDistortionR->Write();
4177 hIntDistortionP->Write();
4178 hIntDistortionZ->Write();
4179 if (andCartesian)
4180 {
4181 hIntDistortionX->Write();
4182 hIntDistortionY->Write();
4183 }
4184 dTree->Write();
4185 outf->Close();
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209 std::cout << "wrote map and summary to " << filebase << std::endl;
4210 std::cout << "map: " << distortionFilename << std::endl;
4211 std::cout << "summary: " << summaryFilename << std::endl;
4212
4213 return;
4214 }
4215
4216 TVector3 AnnularFieldSim::swimTo(float zdest, const TVector3 &start, bool interpolate, bool useAnalytic)
4217 {
4218 int defaultsteps = 100;
4219 int goodtostep = 0;
4220 if (useAnalytic)
4221 {
4222 return swimToInAnalyticSteps(zdest, start, defaultsteps, &goodtostep);
4223 }
4224 return swimToInSteps(zdest, start, defaultsteps, interpolate, &goodtostep);
4225 }
4226
4227 TVector3 AnnularFieldSim::GetStepDistortion(float zdest, const TVector3 &start, bool interpolate, bool useAnalytic)
4228 {
4229
4230
4231
4232
4233 int rt;
4234 int pt;
4235 int zt;
4236 BoundsCase zBound = GetZindexAndCheckBounds(start.Z(), &zt);
4237 if (GetRindexAndCheckBounds(start.Perp(), &rt) != InBounds || GetPhiIndexAndCheckBounds(FilterPhiPos(start.Phi()), &pt) != InBounds || (zBound != InBounds && zBound != OnHighEdge))
4238 {
4239
4240
4241
4242 return start;
4243 }
4244
4245
4246
4247 double zdist = zdest - start.Z();
4248
4249
4250
4251 if (std::abs(zdist) < ALMOST_ZERO * step.Z())
4252 {
4253
4254 return zero_vector;
4255 }
4256
4257 TVector3 fieldInt;
4258 TVector3 fieldIntB;
4259
4260
4261 if (useAnalytic)
4262 {
4263 fieldInt = analyticFieldIntegral(zdest, start, Efield);
4264 fieldIntB = analyticFieldIntegral(zdest, start, Bfield);
4265 }
4266 else if (interpolate)
4267 {
4268 fieldInt = interpolatedFieldIntegral(zdest, start, Efield);
4269 fieldIntB = interpolatedFieldIntegral(zdest, start, Bfield);
4270 }
4271 else
4272 {
4273 fieldInt = fieldIntegral(zdest, start, Efield);
4274 fieldIntB = fieldIntegral(zdest, start, Bfield);
4275 }
4276
4277 if (std::abs(fieldInt.Z() / zdist) < ALMOST_ZERO)
4278 {
4279 std::cout << "GetStepDistortion is attempting to swim with no drift field:" << std::endl;
4280 std::cout << std::format("GetStepDistortion: ({:.4f},{:.4f},{:.4f}) to z={:.4f}", start.X(), start.Y(), start.Z(), zdest) << std::endl;
4281 std::cout << std::format("GetStepDistortion: fieldInt=({:E},{:E},{:E})", fieldInt.X(), fieldInt.Y(), fieldInt.Z()) << std::endl;
4282 exit(1);
4283 }
4284
4285 double EfieldZ = fieldInt.Z() / zdist;
4286 double BfieldZ = fieldIntB.Z() / zdist;
4287
4288
4289
4290
4291
4292 double omegatau = omegatau_nominal;
4293
4294
4295
4296 double T1om = langevin_T1 * omegatau;
4297 double T2om2 = langevin_T2 * omegatau * langevin_T2 * omegatau;
4298 double c0 = 1 / (1 + T2om2);
4299 double c1 = T1om / (1 + T1om * T1om);
4300 double c2 = T2om2 / (1 + T2om2);
4301
4302 TVector3 EintOverEz = 1 / EfieldZ * fieldInt;
4303 TVector3 BintOverBz = 1 / BfieldZ * fieldIntB;
4304
4305
4306
4307
4308 double deltaX = c0 * EintOverEz.X() + c1 * EintOverEz.Y() - c1 * BintOverBz.Y() + c2 * BintOverBz.X();
4309 double deltaY = c0 * EintOverEz.Y() - c1 * EintOverEz.X() + c1 * BintOverBz.X() + c2 * BintOverBz.Y();
4310
4311
4312 double vprime = (5000 * cm / s) / (100 * V / cm);
4313 double vdoubleprime = 0;
4314
4315
4316
4317
4318 double deltaZ = vprime / vdrift * (fieldInt.Z() - zdist * Enominal) + vdoubleprime / vdrift * (fieldInt.Z() - Enominal * zdist) * (fieldInt.Z() - Enominal * zdist) / (2 * zdist) - 0.5 / zdist * (EintOverEz.X() * EintOverEz.X() + EintOverEz.Y() * EintOverEz.Y()) + c1 / zdist * (EintOverEz.X() * BintOverBz.Y() - EintOverEz.Y() * BintOverBz.X()) + c2 / zdist * (EintOverEz.X() * BintOverBz.X() + EintOverEz.Y() * BintOverBz.Y()) + c2 / zdist * (BintOverBz.X() * BintOverBz.X() + BintOverBz.Y() * BintOverBz.Y());
4319
4320 if (false)
4321 {
4322 std::cout << std::format("GetStepDistortion: (c0,c1,c2)=({:E},{:E},{:E})", c0, c1, c2) << std::endl;
4323 std::cout << std::format("GetStepDistortion: EintOverEz==({:E},{:E},{:E})", EintOverEz.X(), EintOverEz.Y(), EintOverEz.Z()) << std::endl;
4324 std::cout << std::format("GetStepDistortion: BintOverBz==({:E},{:E},{:E})", BintOverBz.X(), BintOverBz.Y(), BintOverBz.Z()) << std::endl;
4325 std::cout << std::format("GetStepDistortion: ({:.4f},{:.4f},{:.4f}) to z={:.4f}", start.X(), start.Y(), start.Z(), zdest) << std::endl;
4326 std::cout << std::format("GetStepDistortion: fieldInt=({:E},{:E},{:E})", fieldInt.X(), fieldInt.Y(), fieldInt.Z()) << std::endl;
4327 std::cout << std::format("GetStepDistortion: delta=({:E},{:E},{:E})", deltaX, deltaY, deltaZ) << std::endl;
4328 }
4329
4330
4331 if (false)
4332 {
4333 std::cout << "GetStepDistortion produced a very large zdistortion!" << std::endl;
4334 std::cout << std::format("GetStepDistortion: zdist={:.4f}, deltaZ={:.4f}, Delta/z={:.4f}", zdist, deltaZ, deltaZ / zdist) << std::endl;
4335 std::cout << std::format("GetStepDistortion: average field Z: Ez={:.4f}V/cm, Bz={:.4f}T", EfieldZ / (V / cm), BfieldZ / Tesla) << std::endl;
4336 std::cout << std::format("GetStepDistortion: (c0,c1,c2)=({:E},{:E},{:E})", c0, c1, c2) << std::endl;
4337 std::cout << std::format("GetStepDistortion: EintOverEz==({:E},{:E},{:E})", EintOverEz.X(), EintOverEz.Y(), EintOverEz.Z()) << std::endl;
4338 std::cout << std::format("GetStepDistortion: BintOverBz==({:E},{:E},{:E})", BintOverBz.X(), BintOverBz.Y(), BintOverBz.Z()) << std::endl;
4339 std::cout << std::format("GetStepDistortion: ({:.4f},{:.4f},{:.4f}) to z={:.4f}", start.X(), start.Y(), start.Z(), zdest) << std::endl;
4340 std::cout << std::format("GetStepDistortion: EfieldInt=({:E},{:E},{:E})", fieldInt.X(), fieldInt.Y(), fieldInt.Z()) << std::endl;
4341 std::cout << std::format("GetStepDistortion: BfieldInt=({:E},{:E},{:E})", fieldIntB.X(), fieldIntB.Y(), fieldIntB.Z()) << std::endl;
4342 std::cout << std::format("GetStepDistortion: delta=({:E},{:E},{:E})", deltaX, deltaY, deltaZ) << std::endl;
4343
4344
4345 }
4346
4347 if (std::abs(deltaX) < 1E-20 && !(chargeCase == NoSpacecharge))
4348 {
4349 std::cout << std::format("GetStepDistortion produced a very small deltaX: {:E}", deltaX) << std::endl;
4350 std::cout << std::format("GetStepDistortion: (c0,c1,c2)=({:E},{:E},{:E})", c0, c1, c2) << std::endl;
4351 std::cout << std::format("GetStepDistortion: EintOverEz==({:E},{:E},{:E})", EintOverEz.X(), EintOverEz.Y(), EintOverEz.Z()) << std::endl;
4352 std::cout << std::format("GetStepDistortion: BintOverBz==({:E},{:E},{:E})", BintOverBz.X(), BintOverBz.Y(), BintOverBz.Z()) << std::endl;
4353 std::cout << std::format("GetStepDistortion: ({:.4f},{:.4f},{:.4f}) to z={:.4f}", start.X(), start.Y(), start.Z(), zdest) << std::endl;
4354 std::cout << std::format("GetStepDistortion: fieldInt=({:E},{:E},{:E})", fieldInt.X(), fieldInt.Y(), fieldInt.Z()) << std::endl;
4355 std::cout << std::format("GetStepDistortion: delta=({:E},{:E},{:E})", deltaX, deltaY, deltaZ) << std::endl;
4356
4357
4358 }
4359
4360
4361 if (false)
4362 {
4363 std::cout << std::format("GetStepDistortion: (c0,c1,c2)=({:E},{:E},{:E})", c0, c1, c2) << std::endl;
4364 std::cout << std::format("GetStepDistortion: EintOverEz==({:E},{:E},{:E})", EintOverEz.X(), EintOverEz.Y(), EintOverEz.Z()) << std::endl;
4365 std::cout << std::format("GetStepDistortion: BintOverBz==({:E},{:E},{:E})", BintOverBz.X(), BintOverBz.Y(), BintOverBz.Z()) << std::endl;
4366 std::cout << std::format("GetStepDistortion: ({:.4f},{:.4f},{:.4f}) (rp)=({:.4f},{:.4f}) to z={:.4f}",
4367 start.X(), start.Y(), start.Z(), start.Perp(), start.Phi(), zdest)
4368 << std::endl;
4369 std::cout << std::format("GetStepDistortion: fieldInt=({:E},{:E},{:E})", fieldInt.X(), fieldInt.Y(), fieldInt.Z()) << std::endl;
4370 std::cout << std::format("GetStepDistortion: delta=({:E},{:E},{:E})", deltaX, deltaY, deltaZ) << std::endl;
4371 exit(1);
4372 }
4373
4374
4375
4376 TVector3 shift(deltaX, deltaY, deltaZ);
4377 if (debug_distortionScale.Mag() > 0)
4378 {
4379 shift.RotateZ(-start.Phi());
4380
4381
4382 shift.SetXYZ(shift.X() * debug_distortionScale.X(), shift.Y() * debug_distortionScale.Y(), shift.Z() * debug_distortionScale.Z());
4383 shift.RotateZ(start.Phi());
4384 }
4385
4386 return shift;
4387 }
4388
4389
4390 std::string AnnularFieldSim::GetLookupString()
4391 {
4392 if (lookupCase == LookupCase::Full3D)
4393 {
4394 return std::format("Full3D ({} x {} x {}) with ({} x {} x {}) roi", nr, nphi, nz, nr_roi, nphi_roi, nz_roi);
4395 }
4396
4397 if (lookupCase == LookupCase::PhiSlice)
4398 {
4399 return std::format("PhiSlice ({} x {} x {}) with ({} x 1 x {}) roi", nr, nphi, nz, nr_roi, nz_roi);
4400 }
4401
4402 return "broken";
4403 }
4404 std::string AnnularFieldSim::GetGasString()
4405 {
4406 return std::format("vdrift={:.2f}cm/us, Enom={:.2f}V/cm, Bnom={:.2f}T, omtau={:.4E}", vdrift / (cm / us), Enominal / (V / cm), Bnominal / Tesla, omegatau_nominal);
4407 }
4408
4409 std::string AnnularFieldSim::GetFieldString()
4410 {
4411 return std::format("{}, {}", Efieldname, Bfieldname);
4412 }
4413
4414
4415 TVector3 AnnularFieldSim::GetFieldAt(const TVector3 &pos)
4416 {
4417
4418
4419 int r;
4420 int p;
4421 int z;
4422
4423 if (GetRindexAndCheckBounds(pos.Perp(), &r) == BoundsCase::OutOfBounds)
4424 {
4425 return zero_vector;
4426 }
4427 if (GetPhiIndexAndCheckBounds(FilterPhiPos(pos.Phi()), &p) == BoundsCase::OutOfBounds)
4428 {
4429 return zero_vector;
4430 }
4431 if (GetZindexAndCheckBounds(pos.Z(), &z) == BoundsCase::OutOfBounds)
4432 {
4433 if (hasTwin)
4434 {
4435 return twin->GetFieldAt(pos);
4436 }
4437 return zero_vector;
4438 }
4439 return Efield->Get(r, p, z);
4440 }
4441
4442
4443 TVector3 AnnularFieldSim::GetBFieldAt(const TVector3 &pos)
4444 {
4445
4446
4447 int r;
4448 int p;
4449 int z;
4450
4451 if (GetRindexAndCheckBounds(pos.Perp(), &r) == BoundsCase::OutOfBounds)
4452 {
4453 return zero_vector;
4454 }
4455 if (GetPhiIndexAndCheckBounds(FilterPhiPos(pos.Phi()), &p) == BoundsCase::OutOfBounds)
4456 {
4457 return zero_vector;
4458 }
4459 if (GetZindexAndCheckBounds(pos.Z(), &z) == BoundsCase::OutOfBounds)
4460 {
4461 if (hasTwin)
4462 {
4463 return twin->GetBFieldAt(pos);
4464 }
4465 return zero_vector;
4466 }
4467 return Bfield->Get(r, p, z);
4468 }
4469
4470
4471 float AnnularFieldSim::GetChargeAt(const TVector3 &pos)
4472 {
4473 int z;
4474 BoundsCase zbound = GetZindexAndCheckBounds(pos.Z(), &z);
4475 if (zbound == OutOfBounds)
4476 {
4477 if (hasTwin)
4478 {
4479 return twin->GetChargeAt(pos);
4480 }
4481 std::cout << std::format("Caution: tried to read charge at zbin={}! No twin available to handle this", z) << std::endl;
4482 return -999;
4483 }
4484
4485 return q->GetChargeAtPosition(pos.Perp(), FilterPhiPos(pos.Phi()), pos.Z());
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499 }