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

Collection of numerical routines. More...

Namespaces

namespace  CERNLIB
 Linear algebra routines from CERNLIB.
 
namespace  QUADPACK
 

Functions

constexpr std::array< double, 2 > GaussLegendreNodes2 ()
 
constexpr std::array< double, 2 > GaussLegendreWeights2 ()
 
constexpr std::array< double, 3 > GaussLegendreNodes3 ()
 
constexpr std::array< double, 3 > GaussLegendreWeights3 ()
 
constexpr std::array< double, 4 > GaussLegendreNodes4 ()
 
constexpr std::array< double, 4 > GaussLegendreWeights4 ()
 
constexpr std::array< double, 5 > GaussLegendreNodes5 ()
 
constexpr std::array< double, 5 > GaussLegendreWeights5 ()
 
constexpr std::array< double, 6 > GaussLegendreNodes6 ()
 
constexpr std::array< double, 6 > GaussLegendreWeights6 ()
 
double Legendre (const unsigned int n, const double x)
 Legendre polynomials.
 
double BesselI0S (const double xx)
 
double BesselI1S (const double xx)
 
double BesselK0S (const double xx)
 
double BesselK0L (const double xx)
 
double BesselK1S (const double xx)
 
double BesselK1L (const double xx)
 
double Divdif (const std::vector< double > &f, const std::vector< double > &a, int nn, double x, int mm)
 
bool Boxin2 (const std::vector< std::vector< double > > &value, const std::vector< double > &xAxis, const std::vector< double > &yAxis, const int nx, const int ny, const double xx, const double yy, double &f, const int iOrder)
 
bool Boxin3 (const std::vector< std::vector< std::vector< double > > > &value, const std::vector< double > &xAxis, const std::vector< double > &yAxis, const std::vector< double > &zAxis, const int nx, const int ny, const int nz, const double xx, const double yy, const double zz, double &f, const int iOrder)
 
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.
 

Detailed Description

Collection of numerical routines.

Function Documentation

◆ BesselI0S()

double Garfield::Numerics::BesselI0S ( const double xx)
inline

Modified Bessel functions. Series expansions from Abramowitz and Stegun.

Definition at line 178 of file Numerics.hh.

178 {
179 const double y = xx / 3.75;
180 const double y2 = y * y;
181 return 1. + 3.5156229 * y2 + 3.0899424 * y2 * y2 +
182 1.2067492 * pow(y2, 3) + 0.2659732 * pow(y2, 4) +
183 0.0360768 * pow(y2, 5) + 0.0045813 * pow(y2, 6);
184}

Referenced by BesselK0S().

◆ BesselI1S()

double Garfield::Numerics::BesselI1S ( const double xx)
inline

Definition at line 186 of file Numerics.hh.

186 {
187 const double y = xx / 3.75;
188 const double y2 = y * y;
189 return xx *
190 (0.5 + 0.87890594 * y2 + 0.51498869 * y2 * y2 +
191 0.15084934 * pow(y2, 3) + 0.02658733 * pow(y2, 4) +
192 0.00301532 * pow(y2, 5) + 0.00032411 * pow(y2, 6));
193}

Referenced by BesselK1S().

◆ BesselK0L()

double Garfield::Numerics::BesselK0L ( const double xx)
inline

Definition at line 204 of file Numerics.hh.

204 {
205 const double y = 2. / xx;
206 return (exp(-xx) / sqrt(xx)) *
207 (1.25331414 - 0.07832358 * y + 0.02189568 * y * y -
208 0.01062446 * pow(y, 3) + 0.00587872 * pow(y, 4) -
209 0.00251540 * pow(y, 5) + 0.00053208 * pow(y, 6));
210}

◆ BesselK0S()

double Garfield::Numerics::BesselK0S ( const double xx)
inline

Definition at line 195 of file Numerics.hh.

195 {
196 const double y = xx / 2.;
197 const double y2 = y * y;
198 return -log(y) * BesselI0S(xx) - Gamma +
199 0.42278420 * y2 + 0.23069756 * y2 * y2 +
200 0.03488590 * pow(y2, 3) + 0.00262698 * pow(y2, 4) +
201 0.00010750 * pow(y2, 5) + 0.00000740 * pow(y2, 6);
202}
double BesselI0S(const double xx)
Definition Numerics.hh:178

◆ BesselK1L()

double Garfield::Numerics::BesselK1L ( const double xx)
inline

Definition at line 222 of file Numerics.hh.

222 {
223 const double y = 2. / xx;
224 return (exp(-xx) / sqrt(xx)) *
225 (1.25331414 + 0.23498619 * y - 0.03655620 * y * y +
226 0.01504268 * pow(y, 3) - 0.00780353 * pow(y, 4) +
227 0.00325614 * pow(y, 5) - 0.00068245 * pow(y, 6));
228}

◆ BesselK1S()

double Garfield::Numerics::BesselK1S ( const double xx)
inline

Definition at line 212 of file Numerics.hh.

212 {
213 const double y = xx / 2.;
214 const double y2 = y * y;
215 return log(y) * BesselI1S(xx) +
216 (1. / xx) *
217 (1. + 0.15443144 * y2 - 0.67278579 * y2 * y2 -
218 0.18156897 * pow(y2, 3) - 0.01919402 * pow(y2, 4) -
219 0.00110404 * pow(y2, 5) - 0.00004686 * pow(y2, 6));
220}
double BesselI1S(const double xx)
Definition Numerics.hh:186

◆ Boxin2()

bool Garfield::Numerics::Boxin2 ( const std::vector< std::vector< double > > & value,
const std::vector< double > & xAxis,
const std::vector< double > & yAxis,
const int nx,
const int ny,
const double xx,
const double yy,
double & f,
const int iOrder )

Interpolation of order 1 and 2 in an irregular rectangular two-dimensional grid.

Definition at line 1354 of file Numerics.cc.

