1
0
Fork 0
mirror of https://github.com/gwm17/PunchTable.git synced 2024-05-19 15:23:20 -04:00

First commit

This commit is contained in:
Gordon McCann 2022-06-09 16:24:29 -04:00
commit 11cb738ce7
13 changed files with 3244 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
build/
bin/
lib/
include/
.vscode/
*.o
*.so
*.dylib
*.a
*.dll
*.lib
*.root
*.swp
.DS_Store
!.gitignore

10
CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.16)
project(PunchTable)
set(CXX_STANDARD 17)
set(PUNCHTABLE_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin)
add_subdirectory(src/vendor/catima)
add_subdirectory(src)

2501
etc/mass.txt Normal file

File diff suppressed because it is too large Load Diff

17
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
add_executable(PunchTable)
target_include_directories(PunchTable PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_sources(PunchTable PRIVATE
CubicSpline.h
CubicSpline.cpp
MassLookup.h
MassLookup.cpp
PunchTable.h
PunchTable.cpp
GenerateTable.h
GenerateTable.cpp
)
target_link_libraries(PunchTable catima)
set_target_properties(PunchTable PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PUNCHTABLE_BINARY_DIR})

235
src/CubicSpline.cpp Normal file
View File

@ -0,0 +1,235 @@
/*
CubicSpline.cpp
Class for generating cubic splines of data in tables or held in memory. Cubic splines are a form of interpolation,
somewhat more advanced than linear interpolation, but not so complicated that it significantly slows down a calculation.
For more information see Wikipedia or Num. Rec. in C.
Gordon M. May 2021
*/
#include "CubicSpline.h"
#include <fstream>
#include <iostream>
#include <cmath>
namespace PunchTable {
CubicSpline::CubicSpline() :
m_validFlag(false)
{
}
CubicSpline::CubicSpline(const std::string& filename) :
m_validFlag(false)
{
ReadFile(filename);
}
CubicSpline::CubicSpline(const std::vector<double>& x, const std::vector<double>& y) :
m_validFlag(false)
{
ReadData(x, y);
}
CubicSpline::~CubicSpline() {}
/*
Expected file format is a naked (no header) single space separated table:
x y\n
*/
void CubicSpline::ReadFile(const std::string& filename)
{
std::ifstream input(filename);
if(!input.is_open()) {
std::cerr<<"Unable to open input data at CubicSpline::ReadFile from filename: "<<filename<<std::endl;
m_validFlag = false;
return;
}
std::string junk;
//std::getline(input, junk);
double x, y;
while(input>>x)
{
input>>y;
m_dataX.push_back(x);
m_dataY.push_back(y);
}
if(m_dataX.size() != m_dataY.size())
{
std::cerr<<"Error in CubicSpline::ReadFile! Number of x points not equal to number of y points!"<<std::endl;
m_validFlag = false;
return;
}
m_validFlag = true;
input.close();
MakeSplines();
}
/*
After data is read in splines can be solved. Each data point is referred to as a knot. Endpoint conditions are
derivatives of neighbors must be equal and second derivatives must be zero (natural cubic splines). Solved using gaussian elimination.
*/
void CubicSpline::MakeSplines()
{
if(!m_validFlag)
{
std::cerr<<"Error at CubicSpline::MakeSplines! Unable to generate splines without first initializing data."<<std::endl;
return;
}
int knots = m_dataX.size();
//Matrix and vector data init
double** a = new double*[knots];
for(int i=0; i<knots; i++)
a[i] = new double[knots];
double* b = new double[knots];
double* k = new double[knots];
Spline s;
m_splines.clear();
double x0, x1, x2;
double y0, y1, y2;
//Setup matrix eqn.
for(int i=0; i<knots; i++)
{
if(i == 0)
{
x1 = m_dataX[i];
x2 = m_dataX[i+1];
y1 = m_dataY[i];
y2 = m_dataY[i+1];
a[i][i] = 2.0/(x2-x1);
a[i][i+1] = 1.0/(x2-x1);
b[i] = 3.0*(y2-y1)/(std::pow((x2-x1), 2.0));
s.x1 = x1; s.x2 = x2;
s.y1 = y1; s.y2 = y2;
m_splines.push_back(s);
}
else if(i == (knots-1))
{
x0 = m_dataX[i-1];
x1 = m_dataX[i];
y0 = m_dataY[i-1];
y1 = m_dataY[i];
a[i][i-1] = 1.0/(x1-x0);
a[i][i] = 2.0/(x1-x0);
b[i] = 3.0*(y1-y0)/(std::pow((x1-x0), 2.0));
}
else
{
x0 = m_dataX[i-1];
x1 = m_dataX[i];
x2 = m_dataX[i+1];
y0 = m_dataY[i-1];
y1 = m_dataY[i];
y2 = m_dataY[i+1];
a[i][i-1] = 1.0/(x1-x0);
a[i][i] = 2.0/(x1-x0)+2.0/(x2-x1);
a[i][i+1] = 1.0/(x2-x1);
b[i] = 3.0*(y1-y0)/(std::pow((x1-x0), 2.0))+3.0*(y2-y1)/(std::pow((x2-x1), 2.0));
s.x1 = x1; s.x2 = x2;
s.y1 = y1; s.y2 = y2;
m_splines.push_back(s);
}
}
//solve for curvature vector k using gaussian elimination
a[0][1] /= a[0][0];
b[0] /= a[0][0];
for(int i=1; i<(knots-1); i++)
{
a[i][i+1] /= a[i][i]-a[i][i-1]*a[i-1][i];
b[i] = (b[i] - a[i][i-1]*b[i-1])/(a[i][i]-a[i][i-1]*a[i-1][i]);
}
int g1 = knots-1;
int g2 = knots-2;
b[g1] = (b[g1]-a[g1][g2]*b[g2])/(a[g1][g1]-a[g1][g2]*a[g2][g1]);
k[g1] = b[g1];
for(int i=(knots-2); i>=0; i--)
k[i] = b[i] - a[i][i+1]*k[i+1];
//Fill the spline data
for(size_t i=0; i<m_splines.size(); i++)
{
m_splines[i].k1 = k[i];
m_splines[i].k2 = k[i+1];
}
//deallocate
delete[] b;
delete[] k;
for(int i=0; i<knots; i++)
delete[] a[i];
delete[] a;
}
double CubicSpline::Evaluate(double x)
{
if(!m_validFlag)
{
std::cerr<<"Error at CubicSpline::Evaluate! Unable to evaluate without first generating splines."<<std::endl;
return 0.0;
}
Spline s;
for(size_t i=0; i<m_splines.size(); i++)
{
auto& spline = m_splines[i];
if(x >= spline.x1 && x <= spline.x2)
{
s = spline;
break;
}
else if (i == (m_splines.size() -1))
{
//std::cerr<<"Error at CubicSpline::Evaluate! Input x value: "<<x<<" is not within the spline range min: "<<splines[0].x1<<" max: "<<splines[splines.size()-1].x2<<std::endl;
return 0.0;
}
}
double t = (x-s.x1)/(s.x2-s.x1);
double a = s.k1*(s.x2-s.x1)-(s.y2-s.y1);
double b = -s.k2*(s.x2-s.x1)+(s.y2-s.y1);
return (1.0-t)*s.y1+t*s.y2+t*(1.0-t)*((1.0-t)*a+t*b);
}
//Purely for plotting in ROOT, do not use for caluculations.
double CubicSpline::EvaluateROOT(double* x, double* p)
{
if(!m_validFlag)
{
std::cerr<<"Error at CubicSpline::EvaluateROOT! Unable to evaluate without first generating splines."<<std::endl;
return 0.0;
}
double xval = x[0];
Spline s;
for(size_t i=0; i<m_splines.size(); i++)
{
auto& spline = m_splines[i];
if(xval >= spline.x1 && xval <= spline.x2)
{
s = spline;
break;
}
else if (i == (m_splines.size() -1))
{
return 0.0;
}
}
double t = (xval-s.x1)/(s.x2-s.x1);
double a = s.k1*(s.x2-s.x1)-(s.y2-s.y1);
double b = -s.k2*(s.x2-s.x1)+(s.y2-s.y1);
return (1.0-t)*s.y1+t*s.y2+t*(1.0-t)*((1.0-t)*a+t*b);
}
}

