MUSIC unknown
Analysis for the MUSIC active-target ionization chamber
Loading...
Searching...
No Matches
main_talys_xs.cpp
Go to the documentation of this file.
1// talys-xs: Hauser-Feshbach cross sections from TALYS for this dataset's
2// reaction, written as ROOT graphs cross-section overlays.
3//
4// Runs TALYS in normal kinematics (alpha on the beam nucleus as target; the
5// cross section is the same either way) over a grid of centre-of-mass
6// energies spanning the strips the cross section reports and the reference
7// table, once per configured model, and writes every residual-production
8// channel TALYS produced as a TGraph of E_cm [MeV] vs sigma [mb], named by
9// TALYS's own file stem (rp039090 for Z=39, A=90), into one directory per
10// model (m0, m1, ...) of root_files/talys/talys_xs.root, with the model's
11// label and exact input as TNameds. Which channels make up (a,xn) is the
12// analysis's business, so cross-section sums them itself. Like srim-cache in
13// the simulator, this is the one place the external code is driven;
14// everything downstream only reads the file.
15//
16// TALYS comes from the talys-nix flake input, whose path the build compiles
17// in; TALYS_BIN in the environment overrides it.
18#include "BeamEnergies.hpp"
19#include "Constants.hpp"
20#include "InitUtils.hpp"
21#include "Paths.hpp"
22#include <TDirectory.h>
23#include <TFile.h>
24#include <TGraph.h>
25#include <TMath.h>
26#include <TNamed.h>
27#include <TString.h>
28#include <TSystem.h>
29#include <TSystemDirectory.h>
30#include <cmath>
31#include <cstdio>
32#include <cstdlib>
33#include <fstream>
34#include <iostream>
35#include <sstream>
36#include <string>
37#include <vector>
38
39#ifndef MUSIC_TALYS_BIN
40#define MUSIC_TALYS_BIN ""
41#endif
42
43namespace {
44
45// Grid step and the margin past the reported strips' energies. TALYS is
46// cheap at these energies, so the grid errs on the fine side.
47const Double_t kEcmStep = 0.25;
48const Double_t kEcmMargin = 1.0;
49
50// One TALYS residual-production file: E [MeV] and xs [mb] rows after the
51// YANDF header, with the lab energy converted back to centre-of-mass.
52TGraph *ReadResidual(const TString &path, Double_t cm_per_lab) {
53 std::ifstream in(path.Data());
54 if (!in)
55 return nullptr;
56 std::vector<Double_t> e, xs;
57 std::string line;
58 while (std::getline(in, line)) {
59 if (line.empty() || line[0] == '#')
60 continue;
61 std::istringstream ss(line);
62 Double_t el = 0.0, s = 0.0;
63 if (!(ss >> el >> s))
64 continue;
65 e.push_back(el * cm_per_lab);
66 xs.push_back(s);
67 }
68 if (e.empty())
69 return nullptr;
70 return new TGraph(Int_t(e.size()), &e[0], &xs[0]);
71}
72
73// Run one model in its own work directory and write its graphs into `dir`.
74// Returns the number of residual channels written, -1 on a TALYS failure.
75Int_t RunModel(const TalysModel &model, const TString &work,
76 const TString &talys, const TString &energies,
77 Double_t cm_per_lab, TDirectory *dir) {
78 const CrossSectionConfig &X = Constants::cfg.CROSS_SECTION_CONFIG;
79 gSystem->mkdir(work, kTRUE);
80 TString input;
81 input += "# Written by talys-xs for " + Paths::DatasetName() + ": " +
82 model.label + "\n";
83 input += "projectile a\n";
84 input += "element " + X.BEAM_ELEMENT + "\n";
85 input += Form("mass %d\n", X.BEAM_A);
86 input += "energy energies\n";
87 for (Int_t k = 0; k < Int_t(model.keywords.size()); k++)
88 input += model.keywords[k] + "\n";
89 {
90 std::ofstream en((work + "/energies").Data());
91 en << energies;
92 std::ofstream inp((work + "/talys.inp").Data());
93 inp << input;
94 }
95 for (Int_t k = 0; k < Int_t(model.keywords.size()); k++)
96 std::cout << " " << model.keywords[k] << std::endl;
97 const TString cmd = Form("cd '%s' && '%s' < talys.inp > talys.out 2>&1",
98 work.Data(), talys.Data());
99 if (std::system(cmd.Data()) != 0) {
100 std::cerr << "talys-xs: TALYS failed; see " << work << "/talys.out"
101 << std::endl;
102 return -1;
103 }
104
105 dir->cd();
106 TNamed("label", model.label.Data()).Write();
107 TNamed("input", input.Data()).Write();
108 Int_t n_graphs = 0;
109 TSystemDirectory sd("work", work);
110 TList *files = sd.GetListOfFiles();
111 for (TIter it(files); TObject *o = it();) {
112 const TString name = o->GetName();
113 Int_t z = 0, a = 0;
114 if (!name.EndsWith(".tot") || sscanf(name.Data(), "rp%3d%3d", &z, &a) != 2)
115 continue;
116 TGraph *g = ReadResidual(work + "/" + name, cm_per_lab);
117 if (!g)
118 continue;
119 g->SetName(Form("rp%03d%03d", z, a));
120 g->SetTitle(Form("Z=%d A=%d residual production;E_{c.m.} [MeV];#sigma "
121 "[mb]",
122 z, a));
123 g->Write();
124 delete g;
125 n_graphs++;
126 }
127 delete files;
128 return n_graphs;
129}
130
131} // namespace
132
133int main() {
134 InitUtils::SetROOTPreferences(PlotSaveFormat::kPNG,
135 Paths::ResultsDir() + "/plots",
136 Paths::ResultsDir() + "/root_files");
137 const CrossSectionConfig &X = Constants::cfg.CROSS_SECTION_CONFIG;
138 if (X.BEAM_A <= 0 || X.BEAM_Z <= 0 || X.BEAM_ELEMENT.IsNull() ||
139 X.BEAM_SIM_FILE.IsNull() || X.TALYS_MODELS.empty()) {
140 std::cerr << "talys-xs: this dataset's CROSS_SECTION_CONFIG needs BEAM_A, "
141 "BEAM_Z, BEAM_ELEMENT, BEAM_SIM_FILE and at least one "
142 "TALYS model"
143 << std::endl;
144 return 1;
145 }
146 const Char_t *env = gSystem->Getenv("TALYS_BIN");
147 TString talys = (env && env[0] != '\0') ? TString(env) : MUSIC_TALYS_BIN;
148 if (talys.IsNull())
149 talys = "talys";
150
151 // The grid spans the strips the cross section reports, with a margin, at
152 // the energies it reports them at, and the reference table so the curve
153 // is drawn under every point.
154 Double_t dE[18];
155 Double_t e_strip0 = 0.0;
156 if (!BeamEnergies::Profile(BeamEnergies::SimPath(), dE, e_strip0)) {
157 std::cerr << "talys-xs: cannot read the simulated beam at "
158 << BeamEnergies::SimPath() << std::endl;
159 return 1;
160 }
161 const Double_t cm_frac = BeamEnergies::CmFraction();
162 // TALYS runs the reaction the other way round, alpha on the beam nucleus,
163 // so its lab energy is E_cm * (A_b + A_t) / A_b.
164 const Double_t lab_per_cm =
165 Double_t(X.BEAM_A + TargetGasA(X.TARGET_GAS)) / Double_t(X.BEAM_A);
166 Double_t e_hi =
167 BeamEnergies::LabAtStrip(dE, e_strip0, X.XS_STRIP_MIN) * cm_frac +
168 kEcmMargin;
169 Double_t e_lo = TMath::Max(
170 kEcmStep, (BeamEnergies::LabAtStrip(dE, e_strip0, X.XS_STRIP_MAX) -
171 dE[X.XS_STRIP_MAX]) *
172 cm_frac -
173 kEcmMargin);
174 // ...widened to cover every channel's published table.
175 for (Int_t c = 0; c < Int_t(X.CHANNELS.size()); c++)
176 for (Int_t k = 0; k < Int_t(X.CHANNELS[c].reference_xs.size()); k++) {
177 e_lo = TMath::Min(e_lo, X.CHANNELS[c].reference_xs[k][0] - kEcmMargin);
178 e_hi = TMath::Max(e_hi, X.CHANNELS[c].reference_xs[k][0] + kEcmMargin);
179 }
180 const Double_t ecm_lo =
181 kEcmStep * std::floor(TMath::Max(kEcmStep, e_lo) / kEcmStep);
182 const Double_t ecm_hi = kEcmStep * std::ceil(e_hi / kEcmStep);
183 TString energies;
184 for (Double_t ecm = ecm_lo; ecm <= ecm_hi + 1.0e-9; ecm += kEcmStep)
185 energies += Form("%.4f\n", ecm * lab_per_cm);
186
187 // Everything TALYS touches lives under root_files/talys, which git
188 // ignores: one run per model under work/, the graphs beside them.
189 const TString talys_dir = Paths::ResultsDir() + "/root_files/talys";
190 const TString out_path = talys_dir + "/talys_xs.root";
191 gSystem->mkdir(talys_dir, kTRUE);
192 TFile out(out_path, "RECREATE");
193 if (out.IsZombie()) {
194 std::cerr << "talys-xs: cannot write " << out_path << std::endl;
195 return 1;
196 }
197 TNamed("talys", talys.Data()).Write();
198 std::cout << "talys-xs: alpha + " << X.BEAM_A << X.BEAM_ELEMENT << ", E_cm "
199 << ecm_lo << ".." << ecm_hi << " MeV (strips " << X.XS_STRIP_MIN
200 << ".." << X.XS_STRIP_MAX << "), " << X.TALYS_MODELS.size()
201 << " model(s), " << talys << std::endl;
202 for (Int_t m = 0; m < Int_t(X.TALYS_MODELS.size()); m++) {
203 const TalysModel &model = X.TALYS_MODELS[m];
204 std::cout << " m" << m << ": " << model.label << std::endl;
205 TDirectory *dir = out.mkdir(Form("m%d", m));
206 const Int_t n = RunModel(model, talys_dir + Form("/work/m%d", m), talys,
207 energies, 1.0 / lab_per_cm, dir);
208 if (n <= 0) {
209 if (n == 0)
210 std::cerr << "talys-xs: no residual-production files for "
211 << model.label << std::endl;
212 return 1;
213 }
214 std::cout << " " << n << " residual channels" << std::endl;
215 }
216 out.Close();
217 std::cout << "talys-xs: -> " << out_path << std::endl;
218 return 0;
219}
The dataset configuration, and how it is layered.
Int_t TargetGasA(TargetGas gas)
Mass number of the target nucleus in a fill gas.
Definition Constants.cpp:3
static TString DatasetName()
The dataset's isotope name, e.g.
Definition Paths.cpp:22
static TString ResultsDir()
Absolute path to the directory receiving generated output.
Definition Paths.cpp:24
#define MUSIC_TALYS_BIN
int main()
Double_t LabAtStrip(const Double_t *dE, Double_t e_strip0, Int_t reac)
Lab energy entering a given strip.
Bool_t Profile(const TString &path, Double_t *dE, Double_t &e_strip0)
Read the per-strip energy loss profile from a beam simulation.
Double_t CmFraction()
Centre-of-mass energy fraction for this dataset's reaction.
TString SimPath()
Path to the dataset's beam simulation file.
const DatasetConfig & cfg
The active dataset's configuration, flat block.
What the cross section needs about the experiment rather than the analysis.
Int_t XS_STRIP_MIN
Reaction strips to report a cross section for.
TString BEAM_SIM_FILE
Simulated unreacted beam, read for the energy at each strip.
Int_t BEAM_A
Beam mass number, for the lab-to-centre-of-mass conversion; its Z and element symbol name it to a rea...
std::vector< TalysModel > TALYS_MODELS
Hauser-Feshbach predictions from TALYS.
std::vector< CrossSectionChannel > CHANNELS
The reaction channels measured on this dataset.
One TALYS model variant to compare against.
TString label
Legend label for this model's curve.
std::vector< TString > keywords
TALYS keywords selecting the variant.