Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
Garfield::ComponentAnalyticField Class Reference

#include <ComponentAnalyticField.hh>

+ Inheritance diagram for Garfield::ComponentAnalyticField:

Public Types

enum  Cell {
  A00 , B1X , B1Y , B2X ,
  B2Y , C10 , C2X , C2Y ,
  C30 , D10 , D20 , D30 ,
  D40 , Unknown
}
 

Public Member Functions

 ComponentAnalyticField ()
 Constructor.
 
 ~ComponentAnalyticField ()
 Destructor.
 
MediumGetMedium (const double x, const double y, const double z) override
 Get the medium at a given location (x, y, z).
 
void ElectricField (const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status) override
 
void ElectricField (const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&m, int &status) override
 Calculate the drift field [V/cm] and potential [V] at (x, y, z).
 
bool GetVoltageRange (double &pmin, double &pmax) override
 Calculate the voltage range [V].
 
void WeightingField (const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label) override
 
double WeightingPotential (const double x, const double y, const double z, const std::string &label) override
 
bool GetBoundingBox (double &x0, double &y0, double &z0, double &x1, double &y1, double &z1) override
 Get the bounding box coordinates.
 
bool GetElementaryCell (double &x0, double &y0, double &z0, double &x1, double &y1, double &z1) override
 Get the coordinates of the elementary cell.
 
bool IsWireCrossed (const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc, const bool centre, double &rc) override
 
bool IsInTrapRadius (const double q0, const double x0, const double y0, const double z0, double &xw, double &yx, double &rw) override
 
void SetMedium (Medium *medium)
 Set the medium inside the cell.
 
void AddWire (const double x, const double y, const double diameter, const double voltage, const std::string &label, const double length=100., const double tension=50., const double rho=19.3, const int ntrap=5)
 Add a wire at (x, y) .
 
void AddTube (const double radius, const double voltage, const int nEdges, const std::string &label)
 Add a tube.
 
void AddPlaneX (const double x, const double voltage, const std::string &label)
 Add a plane at constant x.
 
void AddPlaneY (const double y, const double voltage, const std::string &label)
 Add a plane at constant y.
 
void AddPlaneR (const double r, const double voltage, const std::string &label)
 Add a plane at constant radius.
 
void AddPlanePhi (const double phi, const double voltage, const std::string &label)
 Add a plane at constant phi.
 
void AddStripOnPlaneX (const char direction, const double x, const double smin, const double smax, const std::string &label, const double gap=-1.)
 
void AddStripOnPlaneY (const char direction, const double y, const double smin, const double smax, const std::string &label, const double gap=-1.)
 Add a strip in the x or z direction on an existing plane at constant y.
 
void AddStripOnPlaneR (const char direction, const double r, const double smin, const double smax, const std::string &label, const double gap=-1.)
 Add a strip in the phi or z direction on an existing plane at constant radius.
 
void AddStripOnPlanePhi (const char direction, const double phi, const double smin, const double smax, const std::string &label, const double gap=-1.)
 Add a strip in the r or z direction on an existing plane at constant phi.
 
void AddPixelOnPlaneX (const double x, const double ymin, const double ymax, const double zmin, const double zmax, const std::string &label, const double gap=-1., const double rot=0.)
 
void AddPixelOnPlaneY (const double y, const double xmin, const double xmax, const double zmin, const double zmax, const std::string &label, const double gap=-1., const double rot=0.)
 Add a pixel on an existing plane at constant y.
 
void AddPixelOnPlaneR (const double r, const double phimin, const double phimax, const double zmin, const double zmax, const std::string &label, const double gap=-1.)
 Add a pixel on an existing plane at constant radius.
 
void AddPixelOnPlanePhi (const double phi, const double rmin, const double rmax, const double zmin, const double zmax, const std::string &label, const double gap=-1.)
 Add a pixel on an existing plane at constant phi.
 
void SetPeriodicityX (const double s)
 Set the periodic length [cm] in the x-direction.
 
void SetPeriodicityY (const double s)
 Set the periodic length [cm] in the y-direction.
 
void SetPeriodicityPhi (const double phi)
 Set the periodicity [degree] in phi.
 
bool GetPeriodicityX (double &s)
 Get the periodic length in the x-direction.
 
bool GetPeriodicityY (double &s)
 Get the periodic length in the y-direction.
 
bool GetPeriodicityPhi (double &s)
 Get the periodicity [degree] in phi.
 
void SetCartesianCoordinates ()
 Use Cartesian coordinates (default).
 
void SetPolarCoordinates ()
 Use polar coordinates.
 
bool IsPolar () const
 Are polar coordinates being used?

 
void PrintCell ()
 Print all available information on the cell.
 
void AddCharge (const double x, const double y, const double z, const double q)
 Add a point charge.
 
void ClearCharges ()
 Remove all point charges.
 
void PrintCharges () const
 Print a list of the point charges.
 
std::string GetCellType ()
 
void AddReadout (const std::string &label)
 Setup the weighting field for a given group of wires or planes.
 
bool MultipoleMoments (const unsigned int iw, const unsigned int order=4, const bool print=false, const bool plot=false, const double rmult=1., const double eps=1.e-4, const unsigned int nMaxIter=20)
 
void EnableDipoleTerms (const bool on=true)
 Request dipole terms be included for each of the wires (default: off).
 
void EnableChargeCheck (const bool on=true)
 Check the quality of the capacitance matrix inversion (default: off).
 
unsigned int GetNumberOfWires () const
 Get the number of wires.
 
bool GetWire (const unsigned int i, double &x, double &y, double &diameter, double &voltage, std::string &label, double &length, double &charge, int &ntrap) const
 Retrieve the parameters of a wire.
 
unsigned int GetNumberOfPlanesX () const
 Get the number of equipotential planes at constant x.
 
unsigned int GetNumberOfPlanesY () const
 Get the number of equipotential planes at constant y.
 
unsigned int GetNumberOfPlanesR () const
 Get the number of equipotential planes at constant radius.
 
unsigned int GetNumberOfPlanesPhi () const
 Get the number of equipotential planes at constant phi.
 
bool GetPlaneX (const unsigned int i, double &x, double &voltage, std::string &label) const
 Retrieve the parameters of a plane at constant x.
 
bool GetPlaneY (const unsigned int i, double &y, double &voltage, std::string &label) const
 Retrieve the parameters of a plane at constant y.
 
bool GetPlaneR (const unsigned int i, double &r, double &voltage, std::string &label) const
 Retrieve the parameters of a plane at constant radius.
 
bool GetPlanePhi (const unsigned int i, double &phi, double &voltage, std::string &label) const
 Retrieve the parameters of a plane at constant phi.
 
bool GetTube (double &r, double &voltage, int &nEdges, std::string &label) const
 Retrieve the tube parameters.
 
bool ElectricFieldAtWire (const unsigned int iw, double &ex, double &ey)
 
void SetScanningGrid (const unsigned int nX, const unsigned int nY)
 
void SetScanningArea (const double xmin, const double xmax, const double ymin, const double ymax)
 
void SetScanningAreaLargest ()
 
void SetScanningAreaFirstOrder (const double scale=2.)
 
void EnableExtrapolation (const bool on=true)
 
void SetGravity (const double dx, const double dy, const double dz)
 Set the gravity orientation.
 
void GetGravity (double &dx, double &dy, double &dz) const
 Get the gravity orientation.
 
bool ForcesOnWire (const unsigned int iw, std::vector< double > &xMap, std::vector< double > &yMap, std::vector< std::vector< double > > &fxMap, std::vector< std::vector< double > > &fyMap)
 
bool WireDisplacement (const unsigned int iw, const bool detailed, std::vector< double > &csag, std::vector< double > &xsag, std::vector< double > &ysag, double &stretch, const bool print=true)
 
void SetNumberOfShots (const unsigned int n)
 
void SetNumberOfSteps (const unsigned int n)
 Set the number of integration steps within each shot (must be >= 1).
 
- Public Member Functions inherited from Garfield::Component
 Component ()=delete
 Default constructor.
 
 Component (const std::string &name)
 Constructor.
 
virtual ~Component ()
 Destructor.
 
virtual void SetGeometry (Geometry *geo)
 Define the geometry.
 
virtual void Clear ()
 Reset.
 
virtual MediumGetMedium (const double x, const double y, const double z)
 Get the medium at a given location (x, y, z).
 
virtual void ElectricField (const double x, const double y, const double z, double &ex, double &ey, double &ez, Medium *&m, int &status)=0
 
virtual void ElectricField (const double x, const double y, const double z, double &ex, double &ey, double &ez, double &v, Medium *&m, int &status)=0
 Calculate the drift field [V/cm] and potential [V] at (x, y, z).
 
virtual bool GetVoltageRange (double &vmin, double &vmax)=0
 Calculate the voltage range [V].
 
virtual void WeightingField (const double x, const double y, const double z, double &wx, double &wy, double &wz, const std::string &label)
 
virtual double WeightingPotential (const double x, const double y, const double z, const std::string &label)
 
virtual void DelayedWeightingField (const double x, const double y, const double z, const double t, double &wx, double &wy, double &wz, const std::string &label)
 
virtual double DelayedWeightingPotential (const double x, const double y, const double z, const double t, const std::string &label)
 
virtual void MagneticField (const double x, const double y, const double z, double &bx, double &by, double &bz, int &status)
 
void SetMagneticField (const double bx, const double by, const double bz)
 Set a constant magnetic field.
 
virtual bool IsReady ()
 Ready for use?
 
