Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:50

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "ActsExamples/Io/Csv/CsvBFieldWriter.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Units.hpp"
0013 #include "Acts/MagneticField/InterpolatedBFieldMap.hpp"
0014 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0015 #include "Acts/Utilities/Logger.hpp"
0016 #include "Acts/Utilities/Result.hpp"
0017 #include "Acts/Utilities/VectorHelpers.hpp"
0018 #include "ActsExamples/Io/Csv/CsvInputOutput.hpp"
0019 
0020 #include <iomanip>
0021 #include <ostream>
0022 #include <stdexcept>
0023 #include <vector>
0024 
0025 namespace ActsExamples {
0026 template <CsvBFieldWriter::CoordinateType Coord, bool Grid>
0027 void CsvBFieldWriter::run(const Config<Coord, Grid>& config,
0028                           Acts::Logging::Level level) {
0029   ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("CsvBFieldWriter", level));
0030 
0031   // Some helper typedefs, which will make our life easier down the line.
0032   using ConfigType = std::decay_t<decltype(config)>;
0033   using FieldType = typename decltype(ConfigType::bField)::element_type;
0034   using Vector = Acts::ActsVector<ConfigType::NDims>;
0035 
0036   FieldType& field = *config.bField;
0037 
0038   // Determine the columns to write to the CSV file. For Cartesian coordinates,
0039   // we give the x, y, and z coordinates for both the position and the field
0040   // vector. For cylindrical coordinates, we give r and z.
0041   std::vector<std::string> fields;
0042 
0043   if constexpr (Coord == CoordinateType::XYZ) {
0044     fields = {"x", "y", "z", "Bx", "By", "Bz"};
0045   } else {
0046     fields = {"r", "z", "Br", "Bz"};
0047   }
0048 
0049   // Initialize a CSV writer to the specified filename using the specified
0050   // column names.
0051   CsvWriter writer(fields, config.fileName);
0052 
0053   // We proceed by finding the number of bins, as well as the minimum and
0054   // maximum coordinates. This process depends quite heavily on the structure
0055   // of the magnetic field, so we need some compile-time conditionals.
0056   std::array<std::size_t, ConfigType::NDims> bins{};
0057   Vector min, max;
0058 
0059   if constexpr (Grid) {
0060     // First, we should check whether the Bfield actually has a
0061     // three-dimensional bin count and size.
0062     if (bins.size() != ConfigType::NDims ||
0063         field.getMin().size() != ConfigType::NDims ||
0064         field.getMax().size() != ConfigType::NDims) {
0065       throw std::invalid_argument("Incorrect input B-field dimensionality.");
0066     }
0067 
0068     // If our magnetic field is grid-like, we know that it has a built-in size
0069     // and bin count. We can use these, but the user may want to override the
0070     // values with custom values.
0071     for (std::size_t i = 0; i < ConfigType::NDims; ++i) {
0072       // For each dimension, we first get the corresponding value from the
0073       // grid-based vector field...
0074       bins[i] = field.getNBins()[i];
0075       min[i] = field.getMin()[i];
0076       max[i] = field.getMax()[i];
0077 
0078       // ...and then we override them with the optional user-supplied values.
0079       if (config.bins[i]) {
0080         bins[i] = *config.bins[i];
0081       }
0082 
0083       if (config.range[i][0]) {
0084         min[i] = *config.range[i][0];
0085       }
0086 
0087       if (config.range[i][1]) {
0088         max[i] = *config.range[i][1];
0089       }
0090     }
0091   } else {
0092     // If the vector field is not grid based, then there is no side and we are
0093     // forced to use the user-supplied data. Remember that in this case, the
0094     // values are not optional.
0095     for (std::size_t i = 0; i < ConfigType::NDims; ++i) {
0096       bins[i] = config.bins[i];
0097       min[i] = config.range[i][0];
0098       max[i] = config.range[i][1];
0099     }
0100   }
0101 
0102   // Next, we calculate the size (in physical space) of each bin.
0103   Vector delta;
0104 
0105   for (std::size_t i = 0; i < ConfigType::NDims; ++i) {
0106     delta[i] = (max[i] - min[i]) / (bins[i] - 1);
0107   }
0108 
0109   // Create the appropriate magnetic field context and cache to interact with
0110   // the B fields.
0111   Acts::MagneticFieldContext mctx{};
0112   typename FieldType::Cache cache = field.makeCache(mctx);
0113 
0114   // Finally, we can begin to fill the output file with data from our B field.
0115   // Again, the procedure is slightly different depending on whether we are
0116   // working with Cartesian or cylindrical coordinates.
0117   if constexpr (Coord == CoordinateType::XYZ) {
0118     ACTS_INFO("Writing XYZ field of size " << bins[0] << " x " << bins[1]
0119                                            << " x " << bins[2] << " to file "
0120                                            << config.fileName << "...");
0121 
0122     std::size_t total_items = bins[0] * bins[1] * bins[2];
0123 
0124     // For Cartesian coordinates, iterate over bins in the x, y, and z
0125     // directions. Note that we iterate one additional time because we are
0126     // writing the _edges_ of the bins, and the final bin needs to be closed.
0127     for (std::size_t x = 0; x < bins[0]; ++x) {
0128       for (std::size_t y = 0; y < bins[1]; ++y) {
0129         for (std::size_t z = 0; z < bins[2]; ++z) {
0130           // Compute the geometric position of this bin, then request the
0131           // magnetic field vector at that position.
0132           Acts::Vector3 pos = {x * delta[0] + min[0], y * delta[1] + min[1],
0133                                z * delta[2] + min[2]};
0134 
0135           Acts::Vector3 bField;
0136           if (auto fieldMap =
0137                   dynamic_cast<const Acts::InterpolatedMagneticField*>(
0138                       &field)) {
0139             // InterpolatedMagneticField::getField() returns an error for the
0140             // final point (upper edge), which is just outside the field volume.
0141             // So we use getFieldUnchecked instead.
0142             bField = fieldMap->getFieldUnchecked(pos);
0143           } else {
0144             Acts::Result<Acts::Vector3> flx = field.getField(pos, cache);
0145 
0146             // The aforementioned method is not guaranteed to succeed, so we
0147             // must check for a valid result, and then write it to disk. If the
0148             // result is invalid, throw an exception.
0149             if (flx.ok()) {
0150               bField = *flx;
0151             } else {
0152               throw std::runtime_error("B-field returned a non-extant value!");
0153             }
0154           }
0155 
0156           writer.append(pos[0] / Acts::UnitConstants::mm,
0157                         pos[1] / Acts::UnitConstants::mm,
0158                         pos[2] / Acts::UnitConstants::mm,
0159                         bField[0] / Acts::UnitConstants::T,
0160                         bField[1] / Acts::UnitConstants::T,
0161                         bField[2] / Acts::UnitConstants::T);
0162 
0163           // This final part is some diagnostic to convince the user that the
0164           // program is still running. We periodically provide the user with
0165           // some useful data.
0166           std::size_t idx = (x * bins[1] * bins[2]) + (y * bins[2]) + z + 1;
0167 
0168           if (idx % 10000 == 0 || idx == total_items) {
0169             ACTS_VERBOSE("Wrote " << idx << " out of " << total_items
0170                                   << " items (" << std::setprecision(3)
0171                                   << ((100.f * idx) / total_items) << "%).");
0172           }
0173         }
0174       }
0175     }
0176   } else {
0177     ACTS_INFO("Writing RZ field of size " << bins[0] << " x " << bins[1]
0178                                           << " to file " << config.fileName
0179                                           << "...");
0180 
0181     std::size_t total_items = bins[0] * bins[1];
0182 
0183     // For cylindrical coordinates, we only need to iterate over the r and z
0184     // coordinates, because we assume rotational cylindrical symmetry. This
0185     // makes the procedure quite a bit faster, too. Great!
0186     for (std::size_t r = 0; r < bins[0]; ++r) {
0187       for (std::size_t z = 0; z < bins[1]; ++z) {
0188         // Calculate the position (still in three dimensions), assuming that
0189         // the phi coordinate is zero. Then grab the field.
0190         Acts::Vector3 pos(min[0] + r * delta[0], 0.f, min[1] + z * delta[1]);
0191 
0192         Acts::Vector3 bField;
0193         if (auto fieldMap =
0194                 dynamic_cast<const Acts::InterpolatedMagneticField*>(&field)) {
0195           // InterpolatedMagneticField::getField() returns an error for the
0196           // final point (upper edge), which is just outside the field volume.
0197           // So we use getFieldUnchecked instead.
0198           bField = fieldMap->getFieldUnchecked(pos);
0199         } else {
0200           Acts::Result<Acts::Vector3> flx = field.getField(pos, cache);
0201 
0202           // Check the result, then write to disk. We write the r and z
0203           // positions as they are, then we write the z component of the result
0204           // vector as is, and we compute the r-value from the other components
0205           // of the vector.
0206           if (flx.ok()) {
0207             bField = *flx;
0208           } else {
0209             throw std::runtime_error("B-field returned a non-extant value!");
0210           }
0211         }
0212 
0213         writer.append(
0214             pos[0] / Acts::UnitConstants::mm, pos[2] / Acts::UnitConstants::mm,
0215             Acts::VectorHelpers::perp(bField) / Acts::UnitConstants::T,
0216             bField[2] / Acts::UnitConstants::T);
0217 
0218         // As before, print some progress reports for the user to enjoy while
0219         // they wait.
0220         std::size_t idx = (r * bins[1]) + z + 1;
0221 
0222         if (idx % 10000 == 0 || idx == total_items) {
0223           ACTS_VERBOSE("Wrote " << idx << " out of " << total_items
0224                                 << " items (" << std::setprecision(3)
0225                                 << ((100.f * idx) / total_items) << "%).");
0226         }
0227       }
0228     }
0229   }
0230 }
0231 
0232 // Note that this is a C++ source file, and not a header file. The reason for
0233 // this is that we can easily enumerate the different template parameter
0234 // combinations for the function above, and so we want to speed up compilation
0235 // times by just compiling these symbols once into the object file
0236 // corresponding to this TU.
0237 template void CsvBFieldWriter::run<CsvBFieldWriter::CoordinateType::XYZ, true>(
0238     const Config<CoordinateType::XYZ, true>&, Acts::Logging::Level);
0239 template void CsvBFieldWriter::run<CsvBFieldWriter::CoordinateType::RZ, true>(
0240     const Config<CoordinateType::RZ, true>&, Acts::Logging::Level);
0241 template void CsvBFieldWriter::run<CsvBFieldWriter::CoordinateType::XYZ, false>(
0242     const Config<CoordinateType::XYZ, false>&, Acts::Logging::Level);
0243 template void CsvBFieldWriter::run<CsvBFieldWriter::CoordinateType::RZ, false>(
0244     const Config<CoordinateType::RZ, false>&, Acts::Logging::Level);
0245 }  // namespace ActsExamples