Commit a276a1d31ef988c8a1bcd6603b4e81474fd7a8fa

Authored by Jürgen Knödlseder
1 parent abc02a45

Implement COMPTEL background model computation (#3557)

ChangeLog
... ... @@ -5,6 +5,7 @@
5 5  
6 6 Store DRB, DRG and DRX as GCOMDri in GCOMObservation (#3560)
7 7 Implement COMPTEL response for diffuse models (#3558)
  8 + Implement COMPTEL background model computation (#3557)
8 9 Fix reading of scaled FITS columns, remove GFitsTableCol::scale() method (#3553)
9 10 Add gammalib::compare() function (#3552)
10 11 Add DRI constructor to GCOMObservation class
... ...
... ... @@ -77,6 +77,7 @@ The following methods have been added:
77 77 - GCOMObservation::drgname()
78 78 - GCOMObservation::drxname()
79 79 - GCOMObservation::response(GCOMResponse&)
  80 +- GCOMObservation::compute_drb()
80 81 - GCOMDri::tof_correction()
81 82 - GCOMDri::num_superpackets()
82 83 - GCOMDri::num_used_superpackets()
... ... @@ -280,8 +281,8 @@ spatial model gradients (#3203).
280 281 Store DRB, DRG and DRX as instances of GCOMDri instead of GSkyMap in GCOMObservation
281 282 (#3560).
282 283  
283   -Remove internal response caching since caching is handled by the base class response
284   -computation methods (#2973).
  284 +Added GCOMObservation::compute_drb() method to compute background models for COMPTEL.
  285 +So far the PHINOR and BGDLIXA background methods were implemented (#3557).
285 286  
286 287 Added method to set COMPTEL response using GCOMResponse instance.
287 288  
... ... @@ -301,6 +302,9 @@ Added methods to access DRI filenames in GCOMObservation.
301 302  
302 303 Implemented vectorised response computation in GCOMResponse::irf_ptsrc() method (#3501).
303 304  
  305 +Remove internal response caching since caching is handled by the base class response
  306 +computation methods (#2973).
  307 +
304 308  
305 309 22. INTEGRAL/SPI interface
306 310 --------------------------
... ...
doc/source/admin/release_history/2.0.rst
... ... @@ -33,6 +33,8 @@ Improvements
33 33  
34 34 * [`3558 <https://cta-redmine.irap.omp.eu/issues/3558>`_] -
35 35 Implement COMPTEL response for diffuse models
  36 +* [`3557 <https://cta-redmine.irap.omp.eu/issues/3557>`_] -
  37 + Implement COMPTEL background model computation
36 38 * [`3552 <https://cta-redmine.irap.omp.eu/issues/3552>`_] -
37 39 Add ``gammalib::compare()`` function
38 40 * Add DRI constructor to ``GCOMObservation`` class
... ...
inst/com/include/GCOMObservation.hpp
... ... @@ -131,6 +131,12 @@ public:
131 131 void drbname(const GFilename& drbname);
132 132 void drgname(const GFilename& drgname);
133 133 void drxname(const GFilename& drxname);
  134 + void compute_drb(const std::string& method,
  135 + const GCOMDri& drm,
  136 + const int& nrunav = 3,
  137 + const int& navgr = 3,
  138 + const int& nincl = 13,
  139 + const int& nexcl = 0);
134 140  
135 141 protected:
136 142 // Protected methods
... ... @@ -144,6 +150,19 @@ protected:
144 150 bool check_dri(const GCOMDri& map) const;
145 151 void read_attributes(const GFitsHDU* hdu);
146 152 void write_attributes(GFitsHDU* hdu) const;
  153 + void compute_drb_phinor(const GCOMDri& drm);
  154 + void compute_drb_bgdlixa(const GCOMDri& drm,
  155 + const int& nrunav = 3,
  156 + const int& navgr = 3,
  157 + const int& nincl = 13,
  158 + const int& nexcl = 0);
  159 + void get_bgdlixa_phibar_indices(const int& iphibar,
  160 + const int& nincl,
  161 + const int& nexcl,
  162 + int* isel1,
  163 + int* iex1,
  164 + int* iex2,
  165 + int* isel2) const;
147 166  
148 167 // Protected members
149 168 std::string m_instrument; //!< Instrument name
... ...
inst/com/pyext/GCOMObservation.i
... ... @@ -99,6 +99,12 @@ public:
99 99 void drbname(const GFilename& drbname);
100 100 void drgname(const GFilename& drgname);
101 101 void drxname(const GFilename& drxname);
  102 + void compute_drb(const std::string& method,
  103 + const GCOMDri& drm,
  104 + const int& nrunav = 3,
  105 + const int& navgr = 3,
  106 + const int& nincl = 13,
  107 + const int& nexcl = 0);
102 108 };
103 109  
104 110  
... ...
inst/com/src/GCOMObservation.cpp
... ... @@ -57,7 +57,9 @@ const GObservationRegistry g_obs_com_registry(&amp;g_obs_com_seed);
57 57 #define G_LOAD_DRB "GCOMObservation::load_drb(GFilename&)"
58 58 #define G_LOAD_DRG "GCOMObservation::load_drg(GFilename&)"
59 59 #define G_DRM "GCOMObservation::drm(GModels&)"
60   -#define G_ADD_DRM "GCOMObservation::add_drm(GSkyModel&)"
  60 +#define G_COMPUTE_DRB "GCOMObservation::compute_drb(std::string&, GCOMDri&, "\
  61 + "int&, int&, int&, int&"
  62 +#define G_COMPUTE_DRB_PHINOR "GCOMObservation::compute_drb_phinor(GCOMDri&)"
61 63  
62 64 /* __ Macros _____________________________________________________________ */
63 65  
... ... @@ -731,6 +733,45 @@ GCOMDri GCOMObservation::drm(const GModels&amp; models) const
731 733  
732 734  
733 735 /***********************************************************************//**
  736 + * @brief Compute DRB cube
  737 + *
  738 + * @param[in] method Background method (PHINOR or BGDLIXA).
  739 + * @param[in] drm DRM cube.
  740 + * @param[in] nrunav BGDLIXA: number of bins used for running average.
  741 + * @param[in] navgr BGDLIXA: number of bins used for averaging.
  742 + * @param[in] nincl BGDLIXA: number of Phibar layers to include.
  743 + * @param[in] nexcl BGDLIXA: number of Phibar layers to exclude.
  744 + *
  745 + * Computes a COMPTEL DRB cube using either the PHINOR or BGDLIXA method.
  746 + * See the protected methods compute_drb_phinor() and compute_drb_bgdlixa()
  747 + * for more information.
  748 + ***************************************************************************/
  749 +void GCOMObservation::compute_drb(const std::string& method,
  750 + const GCOMDri& drm,
  751 + const int& nrunav,
  752 + const int& navgr,
  753 + const int& nincl,
  754 + const int& nexcl)
  755 +{
  756 + // Branch to relevant method based on specified background method
  757 + if (method == "PHINOR") {
  758 + compute_drb_phinor(drm);
  759 + }
  760 + else if (method == "BGDLIXA") {
  761 + compute_drb_bgdlixa(drm, nrunav, navgr, nincl, nexcl);
  762 + }
  763 + else {
  764 + std::string msg = "Unknown background method \""+method+"\". "
  765 + "Specify either \"PHINOR\" or \"BGDLIXA\".";
  766 + throw GException::invalid_argument(G_COMPUTE_DRB, msg);
  767 + }
  768 +
  769 + // Return
  770 + return;
  771 +}
  772 +
  773 +
  774 +/***********************************************************************//**
734 775 * @brief Print observation information
735 776 *
736 777 * @param[in] chatter Chattiness.
... ... @@ -1135,3 +1176,474 @@ void GCOMObservation::write_attributes(GFitsHDU* hdu) const
1135 1176 // Return
1136 1177 return;
1137 1178 }
  1179 +
  1180 +
  1181 +/***********************************************************************//**
  1182 + * @brief Compute DRB cube using PHINOR method
  1183 + *
  1184 + * @param[in] drm DRM cube.
  1185 + *
  1186 + * @exception GException::invalid_value
  1187 + * Observation does not contain an event cube
  1188 + * @exception GException::invalid_argument
  1189 + * DRM cube is incompatible with DRE
  1190 + ***************************************************************************/
  1191 +void GCOMObservation::compute_drb_phinor(const GCOMDri& drm)
  1192 +{
  1193 + // Extract COMPTEL event cube
  1194 + const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(this->events());
  1195 + if (cube == NULL) {
  1196 + std::string msg = "Observation \""+this->name()+"\" ("+this->id()+") "
  1197 + "does not contain a COMPTEL event cube. Please "
  1198 + "specify a COMPTEL observation containing and event "
  1199 + "cube.";
  1200 + throw GException::invalid_value(G_COMPUTE_DRB_PHINOR, msg);
  1201 + }
  1202 +
  1203 + // Check DRM
  1204 + if (!check_dri(drm)) {
  1205 + std::string msg = "Specified DRM cube is incompatible with DRE. Please "
  1206 + "specify a DRM with a data-space definition that is "
  1207 + "identical to that of the DRE.";
  1208 + throw GException::invalid_argument(G_COMPUTE_DRB_PHINOR, msg);
  1209 + }
  1210 +
  1211 + // Initialise DRB by cloning DRG
  1212 + m_drb = m_drg;
  1213 +
  1214 + // Get DRI sky maps
  1215 + const GSkyMap& map_dre = cube->dre().map();
  1216 + const GSkyMap& map_drm = drm.map();
  1217 + const GSkyMap& map_drg = m_drg.map();
  1218 + GSkyMap& map_drb = const_cast<GSkyMap&>(m_drb.map());
  1219 +
  1220 + // Get data space dimensions
  1221 + int npix = m_drg.map().npix();
  1222 + int nphibar = m_drg.nphibar();
  1223 +
  1224 + // Phibar normalise DRB
  1225 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1226 + double sum_dre = 0.0;
  1227 + double sum_drm = 0.0;
  1228 + double sum_drg = 0.0;
  1229 + for (int ipix = 0; ipix < npix; ++ipix) {
  1230 + sum_dre += map_dre(ipix, iphibar);
  1231 + sum_drm += map_drm(ipix, iphibar);
  1232 + sum_drg += map_drg(ipix, iphibar);
  1233 + }
  1234 + if (sum_drg > 0.0) {
  1235 + double norm = (sum_dre - sum_drm) / sum_drg;
  1236 + for (int ipix = 0; ipix < npix; ++ipix) {
  1237 + map_drb(ipix, iphibar) *= norm;
  1238 + }
  1239 + }
  1240 + else {
  1241 + for (int ipix = 0; ipix < npix; ++ipix) {
  1242 + map_drb(ipix, iphibar) = 0.0;
  1243 + }
  1244 + }
  1245 + }
  1246 +
  1247 + // Return
  1248 + return;
  1249 +}
  1250 +
  1251 +
  1252 +/***********************************************************************//**
  1253 + * @brief Compute DRB cube using BGDLIXA method
  1254 + *
  1255 + * @param[in] drm DRM cube.
  1256 + * @param[in] nrunav Number of bins used for running average.
  1257 + * @param[in] navgr Number of bins used for averaging.
  1258 + * @param[in] nincl Number of Phibar layers to include.
  1259 + * @param[in] nexcl Number of Phibar layers to exclude.
  1260 + *
  1261 + * @exception GException::invalid_value
  1262 + * Observation does not contain an event cube
  1263 + * @exception GException::invalid_argument
  1264 + * DRM cube is incompatible with DRE
  1265 + *
  1266 + * Computes a DRB cube using the BGDLIXA method that is documented in Rob
  1267 + * van Dijk's PhD thesis. The revelant equations from the thesis that are
  1268 + * implemented here are 3.12, 3.12 and 3.14.
  1269 + ***************************************************************************/
  1270 +void GCOMObservation::compute_drb_bgdlixa(const GCOMDri& drm,
  1271 + const int& nrunav,
  1272 + const int& navgr,
  1273 + const int& nincl,
  1274 + const int& nexcl)
  1275 +{
  1276 + // Extract COMPTEL event cube
  1277 + const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(this->events());
  1278 + if (cube == NULL) {
  1279 + std::string msg = "Observation \""+this->name()+"\" ("+this->id()+") "
  1280 + "does not contain a COMPTEL event cube. Please "
  1281 + "specify a COMPTEL observation containing and event "
  1282 + "cube.";
  1283 + throw GException::invalid_value(G_COMPUTE_DRB_PHINOR, msg);
  1284 + }
  1285 +
  1286 + // Check DRM
  1287 + if (!check_dri(drm)) {
  1288 + std::string msg = "Specified DRM cube is incompatible with DRE. Please "
  1289 + "specify a DRM with a data-space definition that is "
  1290 + "identical to that of the DRE.";
  1291 + throw GException::invalid_argument(G_COMPUTE_DRB_PHINOR, msg);
  1292 + }
  1293 +
  1294 + // Initialise DRB and scratch DRI by cloning DRG
  1295 + m_drb = m_drg;
  1296 + GCOMDri dri = m_drg;
  1297 +
  1298 + // Get references to relevant sky maps
  1299 + const GSkyMap& map_dre = cube->dre().map();
  1300 + const GSkyMap& map_drm = drm.map();
  1301 + const GSkyMap& map_drg = m_drg.map();
  1302 + GSkyMap& map_drb = const_cast<GSkyMap&>(m_drb.map());
  1303 + GSkyMap& map_dri = const_cast<GSkyMap&>(dri.map());
  1304 +
  1305 + // Get data space dimensions
  1306 + int nchi = m_drg.nchi();
  1307 + int npsi = m_drg.npsi();
  1308 + int nphibar = m_drg.nphibar();
  1309 + int npix = nchi * npsi;
  1310 + int nsize = npix * nphibar;
  1311 +
  1312 + // Precompute half running average lengths
  1313 + int navgr2 = int(navgr/2);
  1314 + int nexcl2 = int(nexcl/2);
  1315 + int nincl2 = int(nincl/2);
  1316 +
  1317 + // Initialise DRB and scratch DRI
  1318 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1319 + for (int ipix = 0; ipix < npix; ++ipix) {
  1320 + map_drb(ipix, iphibar) = 0.0;
  1321 + map_dri(ipix, iphibar) = 0.0;
  1322 + }
  1323 + }
  1324 +
  1325 + // Phibar normalise DRB (Equation 3.12 in Rob van Dijk's PhD thesis).
  1326 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1327 + double sum_dre = 0.0;
  1328 + double sum_drm = 0.0;
  1329 + double sum_drg = 0.0;
  1330 + for (int ipix = 0; ipix < npix; ++ipix) {
  1331 + sum_dre += map_dre(ipix, iphibar);
  1332 + sum_drm += map_drm(ipix, iphibar);
  1333 + sum_drg += map_drg(ipix, iphibar);
  1334 + }
  1335 + if (sum_drg > 0.0) {
  1336 + double norm = (sum_dre - sum_drm) / sum_drg;
  1337 + for (int ipix = 0; ipix < npix; ++ipix) {
  1338 + map_dri(ipix, iphibar) = map_drg(ipix, iphibar) * norm;
  1339 + }
  1340 + }
  1341 + }
  1342 +
  1343 + // Do 3D running average of DRE and Phibar normalised DRG (Equation 3.13
  1344 + // in Rob van Dijk's PhD thesis). The result is a Phibar normalised DRG
  1345 + // that is normalised over the 3D region to the DRE. The 3D region is all
  1346 + // Phibar layers and [-nrunav,+nrunav] Chi/Psi pixels around the pixel of
  1347 + // consideration.
  1348 + if (nrunav >= 1) {
  1349 +
  1350 + // Loop over Chi pixels
  1351 + for (int ichi = 0; ichi < nchi; ++ichi) {
  1352 +
  1353 + // Compute running average window in Chi
  1354 + int kchi_min = ichi - nrunav;
  1355 + int kchi_max = ichi + nrunav;
  1356 + if (kchi_min < 0) {
  1357 + kchi_min = 0;
  1358 + }
  1359 + if (kchi_max >= nchi) {
  1360 + kchi_max = nchi - 1;
  1361 + }
  1362 +
  1363 + // Loop over Psi pixels
  1364 + for (int ipsi = 0; ipsi < npsi; ++ipsi) {
  1365 +
  1366 + // Compute running average window in Psi
  1367 + int kpsi_min = ipsi - nrunav;
  1368 + int kpsi_max = ipsi + nrunav;
  1369 + if (kpsi_min < 0) {
  1370 + kpsi_min = 0;
  1371 + }
  1372 + if (kpsi_max >= npsi) {
  1373 + kpsi_max = npsi - 1;
  1374 + }
  1375 +
  1376 + // Initialise sums
  1377 + double sum_dre = 0.0;
  1378 + double sum_drm = 0.0;
  1379 + double sum_dri = 0.0;
  1380 +
  1381 + // Compute running average
  1382 + for (int kchi = kchi_min; kchi <= kchi_max; ++kchi) {
  1383 + for (int kpsi = kpsi_min; kpsi <= kpsi_max; ++kpsi) {
  1384 + int kpix = kchi + kpsi * nchi;
  1385 + for (int kphibar = 0; kphibar < nphibar; ++kphibar) {
  1386 + if (map_drg(kpix, kphibar) != 0.0) {
  1387 + sum_dre += map_dre(kpix, kphibar);
  1388 + sum_drm += map_drm(kpix, kphibar);
  1389 + sum_dri += map_dri(kpix, kphibar);
  1390 + }
  1391 + }
  1392 + }
  1393 + }
  1394 +
  1395 + // Renormalise scratch array
  1396 + if (sum_dri != 0.0) {
  1397 + int ipix = ichi + ipsi * nchi;
  1398 + double norm = (sum_dre - sum_drm) / sum_dri;
  1399 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1400 + map_dri(ipix, iphibar) *= norm;
  1401 + }
  1402 + }
  1403 +
  1404 + } // endfor: looped over Phi
  1405 +
  1406 + } // endfor: looped over Chi
  1407 +
  1408 + } // endif: running averaging was requested
  1409 +
  1410 + // First part of Equation (3.14) in Rob van Dijk's PhD thesis.
  1411 + // (DRB = B^0C_L / sum B^0C_L). The DRB is pre-computed by adjusting the
  1412 + // scratch DRI over nincl Phibar layers.
  1413 + int ksel1 = 0;
  1414 + int kex1 = 0;
  1415 + int kex2 = 0;
  1416 + int ksel2 = 0;
  1417 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1418 +
  1419 + // Get Phibar index range for sums
  1420 + get_bgdlixa_phibar_indices(iphibar, nincl, nexcl,
  1421 + &ksel1, &kex1, &kex2, &ksel2);
  1422 +
  1423 + // Loop over Chi/Psi pixels
  1424 + for (int ipix = 0; ipix < npix; ++ipix) {
  1425 +
  1426 + // Initialise DRB value
  1427 + map_drb(ipix, iphibar) = 0.0;
  1428 +
  1429 + // Continue only if scratch DRI is non-zero
  1430 + if (map_dri(ipix, iphibar) != 0.0) {
  1431 +
  1432 + // Initialise sum
  1433 + double sum_dri = 0.0;
  1434 +
  1435 + // Take sum over [ksel1, kex1]
  1436 + if (kex1 >= 0) {
  1437 + for (int kphibar = ksel1; kphibar <= kex1; ++kphibar) {
  1438 + sum_dri += map_dri(ipix, kphibar);
  1439 + }
  1440 + }
  1441 +
  1442 + // Take sum over [kxe2, ksel2]
  1443 + if (kex2 < nphibar) {
  1444 + for (int kphibar = kex2; kphibar <= ksel2; ++kphibar) {
  1445 + sum_dri += map_dri(ipix, kphibar);
  1446 + }
  1447 + }
  1448 +
  1449 + // If sum is not zero then set DRB
  1450 + if (sum_dri != 0.0) {
  1451 + map_drb(ipix, iphibar) = map_dri(ipix, iphibar) / sum_dri;
  1452 + }
  1453 +
  1454 + } // endif: scratch DRI was non-zero
  1455 +
  1456 + } // endfor: looped over Chi/Psi pixels
  1457 +
  1458 + } // endfor: looped over Phibar
  1459 +
  1460 + // Second part of Equation (3.14) in Rob van Dijk's PhD thesis that
  1461 + // corresponds to G * [E / G]^s that will be stored in scratch DRI.
  1462 + //
  1463 + // NOTES:
  1464 + // - we replaced DRE by DRE-DRM in the nominator of the running
  1465 + // average since and source component should of course be
  1466 + // subtracted
  1467 + // - we implemented an alternative running average computation where
  1468 + // the ratio between the DRE and DRG sums is taken. This avoids
  1469 + // the divergence of the ratio at the edge of the DRG.
  1470 + for (int ichi = 0; ichi < nchi; ++ichi) {
  1471 +
  1472 + // Compute running average window in Chi
  1473 + int kchi_min = ichi - navgr2;
  1474 + int kchi_max = ichi + navgr2;
  1475 + if (kchi_min < 0) {
  1476 + kchi_min = 0;
  1477 + }
  1478 + if (kchi_max >= nchi) {
  1479 + kchi_max = nchi - 1;
  1480 + }
  1481 +
  1482 + // Loop over Psi pixels
  1483 + for (int ipsi = 0; ipsi < npsi; ++ipsi) {
  1484 +
  1485 + // Compute running average window in Psi
  1486 + int kpsi_min = ipsi - navgr2;
  1487 + int kpsi_max = ipsi + navgr2;
  1488 + if (kpsi_min < 0) {
  1489 + kpsi_min = 0;
  1490 + }
  1491 + if (kpsi_max >= npsi) {
  1492 + kpsi_max = npsi - 1;
  1493 + }
  1494 +
  1495 + // Loop over Phibar layers
  1496 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1497 +
  1498 + // Initialise sums
  1499 + double sum_dre = 0.0;
  1500 + double sum_drm = 0.0;
  1501 + double sum_drg = 0.0;
  1502 +
  1503 + // Compute running average
  1504 + for (int kchi = kchi_min; kchi <= kchi_max; ++kchi) {
  1505 + for (int kpsi = kpsi_min; kpsi <= kpsi_max; ++kpsi) {
  1506 + int kpix = kchi + kpsi * nchi;
  1507 + sum_dre += map_dre(kpix, iphibar);
  1508 + sum_drm += map_drm(kpix, iphibar);
  1509 + sum_drg += map_drg(kpix, iphibar);
  1510 + }
  1511 + }
  1512 +
  1513 + // Multiply average by DRG and store it in scratch DRI
  1514 + int ipix = ichi + ipsi * nchi;
  1515 + if (sum_drg > 0.0) {
  1516 + double norm = (sum_dre - sum_drm) / sum_drg;
  1517 + map_dri(ipix, iphibar) = map_drg(ipix, iphibar) * norm;
  1518 + }
  1519 + else {
  1520 + map_dri(ipix, iphibar) = 0.0;
  1521 + }
  1522 +
  1523 + } // endfor: looped over Phibar layers
  1524 +
  1525 + } // endfor: looped over Psi pixels
  1526 +
  1527 + } // endfor: looped over Chi pixels
  1528 +
  1529 + // Third part of Equation (3.14) in Rob van Dijk's PhD thesis that sums
  1530 + // G * [E / G]^s over a subset of Phibar layers (the first parenthesis)
  1531 + // and multplies with B^0C_L / sum B^0C_L
  1532 + for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
  1533 +
  1534 + // Get Phibar index range for sums
  1535 + get_bgdlixa_phibar_indices(iphibar, nincl, nexcl,
  1536 + &ksel1, &kex1, &kex2, &ksel2);
  1537 +
  1538 + // Loop over Chi/Psi pixels
  1539 + for (int ipix = 0; ipix < npix; ++ipix) {
  1540 +
  1541 + // If DRB is not empty then correct it
  1542 + if (map_drb(ipix, iphibar) != 0.0) {
  1543 +
  1544 + // Initialise DRI sum
  1545 + double sum_dri = 0.0;
  1546 +
  1547 + // Take sum over [ksel1, kex1]
  1548 + if (kex1 >= 0) {
  1549 + for (int kphibar = ksel1; kphibar <= kex1; ++kphibar) {
  1550 + sum_dri += map_dri(ipix, kphibar);
  1551 + }
  1552 + }
  1553 +
  1554 + // Take sum over [kxe2, ksel2]
  1555 + if (kex2 < nphibar) {
  1556 + for (int kphibar = kex2; kphibar <= ksel2; ++kphibar) {
  1557 + sum_dri += map_dri(ipix, kphibar);
  1558 + }
  1559 + }
  1560 +
  1561 + // Correct DRB
  1562 + map_drb(ipix, iphibar) *= sum_dri;
  1563 +
  1564 + } // endif: DRB was not empty
  1565 +
  1566 + } // endfor: looped over Chi/Psi pixels
  1567 +
  1568 + } // endfor: looped over Phibar
  1569 +
  1570 + // Return
  1571 + return;
  1572 +}
  1573 +
  1574 +
  1575 +/***********************************************************************//**
  1576 + * @brief Compute Phibar index range for BGDLIXA background method
  1577 + *
  1578 + * @param[in] iphibar Phibar layer index.
  1579 + * @param[in] nincl Number of Phibar layers to include.
  1580 + * @param[in] nexcl Number of Phibar layers to exclude.
  1581 + * @param[out] isel1 Start index for first sum.
  1582 + * @param[out] iex1 Stop index for first sum.
  1583 + * @param[out] iex2 Start index for second sum.
  1584 + * @param[out] isel2 Stop index for second sum.
  1585 + *
  1586 + * This method is a helper method for the BGDLIXA background method. It
  1587 + * computes the Phibar index range for the third step of the background
  1588 + * model computation. The third step corresponds to Equation (3.14) in Rob
  1589 + * van Dijk's PhD thesis.
  1590 + *
  1591 + * The Phibar sum will be taken over the index ranges [isel1, iex1] and
  1592 + * [ixe2, isel2].
  1593 + ***************************************************************************/
  1594 +void GCOMObservation::get_bgdlixa_phibar_indices(const int& iphibar,
  1595 + const int& nincl,
  1596 + const int& nexcl,
  1597 + int* isel1,
  1598 + int* iex1,
  1599 + int* iex2,
  1600 + int* isel2) const
  1601 +{
  1602 + // Get number of Phibar layers
  1603 + int nphibar = m_drg.nphibar();
  1604 + int imax = nphibar - 1;
  1605 +
  1606 + // Precompute half running average lengths
  1607 + int nexcl2 = int(nexcl/2);
  1608 + int nincl2 = int(nincl/2);
  1609 +
  1610 + // Compute Phibar index range without respecting boundaries
  1611 + *iex1 = iphibar - nexcl2 - 1;
  1612 + *iex2 = iphibar + nexcl2 + 1;
  1613 + *isel1 = iphibar - nincl2;
  1614 + *isel2 = iphibar + nincl2;
  1615 +
  1616 + // If no Phibar layers are to be excluded then make sure that the
  1617 + // Phibar layer range is continuous
  1618 + if (nexcl == 0) {
  1619 + (*iex2)--;
  1620 + }
  1621 +
  1622 + // Make sure that start index of first sum and stop index of second
  1623 + // some are within the validity range
  1624 + if (*isel1 < 0) {
  1625 + *isel1 = 0;
  1626 + }
  1627 + if (*isel2 > imax) {
  1628 + *isel2 = imax;
  1629 + }
  1630 +
  1631 + // Make sure that the sums use always ncincl Phibar layers, also near
  1632 + // the edges
  1633 + if (*isel1 == 0) {
  1634 + *isel2 = nincl - 1;
  1635 + if (nincl > imax) {
  1636 + *isel2 = imax;
  1637 + }
  1638 +
  1639 + }
  1640 + if (*isel2 == imax) {
  1641 + *isel1 = nphibar - nincl;
  1642 + if (*isel1 < 0) {
  1643 + *isel1 = 0;
  1644 + }
  1645 + }
  1646 +
  1647 + // Return
  1648 + return;
  1649 +}
... ...