22 #include <FairLogger.h>
24 #include <Math/AxisAngle.h>
25 #include <Math/Functor.h>
26 #include <Math/Vector4D.h>
27 #include <Math/VectorUtil.h>
28 #include <TClonesArray.h>
31 #include <Fit/FitConfig.h>
32 #include <Fit/FitResult.h>
33 #include <Fit/Fitter.h>
45 using Polar3D = ROOT::Math::Polar3DVector;
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);
69 fParameters[
"thBeam"] = std::make_shared<AtStudentDistribution>(0, 1 * TMath::DegToRad());
70 fParameters[
"lambda"] = std::make_shared<AtUniformDistribution>(0, 0);
76 const auto &fissionEvent =
dynamic_cast<const AtFissionEvent &
>(event);
79 std::vector<XYZVector> start, dirs;
85 fParameters[
"th0"]->SetMean(dir.Theta());
86 fParameters[
"ph0"]->SetMean(dir.Phi());
87 LOG(debug) <<
"Parameters: " << dir.Theta() * TMath::RadToDeg() <<
" " << dir.Phi() * TMath::RadToDeg();
90 dir = dir = pat->GetFragmentDirection(1).Unit();
93 fParameters[
"th1"]->SetMean(dir.Theta());
94 fParameters[
"ph1"]->SetMean(dir.Phi());
95 LOG(debug) <<
"Parameters: " << dir.Theta() * TMath::RadToDeg() <<
" " << dir.Phi() * TMath::RadToDeg();
97 auto vertex = pat->GetVertex();
99 fParameters[
"vX"]->SetMean(vertex.X());
100 fParameters[
"vY"]->SetMean(vertex.Y());
101 fParameters[
"vZ"]->SetMean(1000 - vertex.Z());
103 fParameters[
"Z"]->SetMean(fCN.Z / 2);
104 fParameters[
"Z"]->SetSpread(3);
105 fParameters[
"lambda"]->SetMean(fissionEvent.GetLambda());
112 auto charge = ObjectiveCharge(expFission, SimEventID, definition);
114 auto pos = ObjectivePositionPads(expFission, SimEventID);
115 LOG(info) <<
"Chi2 Pos: " << pos <<
" Chi2 Q: " << charge;
131 AtEvent &simEvent = fEventArray.at(SimEventID);
136 auto comp = [](
const AtHit *a,
const AtHit *b) {
return a->
GetCharge() < b->GetCharge(); };
138 auto maxQ1 = (*std::max_element(fragHits1.begin(), fragHits1.end(), comp))->GetCharge();
139 auto maxQ2 = (*std::max_element(fragHits2.begin(), fragHits2.end(), comp))->GetCharge();
143 std::vector<std::vector<double>> expCharge, simCharge;
152 assert(expCharge[0].size() == simCharge[0].size() && expCharge[1].size() == simCharge[1].size());
154 auto func = [&expCharge, &simCharge, maxQ1, maxQ2](
const double *par) {
156 for (
int i = 0; i < expCharge[0].size(); ++i) {
157 double chi = (expCharge[0][i] - par[0] * simCharge[0][i]) / (0.1 * maxQ1);
160 chi2_1 /= expCharge[0].size();
163 for (
int i = 0; i < expCharge[1].size(); ++i) {
164 double chi = (expCharge[1][i] - par[0] * simCharge[1][i]) / (0.1 * maxQ2);
167 chi2_2 /= expCharge[1].size();
169 return chi2_1 + chi2_2;
171 auto functor = ROOT::Math::Functor(func, 1);
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();
179 LOG(error) <<
"Failed to fit the charge curves. Not adding to objective.";
180 return std::numeric_limits<double>::max();
182 auto &result = fitter.Result();
183 double amp = result.Parameter(0);
186 return result.MinFcnValue();
191 AtEvent &simEvent = fEventArray.at(SimEventID);
194 std::vector<double> exp, sim;
197 assert(exp.size() == sim.size());
199 for (
int i = 0; i < exp.size(); ++i) {
201 double chi = (exp[i] - sim[i]) / (1 * 0.320 * .815 * 10);
204 return chi2 / exp.size();
209 AtEvent &simEvent = fEventArray.at(SimEventID);
213 std::array<std::vector<double>, 2> exp;
214 std::array<std::vector<double>, 2> sim;
215 for (
int i = 0; i < 2; ++i) {
220 return ObjectiveCharge(exp, sim, def);
226 if (exp.size() == 0 || exp.size() != sim.size())
227 return std::numeric_limits<double>::max();
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);
234 LOG(debug) <<
"A: " << par[0] <<
" Chi2: " << chi2;
241 if (exp.size() == 0 || exp.size() != sim.size())
242 return std::numeric_limits<double>::max();
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));
251 LOG(debug) <<
"A: " << par[0] <<
" Chi2: " << chi2;
259 return std::numeric_limits<double>::max();
263 for (
int i = 0; i < exp.size(); ++i) {
264 chi2 += (exp[i] - par[0] * sim[i]) * (exp[i] - par[0] * sim[i]);
269 LOG(debug) <<
"A: " << par[0] <<
" Chi2: " << chi2;
274 const std::array<std::vector<double>, 2> &simFull,
AtMCResult &definition)
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]);
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);
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();
300 LOG(error) <<
"Failed to fit the charge curves. Not adding to objective.";
301 return std::numeric_limits<double>::max();
303 auto &result = fitter.Result();
304 double amp = result.Parameter(0);
306 definition.
fParameters[
"qChi2"] = result.MinFcnValue();
307 return result.MinFcnValue();
311 std::vector<double> A = {fAmp};
312 auto chi2 = fObjCharge(exp, sim, A.data());
320 AtEvent &simEvent = fEventArray.at(SimEventID);
325 LOG(debug) << fragHits.size() <<
" " << expEvent.
GetFragHits(0).size() <<
" " << expEvent.
GetFragHits(1).size();
327 std::sort(fragHits.begin(), fragHits.end(),
328 [](
const AtHit *a,
const AtHit *b) { return a->GetPadNum() < b->GetPadNum(); });
330 auto simHit = simEvent.
GetHits().begin();
332 auto map = fPulse->GetMap();
337 for (
auto expHit : fragHits) {
338 auto padNum = expHit->GetPadNum();
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) {
350 LOG(debug) <<
"Did not find pad " << padNum <<
" in the simulated event or it's in the beam region";
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();
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;
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;
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;
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;
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;
433 return {
Ion{Z1, A1},
Ion{CN.
Z - Z1, CN.
A - A1}};
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();
446 return beamInLabFrame;
463 auto foldingAngle = ROOT::Math::VectorUtil::Angle(ffDir[0], ffDir[1]);
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);
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;
481 auto norm = ffDir[0].Cross(ffDir[1]).Unit();
482 auto rot1 = ROOT::Math::AxisAngle(norm, angle1);
483 auto beamDir = rot1(ffDir[0]);
489 LOG(debug) <<
"Beam dir: " << beamDir;
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();
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();
505 auto norm = ffDir[0].Cross(ffDir[1]).Unit();
506 auto projBeam = fNominalBeamDir - fNominalBeamDir.Dot(norm) * norm;
510 auto beamInLabFrame =
XYZVector(rot.Inverse()(beamDir));
512 LOG(info) <<
" Sampled beam deviates from nominal by "
513 << ROOT::Math::VectorUtil::Angle(beamInLabFrame, projBeam) * TMath::RadToDeg() <<
" deg.";
516 res.
fParameters[
"beamX"] = beamInLabFrame.Unit().X();
517 res.
fParameters[
"beamY"] = beamInLabFrame.Unit().Y();
518 res.
fParameters[
"beamZ"] = beamInLabFrame.Unit().Z();
520 return beamInLabFrame.Unit();
541 while (Z1 > fZmax || Z1 < fZmin) {
542 result.
fParameters[
"Z"] = fParameters[
"Z"]->Sample();
545 int A1 = std::round(fCN.A * (
double)Z1 / fCN.Z);
551 result.
fParameters[
"thCM"] = 90 * TMath::DegToRad();
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;
580 auto lineModel =
dynamic_cast<AtLineChargeModel *
>(fSim->GetSpaceChargeModel().get());
583 LOG(debug) <<
"Setting Lambda: " << def.
fParameters[
"lambda"];
585 }
else if (lineModel) {
587 LOG(debug) <<
"Setting Lambda: " << def.
fParameters[
"lambda"];
590 LOG(debug) <<
"No space charge to apply.";
592 auto vertex = GetVertex(def);
593 LOG(debug) <<
"Setting vertex: " << vertex;
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;
601 double KE = violaEn(fCN.A, fCN.Z);
609 auto mom = GetMomDirLab(def);
611 auto beamDir = GetBeamDir(def, mom, p1);
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'";
622 beamDir = toLabPrime(beamDir);
623 for (
int i = 0; i < 2; ++i)
624 mom[i] = toLabPrime(mom[i]);
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);
633 SetMomMagnitude(mom, p1);
634 std::vector<XYZEVector> ffMom(2);
638 auto pBeam = ffMom[0] + ffMom[1];
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();
646 LOG(debug) <<
"Transforming back to lab frame";
649 for (
int i = 0; i < 2; ++i)
650 ffMom[i] = toLabPrime.Inverse()(ffMom[i]);
653 pBeam = ffMom[0] + ffMom[1];
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();
662 for (
int i = 0; i < 2; ++i)
663 fSim->SimulateParticle(fragID[i].Z, fragID[i].A, vertex, ffMom[i]);
665 return fSim->GetPointsArray();