Project
Loading...
Searching...
No Matches
testMagneticField.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#define BOOST_TEST_MODULE Test MagneticField
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15#include <boost/test/unit_test.hpp>
16#include <iostream>
17#include "Field/MagneticField.h"
18#include "Field/MagFieldFast.h"
19#include <memory>
20#include <fairlogger/Logger.h> // for FairLogger
21#include <TFile.h>
22#include <TStopwatch.h>
23#include <TRandom.h>
24#include <algorithm>
25#include <array>
26#include <thread>
27#include <vector>
28
29using namespace o2::field;
30
31BOOST_AUTO_TEST_CASE(MagneticField_test)
32{
33 // create magnetic field
34 std::unique_ptr<MagneticField> fld = std::make_unique<MagneticField>("Maps", "Maps", 1., 1., o2::field::MagFieldParam::k5kG);
35 double bz0 = fld->solenoidField();
36 LOG(info) << "Created default magnetic field for " << bz0 << "kG";
37 const double nomBz = 5.00685;
38 BOOST_CHECK_CLOSE(bz0, nomBz, 0.1);
39
40 const int ntst = 10000;
41 float rnd[3];
42 double xyz[ntst][3] = {}, bxyz[ntst][3] = {}, dummyAcc = 0.;
43 // fill input
44 for (int it = ntst; it--;) {
45 gRandom->RndmArray(3, rnd);
46 xyz[it][0] = rnd[0] * 400. * TMath::Cos(rnd[1] * TMath::Pi() * 2);
47 xyz[it][1] = rnd[1] * 400. * TMath::Sin(rnd[1] * TMath::Pi() * 2);
48 xyz[it][2] = (rnd[0] - 0.5) * 250;
49 }
50
51 const int repFactor = 50;
52 // timing: slow field
53 TStopwatch swSlow;
54 swSlow.Start();
55 for (int ii = repFactor; ii--;) {
56 for (int it = ntst; it--;) {
57 fld->Field(xyz[it], bxyz[it]);
58 }
59 }
60 swSlow.Stop();
61
62 // init fast field
63 fld->AllowFastField(true);
64
65 // timing: fast field
66 TStopwatch swFast;
67 swFast.Start();
68 double bfast[3];
69 for (int ii = repFactor; ii--;) {
70 for (int it = ntst; it--;) {
71 fld->Field(xyz[it], bfast);
72 }
73 }
74 swFast.Stop();
75 //
76 double sS = swSlow.CpuTime() / (ntst * repFactor);
77 double sF = swFast.CpuTime() / (ntst * repFactor);
78 double rat = sF > 0. ? sS / sF : -1;
79 LOG(info) << "Timing: Exact param: " << sS << " Fast param: " << sF
80 << "s/call -> factor " << rat;
81
82 // compare slow/fast param precision
83 double mean[3] = {0.}, rms[3] = {0.};
84 const char comp[] = "XYZ";
85 LOG(info) << "Relative precision of fast field wrt exact field";
86 for (int it = ntst; it--;) {
87 fld->Field(xyz[it], bfast);
88 for (int i = 0; i < 3; i++) {
89 double df = bxyz[it][i] - bfast[i];
90 mean[i] += df;
91 rms[i] += df * df;
92 }
93 }
94 for (int i = 0; i < 3; i++) {
95 mean[i] /= ntst;
96 rms[i] /= ntst;
97 rms[i] -= mean[i] * mean[i];
98 rms[i] = TMath::Sqrt(rms[i]);
99 LOG(info) << "deltaB" << comp[i] << ": "
100 << " mean=" << mean[i] << "(" << mean[i] / nomBz * 100. << "%)"
101 << " RMS =" << rms[i] << "(" << rms[i] / nomBz * 100. << "%)";
102 BOOST_CHECK(TMath::Abs(mean[i] / nomBz) < 1.e-3);
103 BOOST_CHECK(TMath::Abs(rms[i] / nomBz) < 1.e-3);
104 }
105}
106
107BOOST_AUTO_TEST_CASE(MagneticField_reinitialization_test)
108{
109 // The measured map is transient, so a MagneticField read back from a file has to be
110 // re-created before it can be used. That must reproduce the field vectors, not merely
111 // their magnitude: a sign flip leaves |B| untouched.
112 const double points[][3] = {
113 {0., 0., 0.}, // solenoid, on axis
114 {100., 50., 100.}, // solenoid, off axis
115 {10., 10., -900.}, // muon dipole
116 {0., 0., 1000.}, // compensator 1A, side A
117 {0., 0., -2049.}, // compensator 2C, side C
118 {0., 0., 2049.} // compensator 2A, side A
119 };
120 const int npoints = sizeof(points) / sizeof(points[0]);
121 const double tolerance = 1.e-9; // kGauss
122
123 std::unique_ptr<MagneticField> fld = std::make_unique<MagneticField>("Maps", "Maps", 1., 1., MagFieldParam::k5kG);
124 const double facSol = fld->getFactorSolenoid(), facDip = fld->getFactorDipole();
125 double bref[npoints][3] = {};
126 for (int ip = 0; ip < npoints; ip++) {
127 fld->Field(points[ip], bref[ip]);
128 // a point where the field vanishes would make the comparisons below vacuous
129 BOOST_CHECK(TMath::Abs(bref[ip][0]) + TMath::Abs(bref[ip][1]) + TMath::Abs(bref[ip][2]) > tolerance);
130 }
131
132 const char* fname = "testMagneticFieldReinitialization.root";
133 {
134 TFile fout(fname, "recreate");
135 fout.WriteObject(fld.get(), "field");
136 }
137 TFile fin(fname);
138 auto* fldRead = fin.Get<MagneticField>("field");
139 BOOST_REQUIRE(fldRead != nullptr);
140 fldRead->CreateField();
141 BOOST_CHECK_EQUAL(fldRead->getFactorSolenoid(), facSol);
142 BOOST_CHECK_EQUAL(fldRead->getFactorDipole(), facDip);
143 for (int ip = 0; ip < npoints; ip++) {
144 double b[3] = {};
145 fldRead->Field(points[ip], b);
146 for (int i = 0; i < 3; i++) {
147 BOOST_CHECK_SMALL(b[i] - bref[ip][i], tolerance);
148 }
149 }
150}
151
152BOOST_AUTO_TEST_CASE(MagneticField_threads)
153{
154 // evaluating one field from several threads at once gives bit-identical values to a sequential pass
155 MagneticField fld("Maps", "Maps", 1., 1., o2::field::MagFieldParam::k5kG);
156 constexpr int npoints = 20000;
157 constexpr int nthreads = 8;
158 std::vector<std::array<double, 3>> xyz(npoints);
159 for (auto& p : xyz) {
160 p = {gRandom->Uniform(-450., 450.), gRandom->Uniform(-450., 450.), gRandom->Uniform(-1500., 1500.)};
161 }
162 // field, Bz, TPC integral and TPC ratio integral for every point
163 constexpr int nval = 10;
164 auto evaluate = [&fld, &xyz](std::vector<double>& out) {
165 out.resize(npoints * nval);
166 for (int i = 0; i < npoints; ++i) {
167 fld.Field(xyz[i].data(), &out[i * nval]);
168 out[i * nval + 3] = fld.getBz(xyz[i].data());
169 fld.getTPCIntegral(xyz[i].data(), &out[i * nval + 4]);
170 fld.getTPCRatIntegral(xyz[i].data(), &out[i * nval + 7]);
171 }
172 };
173 std::vector<double> reference;
174 evaluate(reference);
175 std::vector<std::vector<double>> results(nthreads);
176 std::vector<std::thread> threads;
177 for (int t = 0; t < nthreads; ++t) {
178 threads.emplace_back(evaluate, std::ref(results[t]));
179 }
180 for (auto& th : threads) {
181 th.join();
182 }
183 for (int t = 0; t < nthreads; ++t) {
184 auto diff = std::mismatch(reference.begin(), reference.end(), results[t].begin());
185 BOOST_CHECK_MESSAGE(diff.first == reference.end(), "thread " << t << " differs at value " << (diff.first - reference.begin()));
186 }
187}
int32_t i
Definition of the fast magnetic field parametrization MagFieldFast.
Definition of the MagF class.
Double_t getBz(const Double_t *xyz) const
Method to calculate the field at point xyz.
void getTPCRatIntegral(const Double_t *xyz, Double_t *b) const
Method to calculate the integral_0^z of br,bt,bz.
void Field(const Double_t *__restrict__ point, Double_t *__restrict__ bField) override
void getTPCIntegral(const Double_t *xyz, Double_t *b) const
Method to calculate the integral_0^z of br,bt,bz.
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint reference
Definition glcorearb.h:5487
GLboolean * data
Definition glcorearb.h:298
BOOST_AUTO_TEST_CASE(MagneticField_test)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
BOOST_CHECK(tree)
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())