diff --git a/Detector/CMakeLists.txt b/Detector/CMakeLists.txt index 57e6f2ce1389dc6690edf864c20b8862b81313f5..d628d7ab5b402422bff9135b662a12688c56841f 100644 --- a/Detector/CMakeLists.txt +++ b/Detector/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory(DetInterface) add_subdirectory(DetSegmentation) add_subdirectory(GeomSvc) add_subdirectory(Identifier) +add_subdirectory(MagneticFieldMap) diff --git a/Detector/MagneticFieldMap/CMakeLists.txt b/Detector/MagneticFieldMap/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7c65b50318a2d0040922057a2fcc36a84db3ec50 --- /dev/null +++ b/Detector/MagneticFieldMap/CMakeLists.txt @@ -0,0 +1,22 @@ +################################################################################# +##Package : MagneticFieldMap +################################################################################# + +gaudi_add_module(MagneticFieldMap + SOURCES src/GenericBFieldMapBrBz.cpp + src/GenericBFieldMapBrBzFactory.cpp + src/FieldMapFileProvider.cpp + LINK Gaudi::GaudiKernel + ${DD4hep_COMPONENT_LIBRARIES} + ${ROOT_LIBRARIES} +) + +set(LIBRARY_OUTPUT_PATH ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) +message(STATUS "LIBRARY_OUTPUT_PATH -> ${LIBRARY_OUTPUT_PATH}") +dd4hep_generate_rootmap(MagneticFieldMap) + +install(TARGETS MagneticFieldMap + EXPORT CEPCSWTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib + COMPONENT dev) diff --git a/Detector/MagneticFieldMap/src/FieldMapFileProvider.cpp b/Detector/MagneticFieldMap/src/FieldMapFileProvider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2eb352e4912a7d63eb797104beab25f59ec706ec --- /dev/null +++ b/Detector/MagneticFieldMap/src/FieldMapFileProvider.cpp @@ -0,0 +1,283 @@ +#include "FieldMapFileProvider.h" + +#include <cmath> +#include <string> +#include <map> +#include <vector> +#include <iostream> +#include <fstream> +#include <sstream> + +FieldMapFileProvider::FieldMapFileProvider(const std::string& url_) + : m_url(url_), nr(-1), nz(-1), + rBinMin(-1), zBinMin(-1), + rBinMax(-1), zBinMax(-1), + drBin(-1), dzBin(-1) { + init(); +} + +int FieldMapFileProvider::rBinIdx(double r, double& rn) { + // + // | --- | --- | --- | + // ^ ^ ^ + // rmin | rmax + // r + // | + // return + + + double absr = std::fabs(r); + + int idx = -1; + + // std::cout << "FieldMapFileProvider::rBinIdx: " + // << " r: " << r + // << " rBinMin: " << rBinMin + // << " rBinMax: " << rBinMax + // << std::endl; + + if ( rBinMin <= absr && absr < rBinMax) { + idx = (absr - rBinMin) / drBin; + double r0 = rBinMin + idx*drBin; + rn = (absr - r0)/drBin; + } + + return idx; +} + +int FieldMapFileProvider::zBinIdx(double z, double& zn) { + double absz = std::fabs(z); + + int idx = -1; + + if ( zBinMin <= absz && absz < zBinMax) { + idx = (absz - zBinMin) / dzBin; + double z0 = zBinMin + idx*dzBin; + zn = (absz - z0)/dzBin; + } + + return idx; +} + +void FieldMapFileProvider::access(int rbin, int zbin, double& Br, double& Bz) { + // the valid bin should between [0, n) + // if the point is not in the valid region, return 0 + if ((rbin < 0 || rbin >= nr) || (zbin < 0 || zbin >= nr)) { + Br = 0; + Bz = 0; + return; + } + + // convert to the internal table (with left col and top row) + int ridx = rbin+1; + int zidx = zbin+1; + + int globalidx = ridx + zidx*(nr+1); + + Br = Brdata[globalidx]; + Bz = Bzdata[globalidx]; +} + +// ====================== +// Below are private impl +// ====================== + +void FieldMapFileProvider::init() { + // parse the url + // Br=/tmp/lint/CEPCSW/Br.csv;Bz=/tmp/lint/CEPCSW/Bz.csv + + std::map<std::string, std::string> url_parsed; + + size_t idx_begin = 0; + size_t idx_end = m_url.find(";"); + + while (true) { + std::string keyval = m_url.substr(idx_begin, idx_end-idx_begin); + + if (keyval.size()) { + std::cout << "---> keyval: " << keyval << std::endl; + // separate by '=' + size_t idx_sep = keyval.find("="); + if (idx_sep == std::string::npos) { + std::string error_msg = "[ERROR] FieldMapFileProvider: Pleaes specify label=path. "; + throw std::runtime_error(error_msg); + + } + std::string key = keyval.substr(0, idx_sep); + std::string val = keyval.substr(idx_sep+1); + std::cout << "-----> key: " << key << std::endl; + std::cout << "-----> val: " << val << std::endl; + // insert into map + + // duplicated + if (url_parsed.count(key)) { + std::string error_msg = "[ERROR] FieldMapFileProvider: duplicated key '" + key + "'"; + throw std::runtime_error(error_msg); + } + + url_parsed[key] = val; + } + + if (idx_end == std::string::npos) { + break; + } + + idx_begin = idx_end+1; + idx_end = m_url.find(';', idx_begin); + } + + // access the map 'url_parsed' + std::string key; + + // load Br + key = "Br"; + if (url_parsed.count(key) == 0) { + std::string error_msg = "[ERROR] FieldMapFileProvider: missing key '" + key + "'"; + throw std::runtime_error(error_msg); + } + loadBr(url_parsed[key]); + + // load Bz + key = "Bz"; + if (url_parsed.count(key) == 0) { + std::string error_msg = "[ERROR] FieldMapFileProvider: missing key '" + key + "'"; + throw std::runtime_error(error_msg); + } + loadBz(url_parsed[key]); + +} + + + +bool FieldMapFileProvider::loadBr(const std::string& fn) { + + // ----> r + // | + // v + // z + // the shape + int ncol = -1; + int nrow = -1; + + // load data + bool st = loadCSV(fn, Brdata, ncol, nrow); + + // update the metadata + // for (int i = 0; i < ncol; ++i) { + // std::cout << "i: " << i << " data[i]: " << data[i] << std::endl; + // } + nr = ncol-1; // skip the top row and left col + nz = nrow-1; + + rBinMin = Brdata[1]; + zBinMin = Brdata[ncol]; + + rBinMax = Brdata[ncol-1]; + zBinMax = Brdata[ncol*(nrow-1)]; + + drBin = (rBinMax-rBinMin) / (nr-1); + dzBin = (zBinMax-zBinMin) / (nz-1); + + std::cout << "nr: " << nr << std::endl; + std::cout << "nz: " << nz << std::endl; + std::cout << "rBinMin: " << rBinMin << std::endl; + std::cout << "zBinMin: " << zBinMin << std::endl; + std::cout << "rBinMax: " << rBinMax << std::endl; + std::cout << "zBinMax: " << zBinMax << std::endl; + std::cout << "drBin: " << drBin << std::endl; + std::cout << "dzBin: " << dzBin << std::endl; + + + + return true; +} + +bool FieldMapFileProvider::loadBz(const std::string& fn) { + + // the shape + int ncol = -1; + int nrow = -1; + bool st = loadCSV(fn, Bzdata, ncol, nrow); + + return true; +} + +bool FieldMapFileProvider::loadCSV(const std::string& fn, + std::vector<double>& data, + int& ncol, int& nrow) { + + std::ifstream input(fn); + std::string tmpline; + + ncol = 0; + nrow = 0; + + + while(std::getline(input, tmpline)) { + std::cout << "------> " << tmpline << std::endl; + ++nrow; + + // split by , + int col_per_line = 0; + + size_t idx_begin = 0; + size_t idx_end = tmpline.find(","); + + while (true) { + std::cout << "----------------> idx: " + << idx_begin << "->" << idx_end << std::endl; + std::string val = tmpline.substr(idx_begin, idx_end-idx_begin); + double v = 0.0; + // parse val + std::stringstream ss; + ss << val; + ss >> v; + // if (not ss.good()) { + // v = 0; + // ss.clear(); + // } + + std::cout << "----------------> v: " << v << std::endl; + // if (val.size()==0 || ) { // if it is empty or not number, use 0 + // data.push_back(0.0); + // } else { + // } + data.push_back(v); + + ++col_per_line; + + // break + if (idx_end == std::string::npos) { + break; + } + + // goto next val + idx_begin = idx_end+1; + idx_end = tmpline.find(',', idx_begin); + + } + + // if it is the first line, we need to store the column sizes + if (ncol == 0) { + ncol = col_per_line; + } + + std::cout << "--> ncol: " << ncol + << " col_per_line: " << col_per_line + << std::endl; + + if (ncol != col_per_line) { + std::string error_msg = "[ERROR] FieldMapFileProvider: Mismatch columns. "; + throw std::runtime_error(error_msg); + } + + } + + std::cout << "SUMMARY: " + << " size of the data: " << data.size() + << " shape (ncol: " << ncol + << " , nrow: " << nrow + << ")" << std::endl; + + return true; +} diff --git a/Detector/MagneticFieldMap/src/FieldMapFileProvider.h b/Detector/MagneticFieldMap/src/FieldMapFileProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..8a754a7a2a1ae41864ca7c339adc968a12fb288a --- /dev/null +++ b/Detector/MagneticFieldMap/src/FieldMapFileProvider.h @@ -0,0 +1,52 @@ +#ifndef FieldMapFileProvider_h +#define FieldMapFileProvider_h + +#include "IFieldMapProvider.h" + +#include <string> +#include <vector> + +class FieldMapFileProvider: public IFieldMapProvider { + +public: + FieldMapFileProvider(const std::string& url_); + + // Meta data about the map + virtual int rBinIdx(double r, double& rn); + virtual int zBinIdx(double z, double& zn); + + // The Br and Bz + virtual void access(int rbin, int zbin, double& Br, double& Bz); + +private: + void init(); + + bool loadBr(const std::string& fn); + bool loadBz(const std::string& fn); + + bool loadCSV(const std::string& fn, std::vector<double>& data, int& ncol, int& nrow); +private: + std::string m_url; // the url passed from GenericBFieldMapBrBz + + std::string m_filename_Br; + std::string m_filename_Bz; + + // the data will include the left col (z) and top row (r) + std::vector<double> Brdata; + std::vector<double> Bzdata; + + // in this impl, the rBin/zBin should be consistent between Br and Bz. + int nr; // include the two endpoints, so nr = nrBin + 1 + int nz; + + double rBinMin; + double zBinMin; + + double rBinMax; + double zBinMax; + + double drBin; + double dzBin; +}; + +#endif diff --git a/Detector/MagneticFieldMap/src/GenericBFieldMapBrBz.cpp b/Detector/MagneticFieldMap/src/GenericBFieldMapBrBz.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b3218b3c2f47521c892e854e2732728cb7e7a72 --- /dev/null +++ b/Detector/MagneticFieldMap/src/GenericBFieldMapBrBz.cpp @@ -0,0 +1,118 @@ + +#include "GenericBFieldMapBrBz.h" + +#include "FieldMapFileProvider.h" + +#include <cmath> + +#include "DD4hep/DD4hepUnits.h" + +GenericBFieldMapBrBz::GenericBFieldMapBrBz() + : m_provider(nullptr) { + type = dd4hep::CartesianField::MAGNETIC; + +} + +void GenericBFieldMapBrBz::fieldComponents(const double* pos, double* field) { + if (!m_provider) { + std::string error_msg = "[ERROR] GenericBFieldMapBrBz: No provider! "; + throw std::runtime_error(error_msg); + } + + // convert pos to r/z + double x = pos[0] / dd4hep::m; // convert to meter + double y = pos[1] / dd4hep::m; + double z = pos[2] / dd4hep::m; + double r = sqrt(x*x+y*y); + double phi = atan2(y, x); + + // std::cout << " r: " << r + // << " x: " << x + // << " y: " << y + // << " z: " << z + // << " field Bx: " << field[0] + // << " field By: " << field[1] + // << " field Bz: " << field[2] + // << std::endl; + + // get the point r0/z0, r1/z1 + + // -- normalized r/z at the bin + double rn = 0.0; + double zn = 0.0; + + int ir0 = 0; + int iz0 = 0; + + ir0 = m_provider->rBinIdx(r, rn); + iz0 = m_provider->zBinIdx(z, zn); + + // not in the valid return + if (ir0 < 0 || iz0 < 0) { + // std::cout << "SKIP due to " + // << " r: " << r + // << " x: " << x + // << " y: " << y + // << " z: " << z + // << " ir0: " << ir0 + // << " iz0: " << iz0 + // << " rn: " << rn + // << " zn: " << zn + // << std::endl; + return; + } + + // std::cout << " r: " << r + // << " x: " << x + // << " y: " << y + // << " z: " << z + // << " ir0: " << ir0 + // << " iz0: " << iz0 + // << " rn: " << rn + // << " zn: " << zn + // << std::endl; + + int ir1 = ir0 + 1; + int iz1 = iz0 + 1; + + // get the Br/Bz at four points + double Br_r0z0 = 0.0; double Bz_r0z0 = 0.0; + double Br_r1z0 = 0.0; double Bz_r1z0 = 0.0; + double Br_r0z1 = 0.0; double Bz_r0z1 = 0.0; + double Br_r1z1 = 0.0; double Bz_r1z1 = 0.0; + + // calculate the field at r/z + m_provider->access(ir0, iz0, Br_r0z0, Bz_r0z0); + m_provider->access(ir1, iz0, Br_r1z0, Bz_r1z0); + m_provider->access(ir0, iz1, Br_r0z1, Bz_r0z1); + m_provider->access(ir1, iz1, Br_r1z1, Bz_r1z1); + + double Br = (1.0 - rn) * (1.0 - zn) * Br_r0z0 + + rn * (1.0 - zn) * Br_r1z0 + + (1.0 - rn) * zn * Br_r0z1 + + rn * zn * Br_r1z1; + + double Bz = (1.0 - rn) * (1.0 - zn) * Bz_r0z0 + + rn * (1.0 - zn) * Bz_r1z0 + + (1.0 - rn) * zn * Bz_r0z1 + + rn * zn * Bz_r1z1; + + // update the global field + field[0] += Br*cos(phi); + field[1] += Br*sin(phi); + field[2] += Bz; + + return; +} + +void GenericBFieldMapBrBz::init_provider(const std::string& provider, const std::string& url) { + if (provider == "file") { + std::cout << "Initialize provider with file. " << std::endl; + m_provider = new FieldMapFileProvider(url); + } else if (provider == "db") { + std::cout << "Initialize provider with file. " << std::endl; + } else { + std::string error_msg = "[ERROR] GenericBFieldMapBrBz: Unknown provider: " + provider; + throw std::runtime_error(error_msg); + } +} diff --git a/Detector/MagneticFieldMap/src/GenericBFieldMapBrBz.h b/Detector/MagneticFieldMap/src/GenericBFieldMapBrBz.h new file mode 100644 index 0000000000000000000000000000000000000000..93abfcfbbb5650d5e5945b774280e6586b1c17c3 --- /dev/null +++ b/Detector/MagneticFieldMap/src/GenericBFieldMapBrBz.h @@ -0,0 +1,35 @@ +#ifndef GenericBFieldMapBrBz_h +#define GenericBFieldMapBrBz_h + +/* + * GenericBFieldMapBrBz is an extension of Cartesian Field in DD4hep. + * - It enables the DD4hep to access the Magnetic Service from Gaudi. + * - It also enables the calculation of Br/Bz at position X. + * - It will get the map from an abstract class IFieldMapProvider. + * + * -- Tao Lin <lintao AT ihep.ac.cn> + */ + +#include <DD4hep/FieldTypes.h> + +#include "IFieldMapProvider.h" + + +class GenericBFieldMapBrBz: public dd4hep::CartesianField::Object { +public: + + GenericBFieldMapBrBz(); + + virtual void fieldComponents(const double* pos, double* field); + +public: + // following are interfaces to configure this field map + void init_provider(const std::string& provider, const std::string& url); + +private: + + IFieldMapProvider* m_provider; +}; + +#endif + diff --git a/Detector/MagneticFieldMap/src/GenericBFieldMapBrBzFactory.cpp b/Detector/MagneticFieldMap/src/GenericBFieldMapBrBzFactory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7d9fe302d75e214e290f52ebf2382441df305c0 --- /dev/null +++ b/Detector/MagneticFieldMap/src/GenericBFieldMapBrBzFactory.cpp @@ -0,0 +1,79 @@ +/* + * In this file, the xml is parsed and the GenericBFieldMapBrBz object is created and configured. + * + * The properties for the GenericBFieldMapBrBz + * - provider (attribute) + * - [file, db] + * - source (tag) + * - the attributes include: + * - url + * - file path for the 'file' mode. + * - DB instance ... for the 'db' mode. + * - rhoMin, rhoMax, zMin, zMax + * + * -- Tao Lin <lintao AT ihep.ac.cn> + */ +#include "GenericBFieldMapBrBz.h" + + +#include <DD4hep/Version.h> +#if DD4HEP_VERSION_GE(0,24) +#include <DD4hep/detail/Handle.inl> +#else +#include <DD4hep/Handle.inl> +#endif + +#include <DD4hep/FieldTypes.h> + + +#include <DD4hep/DetFactoryHelper.h> +#include <XML/Utilities.h> + + +static dd4hep::Ref_t create_GenericBFieldMapBrBz(dd4hep::Detector& , + dd4hep::xml::Handle_t handle ) { + + // 1. retrieve the parameters from xml + dd4hep::xml::Component xmlParameter(handle); + + // - provider + bool hasProvider = xmlParameter.hasAttr(_Unicode(provider)); + if (!hasProvider) { + std::string error_msg = "[ERROR] GenericBFieldMapBrBz: Must specify the 'provider'. "; + throw std::runtime_error(error_msg); + } + + std::string provider = xmlParameter.attr<std::string>(_Unicode(provider)); + + // - source + bool hasSource = xmlParameter.hasChild(_Unicode(source)); + if (!hasSource) { + std::string error_msg = "[ERROR] GenericBFieldMapBrBz: Must specify the 'source' tag. "; + throw std::runtime_error(error_msg); + + } + + dd4hep::xml::Component source(xmlParameter.child(_Unicode(source))); + + // get the url + bool hasUrl = source.hasAttr(_Unicode(url)); + if (!hasUrl) { + std::string error_msg = "[ERROR] GenericBFieldMapBrBz: Must specify the 'url' in 'source' tag. "; + throw std::runtime_error(error_msg); + } + + std::string url = source.attr<std::string>(_Unicode(url)); + + // 2. create the CartesianField + dd4hep::CartesianField obj; + GenericBFieldMapBrBz* ptr = new GenericBFieldMapBrBz(); + + ptr->init_provider(provider, url); + + obj.assign(ptr, xmlParameter.nameStr(), xmlParameter.typeStr()); + + return obj; + +} + +DECLARE_XMLELEMENT(GenericBFieldMapBrBz,create_GenericBFieldMapBrBz) diff --git a/Detector/MagneticFieldMap/src/IFieldMapProvider.h b/Detector/MagneticFieldMap/src/IFieldMapProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..b1e8d948ef3797926f43a892fe21ef61eafdb207 --- /dev/null +++ b/Detector/MagneticFieldMap/src/IFieldMapProvider.h @@ -0,0 +1,26 @@ +#ifndef IFieldMapProvider_h +#define IFieldMapProvider_h + +/* + * IFieldMapProvider will provide the B-Field at a 2D grid (r-z). + * The provider will return the bin index for r/z. Then the GenericBFieldMapBrBz + * will use this bin index to get the values at (r0,z0), (r1,z0), (r0,z1), (r1,z1). + * The interpolation is not computated IFieldMapProvider. Please see GenericBFieldMapBrBz. + * + * -- Tao Lin <lintao AT ihep.ac.cn> + */ + +class IFieldMapProvider { +public: + // Meta data about the map + // return rn/zn is the normalized value in the bin. the value is [0,1] + virtual int rBinIdx(double r, double& rn) = 0; + virtual int zBinIdx(double z, double& zn) = 0; + + // The Br and Bz + virtual void access(int rbin, int zbin, double& Br, double& Bz) = 0; + +}; + +#endif +