44 #include "art/Framework/Core/EDProducer.h"
45 #include "art/Framework/Principal/Event.h"
46 #include "art/Framework/Principal/Run.h"
47 #include "fhiclcpp/ParameterSet.h"
48 #include "art/Framework/Principal/Handle.h"
49 #include "art/Framework/Services/Registry/ServiceHandle.h"
50 #include "art/Framework/Core/ModuleMacros.h"
51 #include "messagefacility/MessageLogger/MessageLogger.h"
52 #include "cetlib_except/exception.h"
53 #include "cetlib/search_path.h"
54 #include "cetlib/exempt_ptr.h"
57 #include "nurandom/RandomUtils/NuRandomService.h"
60 #include "nusimdata/SimulationBase/MCTruth.h"
61 #include "nusimdata/SimulationBase/MCParticle.h"
62 #include "nugen/EventGeneratorBase/evgenbase.h"
86 #include "TGenPhaseSpace.h"
88 #include "TGeoManager.h"
89 #include "TGeoMaterial.h"
91 #include "TGeoVolume.h"
95 #include "TLorentzVector.h"
98 #include "CLHEP/Random/RandFlat.h"
99 #include "CLHEP/Random/RandPoisson.h"
101 namespace simb {
class MCTruth; }
190 explicit RadioGen(fhicl::ParameterSet
const& pset);
202 std::size_t
addvolume(std::string
const& volumeName);
208 TLorentzVector
dirCalc(
double p,
double m);
211 void samplespectrum(std::string nuclide,
int &itype,
double &t,
double &
m,
double &
p);
222 template <
typename Stream>
224 (
Stream&& out, std::size_t iNucl, std::string
const& volumeName = {})
274 constexpr
double m_e = 0.000510998928;
275 constexpr
double m_alpha = 3.727379240;
276 constexpr
double m_neutron = 0.9395654133;
285 , fX0{pset.get< std::vector<double> >(
"X0", {})}
286 ,
fY0{pset.get< std::vector<double> >(
"Y0", {})}
287 ,
fZ0{pset.get< std::vector<double> >(
"Z0", {})}
288 ,
fX1{pset.get< std::vector<double> >(
"X1", {})}
289 ,
fY1{pset.get< std::vector<double> >(
"Y1", {})}
290 ,
fZ1{pset.get< std::vector<double> >(
"Z1", {})}
294 ,
fEngine(art::ServiceHandle<rndm::NuRandomService>{}->createEngine(*
this, pset,
"Seed"))
296 produces< std::vector<simb::MCTruth> >();
297 produces< sumdata::RunData, art::InRun >();
300 auto const nuclide = pset.get< std::vector<std::string>>(
"Nuclide");
301 auto const material = pset.get< std::vector<std::string>>(
"Material");
302 auto const Bq = pset.get< std::vector<double> >(
"BqPercc");
303 auto t0 = pset.get< std::vector<double> >(
"T0", {});
304 auto t1 = pset.get< std::vector<double> >(
"T1", {});
306 if (
fT0.empty() || fT1.empty()) {
307 if (!
fT0.empty() || !fT1.empty()) {
308 throw art::Exception(art::errors::Configuration)
309 <<
"RadioGen T0 and T1 need to be both non-empty, or both empty"
310 " (now T0 has " <<
fT0.size() <<
" entries and T1 has " <<
fT0.size()
313 auto const [ defaultT0, defaultT1 ] = defaulttimewindow();
314 t0.push_back(defaultT0);
315 t1.push_back(defaultT1);
324 mf::LogInfo(
"RadioGen") <<
"Configuring activity:";
327 fNuclide.push_back(nuclide.at(iCoord));
328 fMaterial.push_back(material.at(iCoord));
329 fBq.push_back(Bq.at(iCoord));
331 fT0.push_back(t0[std::min(iCoord, t0.size() - 1U)]);
332 fT1.push_back(t1[std::min(iCoord, t1.size() - 1U)]);
334 dumpNuclideSettings(mf::LogVerbatim(
"RadioGen"), iCoord);
338 std::size_t iNext = fX0.size();
339 auto const volumes = pset.get<std::vector<std::string>>(
"Volumes", {});
342 auto const nVolumes = addvolume(volName);
344 throw art::Exception(art::errors::Configuration)
345 <<
"No volume named '" << volName <<
"' was found!\n";
348 std::size_t
const iVolFirst = fNuclide.size();
350 fNuclide.push_back(nuclide.at(iNext));
351 fMaterial.push_back(material.at(iNext));
352 fBq.push_back(Bq.at(iNext));
354 fT0.push_back(t0[std::min(iNext, t0.size() - 1U)]);
355 fT1.push_back(t1[std::min(iNext, t1.size() - 1U)]);
358 (mf::LogVerbatim(
"RadioGen"), iVolFirst + iVolInstance, volName);
365 unsigned int nsize = fNuclide.size();
367 throw art::Exception(art::errors::Configuration)
368 <<
"No nuclide configured!\n";
371 if ( fMaterial.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size Material vector and Nuclide vector\n";
372 if ( fBq.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size Bq vector and Nuclide vector\n";
373 if (
fT0.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size T0 vector and Nuclide vector\n";
374 if ( fT1.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size T1 vector and Nuclide vector\n";
375 if ( fX0.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size X0 vector and Nuclide vector\n";
376 if (
fY0.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size Y0 vector and Nuclide vector\n";
377 if (
fZ0.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size Z0 vector and Nuclide vector\n";
378 if (
fX1.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size X1 vector and Nuclide vector\n";
379 if (
fY1.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size Y1 vector and Nuclide vector\n";
380 if (
fZ1.size() != nsize )
throw cet::exception(
"RadioGen") <<
"Different size Z1 vector and Nuclide vector\n";
382 for(std::string & nuclideName : fNuclide){
383 if(nuclideName==
"39Ar" ){readfile(
"39Ar",
"Argon_39.root") ;}
384 else if(nuclideName==
"60Co" ){readfile(
"60Co",
"Cobalt_60.root") ;}
385 else if(nuclideName==
"85Kr" ){readfile(
"85Kr",
"Krypton_85.root") ;}
386 else if(nuclideName==
"40K" ){readfile(
"40K",
"Potassium_40.root") ;}
387 else if(nuclideName==
"232Th"){readfile(
"232Th",
"Thorium_232.root");}
388 else if(nuclideName==
"238U" ){readfile(
"238U",
"Uranium_238.root") ;}
389 else if(nuclideName==
"222Rn"){
continue;}
390 else if(nuclideName==
"59Ni"){
continue;}
391 else if(nuclideName==
"42Ar" ){
392 readfile(
"42Ar_1",
"Argon_42_1.root");
393 readfile(
"42Ar_2",
"Argon_42_2.root");
394 readfile(
"42Ar_3",
"Argon_42_3.root");
395 readfile(
"42Ar_4",
"Argon_42_4.root");
396 readfile(
"42Ar_5",
"Argon_42_5.root");
400 std::string searchName = nuclideName;
402 readfile(nuclideName,searchName);
410 art::ServiceHandle<geo::Geometry const> geo;
411 run.put(std::make_unique<sumdata::RunData>(geo->DetectorName()));
418 std::unique_ptr< std::vector<simb::MCTruth> > truthcol(
new std::vector<simb::MCTruth>);
421 truth.SetOrigin(simb::kSingleParticle);
424 for (
unsigned int i=0; i<fNuclide.size(); ++i) {
428 MF_LOG_DEBUG(
"RadioGen") << truth;
429 truthcol->push_back(truth);
430 evt.put(std::move(truthcol));
436 auto const& geom = *(lar::providerFrom<geo::Geometry>());
438 std::vector<geo::GeoNodePath> volumePaths;
439 auto findVolume = [&volumePaths, volumeName](
auto&
path)
441 if (
path.current().GetVolume()->GetName() == volumeName)
442 volumePaths.push_back(
path);
448 navigator.
apply(findVolume);
455 TGeoShape
const* pShape =
path.current().GetVolume()->GetShape();
456 auto pBox =
dynamic_cast<TGeoBBox
const*
>(pShape);
458 throw cet::exception(
"RadioGen") <<
"Volume '"
459 <<
path.current().GetName() <<
"' is a " << pShape->IsA()->GetName()
460 <<
", not a TGeoBBox.\n";
464 = geo::vect::makeFromCoords<geo::Point_t>(pBox->GetOrigin());
466 = { pBox->GetDX(), pBox->GetDY(), pBox->GetDZ() };
476 trans.Transform(origin - diag, min);
477 trans.Transform(origin + diag, max);
482 fX0.push_back(std::min(min.X(), max.X()));
483 fY0.push_back(std::min(min.Y(), max.Y()));
484 fZ0.push_back(std::min(min.Z(), max.Z()));
485 fX1.push_back(std::max(min.X(), max.X()));
486 fY1.push_back(std::max(min.Y(), max.Y()));
487 fZ1.push_back(std::max(min.Z(), max.Z()));
491 return volumePaths.size();
500 art::ServiceHandle<geo::Geometry const> geo;
501 TGeoManager *geomanager = geo->ROOTGeoManager();
504 CLHEP::RandPoisson poisson(
fEngine);
510 long ndecays = poisson.shoot(rate);
512 std::regex
const re_material{
fMaterial[i]};
513 for (
unsigned int idecay=0; idecay<ndecays; idecay++)
520 TLorentzVector pos(
fX0[i] + flat.fire()*(
fX1[i] -
fX0[i]),
526 std::string volmaterial = geomanager->FindNode(pos.X(),pos.Y(),pos.Z())->GetMedium()->GetMaterial()->GetName();
527 if (!std::regex_match(volmaterial, re_material))
continue;
533 std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>> v_prods;
535 if (fNuclide[i] ==
"222Rn")
538 double energy = t +
m;
539 double p2 = energy*energy - m*
m;
542 v_prods.emplace_back(pdgid, m,
dirCalc(p, m));
544 else if(fNuclide[i] ==
"59Ni"){
546 v_prods.emplace_back(pdgid, m,
dirCalc(p,m));
548 else if(fNuclide[i] ==
"42Ar"){
550 double bSelect = flat.fire();
553 v_prods.emplace_back(pdgid, m,
dirCalc(p, m));
555 }
else if(bSelect<0.9954){
557 v_prods.emplace_back(pdgid, m,
dirCalc(p, m));
559 }
else if(bSelect<0.9988){
561 v_prods.emplace_back(pdgid, m,
dirCalc(p, m));
563 }
else if(bSelect<0.9993){
565 v_prods.emplace_back(pdgid, m,
dirCalc(p, m));
569 v_prods.emplace_back(pdgid, m,
dirCalc(p, m));
578 std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m,
dirCalc(p,m));
579 v_prods.push_back(partMassMom);
583 for(
auto prodEntry : v_prods){
586 ti_PDGID pdgid = std::get<0>(prodEntry);
588 TLorentzVector pvec = std::get<2>(prodEntry);
590 std::string primary(
"primary");
594 if (pdgid == 1000020040){
595 simb::MCParticle part(trackid, pdgid, primary,-1,m,1);
596 part.AddTrajectoryPoint(pos, pvec);
600 simb::MCParticle part(trackid, pdgid, primary);
601 part.AddTrajectoryPoint(pos, pvec);
613 double costheta = (2.0*flat.fire() - 1.0);
614 if (costheta < -1.0) costheta = -1.0;
615 if (costheta > 1.0) costheta = 1.0;
616 double const sintheta = sqrt(1.0-costheta*costheta);
617 double const phi = 2.0*M_PI*flat.fire();
618 return TLorentzVector{p*sintheta*std::cos(phi),
619 p*sintheta*std::sin(phi),
629 std::regex
const re_argon{
"42Ar.*"};
630 for (
size_t i=0; i<fNuclide.size(); i++)
632 if (fNuclide[i] == nuclide){
636 else if (std::regex_match(nuclide, re_argon) && fNuclide[i]==
"42Ar") {
643 Bool_t addStatus = TH1::AddDirectoryStatus();
644 TH1::AddDirectory(kFALSE);
648 cet::search_path sp(
"FW_SEARCH_PATH");
649 std::string fn2 =
"Radionuclides/";
651 std::string fullname;
652 sp.find_file(fn2, fullname);
654 if (fullname.empty() ||
stat(fullname.c_str(), &sb)!=0)
655 throw cet::exception(
"RadioGen") <<
"Input spectrum file "
657 <<
" not found in FW_SEARCH_PATH!\n";
659 TFile f(fullname.c_str(),
"READ");
660 TGraph *alphagraph = (TGraph*) f.Get(
"Alphas");
661 TGraph *betagraph = (TGraph*) f.Get(
"Betas");
662 TGraph *gammagraph = (TGraph*) f.Get(
"Gammas");
663 TGraph *neutrongraph = (TGraph*) f.Get(
"Neutrons");
667 int np = alphagraph->GetN();
668 double *
y = alphagraph->GetY();
673 auto alphahist = std::make_unique<TH1D>(name.c_str(),
"Alpha Spectrum",np,0,np);
674 for (
int i=0; i<np; i++) {
675 alphahist->SetBinContent(i+1,y[i]);
676 alphahist->SetBinError(i+1,0);
690 int np = betagraph->GetN();
692 double *
y = betagraph->GetY();
697 auto betahist = std::make_unique<TH1D>(name.c_str(),
"Beta Spectrum",np,0,np);
698 for (
int i=0; i<np; i++) {
699 betahist->SetBinContent(i+1,y[i]);
700 betahist->SetBinError(i+1,0);
713 int np = gammagraph->GetN();
714 double *
y = gammagraph->GetY();
719 auto gammahist = std::make_unique<TH1D>(name.c_str(),
"Gamma Spectrum",np,0,np);
720 for (
int i=0; i<np; i++) {
721 gammahist->SetBinContent(i+1,y[i]);
722 gammahist->SetBinError(i+1,0);
735 int np = neutrongraph->GetN();
736 double *
y = neutrongraph->GetY();
741 auto neutronhist = std::make_unique<TH1D>(name.c_str(),
"Neutron Spectrum",np,0,np);
742 for (
int i=0; i<np; i++)
744 neutronhist->SetBinContent(i+1,y[i]);
745 neutronhist->SetBinError(i+1,0);
757 TH1::AddDirectory(addStatus);
787 throw cet::exception(
"RadioGen") <<
"Ununderstood nuclide: " << nuclide <<
"\n";
790 double rtype = flat.fire();
795 for (
int itry=0;itry<10;itry++)
821 if (itype >= 0)
break;
825 throw cet::exception(
"RadioGen") <<
"Normalization problem with nuclide: " << nuclide <<
"\n";
842 int nbinsx = hist.GetNbinsX();
843 std::vector<double> partialsum;
844 partialsum.resize(nbinsx+1);
847 for (
int i=1;i<=nbinsx;i++)
849 double hc = hist.GetBinContent(i);
850 if ( hc < 0)
throw cet::exception(
"RadioGen") <<
"Negative bin: " << i <<
" " << hist.GetName() <<
"\n";
851 partialsum[i] = partialsum[i-1] + hc;
853 double integral = partialsum[nbinsx];
854 if (integral == 0)
return 0;
856 for (
int i=1;i<=nbinsx;i++) partialsum[i] /= integral;
858 double r1 = flat.fire();
859 int ibin = TMath::BinarySearch(nbinsx,&(partialsum[0]),r1);
860 Double_t
x = hist.GetBinLowEdge(ibin+1);
861 if (r1 > partialsum[ibin]) {
862 x += hist.GetBinWidth(ibin+1)*(r1-partialsum[ibin])/(partialsum[ibin+1] - partialsum[ibin]);
878 std::vector<double> vd_p = {.0015246};
880 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
887 std::vector<double> vd_p = {.0003126};
889 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
898 double chan1 = (0.052 / (0.052+0.020) );
899 if(flat.fire()<chan1){
900 std::vector<double> vd_p = {.0008997};
902 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
906 std::vector<double> vd_p = {.0024243};
908 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
917 double chan1 = ( 0.0033 / (0.0033 + 0.0201 + 0.041) );
double chan2 = ( 0.0201 / (0.0033 + 0.0201 + 0.041) );
918 double chanPick = flat.fire();
919 if(chanPick < chan1){
920 std::vector<double> vd_p = {0.000692, 0.001228};
922 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
925 }
else if (chanPick<(chan1+chan2)){
926 std::vector<double> vd_p = {0.0010212};
928 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
932 std::vector<double> vd_p = {0.0019208};
934 v_prods.emplace_back(pdgid, m,
dirCalc(
p, m));
963 template <
typename Stream>
965 (
Stream&& out, std::size_t iNucl, std::string
const& volumeName )
969 out <<
"[#" << iNucl <<
"] "
971 <<
" (" <<
fBq[iNucl] <<
" Bq/cm^3)"
973 <<
" from " <<
fT0[iNucl] <<
" to " <<
fT1[iNucl] <<
" ns in ( "
974 <<
fX0[iNucl] <<
", " <<
fY0[iNucl] <<
", " <<
fZ0[iNucl] <<
") to ( "
975 <<
fX1[iNucl] <<
", " <<
fY1[iNucl] <<
", " <<
fZ1[iNucl] <<
")";
976 if (!volumeName.empty()) out <<
" (\"" << volumeName <<
"\")";
984 using namespace detinfo::timescales;
987 (art::ServiceHandle<detinfo::DetectorClocksService>()->DataForJob());
989 = art::ServiceHandle<detinfo::DetectorPropertiesService>()->DataForJob();
997 auto const trigTimeTick
1000 { -
static_cast<int>(detInfo.ReadOutWindowSize()) };
1004 double(timings.toTimeScale<
simulation_time>(trigTimeTick + beforeTicks)),
1005 double(timings.toTimeScale<
simulation_time>(trigTimeTick + afterTicks))
std::vector< std::unique_ptr< TH1D > > gammaspectrum
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Vector_t
Type for representation of momenta in 3D space.
TLorentzVector dirCalc(double p, double m)
Utilities related to art service access.
timescale_traits< ElectronicsTimeCategory >::tick_t electronics_tick
A point on the electronics time scale expressed in its ticks.
bool apply(geo::GeoNodePath &path, Op &&op) const
Applies the specified operation to all nodes under the path.
process_name opflash particleana ie x
std::vector< double > fZ0
Bottom corner z position (cm) in world coordinates.
Executes an operation on all the nodes of the ROOT geometry.
Definition of util::enumerate().
std::vector< std::string > fNuclide
List of nuclides to simulate. Example: "39Ar".
std::pair< double, double > defaulttimewindow() const
Returns the start and end of the readout window.
BEGIN_PROLOG could also be dds filename
Class representing a path in ROOT geometry.
Class representing a path in ROOT geometry.
void Ar42Gamma5(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
void dumpNuclideSettings(Stream &&out, std::size_t iNucl, std::string const &volumeName={}) const
Prints the settings for the specified nuclide and volume.
void readfile(std::string nuclide, std::string const &filename)
tuple m
now if test mode generate materials, CRT shell, world, gdml header else just generate CRT shell for u...
std::vector< double > neutronintegral
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
std::vector< std::string > spectrumname
Interface to detinfo::DetectorClocks.
BEGIN_PROLOG triggeremu_data_config_icarus settings PMTADCthresholds sequence::icarus_stage0_multiTPC_TPC physics sequence::icarus_stage0_EastHits_TPC physics sequence::icarus_stage0_WestHits_TPC physics producers purityana0 caloskimCalorimetryCryoE physics caloskimCalorimetryCryoW physics path
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Access the description of detector geometry.
std::vector< std::unique_ptr< TH1D > > alphaspectrum
std::vector< double > fT1
End of time window to simulate in ns.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
std::vector< double > fY1
Top corner y position (cm) in world coordinates.
process_name opflash particleana ie ie y
timescale_traits< ElectronicsTimeCategory >::tick_interval_t electronics_time_ticks
An interval on the electronics time scale expressed in its ticks.
Definitions of geometry vector data types.
timescale_traits< SimulationTimeCategory >::time_point_t simulation_time
A point in time on the simulation time scale.
std::vector< double > fZ1
Top corner z position (cm) in world coordinates.
detinfo::DetectorTimings makeDetectorTimings(detinfo::DetectorClocksData const &detClocks)
int trackidcounter
Serial number for the MC track ID.
fEngine(art::ServiceHandle< rndm::NuRandomService >() ->createEngine(*this, pset,"Seed"))
Utilities to extend the interface of geometry vectors.
void SampleOne(unsigned int i, simb::MCTruth &mct)
Checks that the node represents a box well aligned to world frame axes.
void Ar42Gamma3(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
Test of util::counter and support utilities.
void beginRun(art::Run &run)
Module to generate particles created by radiological decay, patterned off of SingleGen.
double samplefromth1d(TH1D &hist)
std::vector< double > alphaintegral
std::size_t addvolume(std::string const &volumeName)
void samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p)
std::vector< double > betaintegral
void Ar42Gamma2(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
Var Sqrt(const Var &v)
Use to take sqrt of a var.
CLHEP::HepRandomEngine & fEngine
std::vector< double > fT0
Beginning of time window to simulate in ns.
Representation of a node and its ancestry.
void Ar42Gamma4(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
std::vector< std::unique_ptr< TH1D > > neutronspectrum
std::vector< std::string > fMaterial
List of regexes of materials in which to generate the decays. Example: "LAr".
std::vector< double > fX0
Bottom corner x position (cm) in world coordinates.
bool fIsFirstSignalSpecial
std::vector< double > gammaintegral
std::vector< double > fBq
Radioactivity in Becquerels (decay per sec) per cubic cm.
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
RadioGen(fhicl::ParameterSet const &pset)
std::vector< std::unique_ptr< TH1D > > betaspectrum
std::vector< double > fX1
Top corner x position (cm) in world coordinates.
art framework interface to geometry description
ROOT::Math::Transform3D TransformationMatrix
Type of transformation matrix used in geometry.
constexpr Point origin()
Returns a origin position with a point of the specified type.
std::vector< double > fY0
Bottom corner y position (cm) in world coordinates.
void produce(art::Event &evt)