IAP GITLAB

Skip to content
Snippets Groups Projects
NuclearInteraction.h 7.56 KiB
Newer Older
Felix Riehn's avatar
Felix Riehn committed

/**
 * (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_nuclearinteraction_h_
#define _corsika_process_sibyll_nuclearinteraction_h_

#include <corsika/process/InteractionProcess.h>

#include <corsika/environment/Environment.h>
#include <corsika/environment/NuclearComposition.h>
#include <corsika/particles/ParticleProperties.h>
#include <corsika/process/sibyll/ParticleConversion.h>
#include <corsika/process/sibyll/SibStack.h>
#include <corsika/process/sibyll/sibyll2.3c.h>
#include <corsika/random/RNGManager.h>
#include <corsika/units/PhysicalUnits.h>
#include <corsika/utl/COMBoost.h>

namespace corsika::process::sibyll {

  class NuclearInteraction : public corsika::process::InteractionProcess<NuclearInteraction> {

    int fCount = 0;
    int fNucCount = 0;

  public:
    NuclearInteraction(corsika::environment::Environment const& env)
        : fEnvironment(env) {}
    ~NuclearInteraction() {
      std::cout << "Sibyll::NuclearInteraction n=" << fCount << " Nnuc=" << fNucCount
                << std::endl;
    }

    void Init() {

      using corsika::random::RNGManager;
      using std::cout;
      using std::endl;

      // initialize hadronic interaction module
      sibyll_ini_();
      
      // initialize nuclib
      nuc_nuc_ini_();
    }

    std::tuple<corsika::units::si::CrossSectionType, int> GetCrossSection(
        const corsika::particles::Code BeamId, const corsika::particles::Code TargetId,
        const corsika::units::si::HEPEnergyType CoMenergy) {
      using namespace corsika::units::si;
      double sigProd, dummy, dum1, dum2, dum3, dum4;
      double dumdif[3];
      
      if(!corsika::particles::IsNucleus(BeamId)){
	sigProd = std::numeric_limits<double>::infinity();
	return std::make_tuple(sigProd * 1_mbarn, 1);
      }

      // TODO: use nuclib to calc. nuclear cross sections
      // FOR NOW: use proton cross section for nuclei
      auto const BeamIdToUse = corsika::particles::Proton::GetCode();
      std::cout << "WARNING: replacing beam nucleus with proton!" << std::endl;
      
      const int iBeam = process::sibyll::GetSibyllXSCode(BeamIdToUse);
      const double dEcm = CoMenergy / 1_GeV;
      if (corsika::particles::IsNucleus(TargetId)) {
	const int iTarget = corsika::particles::GetNucleusA(TargetId);
	if (iTarget > 18 || iTarget == 0)
	  throw std::runtime_error(
				   "Sibyll target outside range. Only nuclei with A<18 are allowed.");
	sib_sigma_hnuc_(iBeam, iTarget, dEcm, sigProd, dummy);
	return std::make_tuple(sigProd * 1_mbarn, iTarget);
      } else if (TargetId == corsika::particles::Proton::GetCode()) {
	sib_sigma_hp_(iBeam, dEcm, dum1, dum2, sigProd, dumdif, dum3, dum4);
	return std::make_tuple(sigProd * 1_mbarn, 1);
      } else {
	throw std::runtime_error("GetCrossSection: no interaction in sibyll possible");
	// no interaction in sibyll possible, return infinite cross section? or throw?
	sigProd = std::numeric_limits<double>::infinity();
	return std::make_tuple(sigProd * 1_mbarn, 0);
      }
    }

    template <typename Particle, typename Track>
    corsika::units::si::GrammageType GetInteractionLength(Particle const& p, Track&) {

      using namespace corsika::units;
      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();

      if(!corsika::particles::IsNucleus(corsikaBeamId))
	// no nuclear interaction
	return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);

      // beam particles for sibyll : 1, 2, 3 for p, pi, k
      // read from cross section code table

      const HEPMassType 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
      HEPEnergyType Elab = p.GetEnergy() + nucleon_mass;
      MomentumVector pTotLab(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
      pTotLab += p.GetMomentum();
      pTotLab += pTarget;
      auto const pTotLabNorm = pTotLab.norm();
      // calculate cm. energy
      const HEPEnergyType ECoM = sqrt(
				      (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy

      std::cout << "NuclearInteraction: LambdaInt: \n"
		<< " input energy: " << p.GetEnergy() / 1_GeV << endl
		<< " beam pid:" << p.GetPID() << endl;

      if ( Elab >= 8.5_GeV && ECoM >= 10_GeV) {

	// 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
	// weighted sum
	int i = -1;
	double avgTargetMassNumber = 0.;
	si::CrossSectionType weightedProdCrossSection = 0_mbarn;
	// get weights of components from environment/medium
	const auto w = mediumComposition.GetFractions();
	// loop over components in medium
	for (auto const targetId : mediumComposition.GetComponents()) {
	  i++;
	  cout << "NuclearInteraction: get interaction length for target: " << targetId << endl;

	  auto const [productionCrossSection, numberOfNucleons] =
	    GetCrossSection(corsikaBeamId, targetId, ECoM);

	  std::cout << "NuclearInteraction: "
		    << " IntLength: sibyll return (mb): "
		    << productionCrossSection / 1_mbarn << std::endl;
	  weightedProdCrossSection += w[i] * productionCrossSection;
	  avgTargetMassNumber += w[i] * numberOfNucleons;
	}
	cout << "NuclearInteraction: "
	     << "IntLength: weighted CrossSection (mb): "
	     << weightedProdCrossSection / 1_mbarn << endl;

	// calculate interaction length in medium
	GrammageType const int_length =
	  avgTargetMassNumber * corsika::units::constants::u / weightedProdCrossSection;
	std::cout << "NuclearInteraction: "
		  << "interaction length (g/cm2): "
		  << int_length / (0.001_kg) * 1_cm * 1_cm << std::endl;

	return int_length;
      } else {
	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::si;
      using namespace corsika::geometry;
      using std::cout;
      using std::endl;

      const auto corsikaBeamId = p.GetPID();
      const auto kNucleus = IsNucleus(corsikaBeamId);
      if(!kNucleus)
	return process::EProcessReturn::eOk;
      

      // add proton instead
      auto pnew = s.NewParticle();
      pnew.SetPID( corsika::particles::Proton::GetCode() );
      pnew.SetMomentum( p.GetMomentum() );
      pnew.SetEnergy( p.GetEnergy() );	

      // delete current particle
      p.Delete();

      return process::EProcessReturn::eOk;
    }

  private:
    corsika::environment::Environment const& fEnvironment;
    corsika::random::RNG& fRNG =
        corsika::random::RNGManager::GetInstance().GetRandomStream("s_rndm");
  };

} // namespace corsika::process::sibyll

#endif