From f35cd1538a8eb697a206170866ad0961077c5d61 Mon Sep 17 00:00:00 2001 From: Stephen Nicholas Swatman Date: Wed, 19 Feb 2025 16:49:49 +0100 Subject: [PATCH] Add B-field conversion and generation tools This commit adds two new tools which both create magnetic field files for us. The `convert_csv_bfield` tool takes a space-separated magnetic field like the one we have of the ATLAS field and converts it to a binary covfie file. The `generate_constant_bfield` executable generates 2T magnetic fields at different sizes, either with 201x201x301 field points like the ATLAS field, or with `2x2x2` field points. --- examples/CMakeLists.txt | 1 + examples/tools/CMakeLists.txt | 11 + examples/tools/bfield_type.hpp | 20 ++ examples/tools/convert_csv_bfield.cpp | 285 ++++++++++++++++++++ examples/tools/generate_constant_bfield.cpp | 164 +++++++++++ 5 files changed, 481 insertions(+) create mode 100644 examples/tools/CMakeLists.txt create mode 100644 examples/tools/bfield_type.hpp create mode 100644 examples/tools/convert_csv_bfield.cpp create mode 100644 examples/tools/generate_constant_bfield.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 862afb35ab..725013168e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory(options) add_subdirectory(run) add_subdirectory(io) add_subdirectory(simulation) +add_subdirectory(tools) diff --git a/examples/tools/CMakeLists.txt b/examples/tools/CMakeLists.txt new file mode 100644 index 0000000000..c812e19882 --- /dev/null +++ b/examples/tools/CMakeLists.txt @@ -0,0 +1,11 @@ +# TRACCC library, part of the ACTS project (R&D line) +# +# (c) 2022-2023 CERN for the benefit of the ACTS project +# +# Mozilla Public License Version 2.0 + +traccc_add_executable(convert_csv_bfield "convert_csv_bfield.cpp" + LINK_LIBRARIES traccc::core covfie::core Boost::program_options) + +traccc_add_executable(generate_constant_bfield "generate_constant_bfield.cpp" + LINK_LIBRARIES traccc::core covfie::core Boost::program_options) diff --git a/examples/tools/bfield_type.hpp b/examples/tools/bfield_type.hpp new file mode 100644 index 0000000000..da6993e116 --- /dev/null +++ b/examples/tools/bfield_type.hpp @@ -0,0 +1,20 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2025 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +using backend_t = covfie::backend::affine>>>; + +using field_t = covfie::field; diff --git a/examples/tools/convert_csv_bfield.cpp b/examples/tools/convert_csv_bfield.cpp new file mode 100644 index 0000000000..0829853fd4 --- /dev/null +++ b/examples/tools/convert_csv_bfield.cpp @@ -0,0 +1,285 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2025 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#include +#include +#include +#include + +#include "bfield_type.hpp" +#include "traccc/definitions/common.hpp" + +// TODO: Remove this when we have logging +#define TRACCC_INFO(x) \ + do { \ + std::cout << "INFO : " << x << std::endl; \ + } while (0) +#define TRACCC_FATAL(x) \ + do { \ + std::cout << "FATAL : " << x << std::endl; \ + } while (0) + +#define CHECK_IOSTATE(x) \ + do { \ + if (!x.good()) { \ + std::stringstream _exception_ss; \ + _exception_ss << "Stringstream is not good on line " << __LINE__ \ + << " of file " << __FILE__; \ + throw std::runtime_error(_exception_ss.str()); \ + } \ + } while (0) + +void parse_opts(int argc, char* argv[], + boost::program_options::variables_map& vm) { + boost::program_options::options_description opts("general options"); + + opts.add_options()("help", "produce help message")( + "input,i", boost::program_options::value()->required(), + "input magnetic field to read")( + "output,o", boost::program_options::value()->required(), + "output magnetic field to write"); + + boost::program_options::parsed_options parsed = + boost::program_options::command_line_parser(argc, argv) + .options(opts) + .run(); + + boost::program_options::store(parsed, vm); + + if (vm.count("help")) { + std::cout << opts << std::endl; + std::exit(0); + } + + try { + boost::program_options::notify(vm); + } catch (boost::program_options::required_option& e) { + TRACCC_FATAL(e.what()); + std::exit(1); + } +} + +field_t read_bfield(const std::string& fn) { + std::ifstream f; + + float minx = std::numeric_limits::max(); + float maxx = std::numeric_limits::lowest(); + float miny = std::numeric_limits::max(); + float maxy = std::numeric_limits::lowest(); + float minz = std::numeric_limits::max(); + float maxz = std::numeric_limits::lowest(); + + { + TRACCC_INFO("Opening magnetic field to compute field limits"); + + f.open(fn); + + if (!f.good()) { + TRACCC_FATAL("Failed to open input file " << fn << "!"); + std::exit(1); + } + + std::string line; + + TRACCC_INFO("Skipping the first four lines (headers)"); + + for (std::size_t i = 0; i < 4; ++i) { + std::getline(f, line); + } + + float xp, yp, zp; + float Bx, By, Bz; + + (void)Bx, (void)By, (void)Bz; + + std::size_t n_lines = 0; + + TRACCC_INFO("Iterating over lines in the magnetic field file"); + + /* + * Read every line, and update our current minima and maxima + * appropriately. + */ + while (std::getline(f, line)) { + CHECK_IOSTATE(f); + + std::string word; + std::stringstream ss(line); + CHECK_IOSTATE(ss); + + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + xp = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + yp = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + zp = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + Bx = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + By = static_cast(std::atof(word.c_str())); + std::getline(ss, word); + Bz = static_cast(std::atof(word.c_str())); + + minx = std::min(minx, xp); + maxx = std::max(maxx, xp); + + miny = std::min(miny, yp); + maxy = std::max(maxy, yp); + + minz = std::min(minz, zp); + maxz = std::max(maxz, zp); + + ++n_lines; + } + + TRACCC_INFO("Read " << n_lines << " lines of magnetic field data"); + + TRACCC_INFO("Closing magnetic field file"); + + f.close(); + } + + TRACCC_INFO("Field dimensions in x = [" << minx << ", " << maxx << "]"); + TRACCC_INFO("Field dimensions in y = [" << miny << ", " << maxy << "]"); + TRACCC_INFO("Field dimensions in z = [" << minz << ", " << maxz << "]"); + + TRACCC_INFO("Assuming sample spacing of 100.0 in each dimension"); + + /* + * Now that we have the limits of our field, compute the size in each + * dimension. + */ + std::size_t sx = + static_cast(std::lround((maxx - minx) / 100.0)) + 1; + std::size_t sy = + static_cast(std::lround((maxy - miny) / 100.0)) + 1; + std::size_t sz = + static_cast(std::lround((maxz - minz) / 100.0)) + 1; + + TRACCC_INFO("Magnetic field size is " << sx << "x" << sy << "x" << sz); + + TRACCC_INFO("Constructing matching vector field..."); + + covfie::algebra::affine<3> translation = + covfie::algebra::affine<3>::translation(-minx, -miny, -minz); + covfie::algebra::affine<3> scaling = covfie::algebra::affine<3>::scaling( + static_cast(sx - 1) / (maxx - minx), + static_cast(sy - 1) / (maxy - miny), + static_cast(sz - 1) / (maxz - minz)); + + field_t field(covfie::make_parameter_pack( + field_t::backend_t::configuration_t(scaling * translation), + field_t::backend_t::backend_t::configuration_t{}, + field_t::backend_t::backend_t::backend_t::configuration_t{sx, sy, sz})); + field_t::view_t fv(field); + + { + TRACCC_INFO("Re-opening magnetic field to gather data"); + + f.open(fn); + + if (!f.good()) { + TRACCC_FATAL("Failed to open input file " << fn << "!"); + std::exit(1); + } + + std::string line; + + TRACCC_INFO("Skipping the first line (header)"); + + std::getline(f, line); + + float xp, yp, zp; + float Bx, By, Bz; + + std::size_t n_lines = 0; + + TRACCC_INFO("Iterating over lines in the magnetic field file"); + + /* + * Read every line, and update our current minima and maxima + * appropriately. + */ + while (std::getline(f, line)) { + CHECK_IOSTATE(f); + + std::string word; + std::stringstream ss(line); + CHECK_IOSTATE(ss); + + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + xp = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + yp = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + zp = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + Bx = static_cast(std::atof(word.c_str())); + std::getline(ss, word, ' '); + CHECK_IOSTATE(ss); + By = static_cast(std::atof(word.c_str())); + std::getline(ss, word); + Bz = static_cast(std::atof(word.c_str())); + + field_t::view_t::output_t& p = fv.at(xp, yp, zp); + + p[0] = Bx * traccc::unit::T; + p[1] = By * traccc::unit::T; + p[2] = Bz * traccc::unit::T; + + n_lines++; + } + + TRACCC_INFO("Read " << n_lines << " lines of magnetic field data"); + + TRACCC_INFO("Closing magnetic field file"); + + f.close(); + } + + return field; +} + +int main(int argc, char** argv) { + boost::program_options::variables_map vm; + parse_opts(argc, argv, vm); + + TRACCC_INFO("Welcome to the traccc magnetic field converter!"); + TRACCC_INFO("Using magnetic field file \"" << vm["input"].as() + << "\""); + TRACCC_INFO("Starting read of input file..."); + + field_t fb = read_bfield(vm["input"].as()); + + TRACCC_INFO("Writing magnetic field to file \"" + << vm["output"].as() << "\"..."); + + std::ofstream fs(vm["output"].as(), std::ofstream::binary); + + if (!fs.good()) { + TRACCC_FATAL("Failed to open output file " + << vm["output"].as() << "!"); + std::exit(1); + } + + fb.dump(fs); + + fs.close(); + + TRACCC_INFO("Conversion complete, goodbye!"); + + return 0; +} diff --git a/examples/tools/generate_constant_bfield.cpp b/examples/tools/generate_constant_bfield.cpp new file mode 100644 index 0000000000..25635fe733 --- /dev/null +++ b/examples/tools/generate_constant_bfield.cpp @@ -0,0 +1,164 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2025 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#include +#include +#include + +#include "bfield_type.hpp" +#include "traccc/definitions/common.hpp" + +// TODO: Remove this when we have logging +#define TRACCC_INFO(x) \ + do { \ + std::cout << "INFO : " << x << std::endl; \ + } while (0) +#define TRACCC_FATAL(x) \ + do { \ + std::cout << "FATAL : " << x << std::endl; \ + } while (0) + +void parse_opts(int argc, char* argv[], + boost::program_options::variables_map& vm) { + boost::program_options::options_description opts("general options"); + + opts.add_options()("help", "produce help message")( + "size,s", + boost::program_options::value()->required()->default_value( + "big"), + "size of bfield to generate [big or small]")( + "output,o", boost::program_options::value()->required(), + "output magnetic field to write"); + + boost::program_options::parsed_options parsed = + boost::program_options::command_line_parser(argc, argv) + .options(opts) + .run(); + + boost::program_options::store(parsed, vm); + + if (vm.count("help")) { + std::cout << opts << std::endl; + std::exit(0); + } + + try { + boost::program_options::notify(vm); + } catch (boost::program_options::required_option& e) { + TRACCC_FATAL(e.what()); + std::exit(1); + } +} + +field_t create_bfield(bool small) { + float minx = -10000.f; + float maxx = 10000.f; + float miny = -10000.f; + float maxy = 10000.f; + float minz = -15000.f; + float maxz = 15000.f; + + TRACCC_INFO("Field dimensions in x = [" << minx << ", " << maxx << "]"); + TRACCC_INFO("Field dimensions in y = [" << miny << ", " << maxy << "]"); + TRACCC_INFO("Field dimensions in z = [" << minz << ", " << maxz << "]"); + + std::size_t sx, sy, sz; + + if (small) { + sx = 2; + sy = 2; + sz = 2; + } else { + TRACCC_INFO("Assuming sample spacing of 100.0 in each dimension"); + sx = static_cast(std::lround((maxx - minx) / 100.0)) + 1; + sy = static_cast(std::lround((maxy - miny) / 100.0)) + 1; + sz = static_cast(std::lround((maxz - minz) / 100.0)) + 1; + } + + TRACCC_INFO("Magnetic field size is " << sx << "x" << sy << "x" << sz); + + TRACCC_INFO("Filling vector field..."); + + covfie::algebra::affine<3> translation = + covfie::algebra::affine<3>::translation(-minx, -miny, -minz); + + float sfx = static_cast(sx - 1) / (maxx - minx); + float sfy = static_cast(sy - 1) / (maxy - miny); + float sfz = static_cast(sz - 1) / (maxz - minz); + + covfie::algebra::affine<3> scaling = + covfie::algebra::affine<3>::scaling(sfx, sfy, sfz); + + field_t field(covfie::make_parameter_pack( + field_t::backend_t::configuration_t(scaling * translation), + field_t::backend_t::backend_t::configuration_t{}, + field_t::backend_t::backend_t::backend_t::configuration_t{sx, sy, sz})); + field_t::view_t fv(field); + + std::size_t n_points = 0; + + for (std::size_t xi = 0; xi < sx; ++xi) { + for (std::size_t yi = 0; yi < sy; ++yi) { + for (std::size_t zi = 0; zi < sz; ++zi) { + float xp = -minx + (static_cast(xi) * sfx); + float yp = -miny + (static_cast(yi) * sfy); + float zp = -minz + (static_cast(zi) * sfz); + + field_t::view_t::output_t& p = fv.at(xp, yp, zp); + + p[0] = 0.f; + p[1] = 0.f; + p[2] = 1.99724f * traccc::unit::T; + + n_points++; + } + } + } + + TRACCC_INFO("Set " << n_points << " magnetic field points"); + + return field; +} + +int main(int argc, char** argv) { + boost::program_options::variables_map vm; + parse_opts(argc, argv, vm); + + TRACCC_INFO("Welcome to the traccc magnetic field converter!"); + TRACCC_INFO("Starting creation of magnetic field..."); + + bool small; + + if (vm["size"].as() == "small") { + small = true; + } else if (vm["size"].as() == "big") { + small = false; + } else { + throw std::runtime_error("Size is neither \"big\" nor \"small\""); + } + + field_t fb = create_bfield(small); + + TRACCC_INFO("Writing magnetic field to file \"" + << vm["output"].as() << "\"..."); + + std::ofstream fs(vm["output"].as(), std::ofstream::binary); + + if (!fs.good()) { + TRACCC_FATAL("Failed to open output file " + << vm["output"].as() << "!"); + std::exit(1); + } + + fb.dump(fs); + + fs.close(); + + TRACCC_INFO("Conversion complete, goodbye!"); + + return 0; +}