55
src/CubicSpline.h Normal file
View File

@ -0,0 +1,55 @@
/*
CubicSpline.h
Class for generating cubic splines of data in tables or held in memory. Cubic splines are a form of interpolation,
somewhat more advanced than linear interpolation, but not so complicated that it significantly slows down a calculation.
For more information see Wikipedia or Num. Rec. in C.
Gordon M. May 2021
*/
#ifndef CUBICSPLINE_H
#define CUBICSPLINE_H
#include <vector>
#include <string>
namespace PunchTable {
//struct holding final spline info
struct Spline
{
double y1=0, y2=0;
double x1=0, x2=0;
double k1=0, k2=0;
};
class CubicSpline
{
public:
CubicSpline();
CubicSpline(const std::string& filename);
CubicSpline(const std::vector<double>& x, const std::vector<double>& y);
~CubicSpline();
void ReadFile(const std::string& filename);
inline void ReadData(const std::vector<double>& x, const std::vector<double>& y)
{
m_dataX = x;
m_dataY = y;
m_validFlag=true;
MakeSplines();
}
bool IsValid() { return m_validFlag; }
double Evaluate(double x);
double EvaluateROOT(double* x, double* p); //for plotting as ROOT function
private:
void MakeSplines();
std::vector<double> m_dataX;
std::vector<double> m_dataY;
std::vector<Spline> m_splines;
bool m_validFlag;
};
}
#endif