virtual bool GetBoundingBox (double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
 Get the bounding box coordinates.
 
virtual bool GetElementaryCell (double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)
 Get the coordinates of the elementary cell.
 
double IntegrateFluxCircle (const double xc, const double yc, const double r, const unsigned int nI=50)
 
double IntegrateFluxSphere (const double xc, const double yc, const double zc, const double r, const unsigned int nI=20)
 
double IntegrateFluxParallelogram (const double x0, const double y0, const double z0, const double dx1, const double dy1, const double dz1, const double dx2, const double dy2, const double dz2, const unsigned int nU=20, const unsigned int nV=20)
 
double IntegrateWeightingFluxParallelogram (const std::string &label, const double x0, const double y0, const double z0, const double dx1, const double dy1, const double dz1, const double dx2, const double dy2, const double dz2, const unsigned int nU=20, const unsigned int nV=20)
 
double IntegrateFluxLine (const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, const double xp, const double yp, const double zp, const unsigned int nI, const int isign=0)
 
virtual bool IsWireCrossed (const double x0, const double y0, const double z0, const double x1, const double y1, const double z1, double &xc, double &yc, double &zc, const bool centre, double &rc)
 
virtual bool IsInTrapRadius (const double q0, const double x0, const double y0, const double z0, double &xw, double &yw, double &rw)
 
void EnablePeriodicityX (const bool on=true)
 Enable simple periodicity in the $x$ direction.
 
void EnablePeriodicityY (const bool on=true)
 Enable simple periodicity in the $y$ direction.
 
void EnablePeriodicityZ (const bool on=true)
 Enable simple periodicity in the $z$ direction.
 
void IsPeriodic (bool &perx, bool &pery, bool &perz)
 Return periodicity flags.
 
void EnableMirrorPeriodicityX (const bool on=true)
 Enable mirror periodicity in the $x$ direction.
 
void EnableMirrorPeriodicityY (const bool on=true)
 Enable mirror periodicity in the $y$ direction.
 
void EnableMirrorPeriodicityZ (const bool on=true)
 Enable mirror periodicity in the $y$ direction.
 
void IsMirrorPeriodic (bool &perx, bool &pery, bool &perz)
 Return mirror periodicity flags.
 
void EnableAxialPeriodicityX (const bool on=true)
 Enable axial periodicity in the $x$ direction.
 
void EnableAxialPeriodicityY (const bool on=true)
 Enable axial periodicity in the $y$ direction.
 
void EnableAxialPeriodicityZ (const bool on=true)
 Enable axial periodicity in the $z$ direction.
 
void IsAxiallyPeriodic (bool &perx, bool &pery, bool &perz)
 Return axial periodicity flags.
 
void EnableRotationSymmetryX (const bool on=true)
 Enable rotation symmetry around the $x$ axis.
 
void EnableRotationSymmetryY (const bool on=true)
 Enable rotation symmetry around the $y$ axis.
 
void EnableRotationSymmetryZ (const bool on=true)
 Enable rotation symmetry around the $z$ axis.
 
void IsRotationSymmetric (bool &rotx, bool &roty, bool &rotz)
 Return rotation symmetry flags.
 
void EnableDebugging ()
 Switch on debugging messages.
 
void DisableDebugging ()
 Switch off debugging messages.
 
virtual bool HasAttachmentMap () const
 Does the component have attachment maps?
 
virtual bool HasVelocityMap () const
 Does the component have velocity maps?
 
virtual bool ElectronAttachment (const double, const double, const double, double &eta)
 Get the electron attachment coefficient.
 
virtual bool HoleAttachment (const double, const double, const double, double &eta)
 Get the hole attachment coefficient.
 
virtual bool ElectronVelocity (const double, const double, const double, double &vx, double &vy, double &vz)
 Get the electron drift velocity.
 
virtual bool HoleVelocity (const double, const double, const double, double &vx, double &vy, double &vz)
 Get the hole drift velocity.
 
virtual bool GetElectronLifetime (const double, const double, const double, double &etau)
 
virtual bool GetHoleLifetime (const double, const double, const double, double &htau)
 

Additional Inherited Members

virtual void Reset ()=0
 Reset the component.
 
virtual void UpdatePeriodicity ()=0
 Verify periodicities.
 
- Protected Attributes inherited from Garfield::Component
std::string m_className = "Component"
 Class name.
 
Geometrym_geometry = nullptr
 Pointer to the geometry.
 
std::array< double, 3 > m_b0 = {{0., 0., 0.}}
 Constant magnetic field.
 
bool m_ready = false
 Ready for use?
 
bool m_debug = false
 Switch on/off debugging messages.
 
std::array< bool, 3 > m_periodic = {{false, false, false}}
 Simple periodicity in x, y, z.
 
std::array< bool, 3 > m_mirrorPeriodic = {{false, false, false}}
 Mirror periodicity in x, y, z.
 
std::array< bool, 3 > m_axiallyPeriodic = {{false, false, false}}
 Axial periodicity in x, y, z.
 
std::array< bool, 3 > m_rotationSymmetric = {{false, false, false}}
 Rotation symmetry around x-axis, y-axis, z-axis.
 

Detailed Description

Semi-analytic calculation of two-dimensional configurations consisting of wires, planes, and tubes.

Definition at line 16 of file ComponentAnalyticField.hh.

Member Enumeration Documentation

◆ Cell

Constructor & Destructor Documentation

◆ ComponentAnalyticField()

Garfield::ComponentAnalyticField::ComponentAnalyticField ( )

Constructor.

Definition at line 249 of file ComponentAnalyticField.cc.

249 : Component("AnalyticField") {
250 CellInit();
251}
Component()=delete
Default constructor.

◆ ~ComponentAnalyticField()

Garfield::ComponentAnalyticField::~ComponentAnalyticField ( )
inline

Destructor.

Definition at line 21 of file ComponentAnalyticField.hh.

21{}

Member Function Documentation

◆ AddCharge()

void Garfield::ComponentAnalyticField::AddCharge ( const double  x,
const double  y,
const double  z,
const double  q 
)

Add a point charge.

Definition at line 1605 of file ComponentAnalyticField.cc.

1606 {
1607 // Convert from fC to internal units (division by 4 pi epsilon0).
1608 Charge3d charge;
1609 charge.x = x;
1610 charge.y = y;
1611 charge.z = z;
1612 charge.e = q / FourPiEpsilon0;
1613 m_ch3d.push_back(std::move(charge));
1614}

◆ AddPixelOnPlanePhi()

void Garfield::ComponentAnalyticField::AddPixelOnPlanePhi ( const double  phi,
const double  rmin,
const double  rmax,
const double  zmin,
const double  zmax,
const std::string &  label,
const double  gap = -1. 
)

Add a pixel on an existing plane at constant phi.

Definition at line 1397 of file ComponentAnalyticField.cc.

1400 {
1401 if (!m_polar || (!m_ynplan[2] && !m_ynplan[3])) {
1402 std::cerr << m_className << "::AddPixelOnPlanePhi:\n"
1403 << " There are no planes at constant phi.\n";
1404 return;
1405 }
1406
1407 if (fabs(rmax - rmin) < Small || fabs(zmax - zmin) < Small) {
1408 std::cerr << m_className << "::AddPixelOnPlanePhi:\n"
1409 << " Pixel width must be greater than zero.\n";
1410 return;
1411 }
1412 if (rmin < Small || rmax < Small) {
1413 std::cerr << m_className << "::AddPixelOnPlanePhi:\n"
1414 << " Radius must be greater than zero.\n";
1415 return;
1416 }
1417 Pixel pixel;
1418 pixel.type = label;
1419 pixel.ind = -1;
1420 const double smin = log(rmin);
1421 const double smax = log(rmax);
1422 pixel.smin = std::min(smin, smax);
1423 pixel.smax = std::max(smin, smax);
1424 pixel.zmin = std::min(zmin, zmax);
1425 pixel.zmax = std::max(zmin, zmax);
1426 pixel.gap = gap > Small ? DegreeToRad * gap : -1.;
1427
1428 int iplane = 2;
1429 if (m_ynplan[3]) {
1430 const double d0 = fabs(m_coplan[2] - phi * DegreeToRad);
1431 const double d1 = fabs(m_coplan[3] - phi * DegreeToRad);
1432 if (d1 < d0) iplane = 3;
1433 }
1434
1435 m_planes[iplane].pixels.push_back(std::move(pixel));
1436}
std::string m_className
Class name.
Definition: Component.hh:329
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615

◆ AddPixelOnPlaneR()

void Garfield::ComponentAnalyticField::AddPixelOnPlaneR ( const double  r,
const double  phimin,
const double  phimax,
const double  zmin,
const double  zmax,
const std::string &  label,
const double  gap = -1. 
)

Add a pixel on an existing plane at constant radius.

Definition at line 1359 of file ComponentAnalyticField.cc.

1362 {
1363 if (!m_polar || (!m_ynplan[0] && !m_ynplan[1])) {
1364 std::cerr << m_className << "::AddPixelOnPlaneR:\n"
1365 << " There are no planes at constant r.\n";
1366 return;
1367 }
1368
1369 if (fabs(phimax - phimin) < Small || fabs(zmax - zmin) < Small) {
1370 std::cerr << m_className << "::AddPixelOnPlaneR:\n"
1371 << " Pixel width must be greater than zero.\n";
1372 return;
1373 }
1374
1375 Pixel pixel;
1376 pixel.type = label;
1377 pixel.ind = -1;
1378 const double smin = phimin * DegreeToRad;
1379 const double smax = phimax * DegreeToRad;
1380 pixel.smin = std::min(smin, smax);
1381 pixel.smax = std::max(smin, smax);
1382 pixel.zmin = std::min(zmin, zmax);
1383 pixel.zmax = std::max(zmin, zmax);
1384 pixel.gap = gap > Small ? gap : -1.;
1385
1386 int iplane = 0;
1387 if (m_ynplan[1]) {
1388 const double rho = r > 0. ? log(r) : -25.;
1389 const double d0 = fabs(m_coplan[0] - rho);
1390 const double d1 = fabs(m_coplan[1] - rho);
1391 if (d1 < d0) iplane = 1;
1392 }
1393
1394 m_planes[iplane].pixels.push_back(std::move(pixel));
1395}

◆ AddPixelOnPlaneX()

void Garfield::ComponentAnalyticField::AddPixelOnPlaneX ( const double  x,
const double  ymin,
const double  ymax,
const double  zmin,
const double  zmax,
const std::string &  label,
const double  gap = -1.,
const double  rot = 0. 
)

Add a pixel on an existing plane at constant x.

Parameters
xcoordinate of the plane.
yminlower limit of the pixel cell in y,
ymaxupper limit of the pixel cell in y.
zminlower limit of the pixel cell in z.
zmaxupper limit of the pixel cell in z.
labelweighting field identifier.
gapdistance to the opposite plane (optional).
rotrotation angle (rad) of the pixel (optional).

Definition at line 1281 of file ComponentAnalyticField.cc.

1284 {
1285 if (m_polar || (!m_ynplan[0] && !m_ynplan[1])) {
1286 std::cerr << m_className << "::AddPixelOnPlaneX:\n"
1287 << " There are no planes at constant x.\n";
1288 return;
1289 }
1290
1291 if (fabs(ymax - ymin) < Small || fabs(zmax - zmin) < Small) {
1292 std::cerr << m_className << "::AddPixelOnPlaneX:\n"
1293 << " Pixel width must be greater than zero.\n";
1294 return;
1295 }
1296
1297 Pixel pixel;
1298 pixel.type = label;
1299 pixel.ind = -1;
1300 pixel.smin = std::min(ymin, ymax);
1301 pixel.smax = std::max(ymin, ymax);
1302 pixel.zmin = std::min(zmin, zmax);
1303 pixel.zmax = std::max(zmin, zmax);
1304 pixel.gap = gap > Small ? gap : -1.;
1305 if (fabs(rot) > 1.e-9) {
1306 pixel.cphi = cos(rot);
1307 pixel.sphi = sin(rot);
1308 }
1309
1310 int iplane = 0;
1311 if (m_ynplan[1]) {
1312 const double d0 = fabs(m_coplan[0] - x);
1313 const double d1 = fabs(m_coplan[1] - x);
1314 if (d1 < d0) iplane = 1;
1315 }
1316
1317 m_planes[iplane].pixels.push_back(std::move(pixel));
1318}
DoubleAc cos(const DoubleAc &f)
Definition: DoubleAc.cpp:432
DoubleAc sin(const DoubleAc &f)
Definition: DoubleAc.cpp:384

◆ AddPixelOnPlaneY()

void Garfield::ComponentAnalyticField::AddPixelOnPlaneY ( const double  y,
const double  xmin,
const double  xmax,
const double  zmin,
const double  zmax,
const std::string &  label,
const double  gap = -1.,
const double  rot = 0. 
)

Add a pixel on an existing plane at constant y.

Definition at line 1320 of file ComponentAnalyticField.cc.

1323 {
1324 if (m_polar || (!m_ynplan[2] && !m_ynplan[3])) {
1325 std::cerr << m_className << "::AddPixelOnPlaneY:\n"
1326 << " There are no planes at constant y.\n";
1327 return;
1328 }
1329
1330 if (fabs(xmax - xmin) < Small || fabs(zmax - zmin) < Small) {
1331 std::cerr << m_className << "::AddPixelOnPlaneY:\n"
1332 << " Pixel width must be greater than zero.\n";
1333 return;
1334 }
1335
1336 Pixel pixel;
1337 pixel.type = label;
1338 pixel.ind = -1;
1339 pixel.smin = std::min(xmin, xmax);
1340 pixel.smax = std::max(xmin, xmax);
1341 pixel.zmin = std::min(zmin, zmax);
1342 pixel.zmax = std::max(zmin, zmax);
1343 pixel.gap = gap > Small ? gap : -1.;
1344 if (fabs(rot) > 1.e-9) {
1345 pixel.cphi = cos(rot);
1346 pixel.sphi = sin(rot);
1347 }
1348
1349 int iplane = 2;
1350 if (m_ynplan[3]) {
1351 const double d0 = fabs(m_coplan[2] - y);
1352 const double d1 = fabs(m_coplan[3] - y);
1353 if (d1 < d0) iplane = 3;
1354 }
1355
1356 m_planes[iplane].pixels.push_back(std::move(pixel));
1357}

◆ AddPlanePhi()

void Garfield::ComponentAnalyticField::AddPlanePhi ( const double  phi,
const double  voltage,
const std::string &  label 
)

Add a plane at constant phi.

Definition at line 1043 of file ComponentAnalyticField.cc.

1044 {
1045 if (!m_polar) {
1046 std::cerr << m_className << "::AddPlanePhi:\n"
1047 << " Not compatible with Cartesian coordinates; ignored.\n";
1048 return;
1049 }
1050 if (m_ynplan[2] && m_ynplan[3]) {
1051 std::cerr << m_className << "::AddPlanePhi:\n"
1052 << " Cannot have more than two planes at constant phi.\n";
1053 return;
1054 }
1055
1056 if (m_ynplan[2]) {
1057 m_ynplan[3] = true;
1058 m_coplan[3] = phi * DegreeToRad;
1059 m_vtplan[3] = v;
1060 m_planes[3].type = label;
1061 m_planes[3].ind = -1;
1062 } else {
1063 m_ynplan[2] = true;
1064 m_coplan[2] = phi * DegreeToRad;
1065 m_vtplan[2] = v;
1066 m_planes[2].type = label;
1067 m_planes[2].ind = -1;
1068 // Switch off default periodicity.
1069 if (m_pery && std::abs(m_sy - TwoPi) < 1.e-4) {
1070 m_pery = false;
1071 }
1072 }
1073
1074 // Force recalculation of the capacitance and signal matrices.
1075 m_cellset = false;
1076 m_sigset = false;
1077}

◆ AddPlaneR()

void Garfield::ComponentAnalyticField::AddPlaneR ( const double  r,
const double  voltage,
const std::string &  label 
)

Add a plane at constant radius.

Definition at line 1005 of file ComponentAnalyticField.cc.

1006 {
1007 if (!m_polar) {
1008 std::cerr << m_className << "::AddPlaneR:\n"
1009 << " Not compatible with Cartesian coordinates; ignored.\n";
1010 return;
1011 }
1012 if (r <= 0.) {
1013 std::cerr << m_className << "::AddPlaneR:\n"
1014 << " Radius must be larger than zero; plane ignored.\n";
1015 return;
1016 }
1017
1018 if (m_ynplan[0] && m_ynplan[1]) {
1019 std::cerr << m_className << "::AddPlaneR:\n"
1020 << " Cannot have more than two circular planes.\n";
1021 return;
1022 }
1023
1024 if (m_ynplan[0]) {
1025 m_ynplan[1] = true;
1026 m_coplan[1] = log(r);
1027 m_vtplan[1] = v;
1028 m_planes[1].type = label;
1029 m_planes[1].ind = -1;
1030 } else {
1031 m_ynplan[0] = true;
1032 m_coplan[0] = log(r);
1033 m_vtplan[0] = v;
1034 m_planes[0].type = label;
1035 m_planes[0].ind = -1;
1036 }
1037
1038 // Force recalculation of the capacitance and signal matrices.
1039 m_cellset = false;
1040 m_sigset = false;
1041}

◆ AddPlaneX()

void Garfield::ComponentAnalyticField::AddPlaneX ( const double  x,
const double  voltage,
const std::string &  label 
)

Add a plane at constant x.

Definition at line 941 of file ComponentAnalyticField.cc.

942 {
943 if (m_polar) {
944 std::cerr << m_className << "::AddPlaneX:\n"
945 << " Not compatible with polar coordinates; ignored.\n";
946 return;
947 }
948 if (m_ynplan[0] && m_ynplan[1]) {
949 std::cerr << m_className << "::AddPlaneX:\n"
950 << " Cannot have more than two planes at constant x.\n";
951 return;
952 }
953
954 if (m_ynplan[0]) {
955 m_ynplan[1] = true;
956 m_coplan[1] = x;
957 m_vtplan[1] = v;
958 m_planes[1].type = label;
959 m_planes[1].ind = -1;
960 } else {
961 m_ynplan[0] = true;
962 m_coplan[0] = x;
963 m_vtplan[0] = v;
964 m_planes[0].type = label;
965 m_planes[0].ind = -1;
966 }
967
968 // Force recalculation of the capacitance and signal matrices.
969 m_cellset = false;
970 m_sigset = false;
971}

◆ AddPlaneY()

void Garfield::ComponentAnalyticField::AddPlaneY ( const double  y,
const double  voltage,
const std::string &  label 
)

Add a plane at constant y.

Definition at line 973 of file ComponentAnalyticField.cc.

974 {
975 if (m_polar) {
976 std::cerr << m_className << "::AddPlaneY:\n"
977 << " Not compatible with polar coordinates; ignored.\n";
978 return;
979 }
980 if (m_ynplan[2] && m_ynplan[3]) {
981 std::cerr << m_className << "::AddPlaneY:\n"
982 << " Cannot have more than two planes at constant y.\n";
983 return;
984 }
985
986 if (m_ynplan[2]) {
987 m_ynplan[3] = true;
988 m_coplan[3] = y;
989 m_vtplan[3] = v;
990 m_planes[3].type = label;
991 m_planes[3].ind = -1;
992 } else {
993 m_ynplan[2] = true;
994 m_coplan[2] = y;
995 m_vtplan[2] = v;
996 m_planes[2].type = label;
997 m_planes[2].ind = -1;
998 }
999
1000 // Force recalculation of the capacitance and signal matrices.
1001 m_cellset = false;
1002 m_sigset = false;
1003}

Referenced by main().

◆ AddReadout()

void Garfield::ComponentAnalyticField::AddReadout ( const std::string &  label)

Setup the weighting field for a given group of wires or planes.

Definition at line 3535 of file ComponentAnalyticField.cc.

3535 {
3536 // Check if this readout group already exists.
3537 if (std::find(m_readout.begin(), m_readout.end(), label) != m_readout.end()) {
3538 std::cout << m_className << "::AddReadout:\n";
3539 std::cout << " Readout group " << label << " already exists.\n";
3540 return;
3541 }
3542 m_readout.push_back(label);
3543
3544 unsigned int nWiresFound = 0;
3545 for (const auto& wire : m_w) {
3546 if (wire.type == label) ++nWiresFound;
3547 }
3548
3549 unsigned int nPlanesFound = 0;
3550 unsigned int nStripsFound = 0;
3551 unsigned int nPixelsFound = 0;
3552 for (int i = 0; i < 5; ++i) {
3553 if (m_planes[i].type == label) ++nPlanesFound;
3554 for (const auto& strip : m_planes[i].strips1) {
3555 if (strip.type == label) ++nStripsFound;
3556 }
3557 for (const auto& strip : m_planes[i].strips2) {
3558 if (strip.type == label) ++nStripsFound;
3559 }
3560 for (const auto& pixel : m_planes[i].pixels) {
3561 if (pixel.type == label) ++nPixelsFound;
3562 }
3563 }
3564
3565 if (nWiresFound == 0 && nPlanesFound == 0 && nStripsFound == 0 &&
3566 nPixelsFound == 0) {
3567 std::cerr << m_className << "::AddReadout:\n";
3568 std::cerr << " At present there are no wires, planes or strips\n";
3569 std::cerr << " associated to readout group " << label << ".\n";
3570 } else {
3571 std::cout << m_className << "::AddReadout:\n";
3572 std::cout << " Readout group " << label << " comprises:\n";
3573 if (nWiresFound > 1) {
3574 std::cout << " " << nWiresFound << " wires\n";
3575 } else if (nWiresFound == 1) {
3576 std::cout << " 1 wire\n";
3577 }
3578 if (nPlanesFound > 1) {
3579 std::cout << " " << nPlanesFound << " planes\n";
3580 } else if (nPlanesFound == 1) {
3581 std::cout << " 1 plane\n";
3582 }
3583 if (nStripsFound > 1) {
3584 std::cout << " " << nStripsFound << " strips\n";
3585 } else if (nStripsFound == 1) {
3586 std::cout << " 1 strip\n";
3587 }
3588 if (nPixelsFound > 1) {
3589 std::cout << " " << nPixelsFound << " pixels\n";
3590 } else if (nPixelsFound == 1) {
3591 std::cout << " 1 pixel\n";
3592 }
3593 }
3594
3595 m_sigset = false;
3596}

Referenced by main().

◆ AddStripOnPlanePhi()

void Garfield::ComponentAnalyticField::AddStripOnPlanePhi ( const char  direction,
const double  phi,
const double  smin,
const double  smax,
const std::string &  label,
const double  gap = -1. 
)

Add a strip in the r or z direction on an existing plane at constant phi.

Definition at line 1222 of file ComponentAnalyticField.cc.

1227 {
1228 if (!m_polar || (!m_ynplan[2] && !m_ynplan[3])) {
1229 std::cerr << m_className << "::AddStripOnPlanePhi:\n"
1230 << " There are no planes at constant phi.\n";
1231 return;
1232 }
1233
1234 if (dir != 'r' && dir != 'R' && dir != 'z' && dir != 'Z') {
1235 std::cerr << m_className << "::AddStripOnPlanePhi:\n"
1236 << " Invalid direction (" << dir << ").\n"
1237 << " Only strips in r or z direction are possible.\n";
1238 return;
1239 }
1240
1241 if (fabs(smax - smin) < Small) {
1242 std::cerr << m_className << "::AddStripOnPlanePhi:\n"
1243 << " Strip width must be greater than zero.\n";
1244 return;
1245 }
1246
1247 Strip newStrip;
1248 newStrip.type = label;
1249 newStrip.ind = -1;
1250 if (dir== 'z' || dir == 'Z') {
1251 if (smin < Small || smax < Small) {
1252 std::cerr << m_className << "::AddStripOnPlanePhi:\n"
1253 << " Radius must be greater than zero.\n";
1254 return;
1255 }
1256 const double rhomin = log(smin);
1257 const double rhomax = log(smax);
1258 newStrip.smin = std::min(rhomin, rhomax);
1259 newStrip.smax = std::max(rhomin, rhomax);
1260 } else {
1261 newStrip.smin = std::min(smin, smax);
1262 newStrip.smax = std::max(smin, smax);
1263 }
1264 newStrip.gap = gap > Small ? DegreeToRad * gap : -1.;
1265
1266 int iplane = 2;
1267 if (m_ynplan[3]) {
1268 const double d2 = fabs(m_coplan[2] - phi * DegreeToRad);
1269 const double d3 = fabs(m_coplan[3] - phi * DegreeToRad);
1270 if (d3 < d2) iplane = 3;
1271 }
1272
1273 if (dir == 'r' || dir == 'R') {
1274 m_planes[iplane].strips1.push_back(std::move(newStrip));
1275 } else {
1276 m_planes[iplane].strips2.push_back(std::move(newStrip));
1277 }
1278}

◆ AddStripOnPlaneR()

void Garfield::ComponentAnalyticField::AddStripOnPlaneR ( const char  direction,
const double  r,
const double  smin,
const double  smax,
const std::string &  label,
const double  gap = -1. 
)

Add a strip in the phi or z direction on an existing plane at constant radius.

Definition at line 1169 of file ComponentAnalyticField.cc.

1173 {
1174 if (!m_polar || (!m_ynplan[0] && !m_ynplan[1])) {
1175 std::cerr << m_className << "::AddStripOnPlaneR:\n"
1176 << " There are no planes at constant r.\n";
1177 return;
1178 }
1179
1180 if (dir != 'p' && dir != 'P' && dir != 'z' && dir != 'Z') {
1181 std::cerr << m_className << "::AddStripOnPlaneR:\n"
1182 << " Invalid direction (" << dir << ").\n"
1183 << " Only strips in p(hi) or z direction are possible.\n";
1184 return;
1185 }
1186
1187 if (fabs(smax - smin) < Small) {
1188 std::cerr << m_className << "::AddStripOnPlaneR:\n"
1189 << " Strip width must be greater than zero.\n";
1190 return;
1191 }
1192
1193 Strip newStrip;
1194 newStrip.type = label;
1195 newStrip.ind = -1;
1196 if (dir == 'z' || dir == 'Z') {
1197 const double phimin = smin * DegreeToRad;
1198 const double phimax = smax * DegreeToRad;
1199 newStrip.smin = std::min(phimin, phimax);
1200 newStrip.smax = std::max(phimin, phimax);
1201 } else {
1202 newStrip.smin = std::min(smin, smax);
1203 newStrip.smax = std::max(smin, smax);
1204 }
1205 newStrip.gap = gap > Small ? gap : -1.;
1206
1207 int iplane = 0;
1208 if (m_ynplan[1]) {
1209 const double rho = r > 0. ? log(r) : -25.;
1210 const double d0 = fabs(m_coplan[0] - rho);
1211 const double d1 = fabs(m_coplan[1] - rho);
1212 if (d1 < d0) iplane = 1;
1213 }
1214
1215 if (dir == 'p' || dir == 'P') {
1216 m_planes[iplane].strips1.push_back(std::move(newStrip));
1217 } else {
1218 m_planes[iplane].strips2.push_back(std::move(newStrip));
1219 }
1220}

◆ AddStripOnPlaneX()

void Garfield::ComponentAnalyticField::AddStripOnPlaneX ( const char  direction,
const double  x,
const double  smin,
const double  smax,
const std::string &  label,
const double  gap = -1. 
)

Add a strip in the y or z direction on an existing plane at constant x.

Parameters
direction'y' or 'z'.
xcoordinate of the plane.
sminlower limit of the strip in y or z.
smaxupper limit of the strip in y or z.
labelweighting field identifier.
gapdistance to the opposite plane (optional).

Definition at line 1079 of file ComponentAnalyticField.cc.

1083 {
1084 if (m_polar || (!m_ynplan[0] && !m_ynplan[1])) {
1085 std::cerr << m_className << "::AddStripOnPlaneX:\n"
1086 << " There are no planes at constant x.\n";
1087 return;
1088 }
1089
1090 if (dir != 'y' && dir != 'Y' && dir != 'z' && dir != 'Z') {
1091 std::cerr << m_className << "::AddStripOnPlaneX:\n"
1092 << " Invalid direction (" << dir << ").\n"
1093 << " Only strips in y or z direction are possible.\n";
1094 return;
1095 }
1096
1097 if (fabs(smax - smin) < Small) {
1098 std::cerr << m_className << "::AddStripOnPlaneX:\n"
1099 << " Strip width must be greater than zero.\n";
1100 return;
1101 }
1102
1103 Strip newStrip;
1104 newStrip.type = label;
1105 newStrip.ind = -1;
1106 newStrip.smin = std::min(smin, smax);
1107 newStrip.smax = std::max(smin, smax);
1108 newStrip.gap = gap > Small ? gap : -1.;
1109
1110 int iplane = 0;
1111 if (m_ynplan[1]) {
1112 const double d0 = fabs(m_coplan[0] - x);
1113 const double d1 = fabs(m_coplan[1] - x);
1114 if (d1 < d0) iplane = 1;
1115 }
1116
1117 if (dir == 'y' || dir == 'Y') {
1118 m_planes[iplane].strips1.push_back(std::move(newStrip));
1119 } else {
1120 m_planes[iplane].strips2.push_back(std::move(newStrip));
1121 }
1122}

◆ AddStripOnPlaneY()

void Garfield::ComponentAnalyticField::AddStripOnPlaneY ( const char  direction,
const double  y,
const double  smin,
const double  smax,
const std::string &  label,
const double  gap = -1. 
)

Add a strip in the x or z direction on an existing plane at constant y.

Definition at line 1124 of file ComponentAnalyticField.cc.

1128 {
1129 if (m_polar || (!m_ynplan[2] && !m_ynplan[3])) {
1130 std::cerr << m_className << "::AddStripOnPlaneY:\n"
1131 << " There are no planes at constant y.\n";
1132 return;
1133 }
1134
1135 if (dir != 'x' && dir != 'X' && dir != 'z' && dir != 'Z') {
1136 std::cerr << m_className << "::AddStripOnPlaneY:\n"
1137 << " Invalid direction (" << dir << ").\n"
1138 << " Only strips in x or z direction are possible.\n";
1139 return;
1140 }
1141
1142 if (fabs(smax - smin) < Small) {
1143 std::cerr << m_className << "::AddStripOnPlaneY:\n"
1144 << " Strip width must be greater than zero.\n";
1145 return;
1146 }
1147
1148 Strip newStrip;
1149 newStrip.type = label;
1150 newStrip.ind = -1;
1151 newStrip.smin = std::min(smin, smax);
1152 newStrip.smax = std::max(smin, smax);
1153 newStrip.gap = gap > Small ? gap : -1.;
1154
1155 int iplane = 2;
1156 if (m_ynplan[3]) {
1157 const double d2 = fabs(m_coplan[2] - y);
1158 const double d3 = fabs(m_coplan[3] - y);
1159 if (d3 < d2) iplane = 3;
1160 }
1161
1162 if (dir == 'x' || dir == 'X') {
1163 m_planes[iplane].strips1.push_back(std::move(newStrip));
1164 } else {
1165 m_planes[iplane].strips2.push_back(std::move(newStrip));
1166 }
1167}

Referenced by main().

◆ AddTube()

void Garfield::ComponentAnalyticField::AddTube ( const double  radius,
const double  voltage,
const int  nEdges,
const std::string &  label 
)

Add a tube.

Definition at line 901 of file ComponentAnalyticField.cc.

903 {
904 // Check if the provided parameters make sense.
905 if (radius <= 0.0) {
906 std::cerr << m_className << "::AddTube: Unphysical tube dimension.\n";
907 return;
908 }
909
910 if (nEdges < 3 && nEdges != 0) {
911 std::cerr << m_className << "::AddTube: Unphysical number of tube edges ("
912 << nEdges << ")\n";
913 return;
914 }
915
916 // If there is already a tube defined, print a warning message.
917 if (m_tube) {
918 std::cout << m_className << "::AddTube:\n"
919 << " Warning: Existing tube settings will be overwritten.\n";
920 }
921
922 // Set the coordinate system.
923 m_tube = true;
924 m_polar = false;
925
926 // Set the tube parameters.
927 m_cotube = radius;
928 m_cotube2 = radius * radius;
929 m_vttube = voltage;
930
931 m_ntube = nEdges;
932
933 m_planes[4].type = label;
934 m_planes[4].ind = -1;
935
936 // Force recalculation of the capacitance and signal matrices.
937 m_cellset = false;
938 m_sigset = false;
939}

Referenced by GarfieldPhysics::InitializePhysics().

◆ AddWire()

void Garfield::ComponentAnalyticField::AddWire ( const double  x,
const double  y,
const double  diameter,
const double  voltage,
const std::string &  label,
const double  length = 100.,
const double  tension = 50.,
const double  rho = 19.3,
const int  ntrap = 5 
)

Add a wire at (x, y) .

Definition at line 834 of file ComponentAnalyticField.cc.

839 {
840 // Check if the provided parameters make sense.
841 if (diameter <= 0.) {
842 std::cerr << m_className << "::AddWire: Unphysical wire diameter.\n";
843 return;
844 }
845
846 if (tension <= 0.) {
847 std::cerr << m_className << "::AddWire: Unphysical wire tension.\n";
848 return;
849 }
850
851 if (rho <= 0.) {
852 std::cerr << m_className << "::AddWire: Unphysical wire density.\n";
853 return;
854 }
855
856 if (length <= 0.) {
857 std::cerr << m_className << "::AddWire: Unphysical wire length.\n";
858 return;
859 }
860
861 if (ntrap <= 0) {
862 std::cerr << m_className << "::AddWire: Nbr. of trap radii must be > 0.\n";
863 return;
864 }
865
866 if (m_polar && x <= diameter) {
867 std::cerr << m_className << "::AddWire: Wire is too close to the origin.\n";
868 return;
869 }
870
871 // Create a new wire.
872 Wire wire;
873 if (m_polar) {
874 double r = 0., phi = 0.;
875 Polar2Internal(x, y, r, phi);
876 wire.x = r;
877 wire.y = phi;
878 wire.r = 0.5 * diameter / x;
879 } else {
880 wire.x = x;
881 wire.y = y;
882 wire.r = 0.5 * diameter;
883 }
884 wire.v = voltage;
885 wire.u = length;
886 wire.type = label;
887 wire.e = 0.;
888 wire.ind = -1;
889 wire.nTrap = ntrap;
890 wire.tension = tension;
891 wire.density = rho;
892 // Add the wire to the list
893 m_w.push_back(std::move(wire));
894 ++m_nWires;
895
896 // Force recalculation of the capacitance and signal matrices.
897 m_cellset = false;
898 m_sigset = false;
899}

Referenced by GarfieldPhysics::InitializePhysics().

◆ ClearCharges()

void Garfield::ComponentAnalyticField::ClearCharges ( )

Remove all point charges.

Definition at line 1616 of file ComponentAnalyticField.cc.

1616 {
1617 m_ch3d.clear();
1618 m_nTermBessel = 10;
1619 m_nTermPoly = 100;
1620}

◆ ElectricField() [1/2]

void Garfield::ComponentAnalyticField::ElectricField ( const double  x,
const double  y,
const double  z,
double &  ex,
double &  ey,
double &  ez,
double &  v,
Medium *&  m,
int &  status 
)
inlineoverridevirtual

Calculate the drift field [V/cm] and potential [V] at (x, y, z).

Implements Garfield::Component.

Definition at line 41 of file ComponentAnalyticField.hh.

43 {
44 m = nullptr;
45 // Calculate the field.
46 status = Field(x, y, z, ex, ey, ez, v, true);
47 // If the field is ok, get the medium.
48 if (status == 0) {
49 m = m_geometry ? m_geometry->GetMedium(x, y, z) : m_medium;
50 if (!m) {
51 status = -6;
52 } else if (!m->IsDriftable()) {
53 status = -5;
54 }
55 }
56 }
Geometry * m_geometry
Pointer to the geometry.
Definition: Component.hh:332
virtual Medium * GetMedium(const double x, const double y, const double z, const bool tesselated=false) const =0
Retrieve the medium at a given point.

◆ ElectricField() [2/2]

void Garfield::ComponentAnalyticField::ElectricField ( const double  x,
const double  y,
const double  z,
double &  ex,
double &  ey,
double &  ez,
Medium *&  m,
int &  status 
)
inlineoverridevirtual

Calculate the drift field at given point.

Parameters
x,y,zcoordinates [cm].
ex,ey,ezcomponents of the electric field [V/cm].
mpointer to the medium at this location.
statusstatus flag

Status flags:

        0: Inside an active medium
      > 0: Inside a wire of type X
-4 ... -1: On the side of a plane where no wires are
       -5: Inside the mesh but not in an active medium
       -6: Outside the mesh
      -10: Unknown potential type (should not occur)
    other: Other cases (should not occur)

Implements Garfield::Component.

Definition at line 24 of file ComponentAnalyticField.hh.

25 {
26 m = nullptr;
27 // Calculate the field.
28 double v = 0.;
29 status = Field(x, y, z, ex, ey, ez, v, false);
30 // If the field is ok, get the medium.
31 if (status == 0) {
32 m = m_geometry ? m_geometry->GetMedium(x, y, z) : m_medium;
33 if (!m) {
34 status = -6;
35 } else if (!m->IsDriftable()) {
36 status = -5;
37 }
38 }
39 }

◆ ElectricFieldAtWire()

bool Garfield::ComponentAnalyticField::ElectricFieldAtWire ( const unsigned int  iw,
double &  ex,
double &  ey 
)

Calculate the electric field at a given wire position, as if the wire itself were not there, but with the presence of its mirror images.

Definition at line 1774 of file ComponentAnalyticField.cc.

1775 {
1776 //-----------------------------------------------------------------------
1777 // FFIELD - Subroutine calculating the electric field at a given wire
1778 // position, as if the wire itself were not there but with
1779 // the presence of its mirror images.
1780 // VARIABLES : IW : wire number
1781 // EX, EY : x- and y-component of the electric field.
1782 // (Last changed on 27/ 1/96.)
1783 //-----------------------------------------------------------------------
1784 ex = ey = 0.;
1785 // Check the wire number.
1786 if (iw >= m_nWires) {
1787 std::cerr << m_className << "::ElectricFieldAtWire: Index out of range.\n";
1788 return false;
1789 }
1790 // Set the flags appropriately.
1791 std::vector<bool> cnalso(m_nWires, true);
1792 cnalso[iw] = false;
1793
1794 const double xpos = m_w[iw].x;
1795 const double ypos = m_w[iw].y;
1796 // Call the appropriate function.
1797 switch (m_cellType) {
1798 case A00:
1799 FieldAtWireA00(xpos, ypos, ex, ey, cnalso);
1800 break;
1801 case B1X:
1802 FieldAtWireB1X(xpos, ypos, ex, ey, cnalso);
1803 break;
1804 case B1Y:
1805 FieldAtWireB1Y(xpos, ypos, ex, ey, cnalso);
1806 break;
1807 case B2X:
1808 FieldAtWireB2X(xpos, ypos, ex, ey, cnalso);
1809 break;
1810 case B2Y:
1811 FieldAtWireB2Y(xpos, ypos, ex, ey, cnalso);
1812 break;
1813 case C10:
1814 FieldAtWireC10(xpos, ypos, ex, ey, cnalso);
1815 break;
1816 case C2X:
1817 FieldAtWireC2X(xpos, ypos, ex, ey, cnalso);
1818 break;
1819 case C2Y:
1820 FieldAtWireC2Y(xpos, ypos, ex, ey, cnalso);
1821 break;
1822 case C30:
1823 FieldAtWireC30(xpos, ypos, ex, ey, cnalso);
1824 break;
1825 case D10:
1826 FieldAtWireD10(xpos, ypos, ex, ey, cnalso);
1827 break;
1828 case D20:
1829 FieldAtWireD20(xpos, ypos, ex, ey, cnalso);
1830 break;
1831 case D30:
1832 FieldAtWireD30(xpos, ypos, ex, ey, cnalso);
1833 break;
1834 default:
1835 std::cerr << m_className << "::ElectricFieldAtWire:\n"
1836 << " Unknown cell type (id " << m_cellType << ")\n";
1837 return false;
1838 }
1839 // Correct for the equipotential planes.
1840 ex -= m_corvta;
1841 ey -= m_corvtb;
1842 if (m_polar) {
1843 const double r = exp(xpos);
1844 const double er = ex / r;
1845 const double ep = ey / r;
1846 const double ct = cos(ypos);
1847 const double st = sin(ypos);
1848 ex = +ct * er - st * ep;
1849 ey = +st * er + ct * ep;
1850 }
1851 return true;
1852}
DoubleAc exp(const DoubleAc &f)
Definition: DoubleAc.cpp:377

Referenced by ForcesOnWire(), and WireDisplacement().

◆ EnableChargeCheck()

void Garfield::ComponentAnalyticField::EnableChargeCheck ( const bool  on = true)
inline

Check the quality of the capacitance matrix inversion (default: off).

Definition at line 240 of file ComponentAnalyticField.hh.

240{ m_chargeCheck = on; }

◆ EnableDipoleTerms()

void Garfield::ComponentAnalyticField::EnableDipoleTerms ( const bool  on = true)

Request dipole terms be included for each of the wires (default: off).

Definition at line 1438 of file ComponentAnalyticField.cc.

1438 {
1439
1440 m_cellset = false;
1441 m_sigset = false;
1442 m_dipole = on;
1443}

◆ EnableExtrapolation()

void Garfield::ComponentAnalyticField::EnableExtrapolation ( const bool  on = true)
inline

Switch on/off extrapolation of electrostatic forces beyond the scanning area (default: off).

Definition at line 292 of file ComponentAnalyticField.hh.

292{ m_extrapolateForces = on; }

◆ ForcesOnWire()

bool Garfield::ComponentAnalyticField::ForcesOnWire ( const unsigned int  iw,
std::vector< double > &  xMap,
std::vector< double > &  yMap,
std::vector< std::vector< double > > &  fxMap,
std::vector< std::vector< double > > &  fyMap 
)

Calculate a table of the forces acting on a wire.

Parameters
iwindex of the wire
xMapcoordinates of the grid lines in x
yMapcoordinates of the grid lines in y
fxMapx-components of the force at the grid points
fyMapy-components of the force at the grid points

Definition at line 1918 of file ComponentAnalyticField.cc.

1921 {
1922 if (!m_cellset && !Prepare()) {
1923 std::cerr << m_className << "::ForcesOnWire: Cell not set up.\n";
1924 return false;
1925 }
1926
1927 if (iw >= m_nWires) {
1928 std::cerr << m_className << "::ForcesOnWire: Wire index out of range.\n";
1929 return false;
1930 }
1931 if (m_polar) {
1932 std::cerr << m_className << "::ForcesOnWire: Cannot handle polar cells.\n";
1933 return false;
1934 }
1935 const auto& wire = m_w[iw];
1936 // Compute a 'safe box' around the wire.
1937 double bxmin = m_perx ? wire.x - 0.5 * m_sx : 2 * m_xmin - m_xmax;
1938 double bxmax = m_perx ? wire.x + 0.5 * m_sx : 2 * m_xmax - m_xmin;
1939 double bymin = m_pery ? wire.y - 0.5 * m_sy : 2 * m_ymin - m_ymax;
1940 double bymax = m_pery ? wire.y + 0.5 * m_sy : 2 * m_ymax - m_ymin;
1941
1942 // If the initial area is almost zero in 1 direction, make it square.
1943 if (std::abs(bxmax - bxmin) < 0.1 * std::abs(bymax - bymin)) {
1944 bxmin = wire.x - 0.5 * std::abs(bymax - bymin);
1945 bxmax = wire.x + 0.5 * std::abs(bymax - bymin);
1946 } else if (std::abs(bymax - bymin) < 0.1 * std::abs(bxmax - bxmin)) {
1947 bymin = wire.y - 0.5 * std::abs(bxmax - bxmin);
1948 bymax = wire.y + 0.5 * std::abs(bxmax - bxmin);
1949 }
1950 const double dw = 2 * wire.r;
1951 // Scan the other wires.
1952 for (unsigned int j = 0; j < m_nWires; ++j) {
1953 if (j == iw) continue;
1954 const double xj = m_w[j].x;
1955 const double yj = m_w[j].y;
1956 const double dj = 2 * m_w[j].r;
1957 double xnear = m_perx ? xj - m_sx * int(round((xj - wire.x) / m_sx)) : xj;
1958 double ynear = m_pery ? yj - m_sy * int(round((yj - wire.y) / m_sy)) : yj;
1959 if (std::abs(xnear - wire.x) > std::abs(ynear - wire.y)) {
1960 if (xnear < wire.x) {
1961 bxmin = std::max(bxmin, xnear + dj + dw);
1962 if (m_perx) bxmax = std::min(bxmax, xnear + m_sx - dj - dw);
1963 } else {
1964 bxmax = std::min(bxmax, xnear - dj - dw);
1965 if (m_perx) bxmin = std::max(bxmin, xnear - m_sx + dj + dw);
1966 }
1967 } else {
1968 if (ynear < wire.y) {
1969 bymin = std::max({bymin, ynear - dj - dw, ynear + dj + dw});
1970 if (m_pery) bymax = std::min(bymax, ynear + m_sy - dj - dw);
1971 } else {
1972 bymax = std::min({bymax, ynear - dj - dw, ynear + dj + dw});
1973 if (m_pery) bymin = std::max(bymin, ynear - m_sy + dj + dw);
1974 }
1975 }
1976 }
1977 // Scan the planes.
1978 if (m_ynplan[0]) bxmin = std::max(bxmin, m_coplan[0] + dw);
1979 if (m_ynplan[1]) bxmax = std::min(bxmax, m_coplan[1] - dw);
1980 if (m_ynplan[2]) bymin = std::max(bymin, m_coplan[2] + dw);
1981 if (m_ynplan[3]) bymax = std::min(bymax, m_coplan[3] - dw);
1982
1983 // If there is a tube, check all corners.
1984 if (m_tube) {
1985 const double d2 = m_cotube2 - dw * dw;
1986 if (d2 < Small) {
1987 std::cerr << m_className << "::ForcesOnWire:\n Diameter of wire " << iw
1988 << " is too large compared to the tube.\n";
1989 return false;
1990 }
1991
1992 double corr = sqrt((bxmin * bxmin + bymin * bymin) / d2);
1993 if (corr > 1.) {
1994 bxmin /= corr;
1995 bymin /= corr;
1996 }
1997 corr = sqrt((bxmin * bxmin + bymax * bymax) / d2);
1998 if (corr > 1.) {
1999 bxmin /= corr;
2000 bymax /= corr;
2001 }
2002 corr = sqrt((bxmax * bxmax + bymin * bymin) / d2);
2003 if (corr > 1.) {
2004 bxmax /= corr;
2005 bymin /= corr;
2006 }
2007 corr = sqrt((bxmax * bxmax + bymax * bymax) / d2);
2008 if (corr > 1) {
2009 bxmax /= corr;
2010 bymax /= corr;
2011 }
2012 }
2013 // Make sure we found a reasonable 'safe area'.
2014 if ((bxmin - wire.x) * (wire.x - bxmax) <= 0 ||
2015 (bymin - wire.y) * (wire.y - bymax) <= 0) {
2016 std::cerr << m_className << "::ForcesOnWire:\n Unable to find an area "
2017 << "free of elements around wire " << iw << ".\n";
2018 return false;
2019 }
2020 // Now set a reasonable scanning range.
2021 double sxmin = bxmin;
2022 double sxmax = bxmax;
2023 double symin = bymin;
2024 double symax = bymax;
2025 if (m_scanRange == ScanningRange::User) {
2026 // User-specified range.
2027 sxmin = wire.x + m_xScanMin;
2028 symin = wire.y + m_yScanMin;
2029 sxmax = wire.x + m_xScanMax;
2030 symax = wire.y + m_yScanMax;
2031 } else if (m_scanRange == ScanningRange::FirstOrder) {
2032 // Get the field and force at the nominal position.
2033 double ex = 0., ey = 0.;
2034 ElectricFieldAtWire(iw, ex, ey);
2035 double fx = -ex * wire.e * Internal2Newton;
2036 double fy = -ey * wire.e * Internal2Newton;
2037 if (m_useGravitationalForce) {
2038 // Mass per unit length [kg / cm].
2039 const double m = 1.e-3 * wire.density * Pi * wire.r * wire.r;
2040 fx -= m_down[0] * GravitationalAcceleration * m;
2041 fy -= m_down[1] * GravitationalAcceleration * m;
2042 }
2043 const double u2 = wire.u * wire.u;
2044 const double shiftx =
2045 -125 * fx * u2 / (GravitationalAcceleration * wire.tension);
2046 const double shifty =
2047 -125 * fy * u2 / (GravitationalAcceleration * wire.tension);
2048 // If 0th order estimate of shift is not small.
2049 const double tol = 0.1 * wire.r;
2050 if (std::abs(shiftx) > tol || std::abs(shifty) > tol) {
2051 sxmin = std::max(bxmin, std::min(wire.x + m_scaleRange * shiftx,
2052 wire.x - shiftx / m_scaleRange));
2053 symin = std::max(bymin, std::min(wire.y + m_scaleRange * shifty,
2054 wire.y - shifty / m_scaleRange));
2055 sxmax = std::min(bxmax, std::max(wire.x + m_scaleRange * shiftx,
2056 wire.x - shiftx / m_scaleRange));
2057 symax = std::min(bymax, std::max(wire.y + m_scaleRange * shifty,
2058 wire.y - shifty / m_scaleRange));
2059 // If one is very small, make the area square within bounds.
2060 if (std::abs(sxmax - sxmin) < 0.1 * std::abs(symax - symin)) {
2061 sxmin = std::max(bxmin, wire.x - 0.5 * std::abs(symax - symin));
2062 sxmax = std::min(bxmax, wire.x + 0.5 * std::abs(symax - symin));
2063 } else if (std::abs(symax - symin) < 0.1 * std::abs(sxmax - sxmin)) {
2064 symin = std::max(bymin, wire.y - 0.5 * std::abs(sxmax - sxmin));
2065 symax = std::min(bymax, wire.y + 0.5 * std::abs(sxmax - sxmin));
2066 }
2067 }
2068 }
2069 if (m_debug) {
2070 std::cout << m_className << "::ForcesOnWire:\n";
2071 std::printf(" Free area %12.5e < x < %12.5e\n", bxmin, bxmax);
2072 std::printf(" %12.5e < y < %12.5e\n", bymin, bymax);
2073 std::printf(" Scan area %12.5e < x < %12.5e\n", sxmin, sxmax);
2074 std::printf(" %12.5e < y < %12.5e\n", symin, symax);
2075 }
2076
2077 xMap.resize(m_nScanX);
2078 const double stepx = (sxmax - sxmin) / (m_nScanX - 1);
2079 for (unsigned int i = 0; i < m_nScanX; ++i) {
2080 xMap[i] = sxmin + i * stepx;
2081 }
2082 yMap.resize(m_nScanY);
2083 const double stepy = (symax - symin) / (m_nScanY - 1);
2084 for (unsigned int i = 0; i < m_nScanY; ++i) {
2085 yMap[i] = symin + i * stepy;
2086 }
2087 // Save the original coordinates of the wire.
2088 const double x0 = wire.x;
2089 const double y0 = wire.y;
2090 // Prepare interpolation tables.
2091 fxMap.assign(m_nScanX, std::vector<double>(m_nScanY, 0.));
2092 fyMap.assign(m_nScanX, std::vector<double>(m_nScanY, 0.));
2093 bool ok = true;
2094 for (unsigned int i = 0; i < m_nScanX; ++i) {
2095 for (unsigned int j = 0; j < m_nScanY; ++j) {
2096 // Get the wire position for this shift.
2097 m_w[iw].x = xMap[i];
2098 m_w[iw].y = yMap[j];
2099 // Verify the current situation.
2100 if (!WireCheck()) {
2101 std::cerr << m_className << "::ForcesOnWire: Wire " << iw << ".\n"
2102 << " Scan involves a disallowed wire position.\n";
2103 ok = false;
2104 continue;
2105 }
2106 // Recompute the charges for this configuration.
2107 if (!Setup()) {
2108 std::cerr << m_className << "::ForcesOnWire: Wire " << iw << ".\n"
2109 << " Failed to compute charges at a scan point.\n";
2110 ok = false;
2111 continue;
2112 }
2113 // Compute the forces.
2114 double ex = 0., ey = 0.;
2115 ElectricFieldAtWire(iw, ex, ey);
2116 fxMap[i][j] = -ex * wire.e * Internal2Newton;
2117 fyMap[i][j] = -ey * wire.e * Internal2Newton;
2118 }
2119 }
2120 // Place the wire back in its shifted position.
2121 m_w[iw].x = x0;
2122 m_w[iw].y = y0;
2123 Setup();
2124 return ok;
2125}
bool ElectricFieldAtWire(const unsigned int iw, double &ex, double &ey)
bool m_debug
Switch on/off debugging messages.
Definition: Component.hh:341
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314

Referenced by WireDisplacement().

◆ GetBoundingBox()

bool Garfield::ComponentAnalyticField::GetBoundingBox ( double &  xmin,
double &  ymin,
double &  zmin,
double &  xmax,
double &  ymax,
double &  zmax 
)
overridevirtual

Get the bounding box coordinates.

Reimplemented from Garfield::Component.

Definition at line 319 of file ComponentAnalyticField.cc.

321 {
322 // If a geometry is present, try to get the bounding box from there.
323 if (m_geometry) {
324 if (m_geometry->GetBoundingBox(x0, y0, z0, x1, y1, z1)) return true;
325 }
326 // Otherwise, return the cell dimensions.
327 return GetElementaryCell(x0, y0, z0, x1, y1, z1);
328}
bool GetElementaryCell(double &x0, double &y0, double &z0, double &x1, double &y1, double &z1) override
Get the coordinates of the elementary cell.
virtual bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax)=0
Get the bounding box (envelope of the geometry).