1357 {
1358 //-----------------------------------------------------------------------
1359 // BOXIN2 - Interpolation of order 1 and 2 in an irregular rectangular
1360 // 2-dimensional grid.
1361 //-----------------------------------------------------------------------
1362 int iX0 = 0, iX1 = 0;
1363 int iY0 = 0, iY1 = 0;
1364 std::array<double, 3> fX;
1365 std::array<double, 3> fY;
1366 f = 0.;
1367 // Ensure we are in the grid.
1368 if ((xAxis[nx - 1] - x) * (x - xAxis[0]) < 0 ||
1369 (yAxis[ny - 1] - y) * (y - yAxis[0]) < 0) {
1370 std::cerr << "Boxin2: Point not in the grid; no interpolation.\n";
1371 return false;
1372 }
1373 // Make sure we have enough points.
1374 if (iOrder < 0 || iOrder > 2) {
1375 std::cerr << "Boxin2: Incorrect order; no interpolation.\n";
1376 return false;
1377 } else if (nx < 1 || ny < 1) {
1378 std::cerr << "Boxin2: Incorrect number of points; no interpolation.\n";
1379 return false;
1380 }
1381 if (iOrder == 0 || nx <= 1) {
1382 // Zeroth order interpolation in x.
1383 // Find the nearest node.
1384 double dist = fabs(x - xAxis[0]);
1385 int iNode = 0;
1386 for (int i = 1; i < nx; i++) {
1387 if (fabs(x - xAxis[i]) < dist) {
1388 dist = fabs(x - xAxis[i]);
1389 iNode = i;
1390 }
1391 }
1392 // Set the summing range.
1393 iX0 = iNode;
1394 iX1 = iNode;
1395 // Establish the shape functions.
1396 fX = {1., 0., 0.};
1397 } else if (iOrder == 1 || nx <= 2) {
1398 // First order interpolation in x.
1399 // Find the grid segment containing this point.
1400 int iGrid = 0;
1401 for (int i = 1; i < nx; i++) {
1402 if ((xAxis[i - 1] - x) * (x - xAxis[i]) >= 0.) {
1403 iGrid = i;
1404 }
1405 }
1406 const double x0 = xAxis[iGrid - 1];
1407 const double x1 = xAxis[iGrid];
1408 // Ensure there won't be divisions by zero.
1409 if (x1 == x0) {
1410 std::cerr << "Boxin2: Incorrect grid; no interpolation.\n";
1411 return false;
1412 }
1413 // Compute local coordinates.
1414 const double xL = (x - x0) / (x1 - x0);
1415 // Set the summing range.
1416 iX0 = iGrid - 1;
1417 iX1 = iGrid;
1418 // Set the shape functions.
1419 fX = {1. - xL, xL, 0.};
1420 } else if (iOrder == 2) {
1421 // Second order interpolation in x.
1422 // Find the nearest node and the grid segment.
1423 double dist = fabs(x - xAxis[0]);
1424 int iNode = 0;
1425 for (int i = 1; i < nx; ++i) {
1426 if (fabs(x - xAxis[i]) < dist) {
1427 dist = fabs(x - xAxis[i]);
1428 iNode = i;
1429 }
1430 }
1431 // Find the nearest fitting 2x2 matrix.
1432 int iGrid = std::max(1, std::min(nx - 2, iNode));
1433 const double x0 = xAxis[iGrid - 1];
1434 const double x1 = xAxis[iGrid];
1435 const double x2 = xAxis[iGrid + 1];
1436 // Ensure there won't be divisions by zero.
1437 if (x2 == x0) {
1438 std::cerr << "Boxin2: Incorrect grid; no interpolation.\n";
1439 return false;
1440 }
1441 // Compute the alpha and local coordinate for this grid segment.
1442 const double xAlpha = (x1 - x0) / (x2 - x0);
1443 const double xL = (x - x0) / (x2 - x0);
1444 // Ensure there won't be divisions by zero.
1445 if (xAlpha <= 0 || xAlpha >= 1) {
1446 std::cerr << "Boxin2: Incorrect grid; no interpolation.\n";
1447 return false;
1448 }
1449 // Set the summing range.
1450 iX0 = iGrid - 1;
1451 iX1 = iGrid + 1;
1452 // Set the shape functions.
1453 const double xL2 = xL * xL;
1454 fX[0] = xL2 / xAlpha - xL * (1. + xAlpha) / xAlpha + 1.;
1455 fX[1] = (xL2 - xL) / (xAlpha * xAlpha - xAlpha);
1456 fX[2] = (xL2 - xL * xAlpha) / (1. - xAlpha);
1457 }
1458 if (iOrder == 0 || ny <= 1) {
1459 // Zeroth order interpolation in y.
1460 // Find the nearest node.
1461 double dist = fabs(y - yAxis[0]);
1462 int iNode = 0;
1463 for (int i = 1; i < ny; i++) {
1464 if (fabs(y - yAxis[i]) < dist) {
1465 dist = fabs(y - yAxis[i]);
1466 iNode = i;
1467 }
1468 }
1469 // Set the summing range.
1470 iY0 = iNode;
1471 iY1 = iNode;
1472 // Establish the shape functions.
1473 fY = {1., 0., 0.};
1474 } else if (iOrder == 1 || ny <= 2) {
1475 // First order interpolation in y.
1476 // Find the grid segment containing this point.
1477 int iGrid = 0;
1478 for (int i = 1; i < ny; ++i) {
1479 if ((yAxis[i - 1] - y) * (y - yAxis[i]) >= 0) {
1480 iGrid = i;
1481 }
1482 }
1483 const double y0 = yAxis[iGrid - 1];
1484 const double y1 = yAxis[iGrid];
1485 // Ensure there won't be divisions by zero.
1486 if (y1 == y0) {
1487 std::cerr << "Boxin2: Incorrect grid; no interpolation.\n";
1488 return false;
1489 }
1490 // Compute local coordinates.
1491 const double yL = (y - y0) / (y1 - y0);
1492 // Set the summing range.
1493 iY0 = iGrid - 1;
1494 iY1 = iGrid;
1495 // Set the shape functions.
1496 fY = {1. - yL, yL, 0.};
1497 } else if (iOrder == 2) {
1498 // Second order interpolation in y.
1499 // Find the nearest node and the grid segment.
1500 double dist = fabs(y - yAxis[0]);
1501 int iNode = 0;
1502 for (int i = 1; i < ny; ++i) {
1503 if (fabs(y - yAxis[i]) < dist) {
1504 dist = fabs(y - yAxis[i]);
1505 iNode = i;
1506 }
1507 }
1508 // Find the nearest fitting 2x2 matrix.
1509 int iGrid = std::max(1, std::min(ny - 2, iNode));
1510 const double y0 = yAxis[iGrid - 1];
1511 const double y1 = yAxis[iGrid];
1512 const double y2 = yAxis[iGrid + 1];
1513 // Ensure there won't be divisions by zero.
1514 if (y2 == y0) {
1515 std::cerr << "Boxin2: Incorrect grid; no interpolation.\n";
1516 return false;
1517 }
1518 // Compute the alpha and local coordinate for this grid segment.
1519 const double yAlpha = (y1 - y0) / (y2 - y0);
1520 const double yL = (y - y0) / (y2 - y0);
1521 // Ensure there won't be divisions by zero.
1522 if (yAlpha <= 0 || yAlpha >= 1) {
1523 std::cerr << "Boxin2: Incorrect grid; no interpolation.\n";
1524 return false;
1525 }
1526 // Set the summing range.
1527 iY0 = iGrid - 1;
1528 iY1 = iGrid + 1;
1529 // Set the shape functions.
1530 const double yL2 = yL * yL;
1531 fY[0] = yL2 / yAlpha - yL * (1. + yAlpha) / yAlpha + 1.;
1532 fY[1] = (yL2 - yL) / (yAlpha * yAlpha - yAlpha);
1533 fY[2] = (yL2 - yL * yAlpha) / (1. - yAlpha);
1534 }
1535
1536 // Sum the shape functions.
1537 for (int i = iX0; i <= iX1; ++i) {
1538 for (int j = iY0; j <= iY1; ++j) {
1539 f += value[i][j] * fX[i - iX0] * fY[j - iY0];
1540 }
1541 }
1542 return true;
1543}
DoubleAc fabs(const DoubleAc &f)
Definition DoubleAc.h:615