11
src/GenerateTable.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "GenerateTable.h"
#include <fstream>
#include <cmath>
namespace PunchTable {
void GenerateTable(const TableParameters& params)
{
}
}

29
src/GenerateTable.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef GENERATE_TABLE_H
#define GENERATE_TABLE_H
#include <string>
#include <vector>
namespace PunchTable {
struct TableParameters
{
double minKE;
double maxKE;
double stepKE;
double minTheta;
double maxTheta;
double stepTheta;
int projectileZ;
int projectileA;
std::vector<std::vector<int>> targetZ;
std::vector<std::vector<int>> targetA;
std::vector<std::vector<int>> targetS;
double targetThickness;
std::string filename;
};
void GenerateTable(const TableParameters& params);
}
#endif

73
src/MassLookup.cpp Normal file
View File

@ -0,0 +1,73 @@
/*
MassLookup.h
Generates a map for isotopic masses using AMDC data; subtracts away
electron mass from the atomic mass by default. Creates a static global instance
of this map (MASS) for use throughout code it is included into.
Written by G.W. McCann Aug. 2020
*/
#include "MassLookup.h"
#include <iostream>
namespace PunchTable {
MassLookup* MassLookup::s_instance = new MassLookup();
MassLookup::MassLookup()
{
std::ifstream massfile("etc/mass.txt");
if(massfile.is_open())
{
std::string junk, A, element;
int Z;
double atomicMassBig, atomicMassSmall, isotopicMass;
getline(massfile,junk);
getline(massfile,junk);
while(massfile>>junk)
{
massfile>>Z>>A>>element>>atomicMassBig>>atomicMassSmall;
isotopicMass = (atomicMassBig + atomicMassSmall*1e-6 - Z*electron_mass)*u_to_mev;
std::string key = "("+std::to_string(Z)+","+A+")";
massTable[key] = isotopicMass;
elementTable[Z] = element;
}
}
else
{
std::cerr<<"ERR -- Unable to open mass file, check etc directory"<<std::endl;
}
}
MassLookup::~MassLookup() {}
//Returns nuclear mass in MeV
double MassLookup::FindMass(int Z, int A)
{
std::string key = "("+std::to_string(Z)+","+std::to_string(A)+")";
auto data = massTable.find(key);
if(data == massTable.end())
{
std::cerr<<"WARN -- Unable to find mass of (Z,A)=("<<Z<<","<<A<<")."<<std::endl;
return 0.0;
}
return data->second;
}
//returns element symbol
std::string MassLookup::FindSymbol(int Z, int A)
{
auto data = elementTable.find(Z);
if(data == elementTable.end())
{
std::cerr<<"WARN -- Unable to find symbol of (Z,A)=("<<Z<<","<<A<<")."<<std::endl;
return "";
}
std::string fullsymbol = std::to_string(A) + data->second;
return fullsymbol;
}
}

45
src/MassLookup.h Normal file
View File