◆ GetCellType()

std::string Garfield::ComponentAnalyticField::GetCellType ( )
inline

Return the cell type. Cells are classified according to the number and orientation of planes, the presence of periodicities and the location of the wires as one of the following types:

A non-periodic cells with at most 1 x- and 1 y-plane B1X x-periodic cells without x-planes and at most 1 y-plane B1Y y-periodic cells without y-planes and at most 1 x-plane B2X cells with 2 x-planes and at most 1 y-plane B2Y cells with 2 y-planes and at most 1 x-plane C1 doubly periodic cells without planes C2X doubly periodic cells with x-planes C2Y doubly periodic cells with y-planes C3 double periodic cells with x- and y-planes D1 round tubes without axial periodicity D2 round tubes with axial periodicity D3 polygonal tubes without axial periodicity

Definition at line 213 of file ComponentAnalyticField.hh.

213 {
214 if (!m_cellset) {
215 if (CellCheck()) CellType();
216 }
217 return GetCellType(m_cellType);
218 }

Referenced by GetCellType(), and PrintCell().

◆ GetElementaryCell()

bool Garfield::ComponentAnalyticField::GetElementaryCell ( double &  xmin,
double &  ymin,
double &  zmin,
double &  xmax,
double &  ymax,
double &  zmax 
)
overridevirtual

