ANASEN_analysis/Armory/EnergyLoss.h
2026-08-27 16:19:12 -04:00

116 lines
4.2 KiB
C++

#ifndef ENERGYLOSS_H
#define ENERGYLOSS_H
// Computes the final energy for a particle traversing a medium from a known
// initial energy, using the E_vs_x_{particle}.dat lookup tables in
// ../ELoss/{medium}Loss/.
//
// Table convention (matches ../ELoss/Eloss.cpp and anasenMS.cpp):
// column 1 = distance traveled from the point of maximum energy (cm)
// column 2 = kinetic energy at that distance (MeV)
#include <string>
#include <map>
#include <stdexcept>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector>
#include "TVector3.h"
class EnergyLossTable {
public:
static EnergyLossTable& Get(const std::string& particle, const std::string& medium) {
std::string key = medium + "_" + particle;
static std::map<std::string, EnergyLossTable*> cache;
auto it = cache.find(key);
if (it != cache.end()) return *it->second;
std::string path = "../ELoss/" + medium + "Loss/E_vs_x_" + particle + ".dat";
EnergyLossTable* table = new EnergyLossTable(path);
cache[key] = table;
return *table;
}
double EnergyAfterDistance(double initialEnergy_MeV,
double distance_cm) const {
double initialDistance_cm = Interpolate(fEByEnergy_, fXByEnergy_, initialEnergy_MeV);
double finalDistance_cm = initialDistance_cm + distance_cm;
if (finalDistance_cm >= xMax_) return 0.0;
return std::max(0.0, Interpolate(fX_, fE_, finalDistance_cm));
}
private:
explicit EnergyLossTable(const std::string& path) {
std::ifstream input(path);
std::string line;
while (std::getline(input, line)) {
std::istringstream row(line);
double distance_cm, energy_MeV;
if (!(row >> distance_cm >> energy_MeV)) continue;
fEByEnergy_.push_back(energy_MeV);
fXByEnergy_.push_back(distance_cm);
}
if (fEByEnergy_.empty())
throw std::runtime_error("EnergyLossTable: could not read table " + path);
// Generated files list energy from low to high, so distance is descending.
fX_ = fXByEnergy_;
fE_ = fEByEnergy_;
std::reverse(fX_.begin(), fX_.end());
std::reverse(fE_.begin(), fE_.end());
xMax_ = fX_.back();
}
static double Interpolate(const std::vector<double>& x,
const std::vector<double>& y,
double value) {
if (value <= x.front()) return y.front();
if (value >= x.back()) return y.back();
auto upper = std::upper_bound(x.begin(), x.end(), value);
size_t index = static_cast<size_t>(upper - x.begin() - 1);
double fraction = (value - x[index]) / (x[index + 1] - x[index]);
return y[index] + fraction * (y[index + 1] - y[index]);
}
std::vector<double> fXByEnergy_;
std::vector<double> fEByEnergy_;
std::vector<double> fX_;
std::vector<double> fE_;
double xMax_;
};
inline double CalcPathLength_cm(double vx, double vy, double vz,
double fx, double fy, double fz) {
return TVector3(fx - vx, fy - vy, fz - vz).Mag() * 0.1;
}
// Returns the energy remaining after traveling from the vertex to the endpoint.
inline double CalculateEnergyLoss(double vx, double vy, double vz,
double fx, double fy, double fz,
const std::string& particle,
const std::string& medium,
double initialEnergy_MeV,
double& distance_cm) {
const EnergyLossTable& table = EnergyLossTable::Get(particle, medium);
distance_cm = CalcPathLength_cm(vx, vy, vz, fx, fy, fz);
return table.EnergyAfterDistance(initialEnergy_MeV, distance_cm);
}
inline double CalculateOriginalEnergy(double fx, double fy, double fz,
double vx, double vy, double vz,
const std::string& particle,
const std::string& medium,
double initialEnergy_MeV,
double& distance_cm) {
const EnergyLossTable& table = EnergyLossTable::Get(particle, medium);
distance_cm = - CalcPathLength_cm(vx, vy, vz, fx, fy, fz);
return table.EnergyAfterDistance(initialEnergy_MeV, distance_cm);
}
#endif