@ -0,0 +1,45 @@
/*
MassLookup.h
Generates a map for isotopic masses using AMDC data; subtracts away
electron mass from the atomic mass by default. Creates a static global instance
of this map (MASS) for use throughout code it is included into.
Written by G.W. McCann Aug. 2020
Converted to true singleton to simplify usage -- Aug. 2021 GWM
*/
#ifndef MASS_LOOKUP_H
#define MASS_LOOKUP_H
#include <fstream>
#include <string>
#include <unordered_map>
namespace PunchTable {
class MassLookup
{
public:
MassLookup();
~MassLookup();
double FindMass(int Z, int A);
double FindMassU(int Z, int A) { return FindMass(Z, A)/u_to_mev; }
std::string FindSymbol(int Z, int A);
inline static MassLookup& GetInstance() { return *s_instance; }
private:
static MassLookup* s_instance;
std::unordered_map<std::string, double> massTable;
std::unordered_map<int, std::string> elementTable;
//constants
static constexpr double u_to_mev = 931.4940954;
static constexpr double electron_mass = 0.000548579909;
};
}
#endif

109
src/PunchTable.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "PunchTable.h"
#include <fstream>
#include <iostream>
namespace PunchTable {
PunchTable::PunchTable() :
m_validFlag(false)
{
}
PunchTable::PunchTable(const std::string& filename) :
m_validFlag(false)
{
ReadFile(filename);
}
PunchTable::~PunchTable() {}
void PunchTable::ReadFile(const std::string& filename)
{
std::ifstream input(filename);
if(!input.is_open())
{
std::cerr<<"Unable to open table file named "<<filename<<"! Exiting."<<std::endl;
m_validFlag = false;
return;
}
std::string junk;
double thickness;
double theta;
double value;
std::vector<double> energyIn, energyDep;
std::getline(input, junk);
input>>junk>>thickness;
input>>junk>>m_thetaMin>>junk>>m_thetaMax>>junk>>m_thetaStep;
std::getline(input, junk);
std::getline(input, junk);
std::getline(input, junk);
while(input>>junk)
{
if(junk == "begin_theta")
{
energyIn.clear();
energyDep.clear();
input>>theta;
while(input>>junk)
{
if(junk == "end_theta")
break;
energyDep.push_back(std::stod(junk));
input>>value;
energyIn.push_back(value);
}
if(!energyDep.empty())
m_splines.emplace_back(energyDep, energyIn);
else
m_splines.emplace_back();
}
else
{
std::cerr<<"Unexpected expression found when reading punch table: "<<junk<<std::endl;
m_validFlag = false;
return;
}
}
m_validFlag = true;
}
double PunchTable::GetInitialKineticEnergy(double theta_incident, double e_deposited)
{
theta_incident /= s_deg2rad;
if(!m_validFlag)
{
std::cerr<<"PunchTable not initialized at GetInitialKineticEnergy()"<<std::endl;
return 0.0;
}
else if(theta_incident < m_thetaMin || theta_incident > m_thetaMax)
{
std::cerr<<"Theta incident outside of range of calculated values for PunchTable::GetInitialKineticEnergy"<<std::endl;
return 0.0;
}
float thetaf_bin = (theta_incident - m_thetaMin)/m_thetaStep;
int theta_bin = (theta_incident - m_thetaMin)/m_thetaStep;
std::cout<<"theta bin: "<<theta_bin<<" theta_inc: "<<theta_incident<<std::endl;
if(m_splines[theta_bin].IsValid())
{
double initialE = m_splines[theta_bin].Evaluate(e_deposited);
if(initialE == 0.0) //Not in the spline, stopped completely
{
return e_deposited;
}
else
return initialE;
}
else
return e_deposited;
}
}

30
src/PunchTable.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef PUNCHTABLE_H
#define PUNCHTABLE_H
#include "CubicSpline.h"
#include <vector>
#include <string>
#include <cmath>
namespace PunchTable {
class PunchTable {
public:
PunchTable();
PunchTable(const std::string& filename);
~PunchTable();
void ReadFile(const std::string& filename);
double GetInitialKineticEnergy(double theta_incident, double e_deposited);
inline bool IsValid() const { return m_validFlag; }
private:
std::vector<CubicSpline> m_splines;
double m_thetaStep, m_thetaMin, m_thetaMax;
bool m_validFlag;
static constexpr double s_deg2rad = M_PI/180.0;
};
}
#endif