Get the coordinates of the elementary cell.

Reimplemented from Garfield::Component.

Definition at line 330 of file ComponentAnalyticField.cc.

332 {
333 if (!m_cellset && !Prepare()) return false;
334 if (m_polar) {
335 double rmax, thetamax;
336 Internal2Polar(m_xmax, m_ymax, rmax, thetamax);
337 x0 = -rmax;
338 y0 = -rmax;
339 x1 = +rmax;
340 y1 = +rmax;
341 } else {
342 x0 = m_xmin;
343 y0 = m_ymin;
344 x1 = m_xmax;
345 y1 = m_ymax;
346 }
347 z0 = m_zmin;
348 z1 = m_zmax;
349 return true;
350}

Referenced by GetBoundingBox().

◆ GetGravity()

void Garfield::ComponentAnalyticField::GetGravity ( double &  dx,
double &  dy,
double &  dz 
) const

Get the gravity orientation.

Definition at line 1910 of file ComponentAnalyticField.cc.

1911 {
1912
1913 dx = m_down[0];
1914 dy = m_down[1];
1915 dz = m_down[2];
1916}

◆ GetMedium()

Medium * Garfield::ComponentAnalyticField::GetMedium ( const double  x,
const double  y,
const double  z 
)
overridevirtual

Get the medium at a given location (x, y, z).

