Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MagneticFieldOptions.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file MagneticFieldOptions.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019-2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
21 
22 #include <filesystem>
23 #include <memory>
24 #include <stdexcept>
25 #include <string>
26 
27 #include <boost/program_options.hpp>
28 
30  using boost::program_options::bool_switch;
32 
33  // avoid adding the options twice
34  if (desc.find_nothrow("bf-constant-tesla", true) != nullptr) {
35  return;
36  }
37 
38  auto opt = desc.add_options();
39  opt("bf-constant-tesla", value<Reals<3>>(),
40  "Set a constant magnetic field vector in Tesla. If given, this takes "
41  "preference over all other options.");
42  opt("bf-scalable", bool_switch(),
43  "If given, the constant field strength will be scaled differently in "
44  "every event. This is for testing only.");
45  opt("bf-scalable-scalor", value<double>()->default_value(1.25),
46  "Scaling factor for the event-dependent field strength scaling. A unit "
47  "value means that the field strength stays the same for every event.");
48  opt("bf-map-file", value<std::string>(),
49  "Read a magnetic field map from the given file. ROOT and text file "
50  "formats are supported. Only used if no constant field is given.");
51  opt("bf-map-tree", value<std::string>()->default_value("bField"),
52  "Name of the TTree in the ROOT file. Only used if the field map is read "
53  "from a ROOT file.");
54  opt("bf-map-type", value<std::string>()->default_value("xyz"),
55  "Either 'xyz' or 'rz' to define the type of the field map.");
56  opt("bf-map-octantonly", bool_switch(),
57  "If given, the field map is assumed to describe only the first "
58  "octant/quadrant and the field is symmetrically extended to the full "
59  "space.");
60  opt("bf-map-lengthscale-mm", value<double>()->default_value(1.),
61  "Optional length scale modifier for the field map grid. This options "
62  "only needs to be set if the length unit in the field map file is not "
63  "`mm`. The value must scale from the stored unit to the equivalent value "
64  "in `mm`.");
65  opt("bf-map-fieldscale-tesla", value<double>()->default_value(1.),
66  "Optional field value scale modifier for the field map value. This "
67  "option only needs to be set if the field value unit in the field map "
68  "file is not `Tesla`. The value must scale from the stored unit to the "
69  "equivalent value in `Tesla`.");
70  opt("bf-solenoid-mag-tesla", value<double>()->default_value(0.),
71  "The magnitude of a solenoid magnetic field in the center in `Tesla`. "
72  "Only used "
73  "if neither constant field nor a magnetic field map is given.");
74  opt("bf-solenoid-length", value<double>()->default_value(6000),
75  "The length of the solenoid magnetic field in `mm`.");
76  opt("bf-solenoid-radius", value<double>()->default_value(1200),
77  "The radius of the solenoid magnetic field in `mm`.");
78  opt("bf-solenoid-ncoils", value<size_t>()->default_value(1194),
79  "Number of coils for the solenoid magnetic field.");
80  opt("bf-solenoid-map-rlim",
81  value<Interval>()->value_name("MIN:MAX")->default_value({0, 1200}),
82  "The length bounds of the grid created from the analytical solenoid "
83  "field in `mm`.");
84  opt("bf-solenoid-map-zlim",
85  value<Interval>()->value_name("MIN:MAX")->default_value({-3000, 3000}),
86  "The radius bounds of the grid created from the analytical solenoid "
87  "field in `mm`.");
88  opt("bf-solenoid-map-nbins", value<Reals<2>>()->default_value({{150, 200}}),
89  "The number of bins in r-z directions for the grid created from the "
90  "analytical solenoid field.");
91 }
92 
94  Sequencer& sequencer) {
95  if (vars["bf-scalable"].as<bool>()) {
97  sbfCfg.scalor = vars["bf-scalable-scalor"].as<double>();
98  sequencer.addContextDecorator(
99  std::make_shared<ScalableBFieldService>(sbfCfg, Acts::Logging::INFO));
100  }
101 }
102 
103 std::shared_ptr<Acts::MagneticFieldProvider>
105  using namespace ActsExamples::detail;
106  using std::filesystem::path;
107 
109  Acts::getDefaultLogger("MagneticField", Acts::Logging::INFO));
110 
111  // first option: create a constant field
112  if (vars.count("bf-constant-tesla") != 0u) {
113  const auto values = vars["bf-constant-tesla"].as<Reals<3>>();
115  values[1] * Acts::UnitConstants::T,
116  values[2] * Acts::UnitConstants::T);
117  if (vars["bf-scalable"].as<bool>()) {
118  ACTS_INFO("Use a constant magnetic field with per-event scaling");
119  return std::make_shared<ScalableBField>(field);
120  } else {
121  ACTS_INFO("Use a constant magnetic field");
122  return std::make_shared<Acts::ConstantBField>(field);
123  }
124  }
125 
126  // second option: read a field map from a file
127  if (vars.count("bf-map-file") != 0u) {
128  const path file = vars["bf-map-file"].as<std::string>();
129  const auto tree = vars["bf-map-tree"].as<std::string>();
130  const auto type = vars["bf-map-type"].as<std::string>();
131  const auto useOctantOnly = vars["bf-map-octantonly"].as<bool>();
132  const auto lengthUnit =
133  vars["bf-map-lengthscale-mm"].as<double>() * Acts::UnitConstants::mm;
134  const auto fieldUnit =
135  vars["bf-map-fieldscale-tesla"].as<double>() * Acts::UnitConstants::T;
136 
137  bool readRoot = false;
138  if (file.extension() == ".root") {
139  ACTS_INFO("Read magnetic field map from ROOT file '" << file << "'");
140  readRoot = true;
141  } else if (file.extension() == ".txt") {
142  ACTS_INFO("Read magnetic field map from text file '" << file << "'");
143  readRoot = false;
144  } else {
145  ACTS_ERROR("'" << file
146  << "' is an unsupported magnetic field map file type");
147  throw std::runtime_error("Unsupported magnetic field map file type");
148  }
149 
150  if (type == "xyz") {
151  auto mapBins = [](std::array<size_t, 3> bins,
152  std::array<size_t, 3> sizes) {
153  return (bins[0] * (sizes[1] * sizes[2]) + bins[1] * sizes[2] + bins[2]);
154  };
155 
156  ACTS_INFO("Use XYZ field map");
157  if (readRoot) {
159  std::move(mapBins), file.native(), tree, lengthUnit, fieldUnit,
160  useOctantOnly);
161  return std::make_shared<InterpolatedMagneticField3>(std::move(map));
162 
163  } else {
164  auto map = makeMagneticFieldMapXyzFromText(std::move(mapBins),
165  file.native(), lengthUnit,
166  fieldUnit, useOctantOnly);
167  return std::make_shared<InterpolatedMagneticField3>(std::move(map));
168  }
169 
170  } else if (type == "rz") {
171  auto mapBins = [](std::array<size_t, 2> bins,
172  std::array<size_t, 2> sizes) {
173  return (bins[1] * sizes[0] + bins[0]);
174  };
175 
176  ACTS_INFO("Use RZ field map");
177  if (readRoot) {
179  std::move(mapBins), file.native(), tree, lengthUnit, fieldUnit,
180  useOctantOnly);
181  return std::make_shared<InterpolatedMagneticField2>(std::move(map));
182 
183  } else {
184  auto map = makeMagneticFieldMapRzFromText(std::move(mapBins),
185  file.native(), lengthUnit,
186  fieldUnit, useOctantOnly);
187  return std::make_shared<InterpolatedMagneticField2>(std::move(map));
188  }
189 
190  } else {
191  ACTS_ERROR("'" << type << "' is an unknown magnetic field map type");
192  throw std::runtime_error("Unknown magnetic field map type");
193  }
194  }
195 
196  // third option: create a solenoid field
197  if (vars["bf-solenoid-mag-tesla"].as<double>() > 0) {
198  // Construct a solenoid field
199  Acts::SolenoidBField::Config solenoidConfig{};
200  solenoidConfig.length =
201  vars["bf-solenoid-length"].as<double>() * Acts::UnitConstants::mm;
202  solenoidConfig.radius =
203  vars["bf-solenoid-radius"].as<double>() * Acts::UnitConstants::mm;
204  solenoidConfig.nCoils = vars["bf-solenoid-ncoils"].as<size_t>();
205  solenoidConfig.bMagCenter =
206  vars["bf-solenoid-mag-tesla"].as<double>() * Acts::UnitConstants::T;
207  ACTS_INFO("Use solenoid magnetic field with magnitude "
208  << solenoidConfig.bMagCenter / Acts::UnitConstants::T
209  << " Tesla at the center.");
210  const auto solenoidField = Acts::SolenoidBField(solenoidConfig);
211  // The parameters for creating a field map
212  auto getRange = [&](const char* name, auto unit, auto& lower, auto& upper) {
213  auto interval = vars[name].as<Options::Interval>();
214  lower = interval.lower.value() * unit;
215  upper = interval.upper.value() * unit;
216  };
217  std::pair<double, double> rlim, zlim;
218  getRange("bf-solenoid-map-rlim", Acts::UnitConstants::mm, rlim.first,
219  rlim.second);
220  getRange("bf-solenoid-map-zlim", Acts::UnitConstants::mm, zlim.first,
221  zlim.second);
222  const auto nbins = vars["bf-solenoid-map-nbins"].as<Reals<2>>();
223  auto map =
224  Acts::solenoidFieldMap(rlim, zlim, {nbins[0], nbins[1]}, solenoidField);
225  return std::make_shared<InterpolatedMagneticField2>(std::move(map));
226  }
227 
228  // default option: no field
229  ACTS_INFO("Use no magnetic field");
230  return std::make_shared<Acts::NullBField>();
231 }