-
Felix Riehn authored
moved target sampling to DoInteraction, calculate interaction length from weighted production cross section in GetInteractionLength
Felix Riehn authoredmoved target sampling to DoInteraction, calculate interaction length from weighted production cross section in GetInteractionLength
Interaction.h 15.76 KiB
/**
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* See file AUTHORS for a list of contributors.
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#ifndef _corsika_process_sibyll_interaction_h_
#define _corsika_process_sibyll_interaction_h_
#include <corsika/process/InteractionProcess.h>
#include <corsika/process/sibyll/ParticleConversion.h>
#include <corsika/process/sibyll/SibStack.h>
#include <corsika/process/sibyll/sibyll2.3c.h>
#include <corsika/utl/COMBoost.h>
#include <corsika/particles/ParticleProperties.h>
#include <corsika/random/RNGManager.h>
#include <corsika/units/PhysicalUnits.h>
#include <corsika/environment/Environment.h>
#include <corsika/environment/NuclearComposition.h>
namespace corsika::process::sibyll {
class Interaction : public corsika::process::InteractionProcess<Interaction> {
int fCount = 0;
int fNucCount = 0;
public:
Interaction(corsika::environment::Environment const& env) : fEnvironment(env) { }
~Interaction() {
std::cout << "Sibyll::Interaction n=" << fCount << " Nnuc=" << fNucCount
<< std::endl;
}
void Init() {
using corsika::random::RNGManager;
using std::cout;
using std::endl;
corsika::random::RNGManager& rmng = corsika::random::RNGManager::GetInstance();
rmng.RegisterRandomStream("s_rndm");
// initialize Sibyll
sibyll_ini_();
}
template <typename Particle, typename Track>
corsika::units::si::GrammageType GetInteractionLength(Particle& p, Track&) {
using namespace corsika::units;
using namespace corsika::units::hep;
using namespace corsika::units::si;
using namespace corsika::geometry;
using std::cout;
using std::endl;
// coordinate system, get global frame of reference
CoordinateSystem& rootCS =
RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
const particles::Code corsikaBeamId = p.GetPID();
// beam particles for sibyll : 1, 2, 3 for p, pi, k
// read from cross section code table
const int kBeam = process::sibyll::GetSibyllXSCode(corsikaBeamId);
const bool kInteraction = process::sibyll::CanInteract(corsikaBeamId);
const hep::MassType nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() +
corsika::particles::Neutron::GetMass());
// FOR NOW: assume target is at rest
MomentumVector pTarget(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
// total momentum and energy
hep::EnergyType Etot = p.GetEnergy() + nucleon_mass;
MomentumVector Ptot(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
Ptot += p.GetMomentum();
Ptot += pTarget;
// calculate cm. energy
const hep::EnergyType sqs = sqrt(Etot * Etot - Ptot.squaredNorm());
const double Ecm = sqs / 1_GeV;
std::cout << "Interaction: LambdaInt: \n"
<< " input energy: " << p.GetEnergy() / 1_GeV << endl
<< " beam can interact:" << kBeam << endl
<< " beam XS code:" << kBeam << endl
<< " beam pid:" << p.GetPID() << endl;
if (kInteraction) {
// get target from environment
/*
the target should be defined by the Environment,
ideally as full particle object so that the four momenta
and the boosts can be defined..
*/
const auto currentNode = fEnvironment.GetUniverse()->GetContainingNode(p.GetPosition());
const auto mediumComposition = currentNode->GetModelProperties().GetNuclearComposition();
// determine average interaction length
// FOR NOW: weighted sum
int i =-1;
double avgTargetMassNumber = 0.;
si::CrossSectionType weightedProdCrossSection = 0_mbarn;
int kTarget = 0;
// get weights of components from environment/medium
const auto w = mediumComposition.GetFractions();
// loop over components in medium
for (auto targetId: mediumComposition.GetComponents() ){
i++;
cout << "Interaction: get interaction lenght for target: " << targetId << endl;
if(IsNucleus(targetId))
kTarget = GetNucleusA(targetId);
else
if(targetId==corsika::particles::Proton::GetCode())
kTarget = 1;
else
throw std::runtime_error("GetInteractionLength: Sibyll target particle unknown. Only nuclei or protons are allowed.");
cout << "Interaction: kTarget: " << kTarget << endl;
// check if nuclei in range
if(kTarget>18||kTarget==0)
throw std::runtime_error("Sibyll target outside range. Only nuclei with A<18 are allowed.");
// get cross section from sibyll
double sigProd, dummy, dum1, dum2, dum3, dum4;
double dumdif[3];
if (kTarget == 1)
sib_sigma_hp_(kBeam, Ecm, dum1, dum2, sigProd, dumdif, dum3, dum4);
else
sib_sigma_hnuc_(kBeam, kTarget, Ecm, sigProd, dummy);
std::cout << "Interaction: "
<< " IntLength: sibyll return: " << sigProd << std::endl;
weightedProdCrossSection += w[i] * sigProd * 1_mbarn;
avgTargetMassNumber += w[i] * kTarget;
}
cout << "Interaction: "
<< "IntLength: weighted CrossSection (mb): " << weightedProdCrossSection / 1_mbarn
<< endl;
const si::MassType nucleon_mass =
0.93827_GeV / corsika::units::constants::cSquared;
std::cout << "Interaction: "
<< "nucleon mass " << nucleon_mass << std::endl;
// calculate interaction length in medium
#warning check interaction length units
GrammageType int_length = avgTargetMassNumber * nucleon_mass / weightedProdCrossSection;
std::cout << "Interaction: "
<< "interaction length (g/cm2): " << int_length / ( 0.001_kg ) * 1_cm * 1_cm << std::endl;
return int_length;
}
return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
}
template <typename Particle, typename Stack>
corsika::process::EProcessReturn DoInteraction(Particle& p, Stack& s) {
using namespace corsika::units;
using namespace corsika::utl;
using namespace corsika::units::hep;
using namespace corsika::units::si;
using namespace corsika::geometry;
using std::cout;
using std::endl;
cout << "ProcessSibyll: "
<< "DoInteraction: " << p.GetPID() << " interaction? "
<< process::sibyll::CanInteract(p.GetPID()) << endl;
if (process::sibyll::CanInteract(p.GetPID())) {
const CoordinateSystem& rootCS =
RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
Point pOrig = p.GetPosition();
TimeType tOrig = p.GetTime();
// sibyll CS has z along particle momentum
// FOR NOW: hard coded z-axis for corsika frame
QuantityVector<length_d> const zAxis{0_m, 0_m, 1_m};
QuantityVector<length_d> const yAxis{0_m, 1_m, 0_m};
auto rotation_angles = [](MomentumVector const& pin) {
const auto p = pin.GetComponents();
const auto th = acos(p[2] / p.norm());
const auto ph = atan2(
p[1] / 1_GeV, p[0] / 1_GeV); // acos( p[0] / sqrt(p[0]*p[0]+p[1]*p[1] ) );
return std::make_tuple(th, ph);
};
// auto pt = []( MomentumVector &p ){
// return sqrt(p.GetComponents()[0] * p.GetComponents()[0] +
// p.GetComponents()[1] * p.GetComponents()[1]);
// };
// double theta = acos( p.GetMomentum().GetComponents()[2] /
// p.GetMomentum().norm());
auto const [theta, phi] = rotation_angles(p.GetMomentum());
cout << "ProcessSibyll: zenith angle between sibyllCS and rootCS: "
<< theta / M_PI * 180. << endl;
cout << "ProcessSibyll: azimuth angle between sibyllCS and rootCS: "
<< phi / M_PI * 180. << endl;
// double phi = asin( p.GetMomentum().GetComponents()[0]/pt(p.GetMomentum() ) );
const CoordinateSystem tempCS = rootCS.rotate(zAxis, phi);
const CoordinateSystem sibyllCS = tempCS.rotate(yAxis, theta);
const auto sample_target = [](const corsika::environment::NuclearComposition& comp)
{
double prev =0.;
std::vector<float> cumFrac;
for (auto f: comp.GetFractions()){
cumFrac.push_back(prev+f);
prev = cumFrac.back();
}
int a = 1;
const double r = s_rndm_(a);
int i = -1;
//cout << "rndm: " << r << endl;
for (auto cf: cumFrac){
++i;
//cout << "cf: " << cf << " " << comp.GetComponents()[i] << endl;
if(r<cf) break;
}
return comp.GetComponents()[i];
};
const auto currentNode = fEnvironment.GetUniverse()->GetContainingNode(p.GetPosition());
const auto mediumComposition = currentNode->GetModelProperties().GetNuclearComposition();
const auto tg = sample_target( mediumComposition );
cout << "Interaction: target selected: " << tg << endl;
// test if valid in sibyll
// FOR NOW: throw error if not, may need workaround for atmosphere, contains small argon fraction
int kTarget = 0;
if(IsNucleus(tg))
kTarget = GetNucleusA(tg);
else
if(tg==corsika::particles::Proton::GetCode())
kTarget = 1;
else
throw std::runtime_error("Sibyll target particle unknown. Only nuclei with A<18 or protons are allowed.");
// FOR NOW: target is always at rest
const hep::MassType nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() +
corsika::particles::Neutron::GetMass());
const EnergyType Etarget = 0_GeV + nucleon_mass;
const auto pTarget = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
cout << "target momentum (GeV/c): " << pTarget.GetComponents() / 1_GeV << endl;
cout << "beam momentum (GeV/c): " << p.GetMomentum().GetComponents() / 1_GeV
<< endl;
cout << "beam momentum in sibyll frame (GeV/c): "
<< p.GetMomentum().GetComponents(sibyllCS) / 1_GeV << endl;
cout << "position of interaction: " << pOrig.GetCoordinates() << endl;
cout << "time: " << tOrig << endl;
// get energy of particle from stack
/*
stack is in GeV in lab. frame
convert to GeV in cm. frame
(assuming proton at rest as target AND
assuming no pT, i.e. shower frame-z is aligned with hadron-int-frame-z)
*/
// total energy: E_beam + E_target
// in lab. frame: E_beam + m_target*c**2
EnergyType E = p.GetEnergy();
EnergyType Etot = E + Etarget;
// total momentum
MomentumVector Ptot = p.GetMomentum();
// invariant mass, i.e. cm. energy
EnergyType Ecm = sqrt(Etot * Etot - Ptot.squaredNorm());
/*
get transformation between Stack-frame and SibStack-frame
for EAS Stack-frame is lab. frame, could be different for CRMC-mode
the transformation should be derived from the input momenta
*/
const double gamma = Etot / Ecm;
const auto gambet = Ptot / Ecm;
std::cout << "Interaction: "
<< " DoDiscrete: gamma:" << gamma << endl;
std::cout << "Interaction: "
<< " DoDiscrete: gambet:" << gambet.GetComponents() << endl;
Vector<si::momentum_d> pProjectileLab = p.GetMomentum() / constants::c;
//{rootCS, {0_GeV / c, 1_PeV / c, 0_GeV / c}};
EnergyType const eProjectileLab = p.GetEnergy();
//energy(projectileMass, pProjectileLab);
// define target kinematics in lab frame
si::MassType const targetMass = nucleon_mass / constants::cSquared;
// define boost to com frame
COMBoost boost(eProjectileLab, pProjectileLab, targetMass);
cout << "Interaction: new boost: ebeam lab: " << eProjectileLab / 1_GeV << endl
<< "Interaction: new boost: pbeam lab: " << pProjectileLab.GetComponents() / ( 1_GeV / constants::c ) << endl;
// boost projecticle
auto const [eProjectileCoM, pProjectileCoM] =
boost.toCoM(eProjectileLab, pProjectileLab);
cout << "Interaction: new boost: ebeam com: " << eProjectileCoM / 1_GeV << endl
<< "Interaction: new boost: pbeam com: " << pProjectileCoM / ( 1_GeV / constants::c ) << endl;
int kBeam = process::sibyll::ConvertToSibyllRaw(p.GetPID());
std::cout << "Interaction: "
<< " DoInteraction: E(GeV):" << E / 1_GeV
<< " Ecm(GeV): " << Ecm / 1_GeV << std::endl;
if (E < 8.5_GeV || Ecm < 10_GeV) {
std::cout << "Interaction: "
<< " DoInteraction: should have dropped particle.." << std::endl;
// p.Delete(); delete later... different process
} else {
fCount++;
// Sibyll does not know about units..
const double sqs = Ecm / 1_GeV;
// running sibyll, filling stack
sibyll_(kBeam, kTarget, sqs);
// running decays
// setTrackedParticlesStable();
decsib_();
// print final state
int print_unit = 6;
sib_list_(print_unit);
fNucCount += get_nwounded() - 1;
// delete current particle
p.Delete();
// add particles from sibyll to stack
// link to sibyll stack
// here we need to pass projectile momentum and energy to define the local
// sibyll frame and the boosts to the lab. frame
SibStack ss;
// momentum array in Sibyll
MomentumVector Plab_final(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
EnergyType E_final = 0_GeV, Ecm_final = 0_GeV;
for (auto& psib : ss) {
// skip particles that have decayed in Sibyll
if (psib.HasDecayed()) continue;
// transform energy to lab. frame, primitve
// compute beta_vec * p_vec
// arbitrary Lorentz transformation based on sibyll routines
const auto gammaBetaComponents = gambet.GetComponents();
// FOR NOW: fill vector in sibCS and then rotate into rootCS
// can be done in SibStack by passing sibCS
// get momentum vector in sibyllCS
const auto pSibyllComponentsSibCS = psib.GetMomentum().GetComponents();
// temporary vector in sibyllCS
auto SibVector = MomentumVector(sibyllCS, pSibyllComponentsSibCS);
// rotatate to rootCS
const auto pSibyllComponents = SibVector.GetComponents(rootCS);
// boost to lab. frame
hep::EnergyType en_lab = 0. * 1_GeV;
hep::MomentumType p_lab_components[3];
en_lab = psib.GetEnergy() * gamma;
hep::EnergyType pnorm = 0. * 1_GeV;
for (int j = 0; j < 3; ++j)
pnorm += (pSibyllComponents[j] * gammaBetaComponents[j]) / (gamma + 1.);
pnorm += psib.GetEnergy();
for (int j = 0; j < 3; ++j) {
p_lab_components[j] =
pSibyllComponents[j] - (-1) * pnorm * gammaBetaComponents[j];
en_lab -= (-1) * pSibyllComponents[j] * gammaBetaComponents[j];
}
// add to corsika stack
auto pnew = s.NewParticle();
pnew.SetEnergy(en_lab);
pnew.SetPID(process::sibyll::ConvertFromSibyll(psib.GetPID()));
corsika::geometry::QuantityVector<energy_hep_d> p_lab_c{
p_lab_components[0], p_lab_components[1], p_lab_components[2]};
pnew.SetMomentum(MomentumVector(rootCS, p_lab_c));
pnew.SetPosition(pOrig);
pnew.SetTime(tOrig);
Plab_final += pnew.GetMomentum();
E_final += en_lab;
Ecm_final += psib.GetEnergy();
}
std::cout << "conservation (all GeV): E_final=" << E_final / 1_GeV
<< ", Ecm_final=" << Ecm_final / 1_GeV
<< ", Plab_final=" << (Plab_final / 1_GeV).GetComponents()
<< std::endl;
}
}
return process::EProcessReturn::eOk;
}
private:
corsika::environment::Environment const& fEnvironment;
};
} // namespace corsika::process::sibyll
#endif