diff --git a/MagneticField/FaserBFieldData/share/FieldSimulation.py b/MagneticField/FaserBFieldData/share/FieldSimulation.py new file mode 100644 index 0000000000000000000000000000000000000000..e17f617bc727f42de17c5aada7addde94761ac7f --- /dev/null +++ b/MagneticField/FaserBFieldData/share/FieldSimulation.py @@ -0,0 +1,254 @@ +# Calculation of the magnetic field from first principles +# @author Dave Casper, Tobias Boeckh +# see also https://github.com/dwcasper/KalmanFilter/blob/master/Field/BField/__init__.py + + +from math import sin, cos, atan2, sqrt, pi +from auto_diff import true_np as np +from scipy.special import iv, kn +from scipy.integrate import quad + +junk = np.seterr() + +# Geometry description +rMin = 0.104 +rMax = rMin + 0.0825 +zMax = [1.5, 1.0, 1.0] +magnetZ = [-0.81232, 0.637726, 1.837726] +m0 = 1.00 # mag +sectorPhi = np.linspace(0, 2 * pi, 17, True) # central phi value for each sector +magPhi = 2 * sectorPhi # angle of the magnetization in each sector +phiConst = 4 * sqrt(2 - sqrt(2)) # numerical result of phiPrime integration on curved surface +rhoLimit = rMin / 100 # region to use linear approx for rho +proj = np.zeros(17) +magVector = {} + +for j in range(0, 16, True): + magLo = m0 * np.array([cos(magPhi[j]), sin(magPhi[j]), 0]) + magVector[j] = magLo + magHi = m0 * np.array([cos(magPhi[j + 1]), sin(magPhi[j + 1]), 0]) + phiHat = np.array([-sin(j * pi / 8 + pi / 16), cos(j * pi / 8 + pi / 16), 0]) + proj[j] = np.matmul((magLo - magHi), phiHat) + + +def Field(position): + # expects position in millimeters + return bFieldXYZ(position[0] / 1000, position[1] / 1000, position[2] / 1000) + + +# negative derivative (with respect to rho) of integrand over curved surface +def cylDrho(rho, phi, z, rhoPrime, L, k): + if rho < rhoPrime: + bessel_k = kn(1, k * rhoPrime) + if bessel_k == 0: + return 0 + return ( + -m0 * (phiConst / pi**2) * rhoPrime * (iv(0, k * rho) + iv(2, k * rho)) + * bessel_k * cos(k * z) * cos(phi) * sin(k * L / 2) + ) + + else: + bessel_k = kn(0, k * rho) + kn(2, k * rho) + if bessel_k == 0: + return 0 + return ( + -m0 * (phiConst / pi**2) * rhoPrime * iv(1, k * rhoPrime) + * bessel_k * cos(k * z) * cos(phi) * sin(k * L / 2) + ) + + +# negative derivative (with respect to phi) of integrand over curved surface, divided by rho +def cylDphi(rho, phi, z, rhoPrime, L, k): + if rho < rhoLimit: + bessel_k = kn(1, k * rhoPrime) + if bessel_k == 0: + return 0 + return ( + m0 * (phiConst / pi**2) * rhoPrime * bessel_k * cos(k * z) + * sin(k * L / 2) * sin(phi) + ) + elif rho < rhoPrime: + bessel_k = kn(1, k * rhoPrime) + if bessel_k == 0: + return 0 + return ( + m0 * (2 * phiConst / (k * pi**2)) * rhoPrime * iv(1, k * rho) + * bessel_k * cos(k * z) * sin(k * L / 2) * sin(phi) / rho + ) + else: + bessel_k = kn(1, k * rho) + if bessel_k == 0: + return 0 + return ( + m0 * (2 * phiConst / (k * pi**2)) * rhoPrime * iv(1, k * rhoPrime) + * bessel_k * cos(k * z) * sin(k * L / 2) * sin(phi) / rho + ) + + +# negative derivative (with respect to z) of integrand over curved surface; valid for rho < rhoPrime +def cylDz(rho, phi, z, rhoPrime, L, k): + if rho < rhoPrime: + bessel_k = kn(1, k * rhoPrime) + if bessel_k == 0: + return 0 + return ( + -m0 * (2 * phiConst / pi**2) * rhoPrime * iv(1, k * rho) + * bessel_k * sin(k * z) * sin(k * L / 2) * cos(phi) + ) + else: + bessel_k = kn(1, k * rho) + if bessel_k == 0: + return 0 + return ( + m0 * (2 * phiConst / pi**2) * rhoPrime * iv(1, k * rhoPrime) + * bessel_k * sin(k * z) * sin(k * L / 2) * cos(phi) + ) + + +# negative derivative (with respect to rho) of integrand over sector boundary surface +def planeDrho(rho, phi, z, j, L, rhoPrime): + phiPrime = j * pi / 8 + pi / 16 + aSq = rho**2 + rhoPrime**2 - 2 * rho * rhoPrime * cos(phi - phiPrime) + aSqMinus = aSq + (z - L / 2) ** 2 + aSqPlus = aSq + (z + L / 2) ** 2 + return ( + (proj[j] / (4 * pi)) * (rho - rhoPrime * cos(phi - phiPrime)) + * ( + 1 / ((sqrt(aSqPlus) + z + L / 2) * sqrt(aSqPlus)) + - 1 / ((sqrt(aSqMinus) + z - L / 2) * sqrt(aSqMinus)) + ) + ) + + +# negative derivative (with respect to phi) of integrand over sector boundary surface, divided by rho +def planeDphi(rho, phi, z, j, L, rhoPrime): + phiPrime = j * pi / 8 + pi / 16 + aSq = rho**2 + rhoPrime**2 - 2 * rho * rhoPrime * cos(phi - phiPrime) + aSqMinus = aSq + (z - L / 2) ** 2 + aSqPlus = aSq + (z + L / 2) ** 2 + return ( + (proj[j] / (4 * pi)) * rhoPrime + * ( + 1 / ((sqrt(aSqPlus) + z + L / 2) * sqrt(aSqPlus)) + - 1 / ((sqrt(aSqMinus) + z - L / 2) * sqrt(aSqMinus)) + ) + * sin(phi - phiPrime) + ) + + +# negative derivative (with respect to z) of integrand over sector boundary surface, divided by rho +def planeDz(rho, phi, z, j, L, rhoPrime): + phiPrime = j * pi / 8 + pi / 16 + aSq = rho**2 + rhoPrime**2 - 2 * rho * rhoPrime * cos(phi - phiPrime) + aSqMinus = aSq + (z - L / 2) ** 2 + aSqPlus = aSq + (z + L / 2) ** 2 + return (proj[j] / (4 * pi)) * (1 / sqrt(aSqPlus) - 1 / sqrt(aSqMinus)) + + +def field(rho, phi, z, L): + fieldCylRho = lambda k: -cylDrho(rho, phi, z, rMin, L, k) + cylDrho( + rho, phi, z, rMax, L, k + ) + fieldPlaneRho = lambda rhoPrime: sum( + list(map(lambda j: planeDrho(rho, phi, z, j, L, rhoPrime), range(16))) + ) + + fieldCylPhi = lambda k: -cylDphi(rho, phi, z, rMin, L, k) + cylDphi( + rho, phi, z, rMax, L, k + ) + fieldPlanePhi = lambda rhoPrime: sum( + list(map(lambda j: planeDphi(rho, phi, z, j, L, rhoPrime), range(16))) + ) + + fieldCylZ = lambda k: -cylDz(rho, phi, z, rMin, L, k) + cylDz( + rho, phi, z, rMax, L, k + ) + fieldPlaneZ = lambda rhoPrime: sum( + list(map(lambda j: planeDz(rho, phi, z, j, L, rhoPrime), range(16))) + ) + myLimit = 1000 + return np.array( + [ + -quad(fieldCylRho, 0, np.inf, limit=myLimit)[0] + + quad(fieldPlaneRho, rMin, rMax, limit=myLimit)[0], + -quad(fieldCylPhi, 0, np.inf, limit=myLimit)[0] + + quad(fieldPlanePhi, rMin, rMax, limit=myLimit)[0], + quad(fieldCylZ, 0, np.inf, limit=myLimit)[0] + + quad(fieldPlaneZ, rMin, rMax, limit=myLimit)[0], + ] + ) + + +def mField(rho, phi, z): + magnetization = np.zeros(3) + if phi < 0: + phi = phi + 2 * pi + for iz in range(3): + if ( + z < (magnetZ[iz] - zMax[iz] / 2) + or z > (magnetZ[iz] + zMax[iz] / 2) + or rho < rMin + or rho > rMax + ): + continue + j = (phi + pi / 16) // (pi / 8) + j = j % 16 + magnetization = magVector[j] # this is cartesian! + break + rhoUnit = np.array([cos(phi), sin(phi), 0]) + phiUnit = np.array([-sin(phi), cos(phi), 0]) + zUnit = np.array([0, 0, 1]) + return np.array( + [ + np.matmul(magnetization, rhoUnit), + np.matmul(magnetization, phiUnit), + np.matmul(magnetization, zUnit), + ] + ) + + +def mFieldXYZ(x, y, z): + rho = sqrt(x**2 + y**2) + if rho > 0: + phi = atan2(y, x) + else: + phi = 0 + + m = mField(rho, phi, z) + return np.array( + [m[0] * cos(phi) - m[1] * sin(phi), m[0] * sin(phi) + m[1] * cos(phi), m[2]] + ) + + +def bField(rho, phi, z): + return ( + hField(rho, phi, z - magnetZ[0], zMax[0]) + + hField(rho, phi, z - magnetZ[1], zMax[1]) + + hField(rho, phi, z - magnetZ[2], zMax[2]) + + mField(rho, phi, z) + ) + + +def bFieldXYZ(x, y, z): + return hFieldXYZ(x, y, z) + mFieldXYZ(x, y, z) + + +def hField(rho, phi, z): + return ( + field(rho, phi, z - magnetZ[0], zMax[0]) + + field(rho, phi, z - magnetZ[1], zMax[1]) + + field(rho, phi, z - magnetZ[2], zMax[2]) + ) + + +def hFieldXYZ(x, y, z): + rho = sqrt(x**2 + y**2) + if rho > 0: + phi = atan2(y, x) + else: + phi = 0 + + h = hField(rho, phi, z) + return np.array( + [h[0] * cos(phi) - h[1] * sin(phi), h[0] * sin(phi) + h[1] * cos(phi), h[2]] + ) diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx index 066698a478fbea03a6565a056afafd253e449a0d..41611bad5882de7e15ce3de15f6c4833fbf10581 100644 --- a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx +++ b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx @@ -7,6 +7,7 @@ #include "Identifier/Identifier.h" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "CircleFit.h" +#include "Identifier/Identifier.h" #include "LinearFit.h" #include "TrackClassification.h" #include <array> @@ -264,12 +265,13 @@ CircleFitTrackSeedTool::Seed::Seed(const std::vector<Segment> &segments) : size = clusters.size(); } - void CircleFitTrackSeedTool::Seed::fit() { CircleFit::CircleData circleData(positions); CircleFit::Circle circle = CircleFit::circleFit(circleData); momentum = circle.r > 0 ? circle.r * 0.001 * 0.3 * 0.55 : 9999999.; - charge = circle.cy < 0 ? -1 : 1; + // the magnetic field bends a positively charged particle downwards, thus the + // y-component of the center of a fitted circle is negative. + charge = circle.cy < 0 ? 1 : -1; } void CircleFitTrackSeedTool::Seed::fakeFit(double B) { @@ -279,7 +281,9 @@ void CircleFitTrackSeedTool::Seed::fakeFit(double B) { cy = circle.cy; r = circle.r; momentum = r * 0.3 * B * m_MeV2GeV; - charge = circle.cy > 0 ? 1 : -1; + // the magnetic field bends a positively charged particle downwards, thus the + // y-component of the center of a fitted circle is negative. + charge = circle.cy < 0 ? 1 : -1; } void CircleFitTrackSeedTool::Seed::linearFit(const std::vector<Acts::Vector2> &points) { @@ -288,7 +292,6 @@ void CircleFitTrackSeedTool::Seed::linearFit(const std::vector<Acts::Vector2> &p c0 = origin[1] - origin[0] * c1; } - double CircleFitTrackSeedTool::Seed::getY(double z) { double sqt = std::sqrt(-cx*cx + 2*cx*z + r*r - z*z); return abs(cy - sqt) < abs(cy + sqt) ? cy - sqt : cy + sqt;