ATTPCROOT  0.3.0-alpha
A ROOT-based framework for analyzing data from active target detectors
AtMCFission.cxx
Go to the documentation of this file.
1 #include "AtMCFission.h"
2 // IWYU pragma: no_include <ext/alloc_traits.h>
3 #include "AtContainerManip.h" // for GetPointerVector
4 #include "AtE12014.h"
5 #include "AtEvent.h"
6 #include "AtFissionEvent.h"
7 #include "AtHit.h" // for AtHit, AtHit::XYZPoint, AtHit...
8 #include "AtKinematics.h"
9 #include "AtLineChargeModel.h"
10 #include "AtMap.h"
11 #include "AtParameterDistribution.h" // for AtParameterDistribution, MCFi...
12 #include "AtPattern.h" // for AtPattern, AtPatterns
13 #include "AtPatternEvent.h"
14 #include "AtPatternY.h" // for AtPatternY, AtPatternY::XYZVector
15 #include "AtPulse.h"
16 #include "AtRadialChargeModel.h"
17 #include "AtSimpleSimulation.h"
18 #include "AtStudentDistribution.h"
19 #include "AtUniformDistribution.h"
20 #include "AtVectorUtil.h"
21 
22 #include <FairLogger.h> // for Logger, LOG
23 
24 #include <Math/AxisAngle.h> // for AxisAngle
25 #include <Math/Functor.h>
26 #include <Math/Vector4D.h>
27 #include <Math/VectorUtil.h> // for Angle
28 #include <TClonesArray.h> // for TClonesArray
29 #include <TMath.h> // for RadToDeg, C, DegToRad, Pi
30 
31 #include <Fit/FitConfig.h> // for FitConfig
32 #include <Fit/FitResult.h> // for FitResult
33 #include <Fit/Fitter.h>
34 #include <algorithm> // for find_if
35 #include <cassert> // for assert
36 #include <cmath> // for sqrt, exp, sin, round
37 #include <limits> // for numeric_limits
38 #include <map> // for map, map<>::mapped_type
39 #include <memory> // for make_shared, __shared_ptr_access
40 #include <string> // for string
41 #include <vector> // for vector, allocator
42 
43 using namespace MCFitter;
44 using namespace AtPatterns;
45 using Polar3D = ROOT::Math::Polar3DVector;
49 
50 void AtMCFission::SetAmp(float amp)
51 {
52  fAmp = amp;
53  fFitAmp = false;
54 }
55 
57 
58 {
59  fParameters["th0"] = std::make_shared<AtUniformDistribution>(0, 0);
60  fParameters["ph0"] = std::make_shared<AtUniformDistribution>(0, 0);
61  fParameters["th1"] = std::make_shared<AtUniformDistribution>(0, 0);
62  fParameters["ph1"] = std::make_shared<AtUniformDistribution>(0, 0);
63  fParameters["vX"] = std::make_shared<AtUniformDistribution>(0, 0);
64  fParameters["vY"] = std::make_shared<AtUniformDistribution>(0, 0);
65  fParameters["vZ"] = std::make_shared<AtUniformDistribution>(0, 0);
66  fParameters["Z"] = std::make_shared<AtStudentDistribution>(0, 0);
67 
68  // These are the angle to sample w.r.t. the nominal beam axis
69  fParameters["thBeam"] = std::make_shared<AtStudentDistribution>(0, 1 * TMath::DegToRad());
70  fParameters["lambda"] = std::make_shared<AtUniformDistribution>(0, 0);
71 }
72 
74 {
75 
76  const auto &fissionEvent = dynamic_cast<const AtFissionEvent &>(event);
77 
78  auto pat = fissionEvent.GetYPattern();
79  std::vector<XYZVector> start, dirs;
80 
81  //*** Fragment 1 ****/
82  auto dir = pat->GetFragmentDirection(0).Unit();
83  if (dir.Z() < 0)
84  dir.SetZ(-dir.Z());
85  fParameters["th0"]->SetMean(dir.Theta());
86  fParameters["ph0"]->SetMean(dir.Phi());
87  LOG(debug) << "Parameters: " << dir.Theta() * TMath::RadToDeg() << " " << dir.Phi() * TMath::RadToDeg();
88 
89  /*** Fragment 2 ****/
90  dir = dir = pat->GetFragmentDirection(1).Unit();
91  if (dir.Z() < 0)
92  dir.SetZ(-dir.Z());
93  fParameters["th1"]->SetMean(dir.Theta());
94  fParameters["ph1"]->SetMean(dir.Phi());
95  LOG(debug) << "Parameters: " << dir.Theta() * TMath::RadToDeg() << " " << dir.Phi() * TMath::RadToDeg();
96 
97  auto vertex = pat->GetVertex();
98 
99  fParameters["vX"]->SetMean(vertex.X());
100  fParameters["vY"]->SetMean(vertex.Y());
101  fParameters["vZ"]->SetMean(1000 - vertex.Z());
102 
103  fParameters["Z"]->SetMean(fCN.Z / 2);
104  fParameters["Z"]->SetSpread(3);
105  fParameters["lambda"]->SetMean(fissionEvent.GetLambda());
106 }
107 
108 double AtMCFission::ObjectiveFunction(const AtBaseEvent &expEvent, int SimEventID, AtMCResult &definition)
109 {
110  // Make sure we were passed the right event type
111  auto expFission = dynamic_cast<const AtFissionEvent &>(expEvent);
112  auto charge = ObjectiveCharge(expFission, SimEventID, definition);
113  // auto charge = ObjectiveChargePads(expFission, SimEventID, definition);
114  auto pos = ObjectivePositionPads(expFission, SimEventID);
115  LOG(info) << "Chi2 Pos: " << pos << " Chi2 Q: " << charge;
116  definition.fParameters["ObjQ"] = charge;
117  definition.fParameters["ObjPos"] = pos;
118 
119  return charge;
120 }
121 
129 double AtMCFission::ObjectiveChargePads(const AtFissionEvent &expEvent, int SimEventID, AtMCResult &def)
130 {
131  AtEvent &simEvent = fEventArray.at(SimEventID);
132  auto fragHits1 = expEvent.GetFragHits(0);
133  auto fragHits2 = expEvent.GetFragHits(1);
134 
135  // Get the max charge in the experimental event
136  auto comp = [](const AtHit *a, const AtHit *b) { return a->GetCharge() < b->GetCharge(); };
137 
138  auto maxQ1 = (*std::max_element(fragHits1.begin(), fragHits1.end(), comp))->GetCharge();
139  auto maxQ2 = (*std::max_element(fragHits2.begin(), fragHits2.end(), comp))->GetCharge();
140 
141  // Loop through every hit in the experimental event associated with the FF, and create an array
142  // of the corresponding hits in the simulated event
143  std::vector<std::vector<double>> expCharge, simCharge;
144  expCharge.resize(2);
145  simCharge.resize(2);
146  E12014::FillHits(expCharge[0], simCharge[0], fragHits1, ContainerManip::GetPointerVector(simEvent.GetHits()),
148  E12014::FillHits(expCharge[1], simCharge[1], fragHits2, ContainerManip::GetPointerVector(simEvent.GetHits()),
150 
151  // So now that we have our two vectors to compare, run the minimization
152  assert(expCharge[0].size() == simCharge[0].size() && expCharge[1].size() == simCharge[1].size());
153 
154  auto func = [&expCharge, &simCharge, maxQ1, maxQ2](const double *par) {
155  double chi2_1 = 0;
156  for (int i = 0; i < expCharge[0].size(); ++i) {
157  double chi = (expCharge[0][i] - par[0] * simCharge[0][i]) / (0.1 * maxQ1);
158  chi2_1 += chi * chi;
159  }
160  chi2_1 /= expCharge[0].size();
161 
162  double chi2_2 = 0;
163  for (int i = 0; i < expCharge[1].size(); ++i) {
164  double chi = (expCharge[1][i] - par[0] * simCharge[1][i]) / (0.1 * maxQ2);
165  chi2_2 += chi * chi;
166  }
167  chi2_2 /= expCharge[1].size();
168 
169  return chi2_1 + chi2_2;
170  };
171  auto functor = ROOT::Math::Functor(func, 1);
172 
173  std::vector<double> A = {fAmp};
174  ROOT::Fit::Fitter fitter;
175  fitter.Config().SetMinimizer("Minuit2");
176  fitter.SetFCN(functor, A.data());
177  bool ok = fitter.FitFCN();
178  if (!ok) {
179  LOG(error) << "Failed to fit the charge curves. Not adding to objective.";
180  return std::numeric_limits<double>::max();
181  }
182  auto &result = fitter.Result();
183  double amp = result.Parameter(0);
184  def.fParameters["Amp"] = amp;
185  def.fParameters["qChi2"] = result.MinFcnValue();
186  return result.MinFcnValue();
187 }
188 
189 double AtMCFission::ObjectivePositionPads(const AtFissionEvent &expEvent, int SimEventID)
190 {
191  AtEvent &simEvent = fEventArray.at(SimEventID);
192  auto fragHits = expEvent.GetFragHits();
193 
194  std::vector<double> exp, sim;
196 
197  assert(exp.size() == sim.size());
198  double chi2 = 0;
199  for (int i = 0; i < exp.size(); ++i) {
200 
201  double chi = (exp[i] - sim[i]) / (1 * 0.320 * .815 * 10); // 1 TB = 320 ns = .32*.815*10 mm
202  chi2 += chi * chi;
203  }
204  return chi2 / exp.size();
205 }
206 
207 double AtMCFission::ObjectiveCharge(const AtFissionEvent &expEvent, int SimEventID, AtMCResult &def)
208 {
209  AtEvent &simEvent = fEventArray.at(SimEventID);
210  auto fragHits = expEvent.GetFragHits();
211 
212  // Get the charge curves for this event
213  std::array<std::vector<double>, 2> exp;
214  std::array<std::vector<double>, 2> sim;
215  for (int i = 0; i < 2; ++i) {
216  E12014::FillHitSums(exp[i], sim[i], expEvent.GetFragHits(i), ContainerManip::GetPointerVector(simEvent.GetHits()),
218  }
219 
220  return ObjectiveCharge(exp, sim, def);
221 }
222 
223 double
224 AtMCFission::ObjectiveChargeChi2(const std::vector<double> &exp, const std::vector<double> &sim, const double *par)
225 {
226  if (exp.size() == 0 || exp.size() != sim.size())
227  return std::numeric_limits<double>::max();
228 
229  double chi2 = 0;
230  for (int i = 0; i < exp.size(); ++i)
231  chi2 += (exp.at(i) - par[0] * sim.at(i)) * (exp.at(i) - par[0] * sim.at(i)) / exp.at(i);
232 
233  chi2 /= exp.size();
234  LOG(debug) << "A: " << par[0] << " Chi2: " << chi2;
235  return chi2;
236 }
237 
238 double
239 AtMCFission::ObjectiveChargeChi2Norm(const std::vector<double> &exp, const std::vector<double> &sim, const double *par)
240 {
241  if (exp.size() == 0 || exp.size() != sim.size())
242  return std::numeric_limits<double>::max();
243 
244  double chi2 = 0;
245  for (int i = 0; i < exp.size(); ++i) {
246  double chi = (exp.at(i) - par[0] * sim.at(i)) / (0.1 * exp.at(i));
247  chi2 += chi * chi;
248  }
249 
250  chi2 /= exp.size();
251  LOG(debug) << "A: " << par[0] << " Chi2: " << chi2;
252  return chi2;
253 }
254 
255 double
256 AtMCFission::ObjectiveChargeDiff2(const std::vector<double> &exp, const std::vector<double> &sim, const double *par)
257 {
258  if (exp.size() == 0)
259  return std::numeric_limits<double>::max();
260 
261  double chi2 = 0;
262  double Qtot = 0;
263  for (int i = 0; i < exp.size(); ++i) {
264  chi2 += (exp[i] - par[0] * sim[i]) * (exp[i] - par[0] * sim[i]);
265  Qtot += exp[i];
266  }
267 
268  chi2 /= Qtot;
269  LOG(debug) << "A: " << par[0] << " Chi2: " << chi2;
270  return chi2;
271 }
272 
273 double AtMCFission::ObjectiveCharge(const std::array<std::vector<double>, 2> &expFull,
274  const std::array<std::vector<double>, 2> &simFull, AtMCResult &definition)
275 {
276  // Start by trimming the two FFs down by removing the zeros in the charge and
277  // combining into a single array. Don't examine things within 6 TB of the pad plane
278  std::vector<double> exp;
279  std::vector<double> sim;
280  for (int i = 0; i < 2; ++i) {
281  for (int tb = 80; tb < 512; ++tb) {
282  LOG(debug) << i << "," << tb << " " << expFull[i][tb] << " " << simFull[i][tb];
283  if (expFull[i][tb] != 0) {
284  exp.push_back(expFull[i][tb]);
285  sim.push_back(simFull[i][tb]);
286  }
287  }
288  }
289 
290  if (fFitAmp && !(exp.size() == 0 || exp.size() != sim.size())) {
291  LOG(info) << exp.size() << " " << sim.size();
292  auto functor = ROOT::Math::Functor(std::bind(fObjCharge, exp, sim, std::placeholders::_1), 1); // NOLINT
293 
294  std::vector<double> A = {fAmp};
295  ROOT::Fit::Fitter fitter;
296  fitter.Config().SetMinimizer("Minuit2");
297  fitter.SetFCN(functor, A.data());
298  bool ok = fitter.FitFCN();
299  if (!ok) {
300  LOG(error) << "Failed to fit the charge curves. Not adding to objective.";
301  return std::numeric_limits<double>::max();
302  }
303  auto &result = fitter.Result();
304  double amp = result.Parameter(0);
305  definition.fParameters["Amp"] = amp;
306  definition.fParameters["qChi2"] = result.MinFcnValue();
307  return result.MinFcnValue();
308  } else {
309 
310  definition.fParameters["Amp"] = fAmp;
311  std::vector<double> A = {fAmp};
312  auto chi2 = fObjCharge(exp, sim, A.data());
313  definition.fParameters["qChi2"] = chi2;
314  return chi2;
315  }
316 }
317 
318 double AtMCFission::ObjectivePosition(const AtFissionEvent &expEvent, int SimEventID)
319 {
320  AtEvent &simEvent = fEventArray.at(SimEventID);
321 
322  // Sort the hits by pad number
323  simEvent.SortHitArray();
324  auto fragHits = expEvent.GetFragHits();
325  LOG(debug) << fragHits.size() << " " << expEvent.GetFragHits(0).size() << " " << expEvent.GetFragHits(1).size();
326 
327  std::sort(fragHits.begin(), fragHits.end(),
328  [](const AtHit *a, const AtHit *b) { return a->GetPadNum() < b->GetPadNum(); });
329 
330  auto simHit = simEvent.GetHits().begin();
331 
332  auto map = fPulse->GetMap();
333  double chi2 = 0;
334  int nPads = 0;
335 
336  // Get every pad that was in the experimental event (assumes that each pad was only used once)
337  for (auto expHit : fragHits) {
338  auto padNum = expHit->GetPadNum();
339 
340  // Only keep hits where the pad is not inhibited in some way
341  if (fMap->IsInhibited(padNum) != AtMap::InhibitType::kNone)
342  continue;
343 
344  // Look for pad in simulated event
345  auto hitIt = std::find_if(simHit, simEvent.GetHits().end(),
346  [padNum](const AtEvent::HitPtr &hit2) { return hit2->GetPadNum() == padNum; });
347  if (hitIt == simEvent.GetHits().end() || expHit->GetPositionSigma().Z() == 0) {
348  chi2++; // This is the integral of the normalized gaussian that we have nothing to compare to
349  nPads++;
350  LOG(debug) << "Did not find pad " << padNum << " in the simulated event or it's in the beam region";
351  continue;
352  }
353 
354  // Update the chi2 with this hit
355 
356  simHit = hitIt;
357  double simZ = simHit->get()->GetPosition().Z();
358  double simSig = simHit->get()->GetPositionSigma().Z();
359  double expZ = expHit->GetPosition().Z();
360  double expSig = expHit->GetPositionSigma().Z();
361 
362  double diff = ObjectivePosition(expZ, expSig, simZ, simSig);
363  LOG(debug) << "Found " << padNum << " in the simulated event. Simulated hit is " << simZ << " +- " << simSig
364  << " Experimental hit is " << expZ << " +- " << expSig << " Chi2 between pads is " << diff;
365  chi2 += diff;
366  nPads++;
367  }
368  return chi2 / nPads;
369 }
376 double AtMCFission::ObjectivePosition2(double uE, double sE, double uO, double sO)
377 {
378  double num = sE * sE * std::exp(((uE - uO) * (uE - uO)) / (2 * sE * sE - sO * sO));
379  double denom = sO * std::sqrt(2 * sE * sE - sO * sO);
380  return num / denom - 1;
381 }
382 
389 double AtMCFission::ObjectivePosition4(double uE, double sE, double uO, double sO)
390 {
391  double num = 2 * sqrt(2) * sE * sO * std::exp((-(uE - uO) * (uE - uO)) / (2 * (sE * sE + sO * sO)));
392  double denom = std::sqrt(sE * sE + sO * sO);
393  return sE + sO - num / denom;
394 }
395 
402 double AtMCFission::ObjectivePosition(double uE, double sE, double uO, double sO)
403 {
404  double num = 2 * sqrt(2) * std::exp((-(uE - uO) * (uE - uO)) / (2 * (sE * sE + sO * sO)));
405  double denom = std::sqrt(sE * sE + sO * sO);
406  return 1 / sE + 1 / sO - num / denom;
407 }
408 
415 double AtMCFission::ObjectivePosition3(double uE, double sE, double uO, double sO)
416 {
417  double num = sE * sO * std::exp(((uE - uO) * (uE - uO)) / (2 * sE * sE - sO * sO));
418  double denom = std::sqrt(2 * sE * sE - sO * sO);
419  return sE - 2 * sO + num / denom;
420 }
421 
423 {
424 
425  return {res.fParameters["vX"], res.fParameters["vY"], res.fParameters["vZ"]};
426 }
427 
428 std::array<Ion, 2> AtMCFission::GetFragmentSpecies(AtMCResult &res, const Ion &CN)
429 {
430  int Z1 = res.fParameters["Z0"];
431  int A1 = res.fParameters["A0"];
432 
433  return {Ion{Z1, A1}, Ion{CN.Z - Z1, CN.A - A1}};
434 }
439 XYZVector AtMCFission::GetBeamDirSameV(AtMCResult &res, const std::array<XYZVector, 2> &ffDir)
440 {
441  auto beamInLabFrame = (ffDir[0].Unit() + ffDir[1].Unit()) / 2;
442  res.fParameters["beamX"] = beamInLabFrame.Unit().X();
443  res.fParameters["beamY"] = beamInLabFrame.Unit().Y();
444  res.fParameters["beamZ"] = beamInLabFrame.Unit().Z();
445  res.fParameters["beamSel"] = 1;
446  return beamInLabFrame;
447 }
448 
454 XYZVector AtMCFission::GetBeamDir(AtMCResult &res, const std::array<XYZVector, 2> &ffDir, double pTrans)
455 {
456  using namespace AtTools::Kinematics;
457 
458  // Get the transverse velocity of the fission fragments
459  double v1 = GetBeta(pTrans, AtoE(res.fParameters["A0"]));
460  double v2 = GetBeta(pTrans, AtoE(res.fParameters["A1"]));
461 
462  // Get the folding angle between the fision fragments
463  auto foldingAngle = ROOT::Math::VectorUtil::Angle(ffDir[0], ffDir[1]);
464 
465  // The velocity of the beam is related to the folding angle and the transverse velocities
466  // Here we assume that we are at non-relativistic velocities, or the decay angle is closs to
467  // 90 degrees (basically (beta of the beam * beta of FF in z direction in CoM frame) is zero)
468  auto tanA = std::tan(foldingAngle);
469  auto s = std::sqrt(v1 * v1 + 4 * v1 * v2 * tanA * tanA + 2 * v1 * v2 + v2 * v2);
470  auto vBeam = (v1 + v2 + s) / (2 * tanA);
471 
472  // The angle between FF1 and the beam is given by
473  auto angle1 = std::atan(v1 / vBeam);
474  auto angle2 = std::atan(v2 / vBeam);
475  LOG(debug) << "Angle 1: " << angle1 * TMath::RadToDeg() << " Angle 2: " << angle2 * TMath::RadToDeg();
476  LOG(debug) << "Folding angle: " << foldingAngle * TMath::RadToDeg() << " Beam v: " << vBeam;
477 
478  // The beam direction should be the FF direction vector rotated around the normal vector to the plane
479  // containing the fission fragments by the angle between the FF direction and the beam.
480 
481  auto norm = ffDir[0].Cross(ffDir[1]).Unit();
482  auto rot1 = ROOT::Math::AxisAngle(norm, angle1);
483  auto beamDir = rot1(ffDir[0]);
484  res.fParameters["beamX"] = beamDir.Unit().X();
485  res.fParameters["beamY"] = beamDir.Unit().Y();
486  res.fParameters["beamZ"] = beamDir.Unit().Z();
487  res.fParameters["beamSel"] = 0;
488 
489  LOG(debug) << "Beam dir: " << beamDir;
490 
491  LOG(debug) << "Angle 1: " << ROOT::Math::VectorUtil::Angle(ffDir[0], beamDir) * TMath::RadToDeg()
492  << " Angle 2: " << ROOT::Math::VectorUtil::Angle(ffDir[1], beamDir) * TMath::RadToDeg();
493  return beamDir;
494 }
495 
496 XYZVector AtMCFission::GetBeamDirSample(AtMCResult &res, const std::array<XYZVector, 2> &ffDir)
497 {
498  // Sample the deviation of the beam away from the nominal beam direction
499  Polar3D beamDir(1, res.fParameters["thBeam"], 0);
500  LOG(info) << "Sampled beam direction (deviation from nominal)"
501  << ROOT::Math::VectorUtil::Angle(XYZVector(0, 0, 1), beamDir) * TMath::RadToDeg() << " "
502  << beamDir.Theta() * TMath::RadToDeg();
503 
504  // Get our nominal beam direction projected into the plane of the reaction
505  auto norm = ffDir[0].Cross(ffDir[1]).Unit();
506  auto projBeam = fNominalBeamDir - fNominalBeamDir.Dot(norm) * norm;
507 
508  // Rotate the beam vector so our deviation is from the nominal beam axis
509  auto rot = AtTools::GetRotationToZ(projBeam);
510  auto beamInLabFrame = XYZVector(rot.Inverse()(beamDir));
511  // auto beamInLabFrame = projBeam;
512  LOG(info) << " Sampled beam deviates from nominal by "
513  << ROOT::Math::VectorUtil::Angle(beamInLabFrame, projBeam) * TMath::RadToDeg() << " deg.";
514 
515  // Set the beam direction in the result file as well
516  res.fParameters["beamX"] = beamInLabFrame.Unit().X();
517  res.fParameters["beamY"] = beamInLabFrame.Unit().Y();
518  res.fParameters["beamZ"] = beamInLabFrame.Unit().Z();
519  res.fParameters["beamSel"] = 2;
520  return beamInLabFrame.Unit();
521 }
522 
526 std::array<XYZVector, 2> AtMCFission::GetMomDirLab(AtMCResult &res)
527 {
528  XYZVector mom1(Polar3D(1, res.fParameters["th0"], res.fParameters["ph0"]).Unit());
529  XYZVector mom2(Polar3D(1, res.fParameters["th1"], res.fParameters["ph1"]).Unit());
530  return {mom1, mom2};
531 }
532 
534 {
535  // This will sample all the defined patameter distributions and save the results in
536  // the returned AtMCResult
538 
539  // Translate the sampled Z into the particle ID of the FF.
540  int Z1 = std::round(result.fParameters["Z"]);
541  while (Z1 > fZmax || Z1 < fZmin) {
542  result.fParameters["Z"] = fParameters["Z"]->Sample();
543  Z1 = std::round(result.fParameters["Z"]);
544  }
545  int A1 = std::round(fCN.A * (double)Z1 / fCN.Z);
546  result.fParameters["Z0"] = Z1;
547  result.fParameters["A0"] = A1;
548  result.fParameters["Z1"] = fCN.Z - Z1;
549  result.fParameters["A1"] = fCN.A - A1;
550 
551  result.fParameters["thCM"] = 90 * TMath::DegToRad(); // Decay angle in CoM frame
552  return result;
553 }
554 
562 void AtMCFission::SetMomMagnitude(std::array<XYZVector, 2> &moms, double pTrans)
563 {
564  for (auto &mom : moms) {
565  auto angle = mom.Theta();
566  LOG(debug) << "Angle: " << mom.Theta() * TMath::RadToDeg();
567  auto p = pTrans / std::sin(angle);
568  mom = mom.Unit() * p;
569  }
570 }
571 
573 {
574  using namespace AtTools::Kinematics;
575  fSim->NewEvent();
576 
577  // Set the magnitude of the space charge for this event.
578  // TODO: Make this thread safe (deep copy simplesim, and once per thread)
579  auto radialModel = dynamic_cast<AtRadialChargeModel *>(fSim->GetSpaceChargeModel().get());
580  auto lineModel = dynamic_cast<AtLineChargeModel *>(fSim->GetSpaceChargeModel().get());
581  if (radialModel) {
582  radialModel->SetDistortionField(AtLineChargeZDep(def.fParameters["lambda"]));
583  LOG(debug) << "Setting Lambda: " << def.fParameters["lambda"];
584 
585  } else if (lineModel) {
586  lineModel->SetLambda(def.fParameters["lambda"]);
587  LOG(debug) << "Setting Lambda: " << def.fParameters["lambda"];
588 
589  } else
590  LOG(debug) << "No space charge to apply.";
591 
592  auto vertex = GetVertex(def);
593  LOG(debug) << "Setting vertex: " << vertex;
594 
595  auto fragID = GetFragmentSpecies(def, fCN);
596  LOG(debug) << "Setting fragment 1: " << fragID[0].Z << " " << fragID[0].A;
597  LOG(debug) << "Setting fragment 2: " << fragID[1].Z << " " << fragID[1].A;
598 
599  // We have the masses, now calculate the momentum of these two fragments in the CoM frame. We will
600  // fudge and say the mass of a fragment is A amu (neglecting binding energy).
601  double KE = violaEn(fCN.A, fCN.Z);
602  double gamma1 = GetGamma(KE, AtoE(fragID[0].A), AtoE(fragID[1].A));
603  double p1 = GetRelMom(gamma1, AtoE(fragID[0].A));
604  p1 *= sin(def.fParameters["thCM"]);
605 
606  LOG(debug) << "v1: " << GetBeta(gamma1) << " v2: " << GetBeta(GetGamma(KE, AtoE(fragID[1].A), AtoE(fragID[0].A)));
607 
608  // Get the momentum direction for the FF and beam in the lab frame
609  auto mom = GetMomDirLab(def); // Pulled from the data
610  // auto beamDir = GetBeamDirSameV(def, mom);
611  auto beamDir = GetBeamDir(def, mom, p1);
612 
613  LOG(debug) << "p1: " << mom[0];
614  LOG(debug) << "p2: " << mom[1];
615  LOG(debug) << "b: " << beamDir;
616  LOG(debug) << "p1 * b: " << mom[0].Dot(beamDir);
617  LOG(debug) << "p2 * b: " << mom[1].Dot(beamDir);
618  LOG(debug) << "Transforming to Lab'";
619 
620  // Transform the momentum into the Lab' frame (beamDir points along z)
621  auto toLabPrime = AtTools::GetRotationToZ(beamDir);
622  beamDir = toLabPrime(beamDir);
623  for (int i = 0; i < 2; ++i)
624  mom[i] = toLabPrime(mom[i]);
625 
626  LOG(debug) << "p1: " << mom[0];
627  LOG(debug) << "p2: " << mom[1];
628  LOG(debug) << "b: " << beamDir;
629  LOG(debug) << "p1 * b: " << mom[0].Dot(beamDir);
630  LOG(debug) << "p2 * b: " << mom[1].Dot(beamDir);
631 
632  // Set the momentum of the fission fragments in the lab' frame
633  SetMomMagnitude(mom, p1);
634  std::vector<XYZEVector> ffMom(2);
635  ffMom[0] = Get4Vector(mom[0], AtoE(fragID[0].A));
636  ffMom[1] = Get4Vector(mom[1], AtoE(fragID[1].A));
637 
638  auto pBeam = ffMom[0] + ffMom[1];
639 
640  LOG(debug) << "Frag A: " << ffMom[0] << " mass " << EtoA(ffMom[0].M()) << " energy: " << ffMom[0].E() - ffMom[0].M()
641  << " beta: " << ffMom[0].Beta();
642  LOG(debug) << "Frag B: " << ffMom[1] << " mass " << EtoA(ffMom[1].M()) << " energy: " << ffMom[1].E() - ffMom[1].M()
643  << " beta: " << ffMom[1].Beta();
644  LOG(debug) << "Beam: " << pBeam << " mass " << EtoA(pBeam.M()) << " energy: " << pBeam.E() - pBeam.M();
645 
646  LOG(debug) << "Transforming back to lab frame";
647 
648  // Transform the momentum of the FF back into the lab frame.
649  for (int i = 0; i < 2; ++i)
650  ffMom[i] = toLabPrime.Inverse()(ffMom[i]);
651 
652  // Use conservation of 4 momentum to get the beam momentum
653  pBeam = ffMom[0] + ffMom[1];
654 
655  LOG(debug) << "Frag A: " << ffMom[0] << " mass " << EtoA(ffMom[0].M()) << " energy: " << ffMom[0].E() - ffMom[0].M()
656  << " beta: " << ffMom[0].Beta();
657  LOG(debug) << "Frag B: " << ffMom[1] << " mass " << EtoA(ffMom[1].M()) << " energy: " << ffMom[1].E() - ffMom[1].M()
658  << " beta: " << ffMom[1].Beta();
659  LOG(debug) << "Beam: " << pBeam << " mass " << EtoA(pBeam.M()) << " energy: " << pBeam.E() - pBeam.M();
660  def.fParameters["EBeam"] = pBeam.E() - pBeam.M();
661 
662  for (int i = 0; i < 2; ++i)
663  fSim->SimulateParticle(fragID[i].Z, fragID[i].A, vertex, ffMom[i]);
664 
665  return fSim->GetPointsArray();
666 }
MCFitter::AtMCFission::ObjectivePosition
double ObjectivePosition(const AtFissionEvent &expEvent, int SimEventID)
Definition: AtMCFission.cxx:318
AtTools::Kinematics::EtoA
double EtoA(double mass)
Definition: AtKinematics.cxx:304
MCFitter::AtMCFission::GetBeamDirSample
XYZVector GetBeamDirSample(AtMCResult &, const std::array< XYZVector, 2 > &ffDir)
Definition: AtMCFission.cxx:496
AtEvent::SortHitArray
void SortHitArray()
Definition: AtEvent.cxx:52
MCFitter::Ion
Definition: AtMCFission.h:21
AtParameterDistribution.h
AtTools::Kinematics::AtoE
double AtoE(double Amu)
Definition: AtKinematics.cxx:300
MCFitter::AtMCFission::CreateParamDistros
virtual void CreateParamDistros() override
Create the parameter distributions to use for the fit.
Definition: AtMCFission.cxx:56
AtMCFission.h
AtPatternEvent
Definition: AtPatternEvent.h:19
AtTools::Kinematics
Definition: AtKinematics.cxx:243
AtEvent.h
MCFitter::AtMCFission::ObjectiveChargePads
double ObjectiveChargePads(const AtFissionEvent &expEvent, int SimEventID, AtMCResult &def)
Definition: AtMCFission.cxx:129
MCFitter::AtMCFission::SetAmp
void SetAmp(float amp)
Definition: AtMCFission.cxx:50
MCFitter::AtMCFission::SimulateEvent
virtual TClonesArray SimulateEvent(AtMCResult &definition) override
Definition: AtMCFission.cxx:572
AtFissionEvent.h
AtSimpleSimulation.h
AtTools::Kinematics::GetGamma
double GetGamma(double KE, double m1, double m2)
Definition: AtKinematics.cxx:249
XYZVector
ROOT::Math::XYZVector XYZVector
Definition: AtFindVertex.h:20
MCFitter::AtMCFission::SetMomMagnitude
void SetMomMagnitude(std::array< XYZVector, 2 > &mom, double pTrans)
Definition: AtMCFission.cxx:562
AtRadialChargeModel
Space charge model from arbitrary radial E-field,.
Definition: AtRadialChargeModel.h:18
XYZVector
ROOT::Math::XYZVector XYZVector
Definition: AtMCFission.cxx:47
AtPattern.h
AtTools::Kinematics::GetRelMom
double GetRelMom(double gamma, double mass)
Definition: AtKinematics.cxx:292
AtStudentDistribution.h
MCFitter::AtMCFission::ObjectiveChargeChi2
static double ObjectiveChargeChi2(const std::vector< double > &exp, const std::vector< double > &sim, const double *par)
Definition: AtMCFission.cxx:224
AtEvent
Definition: AtEvent.h:22
MCFitter::AtMCFission::ObjectivePosition3
double ObjectivePosition3(double uE, double sE, double uO, double sO)
Definition: AtMCFission.cxx:415
MCFitter::Ion::A
int A
Definition: AtMCFission.h:23
AtVectorUtil.h
MCFitter::AtMCResult::fParameters
ParamMap fParameters
Definition: AtMCResult.h:23
MCFitter::AtMCFission::ObjectiveChargeDiff2
static double ObjectiveChargeDiff2(const std::vector< double > &exp, const std::vector< double > &sim, const double *par)
Definition: AtMCFission.cxx:256
AtE12014.h
AtKinematics.h
E12014::FillZPos
static void FillZPos(std::vector< double > &exp, std::vector< double > &sim, const std::vector< AtHit * > &expHits, const std::vector< AtHit * > &simHits, float satThresh)
Definition: AtE12014.cxx:225
XYZPoint
ROOT::Math::XYZPoint XYZPoint
Definition: AtPatternCircle2D.h:18
MCFitter::AtMCFission::ObjectivePosition2
double ObjectivePosition2(double uE, double sE, double uO, double sO)
Definition: AtMCFission.cxx:376
AtRadialChargeModel.h
AtPatterns::AtPatternY::GetFragmentDirection
XYZVector GetFragmentDirection(int frag) const
Get the direction of the fragment rays.
Definition: AtPatternY.h:55
MCFitter::AtMCFitter::DefineEvent
virtual AtMCResult DefineEvent()
Definition: AtMCFitter.cxx:200
AtTools::GetRotationToZ
ROOT::Math::AxisAngle GetRotationToZ(const Vector &vec)
Definition: AtVectorUtil.h:20
AtFissionEvent::GetYPattern
const AtPatterns::AtPatternY * GetYPattern() const
Definition: AtFissionEvent.cxx:36
AtTools::Kinematics::Get4Vector
ROOT::Math::PxPyPzEVector Get4Vector(Vector mom, double m)
Definition: AtKinematics.h:78
PxPyPzEVector
ROOT::Math::PxPyPzEVector PxPyPzEVector
Definition: AtMCFission.cxx:48
AtHit.h
MCFitter::AtMCFission::GetVertex
XYZPoint GetVertex(AtMCResult &)
Definition: AtMCFission.cxx:422
MCFitter::AtMCResult
Definition: AtMCResult.h:18
AtEvent::GetHits
const HitVector & GetHits() const
Definition: AtEvent.h:116
AtLineChargeModel
Definition: AtLineChargeModel.h:13
AtLineChargeModel.h
AtBaseEvent
Base class for all event types in ATTPCROOT.
Definition: AtBaseEvent.h:20
AtMap::InhibitType::kNone
@ kNone
AtPatternEvent.h
AtLineChargeZDep
Definition: AtRadialChargeModel.h:53
ContainerManip::GetPointerVector
std::vector< T * > GetPointerVector(const std::vector< T > &vec)
Definition: AtContainerManip.h:105
MCFitter::AtMCFission::ObjectivePositionPads
double ObjectivePositionPads(const AtFissionEvent &expEvent, int SimEventID)
Definition: AtMCFission.cxx:189
AtUniformDistribution.h
MCFitter::AtMCFission::ObjectiveFunction
virtual double ObjectiveFunction(const AtBaseEvent &expEvent, int SimEventID, AtMCResult &definition) override
This is the thing we are minimizing between events (SimEventID is index in TClonesArray)
Definition: AtMCFission.cxx:108
AtContainerManip.h
AtPatternY.h
MCFitter::AtMCFission::XYZPoint
ROOT::Math::XYZPoint XYZPoint
Definition: AtMCFission.h:28
MCFitter::Ion::Z
int Z
Definition: AtMCFission.h:22
MCFitter::AtMCFission::ObjectiveChargeChi2Norm
static double ObjectiveChargeChi2Norm(const std::vector< double > &exp, const std::vector< double > &sim, const double *par)
Definition: AtMCFission.cxx:239
MCFitter::AtMCFission::GetFragmentSpecies
std::array< Ion, 2 > GetFragmentSpecies(AtMCResult &, const Ion &CN)
Definition: AtMCFission.cxx:428
MCFitter::AtMCFission::GetBeamDir
XYZVector GetBeamDir(AtMCResult &, const std::array< XYZVector, 2 > &ffDir, double pTrans)
Definition: AtMCFission.cxx:454
AtPatterns
Definition: AtFissionEvent.h:21
AtMap.h
XYZPoint
ROOT::Math::XYZPoint XYZPoint
Definition: AtMCFission.cxx:46
MCFitter::AtMCFission::DefineEvent
virtual AtMCResult DefineEvent() override
Definition: AtMCFission.cxx:533
E12014::FillHitSums
static int FillHitSums(std::vector< double > &exp, std::vector< double > &sim, const std::vector< AtHit * > &expHits, const std::vector< AtHit * > &simHits, int threshold=0, float saturationThreshold=std::numeric_limits< float >::max(), const AtDigiPar *par=nullptr, std::vector< double > *expADC=nullptr, AtRawEvent *expEvent=nullptr)
Definition: AtE12014.cxx:102
AtTools::Kinematics::GetBeta
double GetBeta(double gamma)
Definition: AtKinematics.cxx:267
AtFissionEvent
Definition: AtFissionEvent.h:29
Polar3D
ROOT::Math::Polar3DVector Polar3D
Definition: AtMCFission.cxx:45
MCFitter::AtMCFission::GetMomDirLab
std::array< XYZVector, 2 > GetMomDirLab(AtMCResult &)
Definition: AtMCFission.cxx:526
MCFitter::AtMCFission::XYZVector
ROOT::Math::XYZVector XYZVector
Definition: AtMCFission.h:29
AtEvent::HitPtr
std::unique_ptr< AtHit > HitPtr
Definition: AtEvent.h:25
E12014::FillHits
static void FillHits(std::vector< double > &exp, std::vector< double > &sim, const std::vector< AtHit * > &expHits, const std::vector< AtHit * > &simHits, float satThresh)
Definition: AtE12014.cxx:192
MCFitter::AtMCFission::ObjectiveCharge
double ObjectiveCharge(const AtFissionEvent &expEvent, int SimEventID, AtMCResult &def)
Definition: AtMCFission.cxx:207
E12014::fThreshold
static int fThreshold
Definition: AtE12014.h:26
MCFitter::AtMCFission::ObjectivePosition4
double ObjectivePosition4(double uE, double sE, double uO, double sO)
Definition: AtMCFission.cxx:389
E12014::fSatThreshold
static double fSatThreshold
Definition: AtE12014.h:27
AtFissionEvent::GetFragHits
std::vector< AtHit * > GetFragHits() const
Definition: AtFissionEvent.cxx:86
MCFitter::AtMCFission::SetParamDistributions
virtual void SetParamDistributions(const AtPatternEvent &event) override
Set parameter distributions (mean/spread) from the event.
Definition: AtMCFission.cxx:73
AtHit::GetCharge
Double_t GetCharge() const
Definition: AtHit.h:82
AtHit
Point in space with charge.
Definition: AtHit.h:27
MCFitter::AtMCFission::GetBeamDirSameV
XYZVector GetBeamDirSameV(AtMCResult &, const std::array< XYZVector, 2 > &ffDir)
Definition: AtMCFission.cxx:439
MCFitter
Definition: AtMCResult.cxx:5
AtPulse.h