111
src/main.cpp Normal file
View File

@ -0,0 +1,111 @@
#include "MassLookup.h"
#include "PunchTable.h"
#include <string>
#include <vector>
#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
int main(int argc, char** argv)
{
std::string options = "";
if(argc == 2)
{
options = argv[1];
}
int Zp = 1;
int Ap = 1;
float keStep = 0.01;
float thickness = 500.0 * 1e-4 * 2.3216 * 1e6; //1000 um thick times density of Si-> ug/cm^2
float deg2rad = M_PI/180.0;
float thetaStep = 0.1*deg2rad;
float precision = 1.0e-6;
std::vector<int> ZT(1), AT(1), ST(1);
ZT[0] = 14;
AT[0] = 28;
ST[0] = 1;
EnergyLoss energyLoss;
energyLoss.SetTargetComponents(ZT, AT, ST);
float keMin = 8.23f;
float keMax = 25.0f;
float thetaMin = 0.0f*deg2rad;
float thetaMax = 85.0f*deg2rad;
int nebins = int((keMax-keMin)/keStep);
int ntbins = int ((thetaMax-thetaMin)/thetaStep);
if(options == "--make-table" || options == "")
{
std::cout<<"nebins: "<<nebins<<" ntbins: "<<ntbins<<std::endl;
float keCur;
float thetaCur;
float energy;
std::vector<std::vector<float>> energy_dep(ntbins);
std::vector<std::vector<float>> energy_in(ntbins);
for(int i=0; i<ntbins; i++)
{
std::cout<<"\rWorking on theta bin "<<i+1<<" of "<<ntbins<<std::flush;
thetaCur = thetaMin + i*thetaStep;
for(int j=0; j<nebins; j++)
{
keCur = keMax - j*keStep;
energy = energyLoss.GetEnergyLoss(Zp, Ap, keCur, thickness / std::fabs(std::cos(thetaCur)));
if(std::fabs(energy-keCur) > precision)
{
energy_dep[i].push_back(energy);
energy_in[i].push_back(keCur);
}
}
}
std::cout<<std::endl;
std::ofstream output("tables/test.ptab");
output<<std::setprecision(5);
output<<"Material: ";
for(size_t i=0; i<ZT.size(); i++)
output<<MassLookup::GetInstance()->FindSymbol(ZT[i], AT[i]);
output<<std::endl;
output<<"Thickness: "<<thickness<<std::endl;
output<<"IncidentAngleRange(deg): "<<thetaMin/deg2rad<<" to "<<thetaMax/deg2rad<<" stepSize: "<<thetaStep/deg2rad<<std::endl;
output<<std::setw(16)<<"Energy Deposited"<<"|"<<std::setw(16)<<"Intial Energy"<<std::endl;
output<<"---------------------------------"<<std::endl;
for(int i=0; i<ntbins; i++)
{
thetaCur = thetaMin + i*thetaStep;
output<<"begin_theta "<<thetaCur/deg2rad<<std::endl;
for(unsigned int j=0; j<energy_dep[i].size(); j++)
{
output<<std::setw(16)<<energy_dep[i][j]<<" "<<std::setw(16)<<energy_in[i][j]<<std::endl;
}
output<<"end_theta"<<std::endl;
}
output.close();
}
if(options == "--test" || options == "")
{
std::cout<<"-------------Testing---------"<<std::endl;
PunchTable table("tables/test.ptab");
std::cout<<"Is the table valid? "<<table.IsValid()<<std::endl;
double ke_test = 14.5; //MeV
double theta_test = 35.0*deg2rad;
double ke_dep = energyLoss.GetEnergyLoss(Zp, Ap, ke_test, thickness/std::fabs(std::cos(theta_test)));
double recov_ke = table.GetInitialKineticEnergy(theta_test, ke_dep);
std::cout<<"For a "<<MassLookup::GetInstance()->FindSymbol(Zp, Ap)<<" with kinetic energy "<<ke_test<<" MeV "
<<" the percent error on recovery is "<<(ke_test-recov_ke)/ke_test*100.0<<" where the recovered energy is "<<recov_ke<<" MeV"
<<" and the deposited energy is "<<ke_dep<<" MeV"<<std::endl;
std::cout<<"-----------------------------"<<std::endl;
}
}