Reimplemented from Garfield::Component.

Definition at line 253 of file ComponentAnalyticField.cc.

254 {
255 if (m_geometry) return m_geometry->GetMedium(xin, yin, zin);
256
257 // Make sure the cell is prepared.
258 if (!m_cellset && !Prepare()) return nullptr;
259
260 double xpos = xin, ypos = yin;
261 if (m_polar) Cartesian2Internal(xin, yin, xpos, ypos);
262 // In case of periodicity, move the point into the basic cell.
263 if (m_perx) {
264 xpos -= m_sx * int(round(xin / m_sx));
265 }
266 double arot = 0.;
267 if (m_pery && m_tube) {
268 Cartesian2Polar(xin, yin, xpos, ypos);
269 arot = RadToDegree * m_sy * int(round(DegreeToRad * ypos / m_sy));
270 ypos -= arot;
271 Polar2Cartesian(xpos, ypos, xpos, ypos);
272 } else if (m_pery) {
273 ypos -= m_sy * int(round(ypos / m_sy));
274 }
275
276 // Move the point to the correct side of the plane.
277 if (m_perx && m_ynplan[0] && xpos <= m_coplan[0]) xpos += m_sx;
278 if (m_perx && m_ynplan[1] && xpos >= m_coplan[1]) xpos -= m_sx;
279 if (m_pery && m_ynplan[2] && ypos <= m_coplan[2]) ypos += m_sy;
280 if (m_pery && m_ynplan[3] && ypos >= m_coplan[3]) ypos -= m_sy;
281
282 // In case (xpos, ypos) is located behind a plane there is no field.
283 if (m_tube) {
284 if (!InTube(xpos, ypos, m_cotube, m_ntube)) return nullptr;
285 } else {
286 if ((m_ynplan[0] && xpos < m_coplan[0]) ||
287 (m_ynplan[1] && xpos > m_coplan[1]) ||
288 (m_ynplan[2] && ypos < m_coplan[2]) ||
289 (m_ynplan[3] && ypos > m_coplan[3])) {
290 return nullptr;
291 }
292 }
293
294 // If (xpos, ypos) is within a wire, there is no field either.
295 for (const auto& wire : m_w) {
296 double dx = xpos - wire.x;
297 double dy = ypos - wire.y;
298 // Correct for periodicities.
299 if (m_perx) dx -= m_sx * int(round(dx / m_sx));
300 if (m_pery) dy -= m_sy * int(round(dy / m_sy));
301 // Check the actual position.
302 if (dx * dx + dy * dy < wire.r * wire.r) return nullptr;
303 }
304 return m_medium;
305}

◆ GetNumberOfPlanesPhi()

unsigned int Garfield::ComponentAnalyticField::GetNumberOfPlanesPhi ( ) const

Get the number of equipotential planes at constant phi.

Definition at line 1669 of file ComponentAnalyticField.cc.

1669 {
1670 if (!m_polar) {
1671 return 0;
1672 } else if (m_ynplan[2] && m_ynplan[3]) {
1673 return 2;
1674 } else if (m_ynplan[2] || m_ynplan[3]) {
1675 return 1;
1676 }
1677 return 0;
1678}

◆ GetNumberOfPlanesR()

unsigned int Garfield::ComponentAnalyticField::GetNumberOfPlanesR ( ) const

Get the number of equipotential planes at constant radius.

Definition at line 1658 of file ComponentAnalyticField.cc.

1658 {
1659 if (!m_polar) {
1660 return 0;
1661 } else if (m_ynplan[0] && m_ynplan[1]) {
1662 return 2;
1663 } else if (m_ynplan[0] || m_ynplan[1]) {
1664 return 1;
1665 }
1666 return 0;
1667}

◆ GetNumberOfPlanesX()

unsigned int Garfield::ComponentAnalyticField::GetNumberOfPlanesX ( ) const

Get the number of equipotential planes at constant x.

Definition at line 1636 of file ComponentAnalyticField.cc.

1636 {
1637 if (m_polar) {
1638 return 0;
1639 } else if (m_ynplan[0] && m_ynplan[1]) {
1640 return 2;
1641 } else if (m_ynplan[0] || m_ynplan[1]) {
1642 return 1;
1643 }
1644 return 0;
1645}

◆ GetNumberOfPlanesY()

unsigned int Garfield::ComponentAnalyticField::GetNumberOfPlanesY ( ) const

Get the number of equipotential planes at constant y.

Definition at line 1647 of file ComponentAnalyticField.cc.

1647 {
1648 if (m_polar) {
1649 return 0;
1650 } else if (m_ynplan[2] && m_ynplan[3]) {
1651 return 2;
1652 } else if (m_ynplan[2] || m_ynplan[3]) {
1653 return 1;
1654 }
1655 return 0;
1656}

◆ GetNumberOfWires()

unsigned int Garfield::ComponentAnalyticField::GetNumberOfWires ( ) const
inline

Get the number of wires.

Definition at line 243 of file ComponentAnalyticField.hh.

243{ return m_nWires; }

◆ GetPeriodicityPhi()

bool Garfield::ComponentAnalyticField::GetPeriodicityPhi ( double &  s)

Get the periodicity [degree] in phi.

Definition at line 1517 of file ComponentAnalyticField.cc.

1517 {
1518 if (!m_periodic[1] || (!m_polar && !m_tube)) {
1519 s = 0.;
1520 return false;
1521 }
1522
1523 s = RadToDegree * m_sy;
1524 return true;
1525}
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition: Component.hh:344

◆ GetPeriodicityX()

bool Garfield::ComponentAnalyticField::GetPeriodicityX ( double &  s)

Get the periodic length in the x-direction.

Definition at line 1497 of file ComponentAnalyticField.cc.

1497 {
1498 if (!m_periodic[0] || m_polar) {
1499 s = 0.;
1500 return false;
1501 }
1502
1503 s = m_sx;
1504 return true;
1505}

◆ GetPeriodicityY()

bool Garfield::ComponentAnalyticField::GetPeriodicityY ( double &  s)

Get the periodic length in the y-direction.

Definition at line 1507 of file ComponentAnalyticField.cc.

1507 {
1508 if (!m_periodic[1] || m_polar) {
1509 s = 0.;
1510 return false;
1511 }
1512
1513 s = m_sy;
1514 return true;
1515}

◆ GetPlanePhi()

bool Garfield::ComponentAnalyticField::GetPlanePhi ( const unsigned int  i,
double &  phi,
double &  voltage,
std::string &  label 
) const

Retrieve the parameters of a plane at constant phi.

Definition at line 1750 of file ComponentAnalyticField.cc.

1752 {
1753 if (!m_polar || i >= 2 || (i == 1 && !m_ynplan[3])) {
1754 std::cerr << m_className << "::GetPlanePhi: Index out of range.\n";
1755 return false;
1756 }
1757
1758 phi = RadToDegree * m_coplan[i + 2];
1759 voltage = m_vtplan[i + 2];
1760 label = m_planes[i + 2].type;
1761 return true;
1762}

◆ GetPlaneR()

bool Garfield::ComponentAnalyticField::GetPlaneR ( const unsigned int  i,
double &  r,
double &  voltage,
std::string &  label 
) const

Retrieve the parameters of a plane at constant radius.

Definition at line 1736 of file ComponentAnalyticField.cc.

1738 {
1739 if (!m_polar || i >= 2 || (i == 1 && !m_ynplan[1])) {
1740 std::cerr << m_className << "::GetPlaneR: Index out of range.\n";
1741 return false;
1742 }
1743
1744 r = exp(m_coplan[i]);
1745 voltage = m_vtplan[i];
1746 label = m_planes[i].type;
1747 return true;
1748}

◆ GetPlaneX()

bool Garfield::ComponentAnalyticField::GetPlaneX ( const unsigned int  i,
double &  x,
double &  voltage,
std::string &  label 
) const

Retrieve the parameters of a plane at constant x.

Definition at line 1708 of file ComponentAnalyticField.cc.

1710 {
1711 if (m_polar || i >= 2 || (i == 1 && !m_ynplan[1])) {
1712 std::cerr << m_className << "::GetPlaneX: Index out of range.\n";
1713 return false;
1714 }
1715
1716 x = m_coplan[i];
1717 voltage = m_vtplan[i];
1718 label = m_planes[i].type;
1719 return true;
1720}

◆ GetPlaneY()

bool Garfield::ComponentAnalyticField::GetPlaneY ( const unsigned int  i,
double &  y,
double &  voltage,
std::string &  label 
) const

Retrieve the parameters of a plane at constant y.

Definition at line 1722 of file ComponentAnalyticField.cc.

1724 {
1725 if (m_polar || i >= 2 || (i == 1 && !m_ynplan[3])) {
1726 std::cerr << m_className << "::GetPlaneY: Index out of range.\n";
1727 return false;
1728 }
1729
1730 y = m_coplan[i + 2];
1731 voltage = m_vtplan[i + 2];
1732 label = m_planes[i + 2].type;
1733 return true;
1734}

◆ GetTube()

bool Garfield::ComponentAnalyticField::GetTube ( double &  r,
double &  voltage,
int &  nEdges,
std::string &  label 
) const

Retrieve the tube parameters.

Definition at line 1764 of file ComponentAnalyticField.cc.

1765 {
1766 if (!m_tube) return false;
1767 r = m_cotube;
1768 voltage = m_vttube;
1769 nEdges = m_ntube;
1770 label = m_planes[4].type;
1771 return true;
1772}

◆ GetVoltageRange()

bool Garfield::ComponentAnalyticField::GetVoltageRange ( double &  vmin,
double &  vmax 
)
overridevirtual

Calculate the voltage range [V].

Implements Garfield::Component.

Definition at line 307 of file ComponentAnalyticField.cc.

307 {
308 // Make sure the cell is prepared.
309 if (!m_cellset && !Prepare()) {
310 std::cerr << m_className << "::GetVoltageRange: Cell not set up.\n";
311 return false;
312 }
313
314 pmin = m_vmin;
315 pmax = m_vmax;
316 return true;
317}

◆ GetWire()

bool Garfield::ComponentAnalyticField::GetWire ( const unsigned int  i,
double &  x,
double &  y,
double &  diameter,
double &  voltage,
std::string &  label,
double &  length,
double &  charge,
int &  ntrap 
) const

Retrieve the parameters of a wire.

Definition at line 1680 of file ComponentAnalyticField.cc.

1683 {
1684 if (i >= m_nWires) {
1685 std::cerr << m_className << "::GetWire: Index out of range.\n";
1686 return false;
1687 }
1688
1689 if (m_polar) {
1690 double r = 0., theta = 0.;
1691 Internal2Polar(m_w[i].x, m_w[i].y, r, theta);
1692 x = r;
1693 y = theta;
1694 diameter = 2 * m_w[i].r * r;
1695 } else {
1696 x = m_w[i].x;
1697 y = m_w[i].y;
1698 diameter = 2 * m_w[i].r;
1699 }
1700 voltage = m_w[i].v;
1701 label = m_w[i].type;
1702 length = m_w[i].u;
1703 charge = m_w[i].e;
1704 ntrap = m_w[i].nTrap;
1705 return true;
1706}