◆ Boxin3()

bool Garfield::Numerics::Boxin3 ( const std::vector< std::vector< std::vector< double > > > & value,
const std::vector< double > & xAxis,
const std::vector< double > & yAxis,
const std::vector< double > & zAxis,
const int nx,
const int ny,
const int nz,
const double xx,
const double yy,
const double zz,
double & f,
const int iOrder )

Interpolation of order 1 and 2 in an irregular rectangular three-dimensional grid.

Definition at line 1545 of file Numerics.cc.

1549 {
1550 //-----------------------------------------------------------------------
1551 // BOXIN3 - interpolation of order 1 and 2 in an irregular rectangular
1552 // 3-dimensional grid.
1553 //-----------------------------------------------------------------------
1554 int iX0 = 0, iX1 = 0;
1555 int iY0 = 0, iY1 = 0;
1556 int iZ0 = 0, iZ1 = 0;
1557 std::array<double, 4> fX;
1558 std::array<double, 4> fY;
1559 std::array<double, 4> fZ;
1560
1561 f = 0.;
1562 // Ensure we are in the grid.
1563 const double x = std::min(std::max(xx, std::min(xAxis[0], xAxis[nx - 1])),
1564 std::max(xAxis[0], xAxis[nx - 1]));
1565 const double y = std::min(std::max(yy, std::min(yAxis[0], yAxis[ny - 1])),
1566 std::max(yAxis[0], yAxis[ny - 1]));
1567 const double z = std::min(std::max(zz, std::min(zAxis[0], zAxis[nz - 1])),
1568 std::max(zAxis[0], zAxis[nz - 1]));
1569
1570 // Make sure we have enough points.
1571 if (iOrder < 0 || iOrder > 2) {
1572 std::cerr << "Boxin3: Incorrect order; no interpolation.\n";
1573 return false;
1574 } else if (nx < 1 || ny < 1 || nz < 1) {
1575 std::cerr << "Boxin3: Incorrect number of points; no interpolation.\n";
1576 return false;
1577 }
1578 if (iOrder == 0 || nx == 1) {
1579 // Zeroth order interpolation in x.
1580 // Find the nearest node.
1581 double dist = fabs(x - xAxis[0]);
1582 int iNode = 0;
1583 for (int i = 1; i < nx; i++) {
1584 if (fabs(x - xAxis[i]) < dist) {
1585 dist = fabs(x - xAxis[i]);
1586 iNode = i;
1587 }
1588 }
1589 // Set the summing range.
1590 iX0 = iNode;
1591 iX1 = iNode;
1592 // Establish the shape functions.
1593 fX = {1., 0., 0., 0.};
1594 } else if (iOrder == 1 || nx == 2) {
1595 // First order interpolation in x.
1596 // Find the grid segment containing this point.
1597 int iGrid = 0;
1598 for (int i = 1; i < nx; i++) {
1599 if ((xAxis[i - 1] - x) * (x - xAxis[i]) >= 0.) {
1600 iGrid = i;
1601 }
1602 }
1603 const double x0 = xAxis[iGrid - 1];
1604 const double x1 = xAxis[iGrid];
1605 // Ensure there won't be divisions by zero.
1606 if (x1 == x0) {
1607 std::cerr << "Boxin3: Incorrect grid; no interpolation.\n";
1608 return false;
1609 }
1610 // Compute local coordinates.
1611 const double xL = (x - x0) / (x1 - x0);
1612 // Set the summing range.
1613 iX0 = iGrid - 1;
1614 iX1 = iGrid;
1615 // Set the shape functions.
1616 fX = {1. - xL, xL, 0., 0.};
1617 } else if (iOrder == 2) {
1618 // Second order interpolation in x.
1619 // Find the grid segment containing this point.
1620 int iGrid = 0;
1621 for (int i = 1; i < nx; i++) {
1622 if ((xAxis[i - 1] - x) * (x - xAxis[i]) >= 0.) {
1623 iGrid = i;
1624 }
1625 }
1626 if (iGrid == 1) {
1627 iX0 = iGrid - 1;
1628 iX1 = iGrid + 1;
1629 const double x0 = xAxis[iX0];
1630 const double x1 = xAxis[iX0 + 1];
1631 const double x2 = xAxis[iX0 + 2];
1632 if (x0 == x1 || x0 == x2 || x1 == x2) {
1633 std::cerr << "Boxin3: One or more grid points in x coincide.\n"
1634 << " No interpolation.\n";
1635 return false;
1636 }
1637 fX[0] = (x - x1) * (x - x2) / ((x0 - x1) * (x0 - x2));
1638 fX[1] = (x - x0) * (x - x2) / ((x1 - x0) * (x1 - x2));
1639 fX[2] = (x - x0) * (x - x1) / ((x2 - x0) * (x2 - x1));
1640 } else if (iGrid == nx - 1) {
1641 iX0 = iGrid - 2;
1642 iX1 = iGrid;
1643 const double x0 = xAxis[iX0];
1644 const double x1 = xAxis[iX0 + 1];
1645 const double x2 = xAxis[iX0 + 2];
1646 if (x0 == x1 || x0 == x2 || x1 == x2) {
1647 std::cerr << "Boxin3: One or more grid points in x coincide.\n"
1648 << " No interpolation.\n";
1649 return false;
1650 }
1651 fX[0] = (x - x1) * (x - x2) / ((x0 - x1) * (x0 - x2));
1652 fX[1] = (x - x0) * (x - x2) / ((x1 - x0) * (x1 - x2));
1653 fX[2] = (x - x0) * (x - x1) / ((x2 - x0) * (x2 - x1));
1654 } else {
1655 iX0 = iGrid - 2;
1656 iX1 = iGrid + 1;
1657 const double x0 = xAxis[iX0];
1658 const double x1 = xAxis[iX0 + 1];
1659 const double x2 = xAxis[iX0 + 2];
1660 const double x3 = xAxis[iX0 + 3];
1661 if (x0 == x1 || x0 == x2 || x0 == x3 ||
1662 x1 == x2 || x1 == x3 || x2 == x3) {
1663 std::cerr << "Boxin3: One or more grid points in x coincide.\n"
1664 << " No interpolation.\n";
1665 return false;
1666 }
1667 // Compute the local coordinate for this grid segment.
1668 const double xL = (x - x1) / (x2 - x1);
1669 fX[0] = ((x - x1) * (x - x2) / ((x0 - x1) * (x0 - x2))) * (1. - xL);
1670 fX[1] = ((x - x0) * (x - x2) / ((x1 - x0) * (x1 - x2))) * (1. - xL) +
1671 ((x - x2) * (x - x3) / ((x1 - x2) * (x1 - x3))) * xL;
1672 fX[2] = ((x - x0) * (x - x1) / ((x2 - x0) * (x2 - x1))) * (1. - xL) +
1673 ((x - x1) * (x - x3) / ((x2 - x1) * (x2 - x3))) * xL;
1674 fX[3] = ((x - x1) * (x - x2) / ((x3 - x1) * (x3 - x2))) * xL;
1675 }
1676 }
1677
1678 if (iOrder == 0 || ny == 1) {
1679 // Zeroth order interpolation in y.
1680 // Find the nearest node.
1681 double dist = fabs(y - yAxis[0]);
1682 int iNode = 0;
1683 for (int i = 1; i < ny; i++) {
1684 if (fabs(y - yAxis[i]) < dist) {
1685 dist = fabs(y - yAxis[i]);
1686 iNode = i;
1687 }
1688 }
1689 // Set the summing range.
1690 iY0 = iNode;
1691 iY1 = iNode;
1692 // Establish the shape functions.
1693 fY = {1., 0., 0., 0.};
1694 } else if (iOrder == 1 || ny == 2) {
1695 // First order interpolation in y.
1696 // Find the grid segment containing this point.
1697 int iGrid = 0;
1698 for (int i = 1; i < ny; i++) {
1699 if ((yAxis[i - 1] - y) * (y - yAxis[i]) >= 0.) {
1700 iGrid = i;
1701 }
1702 }
1703 // Ensure there won't be divisions by zero.
1704 const double y0 = yAxis[iGrid - 1];
1705 const double y1 = yAxis[iGrid];
1706 if (y1 == y0) {
1707 std::cerr << "Boxin3: Incorrect grid; no interpolation.\n";
1708 return false;
1709 }
1710 // Compute local coordinates.
1711 const double yL = (y - y0) / (y1 - y0);
1712 // Set the summing range.
1713 iY0 = iGrid - 1;
1714 iY1 = iGrid;
1715 // Set the shape functions.
1716 fY = {1. - yL, yL, 0., 0.};
1717 } else if (iOrder == 2) {
1718 // Second order interpolation in y.
1719 // Find the grid segment containing this point.
1720 int iGrid = 0;
1721 for (int i = 1; i < ny; i++) {
1722 if ((yAxis[i - 1] - y) * (y - yAxis[i]) >= 0.) {
1723 iGrid = i;
1724 }
1725 }
1726 if (iGrid == 1) {
1727 iY0 = iGrid - 1;
1728 iY1 = iGrid + 1;
1729 const double y0 = yAxis[iY0];
1730 const double y1 = yAxis[iY0 + 1];
1731 const double y2 = yAxis[iY0 + 2];
1732 if (y0 == y1 || y0 == y2 || y1 == y2) {
1733 std::cerr << "Boxin3: One or more grid points in y coincide.\n"
1734 << " No interpolation.\n";
1735 return false;
1736 }
1737 fY[0] = (y - y1) * (y - y2) / ((y0 - y1) * (y0 - y2));
1738 fY[1] = (y - y0) * (y - y2) / ((y1 - y0) * (y1 - y2));
1739 fY[2] = (y - y0) * (y - y1) / ((y2 - y0) * (y2 - y1));
1740 } else if (iGrid == ny - 1) {
1741 iY0 = iGrid - 2;
1742 iY1 = iGrid;
1743 const double y0 = yAxis[iY0];
1744 const double y1 = yAxis[iY0 + 1];
1745 const double y2 = yAxis[iY0 + 2];
1746 if (y0 == y1 || y0 == y2 || y1 == y2) {
1747 std::cerr << "Boxin3: One or more grid points in y coincide.\n"
1748 << " No interpolation.\n";
1749 return false;
1750 }
1751 fY[0] = (y - y1) * (y - y2) / ((y0 - y1) * (y0 - y2));
1752 fY[1] = (y - y0) * (y - y2) / ((y1 - y0) * (y1 - y2));
1753 fY[2] = (y - y0) * (y - y1) / ((y2 - y0) * (y2 - y1));
1754 } else {
1755 iY0 = iGrid - 2;
1756 iY1 = iGrid + 1;
1757 const double y0 = yAxis[iY0];
1758 const double y1 = yAxis[iY0 + 1];
1759 const double y2 = yAxis[iY0 + 2];
1760 const double y3 = yAxis[iY0 + 3];
1761 if (y0 == y1 || y0 == y2 || y0 == y3 ||
1762 y1 == y2 || y1 == y3 || y2 == y3) {
1763 std::cerr << "Boxin3: One or more grid points in y coincide.\n"
1764 << " No interpolation.\n";
1765 return false;
1766 }
1767 // Compute the local coordinate for this grid segment.
1768 const double yL = (y - y1) / (y2 - y1);
1769 fY[0] = ((y - y1) * (y - y2) / ((y0 - y1) * (y0 - y2))) * (1. - yL);
1770 fY[1] = ((y - y0) * (y - y2) / ((y1 - y0) * (y1 - y2))) * (1. - yL) +
1771 ((y - y2) * (y - y3) / ((y1 - y2) * (y1 - y3))) * yL;
1772 fY[2] = ((y - y0) * (y - y1) / ((y2 - y0) * (y2 - y1))) * (1. - yL) +
1773 ((y - y1) * (y - y3) / ((y2 - y1) * (y2 - y3))) * yL;
1774 fY[3] = ((y - y1) * (y - y2) / ((y3 - y1) * (y3 - y2))) * yL;
1775 }
1776 }
1777
1778 if (iOrder == 0 || nz == 1) {
1779 // Zeroth order interpolation in z.
1780 // Find the nearest node.
1781 double dist = fabs(z - zAxis[0]);
1782 int iNode = 0;
1783 for (int i = 1; i < nz; i++) {
1784 if (fabs(z - zAxis[i]) < dist) {
1785 dist = fabs(z - zAxis[i]);
1786 iNode = i;
1787 }
1788 }
1789 // Set the summing range.
1790 iZ0 = iNode;
1791 iZ1 = iNode;
1792 // Establish the shape functions.
1793 fZ = {1., 0., 0., 0.};
1794 } else if (iOrder == 1 || nz == 2) {
1795 // First order interpolation in z.
1796 // Find the grid segment containing this point.
1797 int iGrid = 0;
1798 for (int i = 1; i < nz; i++) {
1799 if ((zAxis[i - 1] - z) * (z - zAxis[i]) >= 0.) {
1800 iGrid = i;
1801 }
1802 }
1803 const double z0 = zAxis[iGrid - 1];
1804 const double z1 = zAxis[iGrid];
1805 // Ensure there won't be divisions by zero.
1806 if (z1 == z0) {
1807 std::cerr << "Boxin3: Incorrect grid; no interpolation.\n";
1808 return false;
1809 }
1810 // Compute local coordinates.
1811 const double zL = (z - z0) / (z1 - z0);
1812 // Set the summing range.
1813 iZ0 = iGrid - 1;
1814 iZ1 = iGrid;
1815 // Set the shape functions.
1816 fZ = {1. - zL, zL, 0., 0.};
1817 } else if (iOrder == 2) {
1818 // Second order interpolation in z.
1819 // Find the grid segment containing this point.
1820 int iGrid = 0;
1821 for (int i = 1; i < nz; i++) {
1822 if ((zAxis[i - 1] - z) * (z - zAxis[i]) >= 0.) {
1823 iGrid = i;
1824 }
1825 }
1826 if (iGrid == 1) {
1827 iZ0 = iGrid - 1;
1828 iZ1 = iGrid + 1;
1829 const double z0 = zAxis[iZ0];
1830 const double z1 = zAxis[iZ0 + 1];
1831 const double z2 = zAxis[iZ0 + 2];
1832 if (z0 == z1 || z0 == z2 || z1 == z2) {
1833 std::cerr << "Boxin3: One or more grid points in z coincide.\n"
1834 << " No interpolation.\n";
1835 return false;
1836 }
1837 fZ[0] = (z - z1) * (z - z2) / ((z0 - z1) * (z0 - z2));
1838 fZ[1] = (z - z0) * (z - z2) / ((z1 - z0) * (z1 - z2));
1839 fZ[2] = (z - z0) * (z - z1) / ((z2 - z0) * (z2 - z1));
1840 } else if (iGrid == nz - 1) {
1841 iZ0 = iGrid - 2;
1842 iZ1 = iGrid;
1843 const double z0 = zAxis[iZ0];
1844 const double z1 = zAxis[iZ0 + 1];
1845 const double z2 = zAxis[iZ0 + 2];
1846 if (z0 == z1 || z0 == z2 || z1 == z2) {
1847 std::cerr << "Boxin3: One or more grid points in z coincide.\n"
1848 << " No interpolation.\n";
1849 return false;
1850 }
1851 fZ[0] = (z - z1) * (z - z2) / ((z0 - z1) * (z0 - z2));
1852 fZ[1] = (z - z0) * (z - z2) / ((z1 - z0) * (z1 - z2));
1853 fZ[2] = (z - z0) * (z - z1) / ((z2 - z0) * (z2 - z1));
1854 } else {
1855 iZ0 = iGrid - 2;
1856 iZ1 = iGrid + 1;
1857 const double z0 = zAxis[iZ0];
1858 const double z1 = zAxis[iZ0 + 1];
1859 const double z2 = zAxis[iZ0 + 2];
1860 const double z3 = zAxis[iZ0 + 3];
1861 if (z0 == z1 || z0 == z2 || z0 == z3 ||
1862 z1 == z2 || z1 == z3 || z2 == z3) {
1863 std::cerr << "Boxin3: One or more grid points in z coincide.\n"
1864 << " No interpolation.\n";
1865 return false;
1866 }
1867 // Compute the local coordinate for this grid segment.
1868 const double zL = (z - z1) / (z2 - z1);
1869 fZ[0] = ((z - z1) * (z - z2) / ((z0 - z1) * (z0 - z2))) * (1. - zL);
1870 fZ[1] = ((z - z0) * (z - z2) / ((z1 - z0) * (z1 - z2))) * (1. - zL) +
1871 ((z - z2) * (z - z3) / ((z1 - z2) * (z1 - z3))) * zL;
1872 fZ[2] = ((z - z0) * (z - z1) / ((z2 - z0) * (z2 - z1))) * (1. - zL) +
1873 ((z - z1) * (z - z3) / ((z2 - z1) * (z2 - z3))) * zL;
1874 fZ[3] = ((z - z1) * (z - z2) / ((z3 - z1) * (z3 - z2))) * zL;
1875 }
1876 }
1877
1878 for (int i = iX0; i <= iX1; ++i) {
1879 for (int j = iY0; j <= iY1; ++j) {
1880 for (int k = iZ0; k <= iZ1; ++k) {
1881 f += value[i][j][k] * fX[i - iX0] * fY[j - iY0] * fZ[k - iZ0];
1882 }
1883 }
1884 }
1885 return true;
1886}

