Commit f25aeb73625d6d907da46a75ada6823c19f96513
1 parent
cf3f1ec9
Enable ctbin to produce counts cubes for a joint binned analysis (#2714)
Showing
8 changed files
with
448 additions
and
313 deletions
ChangeLog
| 1 | -2018-11-03 | 1 | +2018-11-07 |
| 2 | 2 | ||
| 3 | * Version 1.6.0 released | 3 | * Version 1.6.0 released |
| 4 | ======================== | 4 | ======================== |
| 5 | 5 | ||
| 6 | + Enable ctbin to produce counts cubes for a joint binned analysis (#2714) | ||
| 6 | Add code generator (#2709) | 7 | Add code generator (#2709) |
| 7 | Add csbkgmodel script (#2694) | 8 | Add csbkgmodel script (#2694) |
| 8 | Compute DETX and DETY in ctmodel if they are not available (#2694) | 9 | Compute DETX and DETY in ctmodel if they are not available (#2694) |
NEWS
| @@ -55,6 +55,14 @@ of the filename is now done by the GApplicationPar class. | @@ -55,6 +55,14 @@ of the filename is now done by the GApplicationPar class. | ||
| 55 | 55 | ||
| 56 | ctbin - CTA event binning | 56 | ctbin - CTA event binning |
| 57 | ------------------------- | 57 | ------------------------- |
| 58 | +Added the option to produce an individual counts cube for each event list in | ||
| 59 | +the input observation definition XML file. The option is enabled by setting | ||
| 60 | +the new hidden parameter "stack" to "no". The cube() method now has an optional | ||
| 61 | +index argument, providing access to the i'th counts cube that was produced. | ||
| 62 | +The cubes() method specifies the number of counts cubes that were produced. | ||
| 63 | +If no counts cube was produced, the cube() method now throws an exception | ||
| 64 | +(#2714). | ||
| 65 | + | ||
| 58 | ctbin now accouts for varying energy thresholds when computing the weight | 66 | ctbin now accouts for varying energy thresholds when computing the weight |
| 59 | array (#2685). | 67 | array (#2685). |
| 60 | 68 |
doc/source/admin/release_history/1.6.rst
| @@ -41,6 +41,8 @@ Bug fixes | @@ -41,6 +41,8 @@ Bug fixes | ||
| 41 | Improvements | 41 | Improvements |
| 42 | ------------ | 42 | ------------ |
| 43 | 43 | ||
| 44 | +* [`2714 <https://cta-redmine.irap.omp.eu/issues/2714>`_] - | ||
| 45 | + Enable :ref:`ctbin` to produce counts cubes for a joint binned analysis | ||
| 44 | * [`2685 <https://cta-redmine.irap.omp.eu/issues/2685>`_] - | 46 | * [`2685 <https://cta-redmine.irap.omp.eu/issues/2685>`_] - |
| 45 | Add ``ctbkgcube::cntcube()`` method | 47 | Add ``ctbkgcube::cntcube()`` method |
| 46 | Account for varying energy thresholds in :ref:`ctbin` weight computation | 48 | Account for varying energy thresholds in :ref:`ctbin` weight computation |
pyext/ctbin.i
| 1 | /*************************************************************************** | 1 | /*************************************************************************** |
| 2 | * ctbin - Event binning tool * | 2 | * ctbin - Event binning tool * |
| 3 | * ----------------------------------------------------------------------- * | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2010-2016 by Juergen Knoedlseder * | 4 | + * copyright (C) 2010-2018 by Juergen Knoedlseder * |
| 5 | * ----------------------------------------------------------------------- * | 5 | * ----------------------------------------------------------------------- * |
| 6 | * * | 6 | * * |
| 7 | * This program is free software: you can redistribute it and/or modify * | 7 | * This program is free software: you can redistribute it and/or modify * |
| @@ -46,10 +46,10 @@ public: | @@ -46,10 +46,10 @@ public: | ||
| 46 | // Methods | 46 | // Methods |
| 47 | void clear(void); | 47 | void clear(void); |
| 48 | void run(void); | 48 | void run(void); |
| 49 | + int cubes(void) const; | ||
| 50 | + const GCTAEventCube& cube(const int& index = 0) const; | ||
| 49 | void save(void); | 51 | void save(void); |
| 50 | void publish(const std::string& name = ""); | 52 | void publish(const std::string& name = ""); |
| 51 | - const GCTAEventCube& cube(void) const; | ||
| 52 | - const GCTAEventCube& cube(const size_t& index) const; | ||
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | 55 |
src/ctbin/ctbin.cpp
| @@ -38,6 +38,7 @@ | @@ -38,6 +38,7 @@ | ||
| 38 | #endif | 38 | #endif |
| 39 | 39 | ||
| 40 | /* __ Method name definitions ____________________________________________ */ | 40 | /* __ Method name definitions ____________________________________________ */ |
| 41 | +#define G_CUBE "ctbin::cube(int&)" | ||
| 41 | #define G_GET_PARAMETERS "ctbin::get_parameters()" | 42 | #define G_GET_PARAMETERS "ctbin::get_parameters()" |
| 42 | #define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)" | 43 | #define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)" |
| 43 | #define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)" | 44 | #define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)" |
| @@ -234,6 +235,9 @@ void ctbin::run(void) | @@ -234,6 +235,9 @@ void ctbin::run(void) | ||
| 234 | // Get task parameters | 235 | // Get task parameters |
| 235 | get_parameters(); | 236 | get_parameters(); |
| 236 | 237 | ||
| 238 | + // Initialise sky direction cache for stacking | ||
| 239 | + init_sky_dir_cache(); | ||
| 240 | + | ||
| 237 | // Write input observation container into logger | 241 | // Write input observation container into logger |
| 238 | log_observations(NORMAL, m_obs, "Input observation"); | 242 | log_observations(NORMAL, m_obs, "Input observation"); |
| 239 | 243 | ||
| @@ -255,33 +259,33 @@ void ctbin::run(void) | @@ -255,33 +259,33 @@ void ctbin::run(void) | ||
| 255 | log_string(NORMAL, msg); | 259 | log_string(NORMAL, msg); |
| 256 | } | 260 | } |
| 257 | 261 | ||
| 258 | - // Initialise cubes and ids if unstacked | ||
| 259 | - if (!m_stack) { | ||
| 260 | - m_cubes = std::vector<GCTAEventCube>(obs_list.size()); | ||
| 261 | - m_ids = std::vector<std::string>(obs_list.size()); | 262 | + // Set number of relevant observations |
| 263 | + int nobs = obs_list.size(); | ||
| 264 | + | ||
| 265 | + // Initialise arrays | ||
| 266 | + if (nobs > 0) { | ||
| 267 | + m_counts = std::vector<GSkyMap>(nobs); | ||
| 268 | + m_weights = std::vector<GSkyMap>(nobs); | ||
| 269 | + m_cubes = std::vector<GCTAEventCube>(nobs); | ||
| 270 | + m_ids = std::vector<std::string>(nobs); | ||
| 271 | + } | ||
| 272 | + else { | ||
| 273 | + m_counts.clear(); | ||
| 274 | + m_weights.clear(); | ||
| 275 | + m_cubes.clear(); | ||
| 276 | + m_ids.clear(); | ||
| 262 | } | 277 | } |
| 263 | 278 | ||
| 264 | // Write header into logger | 279 | // Write header into logger |
| 265 | - log_header1(TERSE, gammalib::number("Bin observation", m_obs.size())); | 280 | + log_header1(TERSE, gammalib::number("Bin observation", nobs)); |
| 266 | 281 | ||
| 267 | // Loop over all unbinned CTA observations in the container | 282 | // Loop over all unbinned CTA observations in the container |
| 268 | #pragma omp parallel for | 283 | #pragma omp parallel for |
| 269 | - for (int i = 0; i < obs_list.size(); ++i) { | 284 | + for (int i = 0; i < nobs; ++i) { |
| 270 | 285 | ||
| 271 | // Get pointer to observation | 286 | // Get pointer to observation |
| 272 | GCTAObservation* obs = obs_list[i]; | 287 | GCTAObservation* obs = obs_list[i]; |
| 273 | 288 | ||
| 274 | - // Make sure that the observation holds a CTA event list. If this | ||
| 275 | - // is not the case then throw an exception. | ||
| 276 | - const GCTAEventList* events = dynamic_cast<const GCTAEventList*> | ||
| 277 | - (obs->events()); | ||
| 278 | - if (events == NULL) { | ||
| 279 | - std::string msg = "CTA Observation does not contain an event " | ||
| 280 | - "list. An event list is needed to fill the " | ||
| 281 | - "counts cube."; | ||
| 282 | - throw GException::invalid_value(G_FILL_CUBE, msg); | ||
| 283 | - } | ||
| 284 | - | ||
| 285 | // Fill the cube | 289 | // Fill the cube |
| 286 | GSkyMap counts = fill_cube(obs); | 290 | GSkyMap counts = fill_cube(obs); |
| 287 | 291 | ||
| @@ -291,88 +295,118 @@ void ctbin::run(void) | @@ -291,88 +295,118 @@ void ctbin::run(void) | ||
| 291 | // Dispose events to free memory | 295 | // Dispose events to free memory |
| 292 | obs->dispose_events(); | 296 | obs->dispose_events(); |
| 293 | 297 | ||
| 298 | + // Post process cubes | ||
| 294 | #pragma omp critical(ctbin_fill_cube) | 299 | #pragma omp critical(ctbin_fill_cube) |
| 295 | - if (m_stack) { | ||
| 296 | - | ||
| 297 | - | ||
| 298 | - m_counts += counts; | ||
| 299 | - | ||
| 300 | - for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) { | ||
| 301 | - for (int pixel = 0; pixel < m_counts.npix(); ++pixel) { | ||
| 302 | - if (weights(pixel, iebin) == 1.0) { | ||
| 303 | - m_weights(pixel, iebin) = 1.0; | ||
| 304 | - } | ||
| 305 | - } | ||
| 306 | - } | ||
| 307 | - | ||
| 308 | - | ||
| 309 | - // If GTI is empty then set its time reference from the observation. | ||
| 310 | - // From then on we keep that time reference | 300 | + { |
| 301 | + // If stacking of cubes is requested then add up counts cube | ||
| 302 | + // and set weights in the first slot of the m_counts and | ||
| 303 | + // m_weights arrays | ||
| 304 | + if (m_stack) { | ||
| 305 | + if (i == 0) { | ||
| 306 | + m_counts[0] = counts; | ||
| 307 | + m_weights[0] = weights; | ||
| 308 | + } | ||
| 309 | + else { | ||
| 310 | + for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) { | ||
| 311 | + for (int pixel = 0; pixel < counts.npix(); ++pixel) { | ||
| 312 | + m_counts[0](pixel, iebin) += counts(pixel, iebin); | ||
| 313 | + if (weights(pixel, iebin) == 1.0) { | ||
| 314 | + m_weights[0](pixel, iebin) = 1.0; | ||
| 315 | + } | ||
| 316 | + } | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | + | ||
| 321 | + // ... otherwise store counts, weights and observation ID | ||
| 322 | + else { | ||
| 323 | + m_counts[i] = counts; | ||
| 324 | + m_weights[i] = weights; | ||
| 325 | + m_ids[i] = obs->id(); | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + } // end: omp critical section | ||
| 329 | + | ||
| 330 | + } // endfor: looped over observations | ||
| 331 | + | ||
| 332 | + // Combine GTIs of all observations and compute total ontime and livetime | ||
| 333 | + for (int i = 0; i < nobs; ++i) { | ||
| 334 | + | ||
| 335 | + // Get pointer to observation | ||
| 336 | + GCTAObservation* obs = obs_list[i]; | ||
| 337 | + | ||
| 338 | + // Get Good Time Intervals for observation | ||
| 339 | + GGti gti = obs->gti(); | ||
| 340 | + | ||
| 341 | + // If resulting GTI is not filled then copy over the reference from | ||
| 342 | + // the observation and use it for the resulting GTI | ||
| 311 | if (m_gti.is_empty()) { | 343 | if (m_gti.is_empty()) { |
| 312 | - m_gti.reference(events->gti().reference()); | 344 | + m_gti.reference(gti.reference()); |
| 313 | } | 345 | } |
| 314 | - | 346 | + |
| 315 | // Append GTIs | 347 | // Append GTIs |
| 316 | - m_gti.extend(events->gti()); | ||
| 317 | - | 348 | + m_gti.extend(gti); |
| 349 | + | ||
| 318 | // Update ontime and livetime | 350 | // Update ontime and livetime |
| 319 | m_ontime += obs->ontime(); | 351 | m_ontime += obs->ontime(); |
| 320 | m_livetime += obs->livetime(); | 352 | m_livetime += obs->livetime(); |
| 321 | - | ||
| 322 | - } else { // don't stack cubes | ||
| 323 | 353 | ||
| 324 | - // Construct a cube from this observation | ||
| 325 | - GCTAEventCube cube(counts, weights, m_ebounds, events->gti()); | ||
| 326 | - m_cubes[i] = cube; | ||
| 327 | - obs->events(cube); | 354 | + } // endfor: looped over all observations |
| 355 | + | ||
| 356 | + // Post process data for a stacked cube | ||
| 357 | + if (m_stack && nobs > 0) { | ||
| 358 | + | ||
| 359 | + // Compute livetime fraction per energy bin so that varying energy | ||
| 360 | + // thresholds are correctly taken into account in the weighting | ||
| 361 | + // array. Computational speed is not optimum, we have to see later | ||
| 362 | + // whether this is an issue. | ||
| 363 | + for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) { | ||
| 364 | + double livetime_ebin = 0.0; | ||
| 365 | + for (int i = 0; i < obs_list.size(); ++i) { | ||
| 366 | + std::vector<bool> usage = | ||
| 367 | + cube_layer_usage(m_ebounds, obs_list[i]->ebounds()); | ||
| 368 | + if (usage[iebin]) { | ||
| 369 | + livetime_ebin += obs_list[i]->livetime(); | ||
| 370 | + } | ||
| 371 | + } | ||
| 372 | + if (m_livetime > 0.0) { | ||
| 373 | + double fraction = livetime_ebin / m_livetime; | ||
| 374 | + for (int pixel = 0; pixel < m_counts[0].npix(); ++pixel) { | ||
| 375 | + if (m_weights[0](pixel, iebin) > 0.0) { | ||
| 376 | + m_weights[0](pixel, iebin) *= fraction; | ||
| 377 | + } | ||
| 378 | + } | ||
| 379 | + } | ||
| 380 | + } | ||
| 328 | 381 | ||
| 329 | - // Store id of the observation | ||
| 330 | - m_ids[i] = obs->id(); | ||
| 331 | - | ||
| 332 | - } | 382 | + // Set a single cube in the observation container |
| 383 | + obs_cube_stacked(); | ||
| 333 | 384 | ||
| 385 | + } // endif: stacked cube post processing | ||
| 334 | 386 | ||
| 335 | - } // endfor: looped over observations | 387 | + // Post process data for non-stacked cubes |
| 388 | + else { | ||
| 336 | 389 | ||
| 337 | - if (m_stack) { | ||
| 338 | - // Compute total livetime in all observations | ||
| 339 | - double livetime = 0.0; | ||
| 340 | - for (int i = 0; i < obs_list.size(); ++i) { | ||
| 341 | - livetime += obs_list[i]->livetime(); | ||
| 342 | - } | ||
| 343 | - | ||
| 344 | - // Compute livetime fraction per energy bin so that varying energy | ||
| 345 | - // thresholds are correctly taken into account in the weighting | ||
| 346 | - // array. Computational speed is not optimum, we have to see later | ||
| 347 | - // whether this is an issue. | ||
| 348 | - for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) { | ||
| 349 | - double livetime_ebin = 0.0; | ||
| 350 | - for (int i = 0; i < obs_list.size(); ++i) { | ||
| 351 | - const GCTAEventList* events = dynamic_cast<const GCTAEventList*> | ||
| 352 | - (obs_list[i]->events()); | ||
| 353 | - std::vector<bool> usage = cube_layer_usage(m_ebounds, | ||
| 354 | - events->ebounds()); | ||
| 355 | - if (usage[iebin]) { | ||
| 356 | - livetime_ebin += obs_list[i]->livetime(); | ||
| 357 | - } | ||
| 358 | - } | ||
| 359 | - if (livetime > 0.0) { | ||
| 360 | - double fraction = livetime_ebin / livetime; | ||
| 361 | - for (int pixel = 0; pixel < m_counts.npix(); ++pixel) { | ||
| 362 | - if (m_weights(pixel, iebin) > 0.0) { | ||
| 363 | - m_weights(pixel, iebin) *= fraction; | ||
| 364 | - } | ||
| 365 | - } | ||
| 366 | - } | ||
| 367 | - } | 390 | + // Loop over all relevant observations |
| 391 | + for (int i = 0; i < nobs; ++i) { | ||
| 368 | 392 | ||
| 369 | - // Build event cube (needs to come before obs_cube() since this method | ||
| 370 | - // relies on correct setting of m_cube) | ||
| 371 | - m_cube = GCTAEventCube(m_counts, m_weights, m_ebounds, m_gti); | 393 | + // Get pointer to observation |
| 394 | + GCTAObservation* obs = obs_list[i]; | ||
| 372 | 395 | ||
| 373 | - // Set a single cube in the observation container | ||
| 374 | - obs_cube(); | ||
| 375 | - } | 396 | + // Build counts cube |
| 397 | + m_cubes[i] = GCTAEventCube(m_counts[i], m_weights[i], m_ebounds, | ||
| 398 | + obs->gti()); | ||
| 399 | + | ||
| 400 | + // Attach event cube in the correct slot of the observation | ||
| 401 | + // container | ||
| 402 | + obs->events(m_cubes[i]); | ||
| 403 | + | ||
| 404 | + // Store ID of the observation for saving of XML file | ||
| 405 | + m_ids[i] = obs->id(); | ||
| 406 | + | ||
| 407 | + } // endfor: looped over relevant observations | ||
| 408 | + | ||
| 409 | + } // endelse: non-stacked cube post processing | ||
| 376 | 410 | ||
| 377 | // Write resulting observation container into logger | 411 | // Write resulting observation container into logger |
| 378 | log_observations(NORMAL, m_obs, "Binned observation"); | 412 | log_observations(NORMAL, m_obs, "Binned observation"); |
| @@ -388,72 +422,108 @@ void ctbin::run(void) | @@ -388,72 +422,108 @@ void ctbin::run(void) | ||
| 388 | 422 | ||
| 389 | 423 | ||
| 390 | /***********************************************************************//** | 424 | /***********************************************************************//** |
| 425 | + * @brief Return event cube at index | ||
| 426 | + * | ||
| 427 | + * @param[in] index Cube index (0,...,cubes()-1). | ||
| 428 | + * @return Reference to event cube. | ||
| 429 | + * | ||
| 430 | + * @exception GException::out_of_range | ||
| 431 | + * Event cube index out of range. | ||
| 432 | + * | ||
| 433 | + * Returns a reference to the event cube at the given index. | ||
| 434 | + ***************************************************************************/ | ||
| 435 | +const GCTAEventCube& ctbin::cube(const int& index) const | ||
| 436 | +{ | ||
| 437 | + // Compile option: raise an exception if index is out of range | ||
| 438 | + if (index < 0 || index >= cubes()) { | ||
| 439 | + throw GException::out_of_range(G_CUBE, "Cube index", index, cubes()); | ||
| 440 | + } | ||
| 441 | + | ||
| 442 | + // Return cube | ||
| 443 | + return m_cubes[index]; | ||
| 444 | +} | ||
| 445 | + | ||
| 446 | + | ||
| 447 | +/***********************************************************************//** | ||
| 391 | * @brief Save counts cube | 448 | * @brief Save counts cube |
| 392 | * | 449 | * |
| 393 | - * Saves the counts cube into a FITS file. | 450 | + * Saves the counts cube. If the counts cube was stacked single FITS file |
| 451 | + * will be written. Otherwise each generated counts cube will be written | ||
| 452 | + * into a FITS file, and an observation definition XML file will be written | ||
| 453 | + * out that contains an observation for each counts cube. | ||
| 394 | ***************************************************************************/ | 454 | ***************************************************************************/ |
| 395 | void ctbin::save(void) | 455 | void ctbin::save(void) |
| 396 | { | 456 | { |
| 397 | // Write header | 457 | // Write header |
| 398 | log_header1(TERSE, "Save counts cube"); | 458 | log_header1(TERSE, "Save counts cube"); |
| 399 | 459 | ||
| 400 | - if (!m_stack) { | ||
| 401 | - | ||
| 402 | - // Loop over all observations | ||
| 403 | - for (size_t i = 0; i < m_obs.size(); ++i) { | ||
| 404 | - | ||
| 405 | - // Loop over all ids of observations that were binned | ||
| 406 | - for (size_t j = 0; j < m_ids.size(); ++j) { | ||
| 407 | - | ||
| 408 | - // Check if ids are equal | ||
| 409 | - if (m_ids[j] == m_obs[i]->id()) { | ||
| 410 | - | ||
| 411 | - // Construct filename | ||
| 412 | - std::string filename(m_ids[j]); | ||
| 413 | - filename.insert(0, m_prefix); | ||
| 414 | - filename.append(".fits"); | ||
| 415 | - | ||
| 416 | - // Set filename for counts cube | ||
| 417 | - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); | ||
| 418 | - obs->eventfile(filename); | ||
| 419 | - | ||
| 420 | - // Save countscube | ||
| 421 | - obs->save(filename, clobber()); | ||
| 422 | - } | ||
| 423 | - } | ||
| 424 | - } | ||
| 425 | - if ((*this)["outcube"].is_valid()) { | ||
| 426 | - // Get counts cube filename | ||
| 427 | - m_outcube = (*this)["outcube"].filename(); | ||
| 428 | - m_obs.save(m_outcube); | ||
| 429 | - } | ||
| 430 | - | ||
| 431 | - } else { // else: stack | ||
| 432 | - | ||
| 433 | - // Save only if filename is valid and if there is at least one | ||
| 434 | - // observation | ||
| 435 | - if ((*this)["outcube"].is_valid() && m_obs.size() > 0) { | ||
| 436 | - | ||
| 437 | - // Get counts cube filename | ||
| 438 | - m_outcube = (*this)["outcube"].filename(); | ||
| 439 | - | ||
| 440 | - // Get CTA observation from observation container | ||
| 441 | - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]); | ||
| 442 | - | ||
| 443 | - // Save only if observation is valid | ||
| 444 | - if (obs != NULL) { | ||
| 445 | - | ||
| 446 | - // Log counts cube file name | ||
| 447 | - log_value(NORMAL, "Counts cube file", m_outcube.url()); | ||
| 448 | - | ||
| 449 | - // Save cube | ||
| 450 | - obs->save(m_outcube, clobber()); | ||
| 451 | - | ||
| 452 | - } // endif: observation was valid | ||
| 453 | - | ||
| 454 | - } // endif: outcube file was valid | ||
| 455 | - | ||
| 456 | - } // endelse: stack | 460 | + // If counts cube was stacked then save first observation |
| 461 | + if (m_stack) { | ||
| 462 | + | ||
| 463 | + // Save only if filename is valid and if there is at least one | ||
| 464 | + // observation | ||
| 465 | + if ((*this)["outcube"].is_valid() && m_obs.size() > 0) { | ||
| 466 | + | ||
| 467 | + // Get counts cube filename | ||
| 468 | + m_outcube = (*this)["outcube"].filename(); | ||
| 469 | + | ||
| 470 | + // Get CTA observation from observation container | ||
| 471 | + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]); | ||
| 472 | + | ||
| 473 | + // Save only if observation is valid | ||
| 474 | + if (obs != NULL) { | ||
| 475 | + | ||
| 476 | + // Log counts cube file name | ||
| 477 | + log_value(NORMAL, "Counts cube file", m_outcube.url()); | ||
| 478 | + | ||
| 479 | + // Save cube | ||
| 480 | + obs->save(m_outcube, clobber()); | ||
| 481 | + | ||
| 482 | + } // endif: observation was valid | ||
| 483 | + | ||
| 484 | + } // endif: outcube file was valid | ||
| 485 | + | ||
| 486 | + } // endif: counts cube was stacked | ||
| 487 | + | ||
| 488 | + // ... otherwise write out all relevant counts cubes and the observation | ||
| 489 | + // definition XML file | ||
| 490 | + else { | ||
| 491 | + | ||
| 492 | + // Loop over all observations | ||
| 493 | + for (int i = 0; i < m_obs.size(); ++i) { | ||
| 494 | + | ||
| 495 | + // Loop over all ids of observations that were binned | ||
| 496 | + for (int j = 0; j < m_ids.size(); ++j) { | ||
| 497 | + | ||
| 498 | + // Skip if ID differes | ||
| 499 | + if (m_ids[j] != m_obs[i]->id()) { | ||
| 500 | + continue; | ||
| 501 | + } | ||
| 502 | + | ||
| 503 | + // Construct filename | ||
| 504 | + std::string filename(m_ids[j]); | ||
| 505 | + filename.insert(0, m_prefix); | ||
| 506 | + filename.append(".fits"); | ||
| 507 | + | ||
| 508 | + // Set filename for counts cube | ||
| 509 | + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); | ||
| 510 | + obs->eventfile(filename); | ||
| 511 | + | ||
| 512 | + // Save countscube | ||
| 513 | + obs->save(filename, clobber()); | ||
| 514 | + | ||
| 515 | + } // endfor: looped over all IDs | ||
| 516 | + | ||
| 517 | + } // endfor: looped over all observations | ||
| 518 | + | ||
| 519 | + // Write XML file | ||
| 520 | + if ((*this)["outcube"].is_valid()) { | ||
| 521 | + m_outcube = (*this)["outcube"].filename(); | ||
| 522 | + m_obs.save(m_outcube); | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + } // endelse: counts cube were not stacked | ||
| 526 | + | ||
| 457 | // Return | 527 | // Return |
| 458 | return; | 528 | return; |
| 459 | } | 529 | } |
| @@ -478,8 +548,10 @@ void ctbin::publish(const std::string& name) | @@ -478,8 +548,10 @@ void ctbin::publish(const std::string& name) | ||
| 478 | // Write counts cube name into logger | 548 | // Write counts cube name into logger |
| 479 | log_value(NORMAL, "Counts cube name", user_name); | 549 | log_value(NORMAL, "Counts cube name", user_name); |
| 480 | 550 | ||
| 481 | - // Publish counts cube | ||
| 482 | - m_counts.publish(user_name); | 551 | + // Publish first counts cube if it exists |
| 552 | + if (cubes() > 0) { | ||
| 553 | + m_counts[0].publish(user_name); | ||
| 554 | + } | ||
| 483 | 555 | ||
| 484 | // Return | 556 | // Return |
| 485 | return; | 557 | return; |
| @@ -505,15 +577,14 @@ void ctbin::init_members(void) | @@ -505,15 +577,14 @@ void ctbin::init_members(void) | ||
| 505 | m_chatter = static_cast<GChatter>(2); | 577 | m_chatter = static_cast<GChatter>(2); |
| 506 | 578 | ||
| 507 | // Initialise protected members | 579 | // Initialise protected members |
| 580 | + m_cubes.clear(); | ||
| 508 | m_counts.clear(); | 581 | m_counts.clear(); |
| 509 | m_weights.clear(); | 582 | m_weights.clear(); |
| 583 | + m_ids.clear(); | ||
| 510 | m_ebounds.clear(); | 584 | m_ebounds.clear(); |
| 511 | m_gti.clear(); | 585 | m_gti.clear(); |
| 512 | - m_cube.clear(); | ||
| 513 | m_ontime = 0.0; | 586 | m_ontime = 0.0; |
| 514 | m_livetime = 0.0; | 587 | m_livetime = 0.0; |
| 515 | - m_cubes.clear(); | ||
| 516 | - m_ids.clear(); | ||
| 517 | 588 | ||
| 518 | // Initialise cache members | 589 | // Initialise cache members |
| 519 | m_dirs.clear(); | 590 | m_dirs.clear(); |
| @@ -538,15 +609,14 @@ void ctbin::copy_members(const ctbin& app) | @@ -538,15 +609,14 @@ void ctbin::copy_members(const ctbin& app) | ||
| 538 | m_chatter = app.m_chatter; | 609 | m_chatter = app.m_chatter; |
| 539 | 610 | ||
| 540 | // Copy protected members | 611 | // Copy protected members |
| 612 | + m_cubes = app.m_cubes; | ||
| 541 | m_counts = app.m_counts; | 613 | m_counts = app.m_counts; |
| 542 | m_weights = app.m_weights; | 614 | m_weights = app.m_weights; |
| 615 | + m_ids = app.m_ids; | ||
| 543 | m_ebounds = app.m_ebounds; | 616 | m_ebounds = app.m_ebounds; |
| 544 | m_gti = app.m_gti; | 617 | m_gti = app.m_gti; |
| 545 | - m_cube = app.m_cube; | ||
| 546 | m_ontime = app.m_ontime; | 618 | m_ontime = app.m_ontime; |
| 547 | m_livetime = app.m_livetime; | 619 | m_livetime = app.m_livetime; |
| 548 | - m_cubes = app.m_cubes; | ||
| 549 | - m_ids = app.m_ids; | ||
| 550 | 620 | ||
| 551 | // Copy cache members | 621 | // Copy cache members |
| 552 | m_dirs = app.m_dirs; | 622 | m_dirs = app.m_dirs; |
| @@ -581,14 +651,8 @@ void ctbin::get_parameters(void) | @@ -581,14 +651,8 @@ void ctbin::get_parameters(void) | ||
| 581 | // information and do not accept counts cubes. | 651 | // information and do not accept counts cubes. |
| 582 | setup_observations(m_obs, false, true, false); | 652 | setup_observations(m_obs, false, true, false); |
| 583 | 653 | ||
| 584 | - // Create an event cube based on task parameters | ||
| 585 | - GCTAEventCube cube = create_cube(m_obs); | ||
| 586 | - | ||
| 587 | - // Get the skymap from the cube and initialise all counts cube bins and | ||
| 588 | - // weights to zero | ||
| 589 | - m_counts = cube.counts(); | ||
| 590 | - m_counts = 0.0; | ||
| 591 | - m_weights = m_counts; | 654 | + // Create an event cube to query task parameters |
| 655 | + GCTAEventCube cube = this->ctool::create_cube(m_obs); | ||
| 592 | 656 | ||
| 593 | // Get energy boundaries | 657 | // Get energy boundaries |
| 594 | m_ebounds = cube.ebounds(); | 658 | m_ebounds = cube.ebounds(); |
| @@ -608,13 +672,6 @@ void ctbin::get_parameters(void) | @@ -608,13 +672,6 @@ void ctbin::get_parameters(void) | ||
| 608 | (*this)["outcube"].query(); | 672 | (*this)["outcube"].query(); |
| 609 | } | 673 | } |
| 610 | 674 | ||
| 611 | - // Cache sky directions in m_counts | ||
| 612 | - m_dirs.resize(m_counts.npix()); | ||
| 613 | - for (int pix = 0; pix < m_counts.npix(); ++pix) { | ||
| 614 | - m_dirs[pix] = m_counts.inx2dir(pix); | ||
| 615 | - m_dirs[pix].radec(m_dirs[pix].ra(), m_dirs[pix].dec()); | ||
| 616 | - } | ||
| 617 | - | ||
| 618 | // Set number of OpenMP threads | 675 | // Set number of OpenMP threads |
| 619 | #ifdef _OPENMP | 676 | #ifdef _OPENMP |
| 620 | int nthreads = (*this)["nthreads"].integer(); | 677 | int nthreads = (*this)["nthreads"].integer(); |
| @@ -632,6 +689,95 @@ void ctbin::get_parameters(void) | @@ -632,6 +689,95 @@ void ctbin::get_parameters(void) | ||
| 632 | 689 | ||
| 633 | 690 | ||
| 634 | /***********************************************************************//** | 691 | /***********************************************************************//** |
| 692 | + * @brief Initialise sky direction cache for cube stack | ||
| 693 | + * | ||
| 694 | + * Initialises a cache that holds the sky directions of each counts cube | ||
| 695 | + * pixel for a stacked analysis. This will speed up the computations later. | ||
| 696 | + * | ||
| 697 | + * If no stacking is requested, the method does nothing. | ||
| 698 | + ***************************************************************************/ | ||
| 699 | +void ctbin::init_sky_dir_cache(void) | ||
| 700 | +{ | ||
| 701 | + // Continue only is stacking is requested | ||
| 702 | + if (m_stack) { | ||
| 703 | + | ||
| 704 | + // Create a sky map from task parameters | ||
| 705 | + GSkyMap map = this->ctool::create_map(m_obs); | ||
| 706 | + | ||
| 707 | + // Initialise sky direction | ||
| 708 | + m_dirs.resize(map.npix()); | ||
| 709 | + | ||
| 710 | + // Compute sky direction for each pixel | ||
| 711 | + for (int pixel = 0; pixel < map.npix(); ++pixel) { | ||
| 712 | + m_dirs[pixel] = map.inx2dir(pixel); | ||
| 713 | + } | ||
| 714 | + | ||
| 715 | + } // endif: stacking was requested | ||
| 716 | + | ||
| 717 | + // Return | ||
| 718 | + return; | ||
| 719 | +} | ||
| 720 | + | ||
| 721 | + | ||
| 722 | +/***********************************************************************//** | ||
| 723 | + * @brief Create counts cube sky map for current observation | ||
| 724 | + * | ||
| 725 | + * @param[in] obs CTA observation. | ||
| 726 | + * | ||
| 727 | + * Creates a counts cube sky map for current observations. | ||
| 728 | + ***************************************************************************/ | ||
| 729 | +GSkyMap ctbin::create_cube(const GCTAObservation* obs) | ||
| 730 | +{ | ||
| 731 | + // Read coordinate system and projection | ||
| 732 | + std::string coordsys = (*this)["coordsys"].string(); | ||
| 733 | + std::string proj = (*this)["proj"].string(); | ||
| 734 | + | ||
| 735 | + // Read sky map reference | ||
| 736 | + double xref = 0.0; | ||
| 737 | + double yref = 0.0; | ||
| 738 | + bool usepnt = (*this)["usepnt"].boolean(); | ||
| 739 | + if (!usepnt) { | ||
| 740 | + xref = (*this)["xref"].real(); | ||
| 741 | + yref = (*this)["yref"].real(); | ||
| 742 | + } | ||
| 743 | + | ||
| 744 | + // Read sky map pixel size and number of bins | ||
| 745 | + double binsz = (*this)["binsz"].real(); | ||
| 746 | + int nxpix = (*this)["nxpix"].integer(); | ||
| 747 | + int nypix = (*this)["nypix"].integer(); | ||
| 748 | + | ||
| 749 | + // Read energy boundaries | ||
| 750 | + GEbounds ebounds = create_ebounds(); | ||
| 751 | + | ||
| 752 | + // If requested, get pointing from observations | ||
| 753 | + if (usepnt) { | ||
| 754 | + | ||
| 755 | + // Get pointing direction for observation | ||
| 756 | + GSkyDir pnt = obs->pointing().dir(); | ||
| 757 | + | ||
| 758 | + // Set xref/yref based on the coordinate system | ||
| 759 | + if (gammalib::toupper(coordsys) == "GAL") { | ||
| 760 | + xref = pnt.l_deg(); | ||
| 761 | + yref = pnt.b_deg(); | ||
| 762 | + } | ||
| 763 | + else { | ||
| 764 | + xref = pnt.ra_deg(); | ||
| 765 | + yref = pnt.dec_deg(); | ||
| 766 | + } | ||
| 767 | + | ||
| 768 | + } // endif: got pointing from observation | ||
| 769 | + | ||
| 770 | + // Initialise counts cube | ||
| 771 | + GSkyMap cube = GSkyMap(proj, coordsys, xref, yref, -binsz, binsz, | ||
| 772 | + nxpix, nypix, ebounds.size()); | ||
| 773 | + | ||
| 774 | + | ||
| 775 | + // Return cube | ||
| 776 | + return cube; | ||
| 777 | +} | ||
| 778 | + | ||
| 779 | + | ||
| 780 | +/***********************************************************************//** | ||
| 635 | * @brief Fill events into counts cube | 781 | * @brief Fill events into counts cube |
| 636 | * | 782 | * |
| 637 | * @param[in] obs CTA observation. | 783 | * @param[in] obs CTA observation. |
| @@ -641,10 +787,10 @@ void ctbin::get_parameters(void) | @@ -641,10 +787,10 @@ void ctbin::get_parameters(void) | ||
| 641 | * | 787 | * |
| 642 | * Fills the events from an event list into the counts cube. | 788 | * Fills the events from an event list into the counts cube. |
| 643 | ***************************************************************************/ | 789 | ***************************************************************************/ |
| 644 | -GSkyMap ctbin::fill_cube(GCTAObservation* obs) | 790 | +GSkyMap ctbin::fill_cube(const GCTAObservation* obs) |
| 645 | { | 791 | { |
| 646 | - // Make sure that the observation holds a CTA event list. If this | ||
| 647 | - // is not the case then throw an exception. | 792 | + // Make sure that the observation holds a CTA event list. If this is |
| 793 | + // not the case then throw an exception. | ||
| 648 | const GCTAEventList* events = dynamic_cast<const GCTAEventList*> | 794 | const GCTAEventList* events = dynamic_cast<const GCTAEventList*> |
| 649 | (obs->events()); | 795 | (obs->events()); |
| 650 | if (events == NULL) { | 796 | if (events == NULL) { |
| @@ -655,7 +801,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -655,7 +801,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 655 | } | 801 | } |
| 656 | 802 | ||
| 657 | // Get the RoI | 803 | // Get the RoI |
| 658 | - const GCTARoi& roi = events->roi(); | 804 | + const GCTARoi& roi = obs->roi(); |
| 659 | 805 | ||
| 660 | // Check for RoI sanity | 806 | // Check for RoI sanity |
| 661 | if (!roi.is_valid()) { | 807 | if (!roi.is_valid()) { |
| @@ -667,7 +813,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -667,7 +813,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 667 | } | 813 | } |
| 668 | 814 | ||
| 669 | // Get counts cube usage flags | 815 | // Get counts cube usage flags |
| 670 | - std::vector<bool> usage = cube_layer_usage(m_ebounds, events->ebounds()); | 816 | + std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds()); |
| 671 | 817 | ||
| 672 | // Initialise binning statistics | 818 | // Initialise binning statistics |
| 673 | int num_outside_roi = 0; | 819 | int num_outside_roi = 0; |
| @@ -676,9 +822,12 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -676,9 +822,12 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 676 | int num_outside_ebds = 0; | 822 | int num_outside_ebds = 0; |
| 677 | int num_in_map = 0; | 823 | int num_in_map = 0; |
| 678 | 824 | ||
| 679 | - // Initialise new empty counts map | ||
| 680 | - GSkyMap counts(m_counts); | ||
| 681 | - counts *= 0.; | 825 | + // Create counts cube |
| 826 | + GSkyMap counts = create_cube(obs); | ||
| 827 | + | ||
| 828 | + // Extract counts cube boundaries | ||
| 829 | + double pixel_x_max = counts.nx() - 0.5; | ||
| 830 | + double pixel_y_max = counts.ny() - 0.5; | ||
| 682 | 831 | ||
| 683 | // Fill counts sky map | 832 | // Fill counts sky map |
| 684 | for (int i = 0; i < events->size(); ++i) { | 833 | for (int i = 0; i < events->size(); ++i) { |
| @@ -699,7 +848,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -699,7 +848,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 699 | // Determine sky pixel | 848 | // Determine sky pixel |
| 700 | GSkyPixel pixel; | 849 | GSkyPixel pixel; |
| 701 | try { | 850 | try { |
| 702 | - pixel = m_counts.dir2pix(dir); | 851 | + pixel = counts.dir2pix(dir); |
| 703 | } | 852 | } |
| 704 | catch (std::exception &e) { | 853 | catch (std::exception &e) { |
| 705 | num_invalid_wcs++; | 854 | num_invalid_wcs++; |
| @@ -708,8 +857,8 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -708,8 +857,8 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 708 | 857 | ||
| 709 | // Skip event if corresponding counts cube pixel is outside the | 858 | // Skip event if corresponding counts cube pixel is outside the |
| 710 | // counts cube map range | 859 | // counts cube map range |
| 711 | - if (pixel.x() < -0.5 || pixel.x() > (m_counts.nx()-0.5) || | ||
| 712 | - pixel.y() < -0.5 || pixel.y() > (m_counts.ny()-0.5)) { | 860 | + if (pixel.x() < -0.5 || pixel.x() > pixel_x_max || |
| 861 | + pixel.y() < -0.5 || pixel.y() > pixel_y_max) { | ||
| 713 | num_outside_map++; | 862 | num_outside_map++; |
| 714 | continue; | 863 | continue; |
| 715 | } | 864 | } |
| @@ -726,7 +875,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -726,7 +875,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 726 | } | 875 | } |
| 727 | 876 | ||
| 728 | // Fill event in skymap | 877 | // Fill event in skymap |
| 729 | - //#pragma omp critical(ctbin_fill_cube) | ||
| 730 | counts(pixel, iebin) += 1.0; | 878 | counts(pixel, iebin) += 1.0; |
| 731 | 879 | ||
| 732 | // Increment number of maps | 880 | // Increment number of maps |
| @@ -734,23 +882,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -734,23 +882,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 734 | 882 | ||
| 735 | } // endfor: looped over all events | 883 | } // endfor: looped over all events |
| 736 | 884 | ||
| 737 | - // Update time information | ||
| 738 | - /*#pragma omp critical(ctbin_fill_cube) | ||
| 739 | - { | ||
| 740 | - // If GTI is empty then set its time reference from the observation. | ||
| 741 | - // From then on we keep that time reference | ||
| 742 | - if (m_gti.is_empty()) { | ||
| 743 | - m_gti.reference(events->gti().reference()); | ||
| 744 | - } | ||
| 745 | - | ||
| 746 | - // Append GTIs | ||
| 747 | - m_gti.extend(events->gti()); | ||
| 748 | - | ||
| 749 | - // Update ontime and livetime | ||
| 750 | - m_ontime += obs->ontime(); | ||
| 751 | - m_livetime += obs->livetime(); | ||
| 752 | - }*/ | ||
| 753 | - | ||
| 754 | // Log filling results | 885 | // Log filling results |
| 755 | #pragma omp critical(ctbin_fill_cube) | 886 | #pragma omp critical(ctbin_fill_cube) |
| 756 | { | 887 | { |
| @@ -779,21 +910,10 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | @@ -779,21 +910,10 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) | ||
| 779 | * Sets the counts cube weights for all bins that are considered for the | 910 | * Sets the counts cube weights for all bins that are considered for the |
| 780 | * specific observation to unity. | 911 | * specific observation to unity. |
| 781 | ***************************************************************************/ | 912 | ***************************************************************************/ |
| 782 | -GSkyMap ctbin::set_weights(GCTAObservation* obs) | 913 | +GSkyMap ctbin::set_weights(const GCTAObservation* obs) |
| 783 | { | 914 | { |
| 784 | - // Make sure that the observation holds a CTA event list. If this | ||
| 785 | - // is not the case then throw an exception. | ||
| 786 | - const GCTAEventList* events = dynamic_cast<const GCTAEventList*> | ||
| 787 | - (obs->events()); | ||
| 788 | - if (events == NULL) { | ||
| 789 | - std::string msg = "CTA Observation does not contain an event " | ||
| 790 | - "list. An event list is needed to fill the " | ||
| 791 | - "counts cube."; | ||
| 792 | - throw GException::invalid_value(G_SET_WEIGHTS, msg); | ||
| 793 | - } | ||
| 794 | - | ||
| 795 | // Get the RoI | 915 | // Get the RoI |
| 796 | - const GCTARoi& roi = events->roi(); | 916 | + const GCTARoi& roi = obs->roi(); |
| 797 | 917 | ||
| 798 | // Check for RoI sanity | 918 | // Check for RoI sanity |
| 799 | if (!roi.is_valid()) { | 919 | if (!roi.is_valid()) { |
| @@ -804,19 +924,32 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) | @@ -804,19 +924,32 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) | ||
| 804 | throw GException::invalid_value(G_SET_WEIGHTS, msg); | 924 | throw GException::invalid_value(G_SET_WEIGHTS, msg); |
| 805 | } | 925 | } |
| 806 | 926 | ||
| 927 | + // Get the RoI centre and radius in radians | ||
| 928 | + GSkyDir roi_centre = roi.centre().dir(); | ||
| 929 | + double roi_radius = roi.radius() * gammalib::deg2rad; | ||
| 930 | + | ||
| 807 | // Get counts cube usage flags | 931 | // Get counts cube usage flags |
| 808 | - std::vector<bool> usage = cube_layer_usage(m_ebounds, events->ebounds()); | 932 | + std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds()); |
| 933 | + | ||
| 934 | + // Create weights cube | ||
| 935 | + GSkyMap weights = create_cube(obs); | ||
| 809 | 936 | ||
| 810 | - // Initialise new empty weights map | ||
| 811 | - GSkyMap weights(m_weights); | ||
| 812 | - weights *= 0.; | 937 | + // Set sky direction cache availability flag |
| 938 | + bool cache = (m_stack && m_dirs.size() == weights.npix()); | ||
| 813 | 939 | ||
| 814 | // Loop over all pixels in counts cube | 940 | // Loop over all pixels in counts cube |
| 815 | - for (int pixel = 0; pixel < m_counts.npix(); ++pixel) { | 941 | + for (int pixel = 0; pixel < weights.npix(); ++pixel) { |
| 816 | 942 | ||
| 817 | // Skip pixel if it is outside the RoI | 943 | // Skip pixel if it is outside the RoI |
| 818 | - if (roi.centre().dir().dist_deg(m_dirs[pixel]) > roi.radius()) { | ||
| 819 | - continue; | 944 | + if (cache) { |
| 945 | + if (m_dirs[pixel].dist(roi_centre) > roi_radius) { | ||
| 946 | + continue; | ||
| 947 | + } | ||
| 948 | + } | ||
| 949 | + else { | ||
| 950 | + if (roi_centre.dist(weights.inx2dir(pixel)) > roi_radius) { | ||
| 951 | + continue; | ||
| 952 | + } | ||
| 820 | } | 953 | } |
| 821 | 954 | ||
| 822 | // Loop over all energy layers of counts cube | 955 | // Loop over all energy layers of counts cube |
| @@ -828,7 +961,6 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) | @@ -828,7 +961,6 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) | ||
| 828 | } | 961 | } |
| 829 | 962 | ||
| 830 | // Signal that bin was filled | 963 | // Signal that bin was filled |
| 831 | - //#pragma omp critical(ctbin_set_weights) | ||
| 832 | weights(pixel, iebin) = 1.0; | 964 | weights(pixel, iebin) = 1.0; |
| 833 | 965 | ||
| 834 | } // endfor: looped over energy layers of counts cube | 966 | } // endfor: looped over energy layers of counts cube |
| @@ -851,8 +983,11 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) | @@ -851,8 +983,11 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) | ||
| 851 | * information in case that a single CTA observation is provided to support | 983 | * information in case that a single CTA observation is provided to support |
| 852 | * binned analysis. | 984 | * binned analysis. |
| 853 | ***************************************************************************/ | 985 | ***************************************************************************/ |
| 854 | -void ctbin::obs_cube(void) | 986 | +void ctbin::obs_cube_stacked(void) |
| 855 | { | 987 | { |
| 988 | + // Set event cube in first slot | ||
| 989 | + m_cubes[0] = GCTAEventCube(m_counts[0], m_weights[0], m_ebounds, m_gti); | ||
| 990 | + | ||
| 856 | // If we have only a single CTA observation in the container, then | 991 | // If we have only a single CTA observation in the container, then |
| 857 | // keep that observation and just attach the event cube to it. Reset | 992 | // keep that observation and just attach the event cube to it. Reset |
| 858 | // the filename, otherwise we still will have the old event filename | 993 | // the filename, otherwise we still will have the old event filename |
| @@ -865,9 +1000,9 @@ void ctbin::obs_cube(void) | @@ -865,9 +1000,9 @@ void ctbin::obs_cube(void) | ||
| 865 | 1000 | ||
| 866 | // Change the event type if we had an unbinned observation | 1001 | // Change the event type if we had an unbinned observation |
| 867 | if (obs->eventtype() == "EventList") { | 1002 | if (obs->eventtype() == "EventList") { |
| 868 | - | 1003 | + |
| 869 | // Assign cube to the observation | 1004 | // Assign cube to the observation |
| 870 | - obs->events(this->cube()); | 1005 | + obs->events(m_cubes[0]); |
| 871 | 1006 | ||
| 872 | } | 1007 | } |
| 873 | 1008 | ||
| @@ -877,7 +1012,8 @@ void ctbin::obs_cube(void) | @@ -877,7 +1012,8 @@ void ctbin::obs_cube(void) | ||
| 877 | else { | 1012 | else { |
| 878 | 1013 | ||
| 879 | // Create empty counts cube | 1014 | // Create empty counts cube |
| 880 | - GCTAEventCube cube(m_counts, m_weights, m_ebounds, obs->gti()); | 1015 | + GCTAEventCube cube(m_counts[0], m_weights[0], m_ebounds, |
| 1016 | + obs->gti()); | ||
| 881 | 1017 | ||
| 882 | // Assign empty cube to observation | 1018 | // Assign empty cube to observation |
| 883 | obs->events(cube); | 1019 | obs->events(cube); |
| @@ -899,49 +1035,49 @@ void ctbin::obs_cube(void) | @@ -899,49 +1035,49 @@ void ctbin::obs_cube(void) | ||
| 899 | // Allocate observation container | 1035 | // Allocate observation container |
| 900 | GObservations container; | 1036 | GObservations container; |
| 901 | 1037 | ||
| 902 | - // Allocate CTA observation. | ||
| 903 | - GCTAObservation obs; | ||
| 904 | - | ||
| 905 | - // Attach event cube to CTA observation | ||
| 906 | - obs.events(this->cube()); | ||
| 907 | - | ||
| 908 | - // Compute average pointing direction for all CTA event lists | ||
| 909 | - double ra = 0.0; | ||
| 910 | - double dec = 0.0; | ||
| 911 | - double number = 0.0; | ||
| 912 | - for (int i = 0; i < m_obs.size(); ++i) { | ||
| 913 | - GCTAObservation* cta = dynamic_cast<GCTAObservation*>(m_obs[i]); | ||
| 914 | - if ((cta != NULL) && (cta->eventtype() == "EventList")) { | ||
| 915 | - ra += cta->pointing().dir().ra(); | ||
| 916 | - dec += cta->pointing().dir().dec(); | ||
| 917 | - number += 1.0; | ||
| 918 | - } | ||
| 919 | - } | ||
| 920 | - if (number > 0.0) { | ||
| 921 | - ra /= number; | ||
| 922 | - dec /= number; | ||
| 923 | - } | ||
| 924 | - GSkyDir dir; | ||
| 925 | - dir.radec(ra, dec); | ||
| 926 | - GCTAPointing pointing(dir); | ||
| 927 | - | ||
| 928 | - // Compute deadtime correction | ||
| 929 | - double deadc = (m_ontime > 0.0) ? m_livetime / m_ontime : 0.0; | ||
| 930 | - | ||
| 931 | - // Set CTA observation attributes | ||
| 932 | - obs.pointing(pointing); | ||
| 933 | - obs.ra_obj(dir.ra_deg()); //!< Dummy | ||
| 934 | - obs.dec_obj(dir.dec_deg()); //!< Dummy | ||
| 935 | - obs.ontime(m_ontime); | ||
| 936 | - obs.livetime(m_livetime); | ||
| 937 | - obs.deadc(deadc); | ||
| 938 | - | ||
| 939 | - // Append CTA observation | ||
| 940 | - container.append(obs); | 1038 | + // Allocate CTA observation. |
| 1039 | + GCTAObservation obs; | ||
| 1040 | + | ||
| 1041 | + // Attach event cube to CTA observation | ||
| 1042 | + obs.events(m_cubes[0]); | ||
| 1043 | + | ||
| 1044 | + // Compute average pointing direction for all CTA event lists | ||
| 1045 | + double ra = 0.0; | ||
| 1046 | + double dec = 0.0; | ||
| 1047 | + double number = 0.0; | ||
| 1048 | + for (int i = 0; i < m_obs.size(); ++i) { | ||
| 1049 | + GCTAObservation* cta = dynamic_cast<GCTAObservation*>(m_obs[i]); | ||
| 1050 | + if ((cta != NULL) && (cta->eventtype() == "EventList")) { | ||
| 1051 | + ra += cta->pointing().dir().ra(); | ||
| 1052 | + dec += cta->pointing().dir().dec(); | ||
| 1053 | + number += 1.0; | ||
| 1054 | + } | ||
| 1055 | + } | ||
| 1056 | + if (number > 0.0) { | ||
| 1057 | + ra /= number; | ||
| 1058 | + dec /= number; | ||
| 1059 | + } | ||
| 1060 | + GSkyDir dir; | ||
| 1061 | + dir.radec(ra, dec); | ||
| 1062 | + GCTAPointing pointing(dir); | ||
| 1063 | + | ||
| 1064 | + // Compute deadtime correction | ||
| 1065 | + double deadc = (m_ontime > 0.0) ? m_livetime / m_ontime : 0.0; | ||
| 1066 | + | ||
| 1067 | + // Set CTA observation attributes | ||
| 1068 | + obs.pointing(pointing); | ||
| 1069 | + obs.ra_obj(dir.ra_deg()); //!< Dummy | ||
| 1070 | + obs.dec_obj(dir.dec_deg()); //!< Dummy | ||
| 1071 | + obs.ontime(m_ontime); | ||
| 1072 | + obs.livetime(m_livetime); | ||
| 1073 | + obs.deadc(deadc); | ||
| 941 | 1074 | ||
| 942 | // Set models in observation container | 1075 | // Set models in observation container |
| 943 | container.models(m_obs.models()); | 1076 | container.models(m_obs.models()); |
| 944 | 1077 | ||
| 1078 | + // Append CTA observation | ||
| 1079 | + container.append(obs); | ||
| 1080 | + | ||
| 945 | // Copy over all remaining non-CTA observations | 1081 | // Copy over all remaining non-CTA observations |
| 946 | for (int i = 0; i < m_obs.size(); ++i) { | 1082 | for (int i = 0; i < m_obs.size(); ++i) { |
| 947 | GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); | 1083 | GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); |
src/ctbin/ctbin.hpp
| 1 | /*************************************************************************** | 1 | /*************************************************************************** |
| 2 | * ctbin - Event binning tool * | 2 | * ctbin - Event binning tool * |
| 3 | * ----------------------------------------------------------------------- * | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2010-2017 by Juergen Knoedlseder * | 4 | + * copyright (C) 2010-2018 by Juergen Knoedlseder * |
| 5 | * ----------------------------------------------------------------------- * | 5 | * ----------------------------------------------------------------------- * |
| 6 | * * | 6 | * * |
| 7 | * This program is free software: you can redistribute it and/or modify * | 7 | * This program is free software: you can redistribute it and/or modify * |
| @@ -81,10 +81,10 @@ public: | @@ -81,10 +81,10 @@ public: | ||
| 81 | // Methods | 81 | // Methods |
| 82 | void clear(void); | 82 | void clear(void); |
| 83 | void run(void); | 83 | void run(void); |
| 84 | + int cubes(void) const; | ||
| 85 | + const GCTAEventCube& cube(const int& index = 0) const; | ||
| 84 | void save(void); | 86 | void save(void); |
| 85 | void publish(const std::string& name = ""); | 87 | void publish(const std::string& name = ""); |
| 86 | - const GCTAEventCube& cube(void) const; | ||
| 87 | - const GCTAEventCube& cube(const size_t& index) const; | ||
| 88 | 88 | ||
| 89 | protected: | 89 | protected: |
| 90 | // Protected methods | 90 | // Protected methods |
| @@ -92,60 +92,46 @@ protected: | @@ -92,60 +92,46 @@ protected: | ||
| 92 | void copy_members(const ctbin& app); | 92 | void copy_members(const ctbin& app); |
| 93 | void free_members(void); | 93 | void free_members(void); |
| 94 | void get_parameters(void); | 94 | void get_parameters(void); |
| 95 | - GSkyMap fill_cube(GCTAObservation* obs); | ||
| 96 | - GSkyMap set_weights(GCTAObservation* obs); | ||
| 97 | - void obs_cube(void); | 95 | + void init_sky_dir_cache(void); |
| 96 | + GSkyMap create_cube(const GCTAObservation* obs); | ||
| 97 | + GSkyMap fill_cube(const GCTAObservation* obs); | ||
| 98 | + GSkyMap set_weights(const GCTAObservation* obs); | ||
| 99 | + void obs_cube_stacked(void); | ||
| 98 | 100 | ||
| 99 | // User parameters | 101 | // User parameters |
| 100 | - GFilename m_outcube; //!< Output counts map file name | ||
| 101 | - bool m_usepnt; //!< Use pointing instead of xref/yref parameters | ||
| 102 | - bool m_stack; //!< Output one stacked cube or multiple cubes | ||
| 103 | - std::string m_prefix; //!< Prefix for output path of multiple cubes | ||
| 104 | - bool m_publish; //!< Publish counts cube? | ||
| 105 | - GChatter m_chatter; //!< Chattiness | 102 | + GFilename m_outcube; //!< Output counts map file name |
| 103 | + bool m_usepnt; //!< Use pointing instead of xref/yref parameters | ||
| 104 | + bool m_stack; //!< Output one stacked cube or multiple cubes | ||
| 105 | + std::string m_prefix; //!< Prefix for output path of multiple cubes | ||
| 106 | + bool m_publish; //!< Publish counts cube? | ||
| 107 | + GChatter m_chatter; //!< Chattiness | ||
| 106 | 108 | ||
| 107 | // Protected members | 109 | // Protected members |
| 108 | - GSkyMap m_counts; //!< Event cube counts | ||
| 109 | - GSkyMap m_weights; //!< Event cube weights | ||
| 110 | - GEbounds m_ebounds; //!< Energy boundaries | ||
| 111 | - GGti m_gti; //!< Good time intervals | ||
| 112 | - GCTAEventCube m_cube; //!< Events cube (for cube() method) | ||
| 113 | - double m_ontime; //!< Total ontime | ||
| 114 | - double m_livetime; //!< Total livetime | ||
| 115 | - std::vector<GCTAEventCube> m_cubes; //!< Event cubes (for cube() method) | ||
| 116 | - std::vector<std::string> m_ids; //!< Observation ids | 110 | + std::vector<GCTAEventCube> m_cubes; //!< Event cubes |
| 111 | + std::vector<GSkyMap> m_counts; //!< List of event cube counts | ||
| 112 | + std::vector<GSkyMap> m_weights; //!< List of event cube weights | ||
| 113 | + std::vector<std::string> m_ids; //!< List of observation ids | ||
| 114 | + GEbounds m_ebounds; //!< Energy boundaries | ||
| 115 | + GGti m_gti; //!< Stacked Good time intervals | ||
| 116 | + double m_ontime; //!< Total ontime | ||
| 117 | + double m_livetime; //!< Total livetime | ||
| 117 | 118 | ||
| 118 | // Cache members | 119 | // Cache members |
| 119 | - std::vector<GSkyDir> m_dirs; //!< Cached GSkyDir for each pixel in m_counts | 120 | + std::vector<GSkyDir> m_dirs; //!< Cached GSkyDir for all pixels |
| 120 | }; | 121 | }; |
| 121 | 122 | ||
| 122 | 123 | ||
| 123 | /***********************************************************************//** | 124 | /***********************************************************************//** |
| 124 | - * @brief Return event cube | 125 | + * @brief Return number of event cubes |
| 125 | * | 126 | * |
| 126 | - * @return Reference to event cube | 127 | + * @return Number of event cubes. |
| 127 | * | 128 | * |
| 128 | - * Returns a reference to the event cube. | 129 | + * Returns number of event cubes. |
| 129 | ***************************************************************************/ | 130 | ***************************************************************************/ |
| 130 | inline | 131 | inline |
| 131 | -const GCTAEventCube& ctbin::cube(void) const | 132 | +int ctbin::cubes(void) const |
| 132 | { | 133 | { |
| 133 | - return m_cube; | ||
| 134 | -} | ||
| 135 | - | ||
| 136 | - | ||
| 137 | -/***********************************************************************//** | ||
| 138 | - * @brief Return event cube at index | ||
| 139 | - * | ||
| 140 | - * @param[in] index Index of cube | ||
| 141 | - * @return Reference to event cube | ||
| 142 | - * | ||
| 143 | - * Returns a reference to the event cube at the given index. | ||
| 144 | - ***************************************************************************/ | ||
| 145 | -inline | ||
| 146 | -const GCTAEventCube& ctbin::cube(const size_t& index) const | ||
| 147 | -{ | ||
| 148 | - return m_cubes[index]; | 134 | + return m_cubes.size(); |
| 149 | } | 135 | } |
| 150 | 136 | ||
| 151 | #endif /* CTBIN_HPP */ | 137 | #endif /* CTBIN_HPP */ |
src/ctbin/ctbin.par
| @@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
| 18 | #=================== | 18 | #=================== |
| 19 | inobs, f, a, events.fits,,, "Input event list or observation definition XML file" | 19 | inobs, f, a, events.fits,,, "Input event list or observation definition XML file" |
| 20 | outcube, f, a, cntcube.fits,,, "Output counts cube file" | 20 | outcube, f, a, cntcube.fits,,, "Output counts cube file" |
| 21 | -stack, b, h, yes,,, "Stack events to one cube?" | 21 | +stack, b, h, yes,,, "Stack events into one cube?" |
| 22 | prefix, s, h, binned_,,, "Prefix for counts cubes in observation definition XML file" | 22 | prefix, s, h, binned_,,, "Prefix for counts cubes in observation definition XML file" |
| 23 | 23 | ||
| 24 | # | 24 | # |
test/test_ctbin.py
| @@ -165,7 +165,7 @@ class Test(test): | @@ -165,7 +165,7 @@ class Test(test): | ||
| 165 | # cube | 165 | # cube |
| 166 | self.test_value(binning.obs().size(), 0, | 166 | self.test_value(binning.obs().size(), 0, |
| 167 | 'Check that empty ctbin has an empty observation container') | 167 | 'Check that empty ctbin has an empty observation container') |
| 168 | - self.test_value(binning.cube().size(), 0, | 168 | + self.test_value(binning.cubes(), 0, |
| 169 | 'Check that empty ctbin has an empty counts cube') | 169 | 'Check that empty ctbin has an empty counts cube') |
| 170 | 170 | ||
| 171 | # Check that saving does not nothing | 171 | # Check that saving does not nothing |
| @@ -220,11 +220,13 @@ class Test(test): | @@ -220,11 +220,13 @@ class Test(test): | ||
| 220 | cpy_bin.logFileOpen() | 220 | cpy_bin.logFileOpen() |
| 221 | cpy_bin.run() | 221 | cpy_bin.run() |
| 222 | 222 | ||
| 223 | - # Check content of observation container and counts cube. Now an empty | ||
| 224 | - # event cube is expected since on input the observation is binned, and | ||
| 225 | - # any binned observation will be skipped. | ||
| 226 | - self._check_observation(cpy_bin, 0) | ||
| 227 | - self._check_cube(cpy_bin.cube(), 0) | 223 | + # Check content of observation container and number of counts cubes. |
| 224 | + # There should be a single binned observation in the observation | ||
| 225 | + # container, which is the one that was produced in the run before. | ||
| 226 | + # Since ctbin finds no unbinned observation in the container, the | ||
| 227 | + # number of cubes should be zero. | ||
| 228 | + self._check_observation(cpy_bin, 245) | ||
| 229 | + self.test_value(cpy_bin.cubes(), 0, 'Check that there are no counts cubes') | ||
| 228 | 230 | ||
| 229 | # Save counts cube | 231 | # Save counts cube |
| 230 | binning.save() | 232 | binning.save() |
| @@ -240,7 +242,7 @@ class Test(test): | @@ -240,7 +242,7 @@ class Test(test): | ||
| 240 | # counts cube | 242 | # counts cube |
| 241 | self.test_value(binning.obs().size(), 0, | 243 | self.test_value(binning.obs().size(), 0, |
| 242 | 'Check that empty ctbin has an empty observation container') | 244 | 'Check that empty ctbin has an empty observation container') |
| 243 | - self.test_value(binning.cube().size(), 0, | 245 | + self.test_value(binning.cubes(), 0, |
| 244 | 'Check that empty ctbin has an empty counts cube') | 246 | 'Check that empty ctbin has an empty counts cube') |
| 245 | 247 | ||
| 246 | # Prepare observation container for stacking of events into a | 248 | # Prepare observation container for stacking of events into a |