◆ IsInTrapRadius()

bool Garfield::ComponentAnalyticField::IsInTrapRadius ( const double  q0,
const double  x0,
const double  y0,
const double  z0,
double &  xw,
double &  yw,
double &  rw 
)
overridevirtual

Determine whether a particle is inside the trap radius of a wire.

Parameters
q0charge of the particle [in elementary charges].
x0,y0,z0position [cm] of the particle.
xw,ywcoordinates of the wire (if applicable).
rwradius of the wire (if applicable).

Reimplemented from Garfield::Component.

Definition at line 751 of file ComponentAnalyticField.cc.

754 {
755
756 double x0 = xin;
757 double y0 = yin;
758 if (m_polar) {
759 Cartesian2Internal(xin, yin, x0, y0);
760 }
761 // In case of periodicity, move the point into the basic cell.
762 int nX = 0, nY = 0, nPhi = 0;
763 if (m_perx) {
764 nX = int(round(x0 / m_sx));
765 x0 -= m_sx * nX;
766 }
767 if (m_pery && m_tube) {
768 Cartesian2Polar(xin, yin, x0, y0);
769 nPhi = int(round(DegreeToRad * y0 / m_sy));
770 y0 -= RadToDegree * m_sy * nPhi;
771 Polar2Cartesian(x0, y0, x0, y0);
772 } else if (m_pery) {
773 nY = int(round(y0 / m_sy));
774 y0 -= m_sy * nY;
775 }
776 // Move the point to the correct side of the plane.
777 std::array<bool, 4> shift = {false, false, false, false};
778 if (m_perx && m_ynplan[0] && x0 <= m_coplan[0]) {
779 x0 += m_sx;
780 shift[0] = true;
781 }
782 if (m_perx && m_ynplan[1] && x0 >= m_coplan[1]) {
783 x0 -= m_sx;
784 shift[1] = true;
785 }
786 if (m_pery && m_ynplan[2] && y0 <= m_coplan[2]) {
787 y0 += m_sy;
788 shift[2] = true;
789 }
790 if (m_pery && m_ynplan[3] && y0 >= m_coplan[3]) {
791 y0 -= m_sy;
792 shift[3] = true;
793 }
794
795 for (const auto& wire : m_w) {
796 // Skip wires with the wrong charge.
797 if (qin * wire.e > 0.) continue;
798 const double dxw0 = wire.x - x0;
799 const double dyw0 = wire.y - y0;
800 const double r2 = dxw0 * dxw0 + dyw0 * dyw0;
801 const double rTrap = wire.r * wire.nTrap;
802 if (r2 < rTrap * rTrap) {
803 xw = wire.x;
804 yw = wire.y;
805 rw = wire.r;
806 if (shift[0]) xw -= m_sx;
807 if (shift[1]) xw += m_sx;
808 if (shift[2]) yw -= m_sy;
809 if (shift[3]) yw += m_sy;
810 if (m_pery && m_tube) {
811 double rhow, phiw;
812 Cartesian2Polar(xw, yw, rhow, phiw);
813 phiw += RadToDegree * m_sy * nPhi;
814 Polar2Cartesian(rhow, phiw, xw, yw);
815 } else if (m_pery) {
816 y0 += m_sy * nY;
817 }
818 if (m_perx) xw += m_sx * nX;
819 if (m_polar) {
820 Internal2Cartesian(xw, yw, xw, yw);
821 rw *= exp(wire.x);
822 }
823 if (m_debug) {
824 std::cout << m_className << "::IsInTrapRadius: (" << xin << ", "
825 << yin << ", " << zin << ")" << " within trap radius.\n";
826 }
827 return true;
828 }
829 }
830
831 return false;
832}

◆ IsPolar()

bool Garfield::ComponentAnalyticField::IsPolar ( ) const
inline

Are polar coordinates being used?

Definition at line 181 of file ComponentAnalyticField.hh.

181{ return m_polar; }

◆ IsWireCrossed()

bool Garfield::ComponentAnalyticField::IsWireCrossed ( const double  x0,
const double  y0,
const double  z0,
const double  x1,
const double  y1,
const double  z1,
double &  xc,
double &  yc,
double &  zc,
const bool  centre,
double &  rc 
)
overridevirtual

Determine whether the line between two points crosses a wire.

Parameters
x0,y0,z0first point [cm].
x1,y1,z1second point [cm]
xc,yc,zcpoint [cm] where the line crosses the wire or the coordinates of the wire centre.
centreflag whether to return the coordinates of the line-wire crossing point or of the wire centre.
rcradius [cm] of the wire.

Reimplemented from Garfield::Component.

Definition at line 650 of file ComponentAnalyticField.cc.

654 {
655 xc = xx0;
656 yc = yy0;
657 zc = z0;
658
659 if (m_w.empty()) return false;
660
661 double x0 = xx0;
662 double y0 = yy0;
663 double x1 = xx1;
664 double y1 = yy1;
665 if (m_polar) {
666 Cartesian2Internal(xx0, yy0, x0, y0);
667 Cartesian2Internal(xx1, yy1, x1, y1);
668 }
669 const double dx = x1 - x0;
670 const double dy = y1 - y0;
671 const double d2 = dx * dx + dy * dy;
672 // Check that the step length is non-zero.
673 if (d2 < Small) return false;
674 const double invd2 = 1. / d2;
675
676 // Check if a whole period has been crossed.
677 if ((m_perx && fabs(dx) >= m_sx) || (m_pery && fabs(dy) >= m_sy)) {
678 std::cerr << m_className << "::IsWireCrossed:\n"
679 << " Particle crossed more than one period.\n";
680 return false;
681 }
682
683 // Both coordinates are assumed to be located inside
684 // the drift area and inside a drift medium.
685 // This should have been checked before this call.
686
687 const double xm = 0.5 * (x0 + x1);
688 const double ym = 0.5 * (y0 + y1);
689 double dMin2 = 0.;
690 for (const auto& wire : m_w) {
691 double xw = wire.x;
692 if (m_perx) {
693 xw += m_sx * int(round((xm - xw) / m_sx));
694 }
695 double yw = wire.y;
696 if (m_pery) {
697 yw += m_sy * int(round((ym - yw) / m_sy));
698 }
699 // Calculate the smallest distance between track and wire.
700 const double xIn0 = dx * (xw - x0) + dy * (yw - y0);
701 // Check if the minimum is located before (x0, y0).
702 if (xIn0 < 0.) continue;
703 const double xIn1 = -(dx * (xw - x1) + dy * (yw - y1));
704 // Check if the minimum is located behind (x1, y1).
705 if (xIn1 < 0.) continue;
706 // Minimum is located between (x0, y0) and (x1, y1).
707 const double xw0 = xw - x0;
708 const double xw1 = xw - x1;
709 const double yw0 = yw - y0;
710 const double yw1 = yw - y1;
711 const double dw02 = xw0 * xw0 + yw0 * yw0;
712 const double dw12 = xw1 * xw1 + yw1 * yw1;
713 if (xIn1 * xIn1 * dw02 > xIn0 * xIn0 * dw12) {
714 dMin2 = dw02 - xIn0 * xIn0 * invd2;
715 } else {
716 dMin2 = dw12 - xIn1 * xIn1 * invd2;
717 }
718 // Add in the times nTrap to account for the trap radius.
719 const double r2 = wire.r * wire.r;
720 if (dMin2 < r2) {
721 // Wire has been crossed.
722 if (centre) {
723 if (m_polar) {
724 Internal2Cartesian(xw, yw, xc, yc);
725 } else {
726 xc = xw;
727 yc = yw;
728 }
729 } else {
730 // Find the point of intersection.
731 const double p = -xIn0 * invd2;
732 const double q = (dw02 - r2) * invd2;
733 const double s = sqrt(p * p - q);
734 const double t = std::min(-p + s, -p - s);
735 if (m_polar) {
736 Internal2Cartesian(x0 + t * dx, y0 + t * dy, xc, yc);
737 } else {
738 xc = x0 + t * dx;
739 yc = y0 + t * dy;
740 }
741 zc = z0 + t * (z1 - z0);
742 }
743 rc = wire.r;
744 if (m_polar) rc *= exp(wire.x);
745 return true;
746 }
747 }
748 return false;
749}

◆ MultipoleMoments()

bool Garfield::ComponentAnalyticField::MultipoleMoments ( const unsigned int  iw,
const unsigned int  order = 4,
const bool  print = false,
const bool  plot = false,
const double  rmult = 1.,
const double  eps = 1.e-4,
const unsigned int  nMaxIter = 20 
)

Calculate multipole moments for a given wire.

Parameters
iwIndex of the wire.
orderOrder of the highest multipole moment.
printPrint information about the fitting process.
plotPlot the potential and multipole fit around the wire.
rmultDistance in multiples of the wire radius at which the decomposition is to be carried out.
epsUsed in the fit for calculating the covariance matrix.
nMaxIterMaximum number of iterations in the fit.

Definition at line 10400 of file ComponentAnalyticField.cc.

10403 {
10404
10405 //-----------------------------------------------------------------------
10406 // EFMWIR - Computes the dipole moment of a given wire.
10407 //-----------------------------------------------------------------------
10408 if (!m_cellset && !Prepare()) return false;
10409 // Check input parameters.
10410 if (iw >= m_nWires) {
10411 std::cerr << m_className << "::MultipoleMoments:\n"
10412 << " Wire index out of range.\n";
10413 return false;
10414 }
10415 if (eps <= 0.) {
10416 std::cerr << m_className << "::MultipoleMoments:\n"
10417 << " Epsilon must be positive.\n";
10418 return false;
10419 }
10420 if (nPoles == 0) {
10421 std::cerr << m_className << "::MultipoleMoments:\n"
10422 << " Multipole order out of range.\n";
10423 return false;
10424 }
10425 if (rmult <= 0.) {
10426 std::cerr << m_className << "::MultipoleMoments:\n"
10427 << " Radius multiplication factor out of range.\n";
10428 return false;
10429 }
10430
10431 const double xw = m_w[iw].x;
10432 const double yw = m_w[iw].y;
10433 // Set the radius of the wire to 0.
10434 const double rw = m_w[iw].r;
10435 m_w[iw].r = 0.;
10436
10437 // Loop around the wire.
10438 constexpr unsigned int nPoints = 20000;
10439 std::vector<double> angle(nPoints, 0.);
10440 std::vector<double> volt(nPoints, 0.);
10441 std::vector<double> weight(nPoints, 1.);
10442 for (unsigned int i = 0; i < nPoints; ++i) {
10443 // Set angle around wire.
10444 angle[i] = TwoPi * (i + 1.) / nPoints;
10445 // Compute E field, make sure the point is in a free region.
10446 const double x = xw + rmult * rw * cos(angle[i]);
10447 const double y = yw + rmult * rw * sin(angle[i]);
10448 double ex = 0., ey = 0., ez = 0., v = 0.;
10449 if (Field(x, y, 0., ex, ey, ez, v, true) != 0) {
10450 std::cerr << m_className << "::MultipoleMoments:\n"
10451 << " Unexpected location code. Computation stopped.\n";
10452 m_w[iw].r = rw;
10453 return false;
10454 }
10455 // Assign the result to the fitting array.
10456 volt[i] = v;
10457 }
10458 // Restore the wire diameter.
10459 m_w[iw].r = rw;
10460
10461 // Determine the maximum, minimum and average.
10462 double vmin = *std::min_element(volt.cbegin(), volt.cend());
10463 double vmax = *std::max_element(volt.cbegin(), volt.cend());
10464 double vave = std::accumulate(volt.cbegin(), volt.cend(), 0.) / nPoints;
10465 // Subtract the wire potential to centre the data more or less.
10466 for (unsigned int i = 0; i < nPoints; ++i) volt[i] -= vave;
10467 vmax -= vave;
10468 vmin -= vave;
10469
10470 // Perform the fit.
10471 const double vm = 0.5 * fabs(vmin) + fabs(vmax);
10472 double chi2 = 1.e-6 * nPoints * vm * vm;
10473 const double dist = 1.e-3 * (1. + vm);
10474 const unsigned int nPar = 2 * nPoles + 1;
10475 std::vector<double> pars(nPar, 0.);
10476 std::vector<double> epar(nPar, 0.);
10477 pars[0] = 0.5 * (vmax + vmin);
10478 for (unsigned int i = 1; i <= nPoles; ++i) {
10479 pars[2 * i - 1] = 0.5 * (vmax - vmin);
10480 pars[2 * i] = 0.;
10481 }
10482
10483 auto f = [nPoles](const double x, const std::vector<double>& par) {
10484 // EFMFUN
10485 // Sum the series, initial value is the monopole term.
10486 double sum = par[0];
10487 for (unsigned int k = 1; k <= nPoles; ++k) {
10488 // Obtain the Legendre polynomial of this order and add to the series.
10489 const float cphi = cos(x - par[2 * k]);
10490 sum += par[2 * k - 1] * sqrt(k + 0.5) * Numerics::Legendre(k, cphi);
10491 }
10492 return sum;
10493 };
10494
10495 if (!Numerics::LeastSquaresFit(f, pars, epar, angle, volt, weight,
10496 nMaxIter, dist, chi2, eps, m_debug, print)) {
10497 std::cerr << m_className << "::MultipoleMoments:\n"
10498 << " Fitting the multipoles failed; computation stopped.\n";
10499 }
10500 // Plot the result of the fit.
10501 if (plot) {
10502 const std::string name = ViewBase::FindUnusedCanvasName("cMultipole");
10503 TCanvas* cfit = new TCanvas(name.c_str(), "Multipoles");
10504 cfit->SetGridx();
10505 cfit->SetGridy();
10506 cfit->DrawFrame(0., vmin, TwoPi, vmax,
10507 ";Angle around the wire [rad]; Potential - average [V]");
10508 TGraph graph;
10509 graph.SetLineWidth(2);
10510 graph.SetLineColor(kBlack);
10511 graph.DrawGraph(angle.size(), angle.data(), volt.data(), "lsame");
10512 // Sum of contributions.
10513 constexpr unsigned int nP = 1000;
10514 std::array<double, nP> xp;
10515 std::array<double, nP> yp;
10516 for (unsigned int i = 0; i < nP; ++i) {
10517 xp[i] = TwoPi * (i + 1.) / nP;
10518 yp[i] = f(xp[i], pars);
10519 }
10520 graph.SetLineColor(kViolet + 3);
10521 graph.DrawGraph(nP, xp.data(), yp.data(), "lsame");
10522 // Individual contributions.
10523 std::vector<double> parres = pars;
10524 for (unsigned int i = 1; i <= nPoles; ++i) parres[2 * i - 1] = 0.;
10525 for (unsigned int j = 1; j <= nPoles; ++j) {
10526 parres[2 * j - 1] = pars[2 * j - 1];
10527 for (unsigned int i = 0; i < nP; ++i) {
10528 yp[i] = f(xp[i], parres);
10529 }
10530 parres[2 * j - 1] = 0.;
10531 graph.SetLineColor(kAzure + j);
10532 graph.DrawGraph(nP, xp.data(), yp.data(), "lsame");
10533 }
10534 gPad->Update();
10535 }
10536
10537 // Print the results.
10538 std::cout << m_className << "::MultipoleMoments:\n"
10539 << " Multipole moments for wire " << iw << ":\n"
10540 << " Moment Value Angle [degree]\n";
10541 std::printf(" %6u %15.8f Arbitrary\n", 0, vave);
10542 for (unsigned int i = 1; i <= nPoles; ++i) {
10543 // Remove radial term from the multipole moment.
10544 const double val = pow(rmult * rw, i) * pars[2 * i - 1];
10545 const double phi = RadToDegree * fmod(pars[2 * i], Pi);
10546 std::printf(" %6u %15.8f %15.8f\n", i, val, phi);
10547 }
10548 return true;
10549}
static std::string FindUnusedCanvasName(const std::string &s)
Find an unused canvas name.
Definition: ViewBase.cc:310
double Legendre(const unsigned int n, const double x)
Legendre polynomials.
Definition: Numerics.hh:119
bool LeastSquaresFit(std::function< double(double, const std::vector< double > &)> f, std::vector< double > &par, std::vector< double > &epar, const std::vector< double > &x, const std::vector< double > &y, const std::vector< double > &ey, const unsigned int nMaxIter, const double diff, double &chi2, const double eps, const bool debug, const bool verbose)
Least-squares minimisation.
Definition: Numerics.cc:1839
DoubleAc pow(const DoubleAc &f, double p)
Definition: DoubleAc.cpp:337