Referenced by Garfield::Medium::Interpolate().

◆ Divdif()

double Garfield::Numerics::Divdif ( const std::vector< double > & f,
const std::vector< double > & a,
int nn,
double x,
int mm )

C++ version of DIVDIF (CERN program library E105) which performs tabular interpolation using symmetrically placed argument points.

Definition at line 1255 of file Numerics.cc.

1256 {
1257
1258 double t[20], d[20];
1259
1260 // Check the arguments.
1261 if (nn < 2) {
1262 std::cerr << "Divdif: Array length < 2.\n";
1263 return 0.;
1264 }
1265 if (mm < 1) {
1266 std::cerr << "Divdif: Interpolation order < 1.\n";
1267 return 0.;
1268 }
1269
1270 // Deal with the case that X is located at first or last point.
1271 const double tol1 = 1.e-6 * fabs(fabs(a[1]) - fabs(a[0]));
1272 const double tol2 = 1.e-6 * fabs(fabs(a[nn-1]) - fabs(a[nn-2]));
1273 if (fabs(x - a[0]) < tol1) return f[0];
1274 if (fabs(x - a[nn - 1]) < tol2) return f[nn - 1];
1275
1276 // Find subscript IX of X in array A.
1277 constexpr int mmax = 10;
1278 const int m = std::min({mm, mmax, nn - 1});
1279 const int mplus = m + 1;
1280 int ix = 0;
1281 int iy = nn + 1;
1282 if (a[0] > a[nn - 1]) {
1283 // Search decreasing arguments.
1284 do {
1285 const int mid = (ix + iy) / 2;
1286 if (x > a[mid - 1]) {
1287 iy = mid;
1288 } else {
1289 ix = mid;
1290 }
1291 } while (iy - ix > 1);
1292 } else {
1293 // Search increasing arguments.
1294 do {
1295 const int mid = (ix + iy) / 2;
1296 if (x < a[mid - 1]) {
1297 iy = mid;
1298 } else {
1299 ix = mid;
1300 }
1301 } while (iy - ix > 1);
1302 }
1303 // Copy reordered interpolation points into (T[I],D[I]), setting
1304 // EXTRA to True if M+2 points to be used.
1305 int npts = m + 2 - (m % 2);
1306 int ip = 0;
1307 int l = 0;
1308 do {
1309 const int isub = ix + l;
1310 if ((1 > isub) || (isub > nn)) {
1311 // Skip point.
1312 npts = mplus;
1313 } else {
1314 // Insert point.
1315 ip++;
1316 t[ip - 1] = a[isub - 1];
1317 d[ip - 1] = f[isub - 1];
1318 }
1319 if (ip < npts) {
1320 l = -l;
1321 if (l >= 0) ++l;
1322 }
1323 } while (ip < npts);
1324
1325 const bool extra = npts != mplus;
1326 // Replace d by the leading diagonal of a divided-difference table,
1327 // supplemented by an extra line if EXTRA is True.
1328 for (l = 1; l <= m; l++) {
1329 if (extra) {
1330 const int isub = mplus - l;
1331 d[m + 1] = (d[m + 1] - d[m - 1]) / (t[m + 1] - t[isub - 1]);
1332 }
1333 int i = mplus;
1334 for (int j = l; j <= m; j++) {
1335 const int isub = i - l;
1336 d[i - 1] = (d[i - 1] - d[i - 2]) / (t[i - 1] - t[isub - 1]);
1337 i--;
1338 }
1339 }
1340 // Evaluate the Newton interpolation formula at X, averaging two values
1341 // of last difference if EXTRA is True.
1342 double sum = d[mplus - 1];
1343 if (extra) {
1344 sum = 0.5 * (sum + d[m + 1]);
1345 }
1346 int j = m;
1347 for (l = 1; l <= m; l++) {
1348 sum = d[j - 1] + (x - t[j - 1]) * sum;
1349 j--;
1350 }
1351 return sum;
1352}

