Commit 20f137dc581f961c97d53189ee68e321048c8234

Authored by Jürgen Knödlseder
1 parent 19518ba9

Add GSkyModel flux_error() and eflux_error() methods (#3074)

ChangeLog
1   -2020-11-07
  1 +2020-11-08
2 2  
3 3 * Version 2.0.0 released
4 4 ========================
... ... @@ -14,6 +14,7 @@
14 14 Implement vectorised response computation (#3203)
15 15 Implement analytical gradients for the radial Gaussian model in a
16 16 stacked analysis (#3203)
  17 + Add GSkyModel flux_error() and eflux_error() methods (#3074)
17 18 Implement pre-computation cache in GModelSpatialGauss (#1300)
18 19  
19 20  
... ...
1 1 New Features and Important Changes in GammaLib 2.0.0
2 2  
3   -7 November 2020
  3 +8 November 2020
4 4  
5 5  
6 6 1. Introduction
... ... @@ -33,6 +33,10 @@ The following methods have been added:
33 33 - GModelSky::flux(GSkyRegion&, GEnergy&, GEnergy&)
34 34 - GModelSky::eflux(GEnergy&, GEnergy&)
35 35 - GModelSky::eflux(GSkyRegion&, GEnergy&, GEnergy&)
  36 +- GModelSky::flux_error(GEnergy&, GEnergy&)
  37 +- GModelSky::flux_error(GSkyRegion&, GEnergy&, GEnergy&)
  38 +- GModelSky::eflux_error(GEnergy&, GEnergy&)
  39 +- GModelSky::eflux_error(GSkyRegion&, GEnergy&, GEnergy&)
36 40 - GModelSpatialRadial::is_energy_dependent()
37 41 - GModelSpatialRadial::is_time_dependent()
38 42 - GOptimizerLM::npars()
... ... @@ -89,6 +93,9 @@ Implemented GMatrixSparse::row() and GMatrixSparse::add_to_row() methods.
89 93  
90 94 8. Model module
91 95 ---------------
  96 +Added methods to GSkyModel to compute the photon flux and energy flux as
  97 +well as their errors (#3439, #3074).
  98 +
92 99 Added analytical gradients to the GModelSpatialRadialGauss class (#3203).
93 100  
94 101 Added is_energy_dependent() and is_time_dependent() methods to GModelSpatialRadial
... ...
README.md
1 1 GammaLib information
2 2 ====================
3   -* Version: 2.0.0.dev (7 November 2020)
  3 +* Version: 2.0.0.dev (8 November 2020)
4 4  
5 5 [![Build Status](https://cta-jenkins.irap.omp.eu/buildStatus/icon?job=gammalib-integrate-os)](https://cta-jenkins.irap.omp.eu/job/gammalib-integrate-os/)
6 6  
... ...
doc/source/admin/release_history/2.0.rst
1 1 .. _2.0:
2 2  
3   -GammaLib 2.0 release (7 November 2020)
  3 +GammaLib 2.0 release (8 November 2020)
4 4 ======================================
5 5  
6 6 Overview
... ... @@ -34,6 +34,8 @@ Improvements
34 34 * [`3203 <https://cta-redmine.irap.omp.eu/issues/3203>`_] -
35 35 Implement vectorised response computation and analytical gradients for
36 36 the radial Gaussian model in a stacked analysis
  37 +* [`3074 <https://cta-redmine.irap.omp.eu/issues/3074>`_] -
  38 + Add ``GSkyModel`` ``flux_error()`` and ``eflux_error()`` methods
37 39 * [`1300 <https://cta-redmine.irap.omp.eu/issues/1300>`_] -
38 40 Implement pre-computation cache in GModelSpatialGauss
39 41  
... ...
include/GModelSky.hpp
... ... @@ -183,6 +183,16 @@ public:
183 183 double eflux(const GSkyRegion& region,
184 184 const GEnergy& emin,
185 185 const GEnergy& emax) const;
  186 + double flux_error(const GEnergy& emin,
  187 + const GEnergy& emax) const;
  188 + double flux_error(const GSkyRegion& region,
  189 + const GEnergy& emin,
  190 + const GEnergy& emax) const;
  191 + double eflux_error(const GEnergy& emin,
  192 + const GEnergy& emax) const;
  193 + double eflux_error(const GSkyRegion& region,
  194 + const GEnergy& emin,
  195 + const GEnergy& emax) const;
186 196  
187 197 protected:
188 198 // Protected methods
... ...
pyext/GModelSky.i
... ... @@ -95,6 +95,16 @@ public:
95 95 double eflux(const GSkyRegion& region,
96 96 const GEnergy& emin,
97 97 const GEnergy& emax) const;
  98 + double flux_error(const GEnergy& emin,
  99 + const GEnergy& emax) const;
  100 + double flux_error(const GSkyRegion& region,
  101 + const GEnergy& emin,
  102 + const GEnergy& emax) const;
  103 + double eflux_error(const GEnergy& emin,
  104 + const GEnergy& emax) const;
  105 + double eflux_error(const GSkyRegion& region,
  106 + const GEnergy& emin,
  107 + const GEnergy& emax) const;
98 108 };
99 109  
100 110  
... ...
src/model/GModelSky.cpp
... ... @@ -1048,15 +1048,15 @@ GPhotons GModelSky::mc(const double&amp; area,
1048 1048  
1049 1049  
1050 1050 /***********************************************************************//**
1051   - * @brief Return sky model flux
  1051 + * @brief Return sky model photon flux
1052 1052 *
1053 1053 * @param[in] emin Minimum energy.
1054 1054 * @param[in] emax Maximum energy.
1055   - * @return Sky model flux (cm\f$^{-2}\f$ s\f$^{-2}\f$)
  1055 + * @return Sky model photon flux (cm\f$^{-2}\f$ s\f$^{-1}\f$)
1056 1056 *
1057   - * Returns the spatially integrate flux in the sky model.
  1057 + * Returns the spatially integrate photon flux in the sky model.
1058 1058 *
1059   - * @todo Take spatial flux normalisation into account
  1059 + * @todo Take spatial photon flux normalisation into account
1060 1060 ***************************************************************************/
1061 1061 double GModelSky::flux(const GEnergy& emin, const GEnergy& emax) const
1062 1062 {
... ... @@ -1107,14 +1107,14 @@ double GModelSky::flux(const GEnergy&amp; emin, const GEnergy&amp; emax) const
1107 1107  
1108 1108  
1109 1109 /***********************************************************************//**
1110   - * @brief Return sky model flux within sky region
  1110 + * @brief Return sky model photon flux within sky region
1111 1111 *
1112 1112 * @param[in] region Sky region.
1113 1113 * @param[in] emin Minimum energy.
1114 1114 * @param[in] emax Maximum energy.
1115   - * @return Sky model flux (cm\f$^{-2}\f$ s\f$^{-2}\f$)
  1115 + * @return Sky model photon flux (cm\f$^{-2}\f$ s\f$^{-1}\f$)
1116 1116 *
1117   - * Returns the flux of the sky model within a given sky region.
  1117 + * Returns the photon flux of the sky model within a given sky region.
1118 1118 ***************************************************************************/
1119 1119 double GModelSky::flux(const GSkyRegion& region,
1120 1120 const GEnergy& emin,
... ... @@ -1176,7 +1176,7 @@ double GModelSky::flux(const GSkyRegion&amp; region,
1176 1176 *
1177 1177 * @param[in] emin Minimum energy.
1178 1178 * @param[in] emax Maximum energy.
1179   - * @return Sky model energy flux (erg cm\f$^{-2}\f$ s\f$^{-2}\f$)
  1179 + * @return Sky model energy flux (erg cm\f$^{-2}\f$ s\f$^{-1}\f$)
1180 1180 *
1181 1181 * Returns the spatially integrate energy flux in the sky model.
1182 1182 ***************************************************************************/
... ... @@ -1234,7 +1234,7 @@ double GModelSky::eflux(const GEnergy&amp; emin, const GEnergy&amp; emax) const
1234 1234 * @param[in] region Sky region.
1235 1235 * @param[in] emin Minimum energy.
1236 1236 * @param[in] emax Maximum energy.
1237   - * @return Sky model flux (cm\f$^{-2}\f$ s\f$^{-2}\f$)
  1237 + * @return Sky model energy flux (erg cm\f$^{-2}\f$ s\f$^{-1}\f$)
1238 1238 *
1239 1239 * Returns the energy flux of the sky model within a given sky region.
1240 1240 ***************************************************************************/
... ... @@ -1294,6 +1294,558 @@ double GModelSky::eflux(const GSkyRegion&amp; region,
1294 1294  
1295 1295  
1296 1296 /***********************************************************************//**
  1297 + * @brief Return sky model photon flux error
  1298 + *
  1299 + * @param[in] emin Minimum energy.
  1300 + * @param[in] emax Maximum energy.
  1301 + * @return Sky model photon flux error (cm\f$^{-2}\f$ s\f$^{-1}\f$)
  1302 + *
  1303 + * Returns the spatially integrate photon flux error in the sky model. The
  1304 + * photon flux error is computed using Gaussian error propagation
  1305 + *
  1306 + * \f[
  1307 + * \Delta F_{ph} = \sqrt{
  1308 + * \sum_i \left( \frac{\partial F_{ph}}{\partial p_i} \right)^2
  1309 + * \left( \Delta p_i \right)^2 }
  1310 + * \f]
  1311 + *
  1312 + * where
  1313 + * \f$F_{ph}\f$ is the sky model photon flux and
  1314 + * \f$p_i\f$ are the sky model parameters.
  1315 + ***************************************************************************/
  1316 +double GModelSky::flux_error(const GEnergy& emin, const GEnergy& emax) const
  1317 +{
  1318 + // Initialise flux error
  1319 + double flux_error = 0.0;
  1320 +
  1321 + // Loop over all model parameters
  1322 + for (int ipar = 0; ipar < size(); ++ipar) {
  1323 +
  1324 + // Get non-const model pointer
  1325 + GModelPar* par = const_cast<GModelPar*>(&(*this)[ipar]);
  1326 +
  1327 + // Consider only free parameters
  1328 + if (par->is_free()) {
  1329 +
  1330 + // Save current model parameter
  1331 + GModelPar current = *par;
  1332 +
  1333 + // Autoscale model parameter
  1334 + par->autoscale();
  1335 +
  1336 + // Get actual parameter value
  1337 + double x = par->factor_value();
  1338 +
  1339 + // Set fixed step size for computation of derivative.
  1340 + const double step_size = 0.0002;
  1341 + double h = step_size;
  1342 +
  1343 + // Re-adjust the step-size h in case that the initial step size is
  1344 + // larger than the allowed parameter range
  1345 + if (par->has_min() && par->has_max()) {
  1346 + double par_h = par->factor_max() - par->factor_min();
  1347 + if (par_h < h) {
  1348 + h = par_h;
  1349 + }
  1350 + }
  1351 +
  1352 + // Initialise derivative
  1353 + double derivative = 0.0;
  1354 +
  1355 + // Continue only if step size is positive
  1356 + if (h > 0.0) {
  1357 +
  1358 + // If we are too close to the minimum boundary use a right
  1359 + // sided difference ...
  1360 + if (par->has_min() && ((x-par->factor_min()) < h)) {
  1361 +
  1362 + // Compute fs1
  1363 + par->factor_value(x);
  1364 + double fs1 = flux(emin, emax);
  1365 +
  1366 + // Compute fs2
  1367 + par->factor_value(x+h);
  1368 + double fs2 = flux(emin, emax);
  1369 +
  1370 + // Compute derivative
  1371 + derivative = (fs1 - fs2) / h;
  1372 +
  1373 + }
  1374 +
  1375 + // ... otherwise if we are too close to the maximum boundary use
  1376 + // a left sided difference ...
  1377 + else if (par->has_max() && ((par->factor_max()-x) < h)) {
  1378 +
  1379 + // Compute fs1
  1380 + par->factor_value(x);
  1381 + double fs1 = flux(emin, emax);
  1382 +
  1383 + // Compute fs2
  1384 + par->factor_value(x-h);
  1385 + double fs2 = flux(emin, emax);
  1386 +
  1387 + // Compute derivative
  1388 + derivative = (fs1 - fs2) / h;
  1389 +
  1390 + }
  1391 +
  1392 + // ... otherwise use a symmetric difference
  1393 + else {
  1394 +
  1395 + // Compute fs1
  1396 + par->factor_value(x+h);
  1397 + double fs1 = flux(emin, emax);
  1398 +
  1399 + // Compute fs2
  1400 + par->factor_value(x-h);
  1401 + double fs2 = flux(emin, emax);
  1402 +
  1403 + // Compute derivative
  1404 + derivative = 0.5 * (fs1 - fs2) / h;
  1405 +
  1406 + }
  1407 +
  1408 + } // endif: step size was positive
  1409 +
  1410 + // Add contribution
  1411 + if (derivative > 0.0) {
  1412 + double factor = derivative * par->factor_error();
  1413 + flux_error += factor * factor;
  1414 + }
  1415 +
  1416 + // Restore current model parameter
  1417 + *par = current;
  1418 +
  1419 + } // endif: parameter was free
  1420 +
  1421 + } // endfor: looped over all parameters
  1422 +
  1423 + // Take square root if required
  1424 + if (flux_error > 0.0) {
  1425 + flux_error = std::sqrt(flux_error);
  1426 + }
  1427 +
  1428 + // Return flux error
  1429 + return flux_error;
  1430 +}
  1431 +
  1432 +
  1433 +/***********************************************************************//**
  1434 + * @brief Return sky model photon flux error within sky region
  1435 + *
  1436 + * @param[in] region Sky region.
  1437 + * @param[in] emin Minimum energy.
  1438 + * @param[in] emax Maximum energy.
  1439 + * @return Sky model photon flux error (cm\f$^{-2}\f$ s\f$^{-1}\f$)
  1440 + *
  1441 + * Returns the spatially integrate photon flux error in the sky model. The
  1442 + * photon flux error is computed using Gaussian error propagation
  1443 + *
  1444 + * \f[
  1445 + * \Delta F_{ph} = \sqrt{
  1446 + * \sum_i \left( \frac{\partial F_{ph}}{\partial p_i} \right)^2
  1447 + * \left( \Delta p_i \right)^2 }
  1448 + * \f]
  1449 + *
  1450 + * where
  1451 + * \f$F_{ph}\f$ is the sky model photon flux and
  1452 + * \f$p_i\f$ are the sky model parameters.
  1453 + ***************************************************************************/
  1454 +double GModelSky::flux_error(const GSkyRegion& region,
  1455 + const GEnergy& emin,
  1456 + const GEnergy& emax) const
  1457 +{
  1458 + // Initialise flux error
  1459 + double flux_error = 0.0;
  1460 +
  1461 + // Loop over all model parameters
  1462 + for (int ipar = 0; ipar < size(); ++ipar) {
  1463 +
  1464 + // Get non-const model pointer
  1465 + GModelPar* par = const_cast<GModelPar*>(&(*this)[ipar]);
  1466 +
  1467 + // Consider only free parameters
  1468 + if (par->is_free()) {
  1469 +
  1470 + // Save current model parameter
  1471 + GModelPar current = *par;
  1472 +
  1473 + // Autoscale model parameter
  1474 + par->autoscale();
  1475 +
  1476 + // Get actual parameter value
  1477 + double x = par->factor_value();
  1478 +
  1479 + // Set fixed step size for computation of derivative.
  1480 + const double step_size = 0.0002;
  1481 + double h = step_size;
  1482 +
  1483 + // Re-adjust the step-size h in case that the initial step size is
  1484 + // larger than the allowed parameter range
  1485 + if (par->has_min() && par->has_max()) {
  1486 + double par_h = par->factor_max() - par->factor_min();
  1487 + if (par_h < h) {
  1488 + h = par_h;
  1489 + }
  1490 + }
  1491 +
  1492 + // Initialise derivative
  1493 + double derivative = 0.0;
  1494 +
  1495 + // Continue only if step size is positive
  1496 + if (h > 0.0) {
  1497 +
  1498 + // If we are too close to the minimum boundary use a right
  1499 + // sided difference ...
  1500 + if (par->has_min() && ((x-par->factor_min()) < h)) {
  1501 +
  1502 + // Compute fs1
  1503 + par->factor_value(x);
  1504 + double fs1 = flux(region, emin, emax);
  1505 +
  1506 + // Compute fs2
  1507 + par->factor_value(x+h);
  1508 + double fs2 = flux(region, emin, emax);
  1509 +
  1510 + // Compute derivative
  1511 + derivative = (fs1 - fs2) / h;
  1512 +
  1513 + }
  1514 +
  1515 + // ... otherwise if we are too close to the maximum boundary use
  1516 + // a left sided difference ...
  1517 + else if (par->has_max() && ((par->factor_max()-x) < h)) {
  1518 +
  1519 + // Compute fs1
  1520 + par->factor_value(x);
  1521 + double fs1 = flux(region, emin, emax);
  1522 +
  1523 + // Compute fs2
  1524 + par->factor_value(x-h);
  1525 + double fs2 = flux(region, emin, emax);
  1526 +
  1527 + // Compute derivative
  1528 + derivative = (fs1 - fs2) / h;
  1529 +
  1530 + }
  1531 +
  1532 + // ... otherwise use a symmetric difference
  1533 + else {
  1534 +
  1535 + // Compute fs1
  1536 + par->factor_value(x+h);
  1537 + double fs1 = flux(region, emin, emax);
  1538 +
  1539 + // Compute fs2
  1540 + par->factor_value(x-h);
  1541 + double fs2 = flux(region, emin, emax);
  1542 +
  1543 + // Compute derivative
  1544 + derivative = 0.5 * (fs1 - fs2) / h;
  1545 +
  1546 + }
  1547 +
  1548 + } // endif: step size was positive
  1549 +
  1550 + // Add contribution
  1551 + if (derivative > 0.0) {
  1552 + double factor = derivative * par->factor_error();
  1553 + flux_error += factor * factor;
  1554 + }
  1555 +
  1556 + // Restore current model parameter
  1557 + *par = current;
  1558 +
  1559 + } // endif: parameter was free
  1560 +
  1561 + } // endfor: looped over all parameters
  1562 +
  1563 + // Take square root if required
  1564 + if (flux_error > 0.0) {
  1565 + flux_error = std::sqrt(flux_error);
  1566 + }
  1567 + // Return flux error
  1568 + return flux_error;
  1569 +}
  1570 +
  1571 +
  1572 +/***********************************************************************//**
  1573 + * @brief Return sky model energy flux error
  1574 + *
  1575 + * @param[in] emin Minimum energy.
  1576 + * @param[in] emax Maximum energy.
  1577 + * @return Sky model energy flux error (erg cm\f$^{-2}\f$ s\f$^{-1}\f$)
  1578 + *
  1579 + * Returns the spatially integrate energy flux error in the sky model. The
  1580 + * energy flux error is computed using Gaussian error propagation
  1581 + *
  1582 + * \f[
  1583 + * \Delta F_E = \sqrt{
  1584 + * \sum_i \left( \frac{\partial F_E}{\partial p_i} \right)^2
  1585 + * \left( \Delta p_i \right)^2 }
  1586 + * \f]
  1587 + *
  1588 + * where
  1589 + * \f$F_E\f$ is the sky model energy flux and
  1590 + * \f$p_i\f$ are the sky model parameters.
  1591 + ***************************************************************************/
  1592 +double GModelSky::eflux_error(const GEnergy& emin, const GEnergy& emax) const
  1593 +{
  1594 + // Initialise flux error
  1595 + double eflux_error = 0.0;
  1596 +
  1597 + // Loop over all model parameters
  1598 + for (int ipar = 0; ipar < size(); ++ipar) {
  1599 +
  1600 + // Get non-const model pointer
  1601 + GModelPar* par = const_cast<GModelPar*>(&(*this)[ipar]);
  1602 +
  1603 + // Consider only free parameters
  1604 + if (par->is_free()) {
  1605 +
  1606 + // Save current model parameter
  1607 + GModelPar current = *par;
  1608 +
  1609 + // Autoscale model parameter
  1610 + par->autoscale();
  1611 +
  1612 + // Get actual parameter value
  1613 + double x = par->factor_value();
  1614 +
  1615 + // Set fixed step size for computation of derivative.
  1616 + const double step_size = 0.0002;
  1617 + double h = step_size;
  1618 +
  1619 + // Re-adjust the step-size h in case that the initial step size is
  1620 + // larger than the allowed parameter range
  1621 + if (par->has_min() && par->has_max()) {
  1622 + double par_h = par->factor_max() - par->factor_min();
  1623 + if (par_h < h) {
  1624 + h = par_h;
  1625 + }
  1626 + }
  1627 +
  1628 + // Initialise derivative
  1629 + double derivative = 0.0;
  1630 +
  1631 + // Continue only if step size is positive
  1632 + if (h > 0.0) {
  1633 +
  1634 + // If we are too close to the minimum boundary use a right
  1635 + // sided difference ...
  1636 + if (par->has_min() && ((x-par->factor_min()) < h)) {
  1637 +
  1638 + // Compute fs1
  1639 + par->factor_value(x);
  1640 + double fs1 = eflux(emin, emax);
  1641 +
  1642 + // Compute fs2
  1643 + par->factor_value(x+h);
  1644 + double fs2 = eflux(emin, emax);
  1645 +
  1646 + // Compute derivative
  1647 + derivative = (fs1 - fs2) / h;
  1648 +
  1649 + }
  1650 +
  1651 + // ... otherwise if we are too close to the maximum boundary use
  1652 + // a left sided difference ...
  1653 + else if (par->has_max() && ((par->factor_max()-x) < h)) {
  1654 +
  1655 + // Compute fs1
  1656 + par->factor_value(x);
  1657 + double fs1 = eflux(emin, emax);
  1658 +
  1659 + // Compute fs2
  1660 + par->factor_value(x-h);
  1661 + double fs2 = eflux(emin, emax);
  1662 +
  1663 + // Compute derivative
  1664 + derivative = (fs1 - fs2) / h;
  1665 +
  1666 + }
  1667 +
  1668 + // ... otherwise use a symmetric difference
  1669 + else {
  1670 +
  1671 + // Compute fs1
  1672 + par->factor_value(x+h);
  1673 + double fs1 = eflux(emin, emax);
  1674 +
  1675 + // Compute fs2
  1676 + par->factor_value(x-h);
  1677 + double fs2 = eflux(emin, emax);
  1678 +
  1679 + // Compute derivative
  1680 + derivative = 0.5 * (fs1 - fs2) / h;
  1681 +
  1682 + }
  1683 +
  1684 + } // endif: step size was positive
  1685 +
  1686 + // Add contribution
  1687 + if (derivative > 0.0) {
  1688 + double factor = derivative * par->factor_error();
  1689 + eflux_error += factor * factor;
  1690 + }
  1691 +
  1692 + // Restore current model parameter
  1693 + *par = current;
  1694 +
  1695 + } // endif: parameter was free
  1696 +
  1697 + } // endfor: looped over all parameters
  1698 +
  1699 + // Take square root if required
  1700 + if (eflux_error > 0.0) {
  1701 + eflux_error = std::sqrt(eflux_error);
  1702 + }
  1703 +
  1704 + // Return flux error
  1705 + return eflux_error;
  1706 +}
  1707 +
  1708 +
  1709 +/***********************************************************************//**
  1710 + * @brief Return sky model energy flux error within sky region
  1711 + *
  1712 + * @param[in] region Sky region.
  1713 + * @param[in] emin Minimum energy.
  1714 + * @param[in] emax Maximum energy.
  1715 + * @return Sky model energy flux error (erg cm\f$^{-2}\f$ s\f$^{-1}\f$)
  1716 + *
  1717 + * Returns the spatially integrate energy flux error in the sky model. The
  1718 + * energy flux error is computed using Gaussian error propagation
  1719 + *
  1720 + * \f[
  1721 + * \Delta F_E = \sqrt{
  1722 + * \sum_i \left( \frac{\partial F_E}{\partial p_i} \right)^2
  1723 + * \left( \Delta p_i \right)^2 }
  1724 + * \f]
  1725 + *
  1726 + * where
  1727 + * \f$F_E\f$ is the sky model energy flux and
  1728 + * \f$p_i\f$ are the sky model parameters.
  1729 + ***************************************************************************/
  1730 +double GModelSky::eflux_error(const GSkyRegion& region,
  1731 + const GEnergy& emin,
  1732 + const GEnergy& emax) const
  1733 +{
  1734 + // Initialise flux error
  1735 + double eflux_error = 0.0;
  1736 +
  1737 + // Loop over all model parameters
  1738 + for (int ipar = 0; ipar < size(); ++ipar) {
  1739 +
  1740 + // Get non-const model pointer
  1741 + GModelPar* par = const_cast<GModelPar*>(&(*this)[ipar]);
  1742 +
  1743 + // Consider only free parameters
  1744 + if (par->is_free()) {
  1745 +
  1746 + // Save current model parameter
  1747 + GModelPar current = *par;
  1748 +
  1749 + // Autoscale model parameter
  1750 + par->autoscale();
  1751 +
  1752 + // Get actual parameter value
  1753 + double x = par->factor_value();
  1754 +
  1755 + // Set fixed step size for computation of derivative.
  1756 + const double step_size = 0.0002;
  1757 + double h = step_size;
  1758 +
  1759 + // Re-adjust the step-size h in case that the initial step size is
  1760 + // larger than the allowed parameter range
  1761 + if (par->has_min() && par->has_max()) {
  1762 + double par_h = par->factor_max() - par->factor_min();
  1763 + if (par_h < h) {
  1764 + h = par_h;
  1765 + }
  1766 + }
  1767 +
  1768 + // Initialise derivative
  1769 + double derivative = 0.0;
  1770 +
  1771 + // Continue only if step size is positive
  1772 + if (h > 0.0) {
  1773 +
  1774 + // If we are too close to the minimum boundary use a right
  1775 + // sided difference ...
  1776 + if (par->has_min() && ((x-par->factor_min()) < h)) {
  1777 +
  1778 + // Compute fs1
  1779 + par->factor_value(x);
  1780 + double fs1 = eflux(region, emin, emax);
  1781 +
  1782 + // Compute fs2
  1783 + par->factor_value(x+h);
  1784 + double fs2 = eflux(region, emin, emax);
  1785 +
  1786 + // Compute derivative
  1787 + derivative = (fs1 - fs2) / h;
  1788 +
  1789 + }
  1790 +
  1791 + // ... otherwise if we are too close to the maximum boundary use
  1792 + // a left sided difference ...
  1793 + else if (par->has_max() && ((par->factor_max()-x) < h)) {
  1794 +
  1795 + // Compute fs1
  1796 + par->factor_value(x);
  1797 + double fs1 = eflux(region, emin, emax);
  1798 +
  1799 + // Compute fs2
  1800 + par->factor_value(x-h);
  1801 + double fs2 = eflux(region, emin, emax);
  1802 +
  1803 + // Compute derivative
  1804 + derivative = (fs1 - fs2) / h;
  1805 +
  1806 + }
  1807 +
  1808 + // ... otherwise use a symmetric difference
  1809 + else {
  1810 +
  1811 + // Compute fs1
  1812 + par->factor_value(x+h);
  1813 + double fs1 = eflux(region, emin, emax);
  1814 +
  1815 + // Compute fs2
  1816 + par->factor_value(x-h);
  1817 + double fs2 = eflux(region, emin, emax);
  1818 +
  1819 + // Compute derivative
  1820 + derivative = 0.5 * (fs1 - fs2) / h;
  1821 +
  1822 + }
  1823 +
  1824 + } // endif: step size was positive
  1825 +
  1826 + // Add contribution
  1827 + if (derivative > 0.0) {
  1828 + double factor = derivative * par->factor_error();
  1829 + eflux_error += factor * factor;
  1830 + }
  1831 +
  1832 + // Restore current model parameter
  1833 + *par = current;
  1834 +
  1835 + } // endif: parameter was free
  1836 +
  1837 + } // endfor: looped over all parameters
  1838 +
  1839 + // Take square root if required
  1840 + if (eflux_error > 0.0) {
  1841 + eflux_error = std::sqrt(eflux_error);
  1842 + }
  1843 + // Return flux error
  1844 + return eflux_error;
  1845 +}
  1846 +
  1847 +
  1848 +/***********************************************************************//**
1297 1849 * @brief Print model information
1298 1850 *
1299 1851 * @param[in] chatter Chattiness.
... ...