◆ PrintCell()

void Garfield::ComponentAnalyticField::PrintCell ( )

Print all available information on the cell.

Definition at line 352 of file ComponentAnalyticField.cc.

352 {
353 //-----------------------------------------------------------------------
354 // CELPRT - Subroutine printing all available information on the cell.
355 //-----------------------------------------------------------------------
356
357 // Make sure the cell is prepared.
358 if (!m_cellset && !Prepare()) {
359 std::cerr << m_className << "::PrintCell: Cell not set up.\n";
360 return;
361 }
362 std::cout << m_className
363 << "::PrintCell: Cell identification: " << GetCellType() << "\n";
364 // Print positions of wires, applied voltages and resulting charges.
365 if (!m_w.empty()) {
366 std::cout << " Table of the wires\n";
367 if (m_polar) {
368 std::cout << " Nr Diameter r phi Voltage";
369 } else {
370 std::cout << " Nr Diameter x y Voltage";
371 }
372 std::cout << " Charge Tension Length Density Label\n";
373 if (m_polar) {
374 std::cout << " [micron] [cm] [deg] [Volt]";
375 } else {
376 std::cout << " [micron] [cm] [cm] [Volt]";
377 }
378 std::cout << " [pC/cm] [g] [cm] [g/cm3]\n";
379 for (unsigned int i = 0; i < m_nWires; ++i) {
380 const auto& w = m_w[i];
381 double xw = w.x;
382 double yw = w.y;
383 double dw = 2 * w.r;
384 if (m_polar) {
385 Internal2Polar(w.x, w.y, xw, yw);
386 dw *= xw;
387 }
388 std::printf(
389 "%4d %9.2f %9.4f %9.4f %9.3f %12.4f %9.2f %9.2f %9.2f \"%s\"\n", i,
390 1.e4 * dw, xw, yw, w.v, w.e * TwoPiEpsilon0 * 1.e-3, w.tension,
391 w.u, w.density, w.type.c_str());
392 }
393 }
394 // Print information on the tube if present.
395 if (m_tube) {
396 std::string shape;
397 if (m_ntube == 0) {
398 shape = "Circular";
399 } else if (m_ntube == 3) {
400 shape = "Triangular";
401 } else if (m_ntube == 4) {
402 shape = "Square";
403 } else if (m_ntube == 5) {
404 shape = "Pentagonal";
405 } else if (m_ntube == 6) {
406 shape = "Hexagonal";
407 } else if (m_ntube == 7) {
408 shape = "Heptagonal";
409 } else if (m_ntube == 8) {
410 shape = "Octagonal";
411 } else {
412 shape = "Polygonal with " + std::to_string(m_ntube) + " corners";
413 }
414 std::cout << " Enclosing tube\n"
415 << " Potential: " << m_vttube << " V\n"
416 << " Radius: " << m_cotube << " cm\n"
417 << " Shape: " << shape << "\n"
418 << " Label: " << m_planes[4].type << "\n";
419 }
420 // Print data on the equipotential planes.
421 if (m_ynplan[0] || m_ynplan[1] || m_ynplan[2] || m_ynplan[3]) {
422 std::cout << " Equipotential planes\n";
423 // First those at const x or r.
424 const std::string xr = m_polar ? "r" : "x";
425 if (m_ynplan[0] && m_ynplan[1]) {
426 std::cout << " There are two planes at constant " << xr << ":\n";
427 } else if (m_ynplan[0] || m_ynplan[1]) {
428 std::cout << " There is one plane at constant " << xr << ":\n";
429 }
430 for (unsigned int i = 0; i < 2; ++i) {
431 if (!m_ynplan[i]) continue;
432 if (m_polar) {
433 std::cout << " r = " << exp(m_coplan[i]) << " cm, ";
434 } else {
435 std::cout << " x = " << m_coplan[i] << " cm, ";
436 }
437 if (fabs(m_vtplan[i]) > 1.e-4) {
438 std::cout << "potential = " << m_vtplan[i] << " V, ";
439 } else {
440 std::cout << "earthed, ";
441 }
442 const auto& plane = m_planes[i];
443 if (plane.type.empty() && plane.type != "?") {
444 std::cout << "label = " << plane.type << ", ";
445 }
446 const unsigned int nStrips = plane.strips1.size() + plane.strips2.size();
447 const unsigned int nPixels = plane.pixels.size();
448 if (nStrips == 0 && nPixels == 0) {
449 std::cout << "no strips or pixels.\n";
450 } else if (nPixels == 0) {
451 std::cout << nStrips << " strips.\n";
452 } else if (nStrips == 0) {
453 std::cout << nPixels << " pixels.\n";
454 } else {
455 std::cout << nStrips << " strips, " << nPixels << " pixels.\n";
456 }
457 for (const auto& strip : plane.strips2) {
458 std::cout << " ";
459 if (m_polar) {
460 double gap = i == 0 ? expm1(strip.gap) : -expm1(-strip.gap);
461 gap *= exp(m_coplan[i]);
462 std::cout << RadToDegree * strip.smin << " < phi < "
463 << RadToDegree * strip.smax
464 << " degrees, gap = " << gap << " cm";
465 } else {
466 std::cout << strip.smin << " < y < " << strip.smax
467 << " cm, gap = " << strip.gap << " cm";
468 }
469 if (!strip.type.empty() && strip.type != "?") {
470 std::cout << " (\"" << strip.type << "\")";
471 }
472 std::cout << "\n";
473 }
474 for (const auto& strip : plane.strips1) {
475 std::cout << " " << strip.smin << " < z < " << strip.smax;
476 if (m_polar) {
477 double gap = i == 0 ? expm1(strip.gap) : -expm1(-strip.gap);
478 gap *= exp(m_coplan[i]);
479 std::cout << " cm, gap = " << gap << " cm";
480 } else {
481 std::cout << " cm, gap = " << strip.gap << " cm";
482 }
483 if (!strip.type.empty() && strip.type != "?") {
484 std::cout << " (\"" << strip.type << "\")";
485 }
486 std::cout << "\n";
487 }
488 for (const auto& pix : plane.pixels) {
489 std::cout << " ";
490 if (m_polar) {
491 std::cout << RadToDegree * pix.smin << " < phi < "
492 << RadToDegree * pix.smax << " degrees, ";
493 } else {
494 std::cout << pix.smin << " < y < " << pix.smax << " cm, ";
495 }
496 std::cout << pix.zmin << " < z < " << pix.zmax << " cm, gap = ";
497 if (m_polar) {
498 double gap = i == 0 ? expm1(pix.gap) : -expm1(-pix.gap);
499 gap *= exp(m_coplan[i]);
500 std::cout << gap << " cm";
501 } else {
502 std::cout << pix.gap << " cm";
503 }
504 if (!pix.type.empty() && pix.type != "?") {
505 std::cout << " (\"" << pix.type << "\")";
506 }
507 std::cout << "\n";
508 }
509 }
510 // Next the planes at constant y or phi
511 const std::string yphi = m_polar ? "phi" : "y";
512 if (m_ynplan[2] && m_ynplan[3]) {
513 std::cout << " There are two planes at constant " << yphi << ":\n";
514 } else if (m_ynplan[2] || m_ynplan[3]) {
515 std::cout << " There is one plane at constant " << yphi << ":\n";
516 }
517 for (unsigned int i = 2; i < 4; ++i) {
518 if (!m_ynplan[i]) continue;
519 if (m_polar) {
520 std::cout << " phi = " << RadToDegree * m_coplan[i] << " degrees, ";
521 } else {
522 std::cout << " y = " << m_coplan[i] << " cm, ";
523 }
524 if (fabs(m_vtplan[i]) > 1.e-4) {
525 std::cout << "potential = " << m_vtplan[i] << " V, ";
526 } else {
527 std::cout << "earthed, ";
528 }
529 const auto& plane = m_planes[i];
530 if (plane.type.empty() && plane.type != "?") {
531 std::cout << "label = " << plane.type << ", ";
532 }
533 const unsigned int nStrips = plane.strips1.size() + plane.strips2.size();
534 const unsigned int nPixels = plane.pixels.size();
535 if (nStrips == 0 && nPixels == 0) {
536 std::cout << "no strips or pixels.\n";
537 } else if (nPixels == 0) {
538 std::cout << nStrips << " strips.\n";
539 } else if (nStrips == 0) {
540 std::cout << nPixels << " pixels.\n";
541 } else {
542 std::cout << nStrips << " strips, " << nPixels << " pixels.\n";
543 }
544 for (const auto& strip : plane.strips2) {
545 std::cout << " ";
546 if (m_polar) {
547 std::cout << exp(strip.smin) << " < r < " << exp(strip.smax)
548 << " cm, gap = " << RadToDegree * strip.gap << " degrees";
549 } else {
550 std::cout << strip.smin << " < x < " << strip.smax
551 << " cm, gap = " << strip.gap << " cm";
552 }
553 if (!strip.type.empty() && strip.type != "?") {
554 std::cout << " (\"" << strip.type << "\")";
555 }
556 std::cout << "\n";
557 }
558 for (const auto& strip : plane.strips1) {
559 std::cout << " " << strip.smin << " < z < " << strip.smax;
560 if (m_polar) {
561 std::cout << " cm, gap = " << RadToDegree * strip.gap << " degrees";
562 } else {
563 std::cout << " cm, gap = " << strip.gap << " cm";
564 }
565 if (!strip.type.empty() && strip.type != "?") {
566 std::cout << " (\"" << strip.type << "\")";
567 }
568 std::cout << "\n";
569 }
570 for (const auto& pix : plane.pixels) {
571 std::cout << " ";
572 if (m_polar) {
573 std::cout << exp(pix.smin) << " < r < " << exp(pix.smax) << " cm, ";
574 } else {
575 std::cout << pix.smin << " < x < " << pix.smax << " cm, ";
576 }
577 std::cout << pix.zmin << " < z < " << pix.zmax << " cm, gap = ";
578 if (m_polar) {
579 std::cout << RadToDegree * pix.gap << " degrees";
580 } else {
581 std::cout << pix.gap << " cm";
582 }
583 if (!pix.type.empty() && pix.type != "?") {
584 std::cout << " (\"" << pix.type << "\")";
585 }
586 std::cout << "\n";
587 }
588 }
589 }
590 // Print the type of periodicity.
591 std::cout << " Periodicity\n";
592 if (m_perx) {
593 std::cout << " The cell is repeated every ";
594 if (m_polar) {
595 std::cout << exp(m_sx) << " cm in r.\n";
596 } else {
597 std::cout << m_sx << " cm in x.\n";
598 }
599 } else {
600 if (m_polar) {
601 std::cout << " The cell is not periodic in r.\n";
602 } else {
603 std::cout << " The cell has no translation periodicity in x.\n";
604 }
605 }
606 if (m_pery) {
607 std::cout << " The cell is repeated every ";
608 if (m_polar) {
609 std::cout << RadToDegree * m_sy << " degrees in phi.\n";
610 } else {
611 std::cout << m_sy << " cm in y.\n";
612 }
613 } else {
614 if (m_polar) {
615 std::cout << " The cell is not periodic in phi.\n";
616 } else {
617 std::cout << " The cell has no translation periodicity in y.\n";
618 }
619 }
620 std::cout << " Other data\n";
621 std::cout << " Gravity vector: (" << m_down[0] << ", " << m_down[1]
622 << ", " << m_down[2] << ") [g].\n";
623 std::cout << " Cell dimensions:\n ";
624 if (!m_polar) {
625 std::cout << m_xmin << " < x < " << m_xmax << " cm, " << m_ymin << " < y < "
626 << m_ymax << " cm.\n";
627 } else {
628 double xminp, yminp;
629 Internal2Polar(m_xmin, m_ymin, xminp, yminp);
630 double xmaxp, ymaxp;
631 Internal2Polar(m_xmax, m_ymax, xmaxp, ymaxp);
632 std::cout << xminp << " < r < " << xmaxp << " cm, " << yminp << " < phi < "
633 << ymaxp << " degrees.\n";
634 }
635 std::cout << " Potential range:\n " << m_vmin << " < V < " << m_vmax
636 << " V.\n";
637 // Print voltage shift in case no equipotential planes are present.
638 if (!(m_ynplan[0] || m_ynplan[1] || m_ynplan[2] || m_ynplan[3] || m_tube)) {
639 std::cout << " All voltages have been shifted by " << m_v0
640 << " V to avoid net wire charge.\n";
641 } else {
642 // Print the net charge on wires.
643 double sum = 0.;
644 for (const auto& w : m_w) sum += w.e;
645 std::cout << " The net charge on the wires is "
646 << 1.e-3 * TwoPiEpsilon0 * sum << " pC/cm.\n";
647 }
648}

◆ PrintCharges()

void Garfield::ComponentAnalyticField::PrintCharges ( ) const

Print a list of the point charges.

Definition at line 1622 of file ComponentAnalyticField.cc.

1622 {
1623 std::cout << m_className << "::PrintCharges:\n";
1624 if (m_ch3d.empty()) {
1625 std::cout << " No charges present.\n";
1626 return;
1627 }
1628 std::cout << " x [cm] y [cm] z [cm] charge [fC]\n";
1629 for (const auto& charge : m_ch3d) {
1630 std::cout << " " << std::setw(9) << charge.x << " " << std::setw(9)
1631 << charge.y << " " << std::setw(9) << charge.z << " "
1632 << std::setw(11) << charge.e * FourPiEpsilon0 << "\n";
1633 }
1634}

◆ SetCartesianCoordinates()

void Garfield::ComponentAnalyticField::SetCartesianCoordinates ( )

Use Cartesian coordinates (default).

Definition at line 1584 of file ComponentAnalyticField.cc.

1584 {
1585 if (m_polar) {
1586 std::cout << m_className << "::SetCartesianCoordinates:\n "
1587 << "Switching to Cartesian coordinates; resetting the cell.\n";
1588 CellInit();
1589 }
1590 m_polar = false;
1591}

◆ SetGravity()

void Garfield::ComponentAnalyticField::SetGravity ( const double  dx,
const double  dy,
const double  dz 
)

Set the gravity orientation.

Definition at line 1896 of file ComponentAnalyticField.cc.