Referenced by Garfield::Sensor::ComputeThresholdCrossings(), and Garfield::Medium::Interpolate1D().

◆ GaussLegendreNodes2()

std::array< double, 2 > Garfield::Numerics::GaussLegendreNodes2 ( )
constexpr

Definition at line 16 of file Numerics.hh.

16 {
17 return {-0.577350269189625765, 0.577350269189625765};
18}

◆ GaussLegendreNodes3()

std::array< double, 3 > Garfield::Numerics::GaussLegendreNodes3 ( )
constexpr

Definition at line 22 of file Numerics.hh.

22 {
23 return {-0.774596669241483377, 0., 0.774596669241483377};
24}

◆ GaussLegendreNodes4()

std::array< double, 4 > Garfield::Numerics::GaussLegendreNodes4 ( )
constexpr

Definition at line 28 of file Numerics.hh.

28 {
29 return {-0.861136311594052575, -0.339981043584856265,
30 0.339981043584856265, 0.861136311594052575};
31}

◆ GaussLegendreNodes5()

std::array< double, 5 > Garfield::Numerics::GaussLegendreNodes5 ( )
constexpr

Definition at line 36 of file Numerics.hh.

36 {
37 return {-0.906179845938663993, -0.538469310105683091, 0.,
38 0.538469310105683091, 0.906179845938663993};
39}

◆ GaussLegendreNodes6()

std::array< double, 6 > Garfield::Numerics::GaussLegendreNodes6 ( )
constexpr

Definition at line 44 of file Numerics.hh.

44 {
45 return {-0.932469514203152028, -0.661209386466264514,
46 -0.238619186083196909, 0.238619186083196909,
47 0.661209386466264514, 0.932469514203152028};
48}

Referenced by Garfield::Sensor::AddSignal(), Garfield::Sensor::AddSignal(), Garfield::Component::IntegrateFluxCircle(), Garfield::Component::IntegrateFluxLine(), and Garfield::Component::IntegrateFluxSphere().

◆ GaussLegendreWeights2()

std::array< double, 2 > Garfield::Numerics::GaussLegendreWeights2 ( )
constexpr

Definition at line 19 of file Numerics.hh.

19 {
20 return {1., 1.};
21}

◆ GaussLegendreWeights3()

std::array< double, 3 > Garfield::Numerics::GaussLegendreWeights3 ( )
constexpr

Definition at line 25 of file Numerics.hh.

25 {
26 return {5. / 9., 8. / 9., 5. / 9.};
27}

◆ GaussLegendreWeights4()

std::array< double, 4 > Garfield::Numerics::GaussLegendreWeights4 ( )
constexpr

Definition at line 32 of file Numerics.hh.

32 {
33 return {0.347854845137453857, 0.652145154862546143,
34 0.652145154862546143, 0.347854845137453857};
35}

◆ GaussLegendreWeights5()

std::array< double, 5 > Garfield::Numerics::GaussLegendreWeights5 ( )
constexpr

Definition at line 40 of file Numerics.hh.

40 {
41 return {0.236926885056189088, 0.478628670499366468, 128. / 225.,
42 0.478628670499366468, 0.236926885056189088};
43}

◆ GaussLegendreWeights6()

std::array< double, 6 > Garfield::Numerics::GaussLegendreWeights6 ( )
constexpr

Definition at line 49 of file Numerics.hh.

49 {
50 return {0.171324492379170345, 0.360761573048138608,
51 0.467913934572691047, 0.467913934572691047,
52 0.360761573048138608, 0.171324492379170345};
53}

Referenced by Garfield::Sensor::AddSignal(), Garfield::Sensor::AddSignal(), Garfield::Component::IntegrateFluxCircle(), Garfield::Component::IntegrateFluxLine(), and Garfield::Component::IntegrateFluxSphere().

◆ LeastSquaresFit()

bool Garfield::Numerics::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 at line 1888 of file Numerics.cc.

1894 {
1895
1896 //-----------------------------------------------------------------------
1897 // LSQFIT - Subroutine fitting the parameters A in the routine F to
1898 // the data points (X,Y) using a least squares method.
1899 // Translated from an Algol routine written by Geert Jan van
1900 // Oldenborgh and Rob Veenhof, based on Stoer + Bulirsch.
1901 // VARIABLES : F( . ,A,VAL) : Subroutine to be fitted.
1902 // (X,Y) : Input data.
1903 // D : Derivative matrix.
1904 // R : Difference vector between Y and F(X,A).
1905 // S : Correction vector for A.
1906 // EPSDIF : Used for differentiating.
1907 // EPS : Numerical resolution.
1908 // (Last updated on 23/ 5/11.)
1909 //-----------------------------------------------------------------------
1910
1911 const unsigned int n = par.size();
1912 const unsigned int m = x.size();
1913 // Make sure that the # degrees of freedom < the number of data points.
1914 if (n > m) {
1915 std::cerr << "LeastSquaresFit: Number of parameters to be varied\n"
1916 << " exceeds the number of data points.\n";
1917 return false;
1918 }
1919
1920 // Check the errors.
1921 if (*std::min_element(ey.cbegin(), ey.cend()) <= 0.) {
1922 std::cerr << "LeastSquaresFit: Not all errors are > 0; no fit done.\n";
1923 return false;
1924 }
1925 chi2 = 0.;
1926 // Largest difference.
1927 double diffc = -1.;
1928 // Initialise the difference vector R.
1929 std::vector<double> r(m, 0.);
1930 for (unsigned int i = 0; i < m; ++i) {
1931 // Compute initial residuals.
1932 r[i] = (y[i] - f(x[i], par)) / ey[i];
1933 // Compute initial maximum difference.
1934 diffc = std::max(std::abs(r[i]), diffc);
1935 // And compute initial chi2.
1936 chi2 += r[i] * r[i];
1937 }
1938 if (debug) {
1939 std::cout << " Input data points:\n"
1940 << " X Y Y - F(X)\n";
1941 for (unsigned int i = 0; i < m; ++i) {
1942 std::printf(" %9u %15.8e %15.8e %15.8e\n", i, x[i], y[i], r[i]);
1943 }
1944 std::cout << " Initial values of the fit parameters:\n"
1945 << " Parameter Value\n";
1946 for (unsigned int i = 0; i < n; ++i) {
1947 std::printf(" %9u %15.8e\n", i, par[i]);
1948 }
1949 }
1950 if (verbose) {
1951 std::cout << " MINIMISATION SUMMARY\n"
1952 << " Initial situation:\n";
1953 std::printf(" Largest difference between fit and target: %15.8e\n",
1954 diffc);
1955 std::printf(" Sum of squares of these differences: %15.8e\n",
1956 chi2);
1957 }
1958 // Start optimising loop.
1959 bool converged = false;
1960 double chi2L = 0.;
1961 for (unsigned int iter = 1; iter <= nMaxIter; ++iter) {
1962 // Check the stopping criteria: (1) max norm, (2) change in chi-squared.
1963 if ((diffc < diff) || (iter > 1 && std::abs(chi2L - chi2) < eps * chi2)) {
1964 if (debug || verbose) {
1965 if (diffc < diff) {
1966 std::cout << " The maximum difference stopping criterion "
1967 << "is satisfied.\n";
1968 } else {
1969 std::cout << " The relative change in chi-squared has dropped "
1970 << "below the threshold.\n";
1971 }
1972 }
1973 converged = true;
1974 break;
1975 }
1976 // Calculate the derivative matrix.
1977 std::vector<std::vector<double> > d(n, std::vector<double>(m, 0.));
1978 for (unsigned int i = 0; i < n; ++i) {
1979 const double epsdif = eps * (1. + std::abs(par[i]));
1980 par[i] += 0.5 * epsdif;
1981 for (unsigned int j = 0; j < m; ++j) d[i][j] = f(x[j], par);
1982 par[i] -= epsdif;
1983 for (unsigned int j = 0; j < m; ++j) {
1984 d[i][j] = (d[i][j] - f(x[j], par)) / (epsdif * ey[j]);
1985 }
1986 par[i] += 0.5 * epsdif;
1987 }
1988 // Invert the matrix in Householder style.
1989 std::vector<double> colsum(n, 0.);
1990 std::vector<int> pivot(n, 0);
1991 for (unsigned int i = 0; i < n; ++i) {
1992 colsum[i] = std::inner_product(d[i].cbegin(), d[i].cend(),
1993 d[i].cbegin(), 0.);
1994 pivot[i] = i;
1995 }
1996 // Decomposition.
1997 std::vector<double> alpha(n, 0.);
1998 bool singular = false;
1999 for (unsigned int k = 0; k < n; ++k) {
2000 double sigma = colsum[k];
2001 unsigned int jbar = k;
2002 for (unsigned int j = k + 1; j < n; ++j) {
2003 if (sigma < colsum[j]) {
2004 sigma = colsum[j];
2005 jbar = j;
2006 }
2007 }
2008 if (jbar != k) {
2009 // Interchange columns.
2010 std::swap(pivot[k], pivot[jbar]);
2011 std::swap(colsum[k], colsum[jbar]);
2012 std::swap(d[k], d[jbar]);
2013 }
2014 sigma = 0.;
2015 for (unsigned int i = k; i < m; ++i) sigma += d[k][i] * d[k][i];
2016 if (sigma == 0. || sqrt(sigma) < 1.e-8 * std::abs(d[k][k])) {
2017 singular = true;
2018 break;
2019 }
2020 alpha[k] = d[k][k] < 0. ? sqrt(sigma) : -sqrt(sigma);
2021 const double beta = 1. / (sigma - d[k][k] * alpha[k]);
2022 d[k][k] -= alpha[k];
2023 std::vector<double> b(n, 0.);
2024 for (unsigned int j = k + 1; j < n; ++j) {
2025 for (unsigned int i = k; i < n; ++i) b[j] += d[k][i] * d[j][i];
2026 b[j] *= beta;
2027 }
2028 for (unsigned int j = k + 1; j < n; ++j) {
2029 for (unsigned int i = k; i < m; ++i) {
2030 d[j][i] -= d[k][i] * b[j];
2031 colsum[j] -= d[j][k] * d[j][k];
2032 }
2033 }
2034 }
2035 if (singular) {
2036 std::cerr << "LeastSquaresFit: Householder matrix (nearly) singular;\n"
2037 << " no further optimisation.\n"
2038 << " Ensure the function depends on the parameters\n"
2039 << " and try to supply reasonable starting values.\n";
2040 break;
2041 }
2042 // Solve.
2043 for (unsigned int j = 0; j < n; ++j) {
2044 double gamma = 0.;
2045 for (unsigned int i = j; i < m; ++i) gamma += d[j][i] * r[i];
2046 gamma *= 1. / (alpha[j] * d[j][j]);
2047 for (unsigned int i = j; i < m; ++i) r[i] += gamma * d[j][i];
2048 }
2049 std::vector<double> z(n, 0.);
2050 z[n - 1] = r[n - 1] / alpha[n - 1];
2051 for (int i = n - 1; i >= 1; --i) {
2052 double sum = 0.;
2053 for (unsigned int j = i + 1; j <= n; ++j) {
2054 sum += d[j - 1][i - 1] * z[j - 1];
2055 }
2056 z[i - 1] = (r[i - 1] - sum) / alpha[i - 1];
2057 }
2058 // Correction vector.
2059 std::vector<double> s(n, 0.);
2060 for (unsigned int i = 0; i < n; ++i) s[pivot[i]] = z[i];
2061 // Generate some debugging output.
2062 if (debug) {
2063 std::cout << " Correction vector in iteration " << iter << ":\n";
2064 for (unsigned int i = 0; i < n; ++i) {
2065 std::printf(" %5u %15.8e\n", i, s[i]);
2066 }
2067 }
2068 // Add part of the correction vector to the estimate to improve chi2.
2069 chi2L = chi2;
2070 chi2 *= 2;
2071 for (unsigned int i = 0; i < n; ++i) par[i] += s[i] * 2;
2072 for (unsigned int i = 0; i <= 10; ++i) {
2073 if (chi2 <= chi2L) break;
2074 if (std::abs(chi2L - chi2) < eps * chi2) {
2075 if (debug) {
2076 std::cout << " Too little improvement; reduction loop halted.\n";
2077 }
2078 break;
2079 }
2080 chi2 = 0.;
2081 const double scale = 1. / pow(2, i);
2082 for (unsigned int j = 0; j < n; ++j) par[j] -= s[j] * scale;
2083 for (unsigned int j = 0; j < m; ++j) {
2084 r[j] = (y[j] - f(x[j], par)) / ey[j];
2085 chi2 += r[j] * r[j];
2086 }
2087 if (debug) {
2088 std::printf(" Reduction loop %3i: chi2 = %15.8e\n", i, chi2);
2089 }
2090 }
2091 // Calculate the max. norm.
2092 diffc = std::abs(r[0]);
2093 for (unsigned int i = 1; i < m; ++i) {
2094 diffc = std::max(std::abs(r[i]), diffc);
2095 }
2096 // Print some debugging output.
2097 if (debug) {
2098 std::cout << " Values of the fit parameters after iteration "
2099 << iter << "\n Parameter Value\n";
2100 for (unsigned int i = 0; i < n; ++i) {
2101 std::printf(" %9u %15.8e\n", i, par[i]);
2102 }
2103 std::printf(" for which chi2 = %15.8e and diff = %15.8e\n",
2104 chi2, diffc);
2105 } else if (verbose) {
2106 std::printf(" Step %3u: largest deviation = %15.8e, chi2 = %15.8e\n",
2107 iter, diffc, chi2);
2108 }
2109 }
2110 // End of fit, perform error calculation.
2111 if (!converged) {
2112 std::cerr << "LeastSquaresFit: Maximum number of iterations reached.\n";
2113 }
2114 // Calculate the derivative matrix for the final settings.
2115 std::vector<std::vector<double> > d(n, std::vector<double>(m, 0.));
2116 for (unsigned int i = 0; i < n; ++i) {
2117 const double epsdif = eps * (1. + std::abs(par[i]));
2118 par[i] += 0.5 * epsdif;
2119 for (unsigned int j = 0; j < m; ++j) d[i][j] = f(x[j], par);
2120 par[i] -= epsdif;
2121 for (unsigned int j = 0; j < m; ++j) {
2122 d[i][j] = (d[i][j] - f(x[j], par)) / (epsdif * ey[j]);
2123 }
2124 par[i] += 0.5 * epsdif;
2125 }
2126 // Calculate the error matrix.
2127 std::vector<std::vector<double> > cov(n, std::vector<double>(n, 0.));
2128 for (unsigned int i = 0; i < n; ++i) {
2129 for (unsigned int j = 0; j < n; ++j) {
2130 cov[i][j] = std::inner_product(d[i].cbegin(), d[i].cend(),
2131 d[j].cbegin(), 0.);
2132 }
2133 }
2134 // Compute the scaling factor for the errors.
2135 double scale = m > n ? chi2 / (m - n) : 1.;
2136 // Invert it to get the covariance matrix.
2137 epar.assign(n, 0.);
2138 if (Garfield::Numerics::CERNLIB::dinv(n, cov) != 0) {
2139 std::cerr << "LeastSquaresFit: Singular covariance matrix; "
2140 << "no error calculation.\n";
2141 } else {
2142 for (unsigned int i = 0; i < n; ++i) {
2143 for (unsigned int j = 0; j < n; ++j) cov[i][j] *= scale;
2144 epar[i] = sqrt(std::max(0., cov[i][i]));
2145 }
2146 }
2147 // Print results.
2148 if (debug) {
2149 std::cout << " Comparison between input and fit:\n"
2150 << " X Y F(X)\n";
2151 for (unsigned int i = 0; i < m; ++i) {
2152 const double fit = f(x[i], par);
2153 std::printf(" %5u %15.8e %15.8e %15.8e\n", i, x[i], y[i], fit);
2154 }
2155 }
2156 if (verbose) {
2157 std::cout << " Final values of the fit parameters:\n"
2158 << " Parameter Value Error\n";
2159 for (unsigned int i = 0; i < n; ++i) {
2160 std::printf(" %9u %15.8e %15.8e\n", i, par[i], epar[i]);
2161 }
2162 std::cout << " The errors have been scaled by a factor of "
2163 << sqrt(scale) << ".\n";
2164 std::cout << " Covariance matrix:\n";
2165 for (unsigned int i = 0; i < n; ++i) {
2166 for (unsigned int j = 0; j < n; ++j) {
2167 std::printf(" %15.8e", cov[i][j]);
2168 }
2169 std::cout << "\n";
2170 }
2171 std::cout << " Correlation matrix:\n";
2172 for (unsigned int i = 0; i < n; ++i) {
2173 for (unsigned int j = 0; j < n; ++j) {
2174 double cor = 0.;
2175 if (cov[i][i] > 0. && cov[j][j] > 0.) {
2176 cor = cov[i][j] / sqrt(cov[i][i] * cov[j][j]);
2177 }
2178 std::printf(" %15.8e", cor);
2179 }
2180 std::cout << "\n";
2181 }
2182 std::cout << " Minimisation finished.\n";
2183 }
2184 return converged;
2185}
int dinv(const int n, std::vector< std::vector< double > > &a)
Replace square matrix A by its inverse.
Definition Numerics.cc:788
DoubleAc pow(const DoubleAc &f, double p)
Definition DoubleAc.cpp:337
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314

Referenced by Garfield::ComponentAnalyticField::MultipoleMoments(), Garfield::ComponentAnalyticField::OptimiseOnGrid(), Garfield::ComponentAnalyticField::OptimiseOnTrack(), and Garfield::ComponentAnalyticField::OptimiseOnWires().

◆ Legendre()

double Garfield::Numerics::Legendre ( const unsigned int n,
const double x )
inline

Legendre polynomials.

Definition at line 162 of file Numerics.hh.

162 {
163
164 if (std::abs(x) > 1.) return 0.;
165 double p0 = 1.;
166 double p1 = x;
167 if (n == 0) return p0;
168 if (n == 1) return p1;
169 for (unsigned int k = 1; k < n; ++k) {
170 p0 = ((2 * k + 1) * x * p1 - k * p0) / (k + 1);
171 std::swap(p0, p1);
172 }
173 return p1;
174}

Referenced by Garfield::ComponentAnalyticField::MultipoleMoments().