1897 {
1898
1899 const double d = sqrt(dx * dx + dy * dy + dz * dz);
1900 if (d > 0.) {
1901 m_down[0] = dx / d;
1902 m_down[1] = dy / d;
1903 m_down[2] = dz / d;
1904 } else {
1905 std::cerr << m_className << "::SetGravity:\n"
1906 << " The gravity vector has zero norm ; ignored.\n";
1907 }
1908}

◆ SetMedium()

void Garfield::ComponentAnalyticField::SetMedium ( Medium medium)
inline

Set the medium inside the cell.

Definition at line 89 of file ComponentAnalyticField.hh.

89{ m_medium = medium; }

Referenced by GarfieldPhysics::InitializePhysics(), and main().

◆ SetNumberOfShots()

void Garfield::ComponentAnalyticField::SetNumberOfShots ( const unsigned int  n)
inline

Set the number of shots used for numerically solving the wire sag differential equation.

Definition at line 326 of file ComponentAnalyticField.hh.

326{ m_nShots = n; }

◆ SetNumberOfSteps()

void Garfield::ComponentAnalyticField::SetNumberOfSteps ( const unsigned int  n)

Set the number of integration steps within each shot (must be >= 1).

Definition at line 2127 of file ComponentAnalyticField.cc.

2127 {
2128 if (n == 0) {
2129 std::cerr << m_className << "::SetNumberOfSteps:\n"
2130 << " Number of steps must be > 0.\n";
2131 return;
2132 }
2133 m_nSteps = n;
2134}

◆ SetPeriodicityPhi()

void Garfield::ComponentAnalyticField::SetPeriodicityPhi ( const double  phi)

Set the periodicity [degree] in phi.

Definition at line 1479 of file ComponentAnalyticField.cc.

1479 {
1480 if (!m_polar && !m_tube) {
1481 std::cerr << m_className << "::SetPeriodicityPhi:\n"
1482 << " Cannot use phi-periodicity with Cartesian coordinates.\n";
1483 return;
1484 }
1485 if (std::abs(360. - s * int(360. / s)) > 1.e-4) {
1486 std::cerr << m_className << "::SetPeriodicityPhi:\n"
1487 << " Phi periods must divide 360; ignored.\n";
1488 return;
1489 }
1490
1491 m_periodic[1] = true;
1492 m_sy = DegreeToRad * s;
1493 m_mtube = int(360. / s);
1494 UpdatePeriodicity();
1495}

◆ SetPeriodicityX()

void Garfield::ComponentAnalyticField::SetPeriodicityX ( const double  s)

Set the periodic length [cm] in the x-direction.

Definition at line 1445 of file ComponentAnalyticField.cc.

1445 {
1446 if (m_polar) {
1447 std::cerr << m_className << "::SetPeriodicityX:\n"
1448 << " Cannot use x-periodicity with polar coordinates.\n";
1449 return;
1450 }
1451 if (s < Small) {
1452 std::cerr << m_className << "::SetPeriodicityX:\n"
1453 << " Periodic length must be greater than zero.\n";
1454 return;
1455 }
1456
1457 m_periodic[0] = true;
1458 m_sx = s;
1459 UpdatePeriodicity();
1460}

◆ SetPeriodicityY()

void Garfield::ComponentAnalyticField::SetPeriodicityY ( const double  s)

Set the periodic length [cm] in the y-direction.

Definition at line 1462 of file ComponentAnalyticField.cc.

1462 {
1463 if (m_polar) {
1464 std::cerr << m_className << "::SetPeriodicityY:\n"
1465 << " Cannot use y-periodicity with polar coordinates.\n";
1466 return;
1467 }
1468 if (s < Small) {
1469 std::cerr << m_className << "::SetPeriodicityY:\n"
1470 << " Periodic length must be greater than zero.\n";
1471 return;
1472 }
1473
1474 m_periodic[1] = true;
1475 m_sy = s;
1476 UpdatePeriodicity();
1477}

◆ SetPolarCoordinates()

void Garfield::ComponentAnalyticField::SetPolarCoordinates ( )

Use polar coordinates.

Definition at line 1593 of file ComponentAnalyticField.cc.

1593 {
1594 if (!m_polar) {
1595 std::cout << m_className << "::SetPolarCoordinates:\n "
1596 << "Switching to polar coordinates; resetting the cell.\n";
1597 CellInit();
1598 }
1599 m_polar = true;
1600 // Set default phi period.
1601 m_pery = true;
1602 m_sy = TwoPi;
1603}

◆ SetScanningArea()

void Garfield::ComponentAnalyticField::SetScanningArea ( const double  xmin,
const double  xmax,
const double  ymin,
const double  ymax 
)

Specify explicitly the boundaries of the the scanning area (i. e. the area in which the electrostatic force acting on a wire is computed).

Definition at line 1870 of file ComponentAnalyticField.cc.

1873 {
1874 if (std::abs(xmax - xmin) < Small || std::abs(ymax - ymin) < Small) {
1875 std::cerr << m_className << "::SetScanningArea:\n"
1876 << " Zero range not permitted.\n";
1877 return;
1878 }
1879 m_scanRange = ScanningRange::User;
1880 m_xScanMin = std::min(xmin, xmax);
1881 m_xScanMax = std::max(xmin, xmax);
1882 m_yScanMin = std::min(ymin, ymax);
1883 m_yScanMax = std::max(ymin, ymax);
1884}

◆ SetScanningAreaFirstOrder()

void Garfield::ComponentAnalyticField::SetScanningAreaFirstOrder ( const double  scale = 2.)

Set the scanning area based on the zeroth-order estimates of the wire shift, enlarged by a scaling factor. This is the default behaviour.

Definition at line 1886 of file ComponentAnalyticField.cc.

1886 {
1887 m_scanRange = ScanningRange::FirstOrder;
1888 if (scale > 0.) {
1889 m_scaleRange = scale;
1890 } else {
1891 std::cerr << m_className << "::SetScanningAreaFirstOrder:\n"
1892 << " Scaling factor must be > 0.\n";
1893 }
1894}

◆ SetScanningAreaLargest()

void Garfield::ComponentAnalyticField::SetScanningAreaLargest ( )
inline

Set the scanning area to the largest area around each wire which is free from other cell elements.

Definition at line 286 of file ComponentAnalyticField.hh.

286{ m_scanRange = ScanningRange::Largest; }

◆ SetScanningGrid()

void Garfield::ComponentAnalyticField::SetScanningGrid ( const unsigned int  nX,
const unsigned int  nY 
)

Set the number of grid lines at which the electrostatic force as function of the wire displacement is computed.

Definition at line 1854 of file ComponentAnalyticField.cc.

1855 {
1856 if (nX < 2) {
1857 std::cerr << m_className << "::SetScanningGrid:\n"
1858 << " Number of x-lines must be > 1.\n";
1859 } else {
1860 m_nScanX = nX;
1861 }
1862 if (nY < 2) {
1863 std::cerr << m_className << "::SetScanningGrid:\n"
1864 << " Number of y-lines must be > 1.\n";
1865 } else {
1866 m_nScanY = nY;
1867 }
1868}

◆ WeightingField()

void Garfield::ComponentAnalyticField::WeightingField ( const double  x,
const double  y,
const double  z,
double &  wx,
double &  wy,
double &  wz,
const std::string &  label 
)
inlineoverridevirtual

Calculate the weighting field at a given point and for a given electrode.

Parameters
x,y,zcoordinates [cm].
wx,wy,wzcomponents of the weighting field [1/cm].
labelname of the electrode

Reimplemented from Garfield::Component.

Definition at line 60 of file ComponentAnalyticField.hh.

62 {
63 wx = wy = wz = 0.;
64 if (!m_sigset) PrepareSignals();
65 Wfield(x, y, z, wx, wy, wz, label);
66 }

◆ WeightingPotential()

double Garfield::ComponentAnalyticField::WeightingPotential ( const double  x,
const double  y,
const double  z,
const std::string &  label 
)
inlineoverridevirtual

Calculate the weighting potential at a given point.

Parameters
x,y,zcoordinates [cm].
labelname of the electrode.
Returns
weighting potential [dimensionless].

Reimplemented from Garfield::Component.

Definition at line 67 of file ComponentAnalyticField.hh.

68 {
69 if (!m_sigset) PrepareSignals();
70 return Wpot(x, y, z, label);
71 }

◆ WireDisplacement()

bool Garfield::ComponentAnalyticField::WireDisplacement ( const unsigned int  iw,
const bool  detailed,
std::vector< double > &  csag,
std::vector< double > &  xsag,
std::vector< double > &  ysag,
double &  stretch,
const bool  print = true 
)

Compute the sag profile of a wire.

Parameters
iwindex of the wire
detailedflag to request a detailed calculation of the profile or only a fast one.
csagcoordinate along the wire.
xsagx components of the sag profile.
ysagy components of the sag profile.
stretchrelative elongation.
printflag to print the calculation results or not.

Definition at line 2136 of file ComponentAnalyticField.cc.

2139 {
2140 if (!m_cellset && !Prepare()) {
2141 std::cerr << m_className << "::WireDisplacement: Cell not set up.\n";
2142 return false;
2143 }
2144 if (iw >= m_nWires) {
2145 std::cerr << m_className
2146 << "::WireDisplacement: Wire index out of range.\n";
2147 return false;
2148 }
2149 if (m_polar) {
2150 std::cerr << m_className
2151 << "::WireDisplacement: Cannot handle polar cells.\n";
2152 return false;
2153 }
2154 const auto& wire = m_w[iw];
2155 // Save the original coordinates.
2156 const double x0 = wire.x;
2157 const double y0 = wire.y;
2158 // First-order approximation.
2159 if (!Setup()) {
2160 std::cerr << m_className << "::WireDisplacement:\n"
2161 << " Charge calculation failed at central position.\n";
2162 return false;
2163 }
2164
2165 double fx = 0., fy = 0.;
2166 if (m_useElectrostaticForce) {
2167 double ex = 0., ey = 0.;
2168 ElectricFieldAtWire(iw, ex, ey);
2169 fx -= ex * wire.e * Internal2Newton;
2170 fy -= ey * wire.e * Internal2Newton;
2171 }
2172 if (m_useGravitationalForce) {
2173 // Mass per unit length [kg / cm].
2174 const double m = 1.e-3 * wire.density * Pi * wire.r * wire.r;
2175 fx -= m_down[0] * GravitationalAcceleration * m;
2176 fy -= m_down[1] * GravitationalAcceleration * m;
2177 }
2178 const double u2 = wire.u * wire.u;
2179 const double shiftx =
2180 -125 * fx * u2 / (GravitationalAcceleration * wire.tension);
2181 const double shifty =
2182 -125 * fy * u2 / (GravitationalAcceleration * wire.tension);
2183 // Get the elongation from this.
2184 const double s = 4 * sqrt(shiftx * shiftx + shifty * shifty) / wire.u;
2185 double length = wire.u;
2186 if (s > Small) {
2187 const double t = sqrt(1 + s * s);
2188 length *= 0.5 * (t + log(s + t) / s);
2189 }
2190 stretch = (length - wire.u) / wire.u;
2191 if (print) {
2192 std::cout << m_className
2193 << "::WireDisplacement:\n"
2194 << " Forces and displacement in 0th order.\n"
2195 << " Wire information: number = " << iw << "\n"
2196 << " type = " << wire.type << "\n"
2197 << " location = (" << wire.x << ", " << wire.y
2198 << ") cm\n"
2199 << " voltage = " << wire.v << " V\n"
2200 << " length = " << wire.u << " cm\n"
2201 << " tension = " << wire.tension << " g\n"
2202 << " In this position: Fx = " << fx << " N/cm\n"
2203 << " Fy = " << fy << " N/cm\n"
2204 << " x-shift = " << shiftx << " cm\n"
2205 << " y-shift = " << shifty << " cm\n"
2206 << " stretch = " << 100. * stretch << "%\n";
2207 }
2208 if (!detailed) {
2209 csag = {0.};
2210 xsag = {shiftx};
2211 ysag = {shifty};
2212 return true;
2213 }
2214 std::vector<double> xMap(m_nScanX, 0.);
2215 std::vector<double> yMap(m_nScanY, 0.);
2216 std::vector<std::vector<double> > fxMap(m_nScanX,
2217 std::vector<double>(m_nScanY, 0.));
2218 std::vector<std::vector<double> > fyMap(m_nScanX,
2219 std::vector<double>(m_nScanY, 0.));
2220 if (!ForcesOnWire(iw, xMap, yMap, fxMap, fyMap)) {
2221 std::cerr << m_className << "::WireDisplacement:\n"
2222 << " Could not compute the electrostatic force table.\n";
2223 return false;
2224 }
2225 // Compute the detailed wire shift.
2226 if (!SagDetailed(wire, xMap, yMap, fxMap, fyMap, csag, xsag, ysag)) {
2227 std::cerr << m_className << "::WireDisplacement: Wire " << iw << ".\n"
2228 << " Computation of the wire sag failed.\n";
2229 return false;
2230 }
2231 // Verify that the wire is in range.
2232 const double sxmin = xMap.front();
2233 const double sxmax = xMap.back();
2234 const double symin = yMap.front();
2235 const double symax = yMap.back();
2236 const unsigned int nSag = xsag.size();
2237 bool outside = false;
2238 length = 0.;
2239 double xAvg = 0.;
2240 double yAvg = 0.;
2241 double xMax = 0.;
2242 double yMax = 0.;
2243 for (unsigned int i = 0; i < nSag; ++i) {
2244 if (x0 + xsag[i] < sxmin || x0 + xsag[i] > sxmax || y0 + ysag[i] < symin ||
2245 y0 + ysag[i] > symax) {
2246 outside = true;
2247 }
2248 xAvg += xsag[i];
2249 yAvg += ysag[i];
2250 xMax = std::max(xMax, std::abs(xsag[i]));
2251 yMax = std::max(yMax, std::abs(ysag[i]));
2252 if (i == 0) continue;
2253 const double dx = xsag[i] - xsag[i - 1];
2254 const double dy = ysag[i] - ysag[i - 1];
2255 const double dz = csag[i] - csag[i - 1];
2256 length += sqrt(dx * dx + dy * dy + dz * dz);
2257 }
2258 xAvg /= nSag;
2259 yAvg /= nSag;
2260 stretch = (length - wire.u) / wire.u;
2261 // Warn if a point outside the scanning area was found.
2262 if (outside) {
2263 std::cerr
2264 << m_className << "::WireDisplacement: Warning.\n "
2265 << "The wire profile is located partially outside the scanning area.\n";
2266 }
2267 if (print) {
2268 std::cout << " Sag profile for wire " << iw << ".\n"
2269 << " Point z [cm] x-sag [um] y-sag [um]\n";
2270 for (unsigned int i = 0; i < nSag; ++i) {
2271 std::printf(" %3d %10.4f %10.4f %10.4f\n",
2272 i, csag[i], xsag[i] * 1.e4, ysag[i] * 1.e4);
2273 }
2274 std::printf(" Average sag in x and y: %10.4f and %10.4f micron\n",
2275 1.e4 * xAvg, 1.e4 * yAvg);
2276 std::printf(" Maximum sag in x and y: %10.4f and %10.4f micron\n",
2277 1.e4 * xMax, 1.e4 * yMax);
2278 std::cout << " Elongation: " << 100. * stretch << "%\n";
2279 }
2280 return true;
2281}
bool ForcesOnWire(const unsigned int iw, std::vector< double > &xMap, std::vector< double > &yMap, std::vector< std::vector< double > > &fxMap, std::vector< std::vector< double > > &fyMap)

The documentation for this class was generated from the following files: