GammaLib 2.2.0.dev
Loading...
Searching...
No Matches
GCOSResponse.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GCOSResponse.cpp - COSI response class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2026 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- *
6 * *
7 * This program is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * *
20 ***************************************************************************/
21/**
22 * @file GCOSResponse.hpp
23 * @brief COSI instrument response function class implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <cstdio> // std::fopen, std::fgets, std::fclose, etc...
32#include <string>
33#include <typeinfo>
34#include "GException.hpp"
35#include "GSource.hpp"
37#include "GFits.hpp"
38#include "GFitsBinTable.hpp"
39#include "GFitsTableLongCol.hpp"
43#include "GCOSResponse.hpp"
44#include "GCOSObservation.hpp"
45
46/* __ Method name definitions ____________________________________________ */
47#define G_IRF "GCOSResponse::irf(GEvent&, GSource&, GObservation&)"
48#define G_NROI "GCOSResponse::nroi(GModelSky&, GEnergy&, GTime&, "\
49 "GObservation&)"
50#define G_EBOUNDS "GCOSResponse::ebounds(GEnergy&)"
51#define G_LOAD "GCOSResponse::load(GFilename&)"
52#define G_LOAD_RESPONSE_CHUNK "GCOSResponse::load_response_chunk(int&)"
53#define G_READ_BOUNDS "GCOSResponse::read_bounds(FILE*, GXmlElement*, "\
54 "GXmlElement*, GXmlElement*, std::string&)"
55#define G_READ_EBOUNDS "GCOSResponse::read_ebounds(FILE*, GXmlElement*, "\
56 "GXmlElement*, GXmlElement*, std::string&)"
57#define G_READ_NDARRAY "GCOSResponse::read_ndarray(FILE*, GXmlElement*, "\
58 "GXmlElement*, GXmlElement*)"
59#define G_READ_RESPONSE_MATRIX "GCOSResponse::read_response_matrix(FILE*, "\
60 "GXmlElement*, GXmlElement*, GXmlElement*, GXmlElement*)"
61#define G_GET_RESPONSE_CHUNK_KEY "GCOSResponse::get_response_chunk_key(int&)"
62
63/* __ Macros _____________________________________________________________ */
64
65/* __ Coding definitions _________________________________________________ */
66
67/* __ Debug definitions __________________________________________________ */
68
69/* __ Constants __________________________________________________________ */
70
71/* __ Using of namespaces ________________________________________________ */
72using namespace gammalib::hdf5;
73
74
75/*==========================================================================
76 = =
77 = Constructors/destructors =
78 = =
79 ==========================================================================*/
80
81/***********************************************************************//**
82 * @brief Void constructor
83 *
84 * Creates an empty COSI response.
85 ***************************************************************************/
87{
88 // Initialise members
90
91 // Return
92 return;
93}
94
95
96/***********************************************************************//**
97 * @brief Load constructor
98 *
99 * @param[in] filename COSI response file.
100 *
101 * Create COSI response by loading data from a response file.
102 ***************************************************************************/
104{
105 // Initialise members
106 init_members();
107
108 // Load file
109 load(filename);
110
111 // Return
112 return;
113}
114
115
116/***********************************************************************//**
117 * @brief Copy constructor
118 *
119 * @param[in] rsp COSI response.
120 **************************************************************************/
122{
123 // Initialise members
124 init_members();
125
126 // Copy members
127 copy_members(rsp);
128
129 // Return
130 return;
131}
132
133
134/***********************************************************************//**
135 * @brief Destructor
136 ***************************************************************************/
138{
139 // Free members
140 free_members();
141
142 // Return
143 return;
144}
145
146
147/*==========================================================================
148 = =
149 = Operators =
150 = =
151 ==========================================================================*/
152
153/***********************************************************************//**
154 * @brief Assignment operator
155 *
156 * @param[in] rsp COSI response.
157 * @return COSI response.
158 *
159 * Assign COSI response to this object. The assignment performs
160 * a deep copy of all information, hence the original object from which the
161 * assignment was made can be destroyed after this operation without any loss
162 * of information.
163 ***************************************************************************/
165{
166 // Execute only if object is not identical
167 if (this != &rsp) {
168
169 // Copy base class members
170 this->GResponse::operator=(rsp);
171
172 // Free members
173 free_members();
174
175 // Initialise members
176 init_members();
177
178 // Copy members
179 copy_members(rsp);
180
181 } // endif: object was not identical
182
183 // Return this object
184 return *this;
185}
186
187
188/*==========================================================================
189 = =
190 = Public methods =
191 = =
192 ==========================================================================*/
193
194/***********************************************************************//**
195 * @brief Clear instance
196 *
197 * Clears COSI response by resetting all class members to an initial
198 * state. Any information that was present before will be lost.
199 ***************************************************************************/
201{
202 // Free class members (base and derived classes, derived class first)
203 free_members();
205
206 // Initialise members
208 init_members();
209
210 // Return
211 return;
212}
213
214
215/***********************************************************************//**
216 * @brief Clone instance
217 *
218 * @return Pointer to deep copy of COSI response.
219 ***************************************************************************/
221{
222 return new GCOSResponse(*this);
223}
224
225
226/***********************************************************************//**
227 * @brief Return value of COSI instrument response for a photon
228 *
229 * @param[in] event Observed event.
230 * @param[in] photon Incident photon.
231 * @param[in] obs Observation.
232 * @return Instrument response $\f(cm^2 sr^{-1})$\f
233 *
234 * @exception GException::invalid_argument
235 * Observation is not a COSI observation.
236 *
237 * Returns the instrument response function for a given observed photon
238 * direction as function of the assumed true photon direction. The result
239 * is given by
240 *
241 * \f[
242 * R(p'|p) =
243 * \f]
244 *
245 * @todo Write down formula
246 * @todo Describe in detail how the response is computed.
247 * @todo Implement method.
248 ***************************************************************************/
249double GCOSResponse::irf(const GEvent& event,
250 const GPhoton& photon,
251 const GObservation& obs) const
252{
253 // Extract COSI observation
254 const GCOSObservation* observation = dynamic_cast<const GCOSObservation*>(&obs);
255 if (observation == NULL) {
256 std::string cls = std::string(typeid(&obs).name());
257 std::string msg = "Observation of type \""+cls+"\" is not a COSI "
258 "observation. Please specify a COSI observation "
259 "as argument.";
261 }
262
263 // Extract COSI event bin
264 const GCOSEventBin* bin = dynamic_cast<const GCOSEventBin*>(&event);
265 if (bin == NULL) {
266 std::string cls = std::string(typeid(&event).name());
267 std::string msg = "Event of type \""+cls+"\" is not a COSI event. "
268 "Please specify a COSI event as argument.";
270 }
271
272 // Initialise IRF
273 double irf = 0.0;
274
275 // Get response indices for true quantities (no interpolation so far)
276 int itdir = int(src_dirs().dir2pix(photon.dir()).index() + 0.5);
277 int iteng = src_ebounds().index(photon.energy());
278 int itpol = 0;
279
280 // Continue only if indices are valid
281 if ((itdir >= 0) && (iteng >= 0)) {
282
283 // Get response chunk index
284 int index = get_response_chunk(itdir, iteng, itpol);
285
286 // Continue only if the response chunk exists
287 if (index != -1) {
288
289 // Get response indices for measured quantities (no interpolation so far)
290 int imdir = int(obs_dirs().dir2pix(bin->dir().dir()).index() + 0.5);
291 int imphi = obs_phis().index(bin->dir().phi());
292 int imeng = obs_ebounds().index(bin->energy());
293 if ((imdir >= 0) && (imphi >= 0) && (imeng >= 0)) {
294
295 // Get response element index (no interpolation so far)
296 int i = (imphi + imeng * obs_phis().size()) * obs_dirs().npix() + imdir;
297
298 // Get response element
299 irf = m_rsp_chunks[index][i];
300
301 // Multiply with effective area
302 irf *= eff_areas()(itdir);
303
304 // Multiply with livetime weight
305 irf *= (observation->spacecraft().livetime(photon.dir()) /
306 observation->spacecraft().livetime());
307
308 } // endif: measured response indices were valid
309
310 } // endif: response chunk existed
311
312 } // endif: true response indices were valid
313
314 // Compile option: Check for NaN/Inf
315 #if defined(G_NAN_CHECK)
317 std::cout << "*** ERROR: GCOSResponse::irf:";
318 std::cout << " NaN/Inf encountered";
319 std::cout << " (irf=" << irf;
320 std::cout << ")";
321 std::cout << std::endl;
322 }
323 #endif
324
325 // Return IRF value
326 return irf;
327}
328
329
330/***********************************************************************//**
331 * @brief Return integral of event probability for a given sky model over ROI
332 *
333 * @param[in] model Sky model.
334 * @param[in] obsEng Observed photon energy.
335 * @param[in] obsTime Observed photon arrival time.
336 * @param[in] obs Observation.
337 * @return 0.0
338 *
339 * @exception GException::feature_not_implemented
340 * Method is not implemented.
341 ***************************************************************************/
342double GCOSResponse::nroi(const GModelSky& model,
343 const GEnergy& obsEng,
344 const GTime& obsTime,
345 const GObservation& obs) const
346{
347 // Method is not implemented
348 std::string msg = "Spatial integration of sky model over the data space "
349 "is not implemented.";
351
352 // Return Npred
353 return (0.0);
354}
355
356
357/***********************************************************************//**
358 * @brief Return true energy boundaries for a specific observed energy
359 *
360 * @param[in] obsEnergy Observed Energy.
361 * @return True energy boundaries for given observed energy.
362 *
363 * @exception GException::feature_not_implemented
364 * Method is not implemented.
365 *
366 * @todo Implement this method if you need energy dispersion.
367 ***************************************************************************/
368GEbounds GCOSResponse::ebounds(const GEnergy& obsEnergy) const
369{
370 // Initialise an empty boundary object
372
373 // Throw an exception
374 std::string msg = "Energy dispersion not implemented.";
376
377 // Return energy boundaries
378 return ebounds;
379}
380
381
382/***********************************************************************//**
383 * @brief Get response chunk
384 *
385 * @param[in] idir True photon direction index [0,...,src_dirs.npix()-1].
386 * @param[in] ieng True photon energy index [0,...,src_ebounds.size()-1].
387 * @param[in] ipol True polarisation angle index [0,...,src_polarisations.size()-1].
388 * @return Response chunk index
389
390 * @exception GException::out_of_range
391 * Indices out of range
392 *
393 * Return index to a response chunk for the specified true photon direction
394 * @p idir, true photon energy @p ieng, and true polarisation angle @p ipol.
395 * If the corresponding response chunk has not yet been loaded it will be
396 * loaded by the method.
397 *
398 * If no valid response chunk exists for the specified parameters, the method
399 * will return -1.
400 ***************************************************************************/
401int GCOSResponse::get_response_chunk(const int& idir,
402 const int& ieng,
403 const int& ipol) const
404{
405 // Initialise index
406 int index = -1;
407
408 // Continue only if data layout pointer is valid
409 if (m_rsp_chunk_datalayout != NULL) {
410
411 // Get chunk dimensions
412 int ndirs = m_src_dirs.npix();
413 int nengs = m_src_ebounds.size();
414 int npols = m_src_pols.size();
415
416 // Check validity of indices
417 if (idir < 0 || idir >= ndirs) {
419 "True photon direction index",
420 idir, ndirs);
421 }
422 if (ieng < 0 || ieng >= nengs) {
424 "True photon energy index",
425 ieng, nengs);
426 }
427 if ((npols > 0) && (ipol < 0 || ipol >= npols)) {
429 "True polarisation angle index",
430 ipol, npols);
431 }
432
433 // Get response chunk index
434 index = get_response_chunk_index(idir, ieng, ipol);
435
436 // Continue only if the index was not already loaded
437 if (index == -1) {
438
439 // Set storage index
440 index = flat_index(idir, ieng, ipol);
441
442 // Get key for chunk (method checks for key validity)
443 const GXmlElement* key = get_response_chunk_key(idir, ieng, ipol);
444
445 // Expand environment variables in response file name
446 std::string fname = gammalib::expand_env(m_rspfile);
447
448 // Open HDF5 file (read-only)
449 FILE* fptr = std::fopen(fname.c_str(), "rb");
450 if (fptr == NULL) {
451 std::string msg = "Unable to open file \""+fname+"\" for "
452 "read access. Please load a readable "
453 "HDF5 file before calling that method.";
455 }
456
457 // Read data (handles data filtering)
458 std::string data = fread_data_chunk(fptr, key,
463
464 // Close file
465 std::fclose(fptr);
466
467 // Get number of data elements and size
468 int length = data.length();
470 int elements = length / size;
471
472 // Initialise vector to hold data
473 std::vector<uint16_t> chunk(elements, 0);
474
475 // Initialise data pointer
476 const char* ptr = data.c_str();
477
478 // Convert data into vector (no checking)
479 for (int i = 0; i < elements; ++i, ptr += size) {
481 chunk[i] = value;
482 }
483
484 // Push back index and data
485 m_rsp_chunk_indices.push_back(index);
486 m_rsp_chunks.push_back(chunk);
487
488 } // endif: index not already loaded
489
490 } // endif: data layout pointer was valid
491
492 // Return index
493 return index;
494}
495
496
497/***********************************************************************//**
498 * @brief Load COSI response
499 *
500 * @param[in] filename COSI response file.
501 *
502 * Load the COSI response either a HDF5 file or a FITS file.
503 *
504 * If a HDF5 file is specified, the method loads the metadata from the file
505 * but does not actually load any response data. Response data are loaded
506 * upon request from the information stored in the HDF5 metadata.
507 *
508 * If a FITS file is specified, the actual response chunks are loaded from
509 * the FITS file, including all response attributes that are needed by the
510 * class. In that case no HDF5 information is provided and loading of new
511 * response chunks upon request is not possible.
512 ***************************************************************************/
513void GCOSResponse::load(const GFilename& filename)
514{
515 // Clear instance
516 clear();
517
518 // If file is a FITS file then read response from FITS file
519 if (filename.is_fits()) {
520
521 // Open FITS file
522 GFits fits(filename);
523
524 // Read response from FITS file
525 read(fits);
526
527 // Close FITS file
528 fits.close();
529
530 } // endif: file was a FITS file
531
532 // ... otherwise load the response from a HDF5 file
533 else {
534
535 // Expand environment variables in filename
536 std::string fname = gammalib::expand_env(filename.url());
537
538 // Open HDF5 file (read-only)
539 FILE* fptr = std::fopen(fname.c_str(), "rb");
540 if (fptr == NULL) {
541 std::string msg = "Unable to open file \""+fname+"\" for read access. "
542 "Please specify a readable file.";
543 throw GException::file_error(G_LOAD, msg);
544 }
545
546 // Read HDF5 file
547 m_hdf5.read(fptr);
548
549 // Read response
550 read_response(fptr);
551
552 // Close file
553 std::fclose(fptr);
554
555 } // endelse: file was not a FITS file
556
557 // Store filename
558 m_rspfile = filename;
559
560 // Return
561 return;
562}
563
564
565/***********************************************************************//**
566 * @brief Save response
567 *
568 * @param[in] filename FITS file name.
569 * @param[in] clobber Overwrite existing file?
570 *
571 * Saves the response chunks into a FITS file. If the file exists already and
572 * the @p clobber parameter is true, the method will overwrite the content of
573 * the existing file. Otherwise, an exception is thrown.
574 ***************************************************************************/
575void GCOSResponse::save(const GFilename& filename, const bool& clobber) const
576{
577 // Initialise FITS file
578 GFits fits;
579
580 // Write response into FITS file
581 write(fits);
582
583 // Save FITS file
584 fits.saveto(filename, clobber);
585
586 // Return
587 return;
588}
589
590
591/***********************************************************************//**
592 * @brief Read response from FITS file
593 *
594 * @param[in] fits FITS file.
595 *
596 * Reads the response from a FITS file. This method overwrites any existing
597 * response information yet it does not modify the response file name or
598 * any HDF5 metadata. In that was it may be used to load response chunks
599 * that were saved on disk without altering the other response information.
600 ***************************************************************************/
601void GCOSResponse::read(const GFits& fits)
602{
603 // Read effective area values
604 const GFitsTable* eff_area = fits.table("EFFECTIVE AREAS");
605 if (!eff_area->is_empty()) {
606 const GFitsTableCol* col_eff_area = (*eff_area)["Aeff"];
607 int size = eff_area->nrows();
608 m_eff_areas = GNdarray(size);
609 for (int i = 0; i < size; ++i) {
610 m_eff_areas(i) = col_eff_area->real(i);
611 }
612 }
613
614 // Read true energy boundaries
615 const GFitsTable* src_ebounds = fits.table("TRUE ENERGY BOUNDARIES");
616 if (!src_ebounds->is_empty()) {
618 }
619
620 // Read true polarisation boundaries
621 const GFitsTable* src_pol = fits.table("TRUE POLARISATION BOUNDARIES");
622 if (!src_pol->is_empty()) {
623 m_src_pols.read(*src_pol);
624 m_src_pols.unit("deg");
625 }
626
627 // Read measured Phi boundaries
628 const GFitsTable* obs_phi = fits.table("MEASURED PHI BOUNDARIES");
629 if (!obs_phi->is_empty()) {
630 m_obs_phis.read(*obs_phi);
631 m_obs_phis.unit("deg");
632 }
633
634 // Read measured energy boundaries
635 const GFitsTable* obs_ebounds = fits.table("MEASURED ENERGY BOUNDARIES");
636 if (!obs_ebounds->is_empty()) {
638 }
639
640 // Read measured sky projection
641 const GFitsTable* obs_healpix = fits.table("MEASURED SKY DIRECTIONS");
642 m_obs_dirs.read(*obs_healpix);
643
644 // Read true sky projection, chunk indices, and response chunks
645 const GFitsTable* src_healpix = fits.table("TRUE SKY DIRECTIONS");
646 m_src_dirs.read(*src_healpix);
647 if (!src_healpix->is_empty()) {
648 const GFitsTableCol* col_index = (*src_healpix)["Index"];
649 const GFitsTableCol* col_name = (*src_healpix)["Name"];
650 int chunks = src_healpix->nrows();
651 m_rsp_chunk_indices.resize(chunks);
652 m_rsp_chunks.resize(chunks);
653 for (int i = 0; i < chunks; ++i) {
654
655 // Read chunk index
656 m_rsp_chunk_indices[i] = col_index->integer(i);
657
658 // Read extension name for chunk
659 std::string extname = col_name->string(i);
660
661 // Get chunk binary table and data column
662 const GFitsTable* chunk = fits.table(extname);
663 if (!chunk->is_empty()) {
664
665 const GFitsTableCol* col_chunk = (*chunk)["Response"];
666
667 // Get number of pixels and columns in column
668 int npix = col_chunk->nrows();
669 int ncols = col_chunk->number();
670
671 // Allocate storage for pixels
672 m_rsp_chunks[i].resize(npix*ncols);
673
674 // Read response chunk
675 for (int col = 0, inx = 0; col < ncols; ++col) {
676 for (int ipix = 0; ipix < npix; ++ipix, ++inx) {
677 m_rsp_chunks[i][inx] = (uint16_t)(col_chunk->integer(ipix, col));
678 }
679 }
680
681 } // endif: chunk table was not empty
682
683 } // endfor: looped over chunks
684 } // endif: table was not empty
685
686 // Return
687 return;
688}
689
690
691/***********************************************************************//**
692 * @brief Write response into FITS file
693 *
694 * @param[in] file FITS file.
695 *
696 * Writes the response into a FITS file. The method appends the following
697 * extensions to the FITS file:
698 * - EFFECTIVE AREAS
699 * - TRUE SKY DIRECTIONS
700 * - TRUE ENERGY BOUNDARIES
701 * - TRUE POLARISATION BOUNDARIES
702 * - MEASURED PHI BOUNDARIES
703 * - MEASURED ENERGY BOUNDARIES
704 * - CHUNKi
705 ***************************************************************************/
706void GCOSResponse::write(GFits& fits) const
707{
708 // Determine number of response chunks
709 int chunks = m_rsp_chunk_indices.size();
710
711 // Write effective area values into binary table and append table to
712 // FITS file
713 GFitsBinTable eff_area;
714 eff_area.extname("EFFECTIVE AREAS");
715 int size = m_eff_areas.size();
716 if (size > 0) {
717 GFitsTableDoubleCol col_eff_area("Aeff", size);
718 //col_eff_area.unit("unit");
719 for (int i = 0; i < size; ++i) {
720 col_eff_area(i) = m_eff_areas(i);
721 }
722 eff_area.append(col_eff_area);
723 }
724 fits.append(eff_area);
725
726 // Write true sky projection, chunk indices and FITS extension names
727 // into binary table and append table to FITS file
728 GFitsBinTable src_healpix;
729 m_src_dirs.write(src_healpix);
730 src_healpix.extname("TRUE SKY DIRECTIONS");
731 if (chunks > 0) {
732 GFitsTableLongCol col_index("Index", chunks);
733 GFitsTableStringCol col_name("Name", chunks, 10);
734 for (int i = 0; i < chunks; ++i) {
735 col_index(i) = m_rsp_chunk_indices[i];
736 col_name(i) = "CHUNK" + gammalib::str(i);
737 }
738 src_healpix.append(col_index);
739 src_healpix.append(col_name);
740 }
741 fits.append(src_healpix);
742
743 // Write true energy boundaries into FITS file
744 m_src_ebounds.write(fits, "TRUE ENERGY BOUNDARIES");
745
746 // Write true polarisation boundaries into binary table and append
747 // table to FITS file
748 m_src_pols.write(fits, "TRUE POLARISATION BOUNDARIES");
749
750 // Write measured Phi boundaries into binary table and append table
751 // to FITS file
752 m_obs_phis.write(fits, "MEASURED PHI BOUNDARIES");
753
754 // Write measured energy boundaries into FITS file
755 m_obs_ebounds.write(fits, "MEASURED ENERGY BOUNDARIES");
756
757 // Write measured sky projection. The projection is written in the header
758 // of an empty binary table.
759 GFitsBinTable obs_healpix;
760 m_obs_dirs.write(obs_healpix);
761 obs_healpix.extname("MEASURED SKY DIRECTIONS");
762 fits.append(obs_healpix);
763
764 // Write response chunks into FITS file
765 for (int i = 0; i < chunks; ++i) {
766 GFitsBinTable chunk;
767 m_obs_dirs.write(chunk);
768 chunk.extname("CHUNK" + gammalib::str(i));
769 int npix = m_obs_dirs.npix();
770 int elements = m_rsp_chunks[i].size();
771 int ncols = elements / npix;
772 GFitsTableUShortCol col_chunk("Response", npix, ncols);
773 for (int col = 0, inx = 0; col < ncols; ++col) {
774 for (int ipix = 0; ipix < npix; ++ipix, ++inx) {
775 col_chunk(ipix, col) = m_rsp_chunks[i][inx];
776 }
777 }
778 chunk.append(col_chunk);
779 fits.append(chunk);
780 }
781
782 // Return
783 return;
784}
785
786
787/***********************************************************************//**
788 * @brief Print COSI response information
789 *
790 * @param[in] chatter Chattiness.
791 * @return String containing COSI response information.
792 ***************************************************************************/
793std::string GCOSResponse::print(const GChatter& chatter) const
794{
795 // Initialise result string
796 std::string result;
797
798 // Continue only if chatter is not silent
799 if (chatter != SILENT) {
800
801 // Append header
802 result.append("=== GCOSResponse ===");
803
804 // Append response information
805 result.append("\n"+gammalib::parformat("Response file")+m_rspfile.url());
806 result.append("\n"+gammalib::parformat("True directions"));
807 if (m_src_dirs.nside() == 0) {
808 result.append("not defined");
809 }
810 else {
811 result.append(gammalib::str(m_src_dirs.npix()));
812 result.append("\n"+gammalib::parformat("True Healpix nside"));
813 result.append(gammalib::str(m_src_dirs.nside()));
814 result.append("\n"+gammalib::parformat("True Healpix ordering"));
815 result.append(m_src_dirs.ordering());
816 }
817 result.append("\n"+gammalib::parformat("True energies"));
818 if (m_src_ebounds.size() == 0) {
819 result.append("not defined");
820 }
821 else {
822 result.append(gammalib::str(m_src_ebounds.emin().keV()));
823 result.append(" - ");
824 result.append(gammalib::str(m_src_ebounds.emax().keV()));
825 result.append(" keV");
826 result.append("\n"+gammalib::parformat("True energy bins"));
827 result.append(gammalib::str(m_src_ebounds.size()));
828 }
829 result.append("\n"+gammalib::parformat("True polarisation angles"));
830 if (m_src_pols.size() == 0) {
831 result.append("not defined");
832 }
833 else {
834 result.append(gammalib::str(m_src_pols.min()));
835 result.append(" - ");
836 result.append(gammalib::str(m_src_pols.max()));
837 result.append(" deg");
838 result.append("\n"+gammalib::parformat("True polar. angle bins"));
839 result.append(gammalib::str(m_src_pols.size()));
840 }
841 result.append("\n"+gammalib::parformat("Measured directions"));
842 if (m_obs_dirs.nside() == 0) {
843 result.append("not defined");
844 }
845 else {
846 result.append(gammalib::str(m_obs_dirs.npix()));
847 result.append("\n"+gammalib::parformat("Measured Healpix nside"));
848 result.append(gammalib::str(m_obs_dirs.nside()));
849 result.append("\n"+gammalib::parformat("Measured Healpix ordering"));
850 result.append(m_obs_dirs.ordering());
851 }
852 result.append("\n"+gammalib::parformat("Measured scatter angles"));
853 if (m_obs_phis.size() == 0) {
854 result.append("not defined");
855 }
856 else {
857 result.append(gammalib::str(m_obs_phis.min()));
858 result.append(" - ");
859 result.append(gammalib::str(m_obs_phis.max()));
860 result.append(" deg");
861 result.append("\n"+gammalib::parformat("Measured scatt. angle bins"));
862 result.append(gammalib::str(m_obs_phis.size()));
863 }
864 result.append("\n"+gammalib::parformat("Measured energies"));
865 if (m_obs_ebounds.size() == 0) {
866 result.append("not defined");
867 }
868 else {
869 result.append(gammalib::str(m_obs_ebounds.emin().keV()));
870 result.append(" - ");
871 result.append(gammalib::str(m_obs_ebounds.emax().keV()));
872 result.append(" keV");
873 result.append("\n"+gammalib::parformat("Measured energy bins"));
874 result.append(gammalib::str(m_obs_ebounds.size()));
875 }
876 result.append("\n"+gammalib::parformat("Effective areas"));
877 if (m_eff_areas.size() == 0) {
878 result.append("not defined");
879 }
880 else {
881 result.append(gammalib::str(min(m_eff_areas)));
882 result.append(" - ");
883 result.append(gammalib::str(max(m_eff_areas)));
884 result.append("\n"+gammalib::parformat("Effective area bins"));
885 result.append(gammalib::str(m_eff_areas.size()));
886 }
887 result.append("\n"+gammalib::parformat("Response chunks loaded"));
888 result.append(gammalib::str(m_rsp_chunk_indices.size()));
889
890 // If chatter is VERBOSE and HDF5 file metadata are not empty then
891 // show the HDF5 file metadata
892 if ((chatter == VERBOSE) && (!m_hdf5.is_empty())) {
893
894 // Get "DRM" entry and header
895 const GXmlElement* drm = m_hdf5.xml_hdf5_entry("DRM");
896 const GXmlElement* drm_header = drm->element("header");
897
898 // Get "AXES" entry and table and "AXIS_DESCRIPTIONS" entry and header
899 const GXmlElement* axes = m_hdf5.xml_hdf5_entry("AXES");
900 const GXmlElement* axes_table = axes->element("group")->element("table");
901 const GXmlElement* axis_desc = m_hdf5.xml_hdf5_entry("AXIS_DESCRIPTIONS");
902 const GXmlElement* axis_desc_header = axis_desc->element("header");
903
904 // Get "COUNTS" entry and header
905 const GXmlElement* counts = m_hdf5.xml_hdf5_entry("COUNTS");
906 const GXmlElement* counts_header = counts->element("header");
907
908 // Get "DRM" information
909 std::string unit = xml_msg_attribute(drm_header, "UNIT", "value");
910
911 // Append "DRM" information
912 result.append("\n"+gammalib::parformat("Response unit")+unit);
913
914 // Get number of axes
915 int naxes = axes_table->elements("entry");
916
917 // Get number of axis description messages
918 int idesc = 0;
919 int ndesc = axis_desc_header->elements("message");
920
921 // Loop over axes
922 for (int i = 0; i < naxes; ++i) {
923
924 // Get table entry
925 const GXmlElement* entry = axes_table->element("entry", i);
926 const GXmlElement* entry_header = entry->element("header");
927
928 // Get header information
929 std::string label = xml_msg_attribute(entry_header, "label", "value");
930 std::string unit = xml_msg_attribute(entry_header, "unit", "value");
931 std::string scale = xml_msg_attribute(entry_header, "scale", "value");
932 std::string nside = xml_msg_attribute(entry_header, "nside", "value");
933 std::string scheme = xml_msg_attribute(entry_header, "scheme", "value");
934 std::string coordsys = xml_msg_attribute(entry_header, "coordsys", "value");
935
936 // Get corresponding axis description message
937 std::string desc = xml_msg_attribute(axis_desc_header, label, "value");
938
939 // Get corresponding axis dimension
940 const GXmlElement* dataspace = xml_msg_dataspace(counts_header);
941 std::string size = dataspace->attribute("size"+gammalib::str(i));
942
943 // Append available axis information
944 result.append("\n"+gammalib::parformat("Axis"+entry->attribute("name"))+label);
945 result.append("\n"+gammalib::parformat(" Size")+size);
946 if (desc.length() > 0) {
947 result.append("\n"+gammalib::parformat(" Description")+desc);
948 }
949 result.append("\n"+gammalib::parformat(" Size")+size);
950 if (unit.length() > 0) {
951 result.append("\n"+gammalib::parformat(" Units")+unit);
952 }
953 if (scale.length() > 0) {
954 result.append("\n"+gammalib::parformat(" Scale")+scale);
955 }
956 if (nside.length() > 0) {
957 result.append("\n"+gammalib::parformat(" Nside")+nside);
958 }
959 if (scheme.length() > 0) {
960 result.append("\n"+gammalib::parformat(" Scheme")+scheme);
961 }
962 if (coordsys.length() > 0) {
963 result.append("\n"+gammalib::parformat(" Coordsys")+coordsys);
964 }
965
966 } // endfor: looped over axes
967
968 } // endif: response file was not empty
969
970 } // endif: chatter was not silent
971
972 // Return result
973 return result;
974}
975
976
977/*==========================================================================
978 = =
979 = Private methods =
980 = =
981 ==========================================================================*/
982
983/***********************************************************************//**
984 * @brief Initialise class members
985 ***************************************************************************/
987{
988 // Initialise members
990 m_hdf5.clear();
998
999 // Initialise members for response chunk handling
1000 m_rsp_chunk_dataspace = NULL;
1001 m_rsp_chunk_datatype = NULL;
1004 m_rsp_chunk_indices.clear();
1005 m_rsp_chunks.clear();
1006
1007 // Return
1008 return;
1009}
1010
1011
1012/***********************************************************************//**
1013 * @brief Copy class members
1014 *
1015 * @param[in] rsp COSI response function.
1016 ***************************************************************************/
1018{
1019 // Copy members
1020 m_rspfile = rsp.m_rspfile;
1021 m_hdf5 = rsp.m_hdf5;
1022 m_src_dirs = rsp.m_src_dirs;
1024 m_src_pols = rsp.m_src_pols;
1025 m_obs_dirs = rsp.m_obs_dirs;
1026 m_obs_phis = rsp.m_obs_phis;
1029
1030 // Copy members for response chunk handling
1033
1034 // Set pointers for response chunk handling
1036
1037 // Return
1038 return;
1039}
1040
1041
1042/***********************************************************************//**
1043 * @brief Delete class members
1044 ***************************************************************************/
1046{
1047 // Return
1048 return;
1049}
1050
1051
1052/***********************************************************************//**
1053 * @brief Read response from HDF5 file
1054 *
1055 * @param[in] fptr File pointer to HDF5 file.
1056 *
1057 * Read response data and information using the metadata that is provided
1058 * by the m_hdf5 member.
1059 *
1060 * This method does nothing if the m_hdf5 member is empty (which means that
1061 * no HDF5 metadata are available).
1062 ***************************************************************************/
1063void GCOSResponse::read_response(FILE* fptr)
1064{
1065 // Continue only if HDF5 metadata are not empty
1066 if (!m_hdf5.is_empty()) {
1067
1068 // Initialise XML element pointers
1069 const GXmlElement* dataspace = NULL;
1070 const GXmlElement* datatype = NULL;
1071 const GXmlElement* datalayout = NULL;
1072 const GXmlElement* datafilter = NULL;
1073
1074 // Get "AXES" entry and table
1075 const GXmlElement* axes = m_hdf5.xml_hdf5_entry("AXES");
1076 const GXmlElement* axes_table = axes->element("group")->element("table");
1077
1078 // Get number of axes
1079 int naxes = axes_table->elements("entry");
1080
1081 // Loop over axes
1082 for (int i = 0; i < naxes; ++i) {
1083
1084 // Get table entry
1085 const GXmlElement* entry = axes_table->element("entry", i);
1086 const GXmlElement* entry_header = entry->element("header");
1087
1088 // Get dataspace, datatype and datalayout
1089 dataspace = xml_msg_dataspace(entry_header);
1090 datatype = xml_msg_datatype(entry_header);
1091 datalayout = xml_msg_datalayout(entry_header);
1092
1093 // Get header information
1094 std::string label = xml_msg_attribute(entry_header, "label", "value");
1095 std::string unit = xml_msg_attribute(entry_header, "unit", "value");
1096 std::string scale = xml_msg_attribute(entry_header, "scale", "value");
1097 std::string nside = xml_msg_attribute(entry_header, "nside", "value");
1098 std::string scheme = xml_msg_attribute(entry_header, "scheme", "value");
1099 std::string coordsys = xml_msg_attribute(entry_header, "coordsys", "value");
1100
1101 // Handle axes
1102 if (label == "Ei") { // True energies
1103 m_src_ebounds = read_ebounds(fptr, dataspace, datatype, datalayout, unit);
1104 }
1105 else if (label == "Em") { // Measured energies
1106 m_obs_ebounds = read_ebounds(fptr, dataspace, datatype, datalayout, unit);
1107 }
1108 else if (label == "Phi") { // Measured energies
1109 m_obs_phis = read_bounds(fptr, dataspace, datatype, datalayout);
1110 m_obs_phis.unit("deg");
1111 }
1112 else if (label == "NuLambda") { // True sky directions
1113 m_src_dirs = GHealpix(gammalib::toint(nside), scheme, "CEL");
1114 }
1115 else if (label == "PsiChi") { // True sky directions
1116 m_obs_dirs = GHealpix(gammalib::toint(nside), scheme, "CEL");
1117 }
1118 else if (label == "Pol") { // True polarisation angles
1119 m_src_pols = read_bounds(fptr, dataspace, datatype, datalayout);
1120 m_src_pols.unit("deg");
1121 }
1122
1123 } // endif: looped over axes
1124
1125 // Get "EFF_AREA" entry and header
1126 const GXmlElement* eff_area = m_hdf5.xml_hdf5_entry("EFF_AREA");
1127 const GXmlElement* eff_area_header = eff_area->element("header");
1128
1129 // Get dataspace, datatype and datalayout
1130 dataspace = xml_msg_dataspace(eff_area_header);
1131 datatype = xml_msg_datatype(eff_area_header);
1132 datalayout = xml_msg_datalayout(eff_area_header);
1133
1134 // Read effective area vector
1135 m_eff_areas = read_ndarray(fptr, dataspace, datatype, datalayout);
1136
1137 } // endif: HDF5 metadata were not empty
1138
1139 // Set pointers for response chunk handling
1141
1142 // Return
1143 return;
1144}
1145
1146
1147/***********************************************************************//**
1148 * @brief Read boundaries from HDF5 file
1149 *
1150 * @param[in] fptr File pointer to HDF5 file.
1151 * @param[in] dataspace Pointer to "Dataspace" XML element.
1152 * @param[in] datatype Pointer to "Datatype" XML element.
1153 * @param[in] datalayout Pointer to "DataStorageLayout" XML element.
1154 * @return Boundaries.
1155 *
1156 * @exception GException::invalid_argument
1157 * Invalid pointer to XML element
1158 *
1159 * Reads boundaries from HDF5 file and returns them as a GBounds object.
1160 *
1161 * The method expects that the @p dataspace, @p datatype and @p datalayout
1162 * pointers are not NULL. An exception is thrown if a NULL pointer is
1163 * encountered.
1164 ***************************************************************************/
1166 const GXmlElement* dataspace,
1167 const GXmlElement* datatype,
1168 const GXmlElement* datalayout)
1169{
1170 // Initialise boundaries
1171 GBounds bounds;
1172
1173 // Check pointers
1174 if (dataspace == NULL) {
1175 std::string msg = "Invalid dataspace pointer specified. No dataspace "
1176 "information was found in the COSI response file. "
1177 "Please specify a valid COSI response file.";
1179 }
1180 if (datatype == NULL) {
1181 std::string msg = "Invalid datatype pointer specified. No datatype "
1182 "information was found in the COSI response file. "
1183 "Please specify a valid COSI response file.";
1185 }
1186 if (datalayout == NULL) {
1187 std::string msg = "Invalid datalayout pointer specified. No datalayout "
1188 "information was found in the COSI response file. "
1189 "Please specify a valid COSI response file.";
1191 }
1192
1193 // Read data
1194 std::string data = fread_data(fptr, dataspace, datatype, datalayout);
1195
1196 // Get number of data elements and their size
1197 int elements = gammalib::toint(dataspace->attribute("elements"));
1198 int size = gammalib::toint(datatype->attribute("size"));
1199
1200 // Loop over data elements
1201 double min;
1202 const char* ptr = data.c_str();
1203 for (int i = 0; i < elements; ++i, ptr += size) {
1204 double max = data_to_double(ptr, datatype);
1205 if (i > 0) {
1206 bounds.append(min, max);
1207 }
1208 min = max;
1209 }
1210
1211 // Return boundaries
1212 return bounds;
1213}
1214
1215
1216/***********************************************************************//**
1217 * @brief Read energy boundaries from HDF5 file
1218 *
1219 * @param[in] fptr File pointer to HDF5 file.
1220 * @param[in] dataspace Pointer to "Dataspace" XML element.
1221 * @param[in] datatype Pointer to "Datatype" XML element.
1222 * @param[in] datalayout Pointer to "DataStorageLayout" XML element.
1223 * @param[in] unit Energy unit string.
1224 * @return Energy boundaries
1225 *
1226 * @exception GException::invalid_argument
1227 * Invalid pointer to XML element
1228 *
1229 * Reads energy boundaries from HDF5 file. If the energy @p unit string is
1230 * empty the energies are assumed to be given in keV.
1231 *
1232 * The method expects that the @p dataspace, @p datatype and @p datalayout
1233 * pointers are not NULL. An exception is thrown if a NULL pointer is
1234 * encountered.
1235 ***************************************************************************/
1237 const GXmlElement* dataspace,
1238 const GXmlElement* datatype,
1239 const GXmlElement* datalayout,
1240 const std::string& unit)
1241{
1242 // Initialise energy boundaries
1244
1245 // Check pointers
1246 if (dataspace == NULL) {
1247 std::string msg = "Invalid dataspace pointer specified. No dataspace "
1248 "information was found in the COSI response file. "
1249 "Please specify a valid COSI response file.";
1251 }
1252 if (datatype == NULL) {
1253 std::string msg = "Invalid datatype pointer specified. No datatype "
1254 "information was found in the COSI response file. "
1255 "Please specify a valid COSI response file.";
1257 }
1258 if (datalayout == NULL) {
1259 std::string msg = "Invalid datalayout pointer specified. No datalayout "
1260 "information was found in the COSI response file. "
1261 "Please specify a valid COSI response file.";
1263 }
1264
1265 // Read data
1266 std::string data = fread_data(fptr, dataspace, datatype, datalayout);
1267
1268 // Get number of data elements and their size
1269 int elements = gammalib::toint(dataspace->attribute("elements"));
1270 int size = gammalib::toint(datatype->attribute("size"));
1271
1272 // Set unit string
1273 std::string str_unit = (unit.empty()) ? "keV" : unit;
1274
1275 // Loop over data elements
1276 GEnergy emin;
1277 const char* ptr = data.c_str();
1278 for (int i = 0; i < elements; ++i, ptr += size) {
1279 double value = data_to_double(ptr, datatype);
1280 GEnergy emax(value, unit);
1281 if (i > 0) {
1282 ebounds.append(emin, emax);
1283 }
1284 emin = emax;
1285 }
1286
1287 // Return energy boundaries
1288 return ebounds;
1289}
1290
1291
1292/***********************************************************************//**
1293 * @brief Read 1-dimensional array from HDF5 file
1294 *
1295 * @param[in] fptr File pointer to HDF5 file.
1296 * @param[in] dataspace Pointer to "Dataspace" XML element.
1297 * @param[in] datatype Pointer to "Datatype" XML element.
1298 * @param[in] datalayout Pointer to "DataStorageLayout" XML element.
1299 * @return Array.
1300 *
1301 * @exception GException::invalid_argument
1302 * Invalid pointer to XML element
1303 *
1304 * Reads 1-dimensional array from HDF5 file and return it as GNdarray object.
1305 *
1306 * The method expects that the @p dataspace, @p datatype and @p datalayout
1307 * pointers are not NULL. An exception is thrown if a NULL pointer is
1308 * encountered.
1309 ***************************************************************************/
1311 const GXmlElement* dataspace,
1312 const GXmlElement* datatype,
1313 const GXmlElement* datalayout)
1314{
1315 // Initialise array
1316 GNdarray array;
1317
1318 // Check pointers
1319 if (dataspace == NULL) {
1320 std::string msg = "Invalid dataspace pointer specified. No dataspace "
1321 "information was found in the COSI response file. "
1322 "Please specify a valid COSI response file.";
1324 }
1325 if (datatype == NULL) {
1326 std::string msg = "Invalid datatype pointer specified. No datatype "
1327 "information was found in the COSI response file. "
1328 "Please specify a valid COSI response file.";
1330 }
1331 if (datalayout == NULL) {
1332 std::string msg = "Invalid datalayout pointer specified. No datalayout "
1333 "information was found in the COSI response file. "
1334 "Please specify a valid COSI response file.";
1336 }
1337
1338 // Read data
1339 std::string data = fread_data(fptr, dataspace, datatype, datalayout);
1340
1341 // Get number of data elements and their size
1342 int elements = gammalib::toint(dataspace->attribute("elements"));
1343 int size = gammalib::toint(datatype->attribute("size"));
1344
1345 // Allocate array
1346 array = GNdarray(elements);
1347
1348 // Store effective area vector
1349 const char* ptr = data.c_str();
1350 for (int i = 0; i < elements; ++i, ptr += size) {
1351 array(i) = data_to_double(ptr, datatype);
1352 }
1353
1354 // Return array
1355 return array;
1356}
1357
1358
1359/***********************************************************************//**
1360 * @brief Read response matrix from HDF5 file
1361 *
1362 * @param[in] fptr File pointer to HDF5 file.
1363 * @param[in] dataspace Pointer to "Dataspace" XML element.
1364 * @param[in] datatype Pointer to "Datatype" XML element.
1365 * @param[in] datalayout Pointer to "DataStorageLayout" XML element.
1366 * @param[in] datafilter Pointer to "DataStorageFilterPipeline" XML element.
1367 *
1368 * @exception GException::invalid_argument
1369 * Invalid pointer to XML element
1370 *
1371 * @todo To be implemented (if we really need that method)
1372 ***************************************************************************/
1374 const GXmlElement* dataspace,
1375 const GXmlElement* datatype,
1376 const GXmlElement* datalayout,
1377 const GXmlElement* datafilter)
1378{
1379 // Check pointers
1380 if (dataspace == NULL) {
1381 std::string msg = "Invalid dataspace pointer provided for response "
1382 "matrix. No dataspace information was provided "
1383 "in the COSI response file. Please specify a "
1384 "valid COSI response file.";
1386 }
1387 if (datatype == NULL) {
1388 std::string msg = "Invalid datatype pointer provided for response "
1389 "matrix. No datatype information was provided "
1390 "in the COSI response file. Please specify a "
1391 "valid COSI response file.";
1393 }
1394 if (datalayout == NULL) {
1395 std::string msg = "Invalid datalayout pointer provided for response "
1396 "matrix. No datalayout information was provided "
1397 "in the COSI response file. Please specify a "
1398 "valid COSI response file.";
1400 }
1401
1402 // Return
1403 return;
1404}
1405
1406
1407/***********************************************************************//**
1408 * @brief Set XML element pointers for response chunk handling
1409 *
1410 * Sets pointers to the "Dataspace", "Datatype", "DataStorageLayout" and
1411 * "DataStorageFilterPipeline" XML elements. If no HDF5 metadata are
1412 * available all pointers are set to NULL.
1413 ***************************************************************************/
1415{
1416 // Initialise pointers
1417 m_rsp_chunk_dataspace = NULL;
1418 m_rsp_chunk_datatype = NULL;
1421
1422 // Continue only if HDF5 metadata are not empty
1423 if (!m_hdf5.is_empty()) {
1424
1425 // Get "COUNTS" entry and header
1426 const GXmlElement* counts = m_hdf5.xml_hdf5_entry("COUNTS");
1427 const GXmlElement* counts_header = counts->element("header");
1428
1429 // Get dataspace, datatype, datalayout and datafilter
1431 m_rsp_chunk_datatype = xml_msg_datatype(counts_header);
1434
1435 } // endif: HDF5 metadata were not empty
1436
1437 // Return
1438 return;
1439}
1440
1441
1442/***********************************************************************//**
1443 * @brief Get pointer to key XML element for specified chunk index
1444 *
1445 * @param[in] idir True photon direction index (0,...,src_dirs.npix()-1).
1446 * @param[in] ieng True photon energy index (0,...,src_ebounds.size()-1).
1447 * @param[in] ipol True polarisation angle index (0,...,src_polarisations.size()-2).
1448 * @return Pointer to key XML element.
1449 *
1450 * @exception GException::runtime_error
1451 * Key XML element not found.
1452 * Key XML element with wrong index found.
1453 *
1454 * Returns key XML element within the data layout B-tree that has an "index0"
1455 * attribute that corresponds to the specified @p index. The method throws
1456 * exceptions if no key element was found or if the key element has the
1457 * wrong index.
1458 *
1459 * This method assumes that the data layout pointer m_rsp_chunk_datalayout is
1460 * valid.
1461 ***************************************************************************/
1463 const int& ieng,
1464 const int& ipol) const
1465{
1466 // Initialise pointer to ket element
1467 const GXmlElement* key = NULL;
1468
1469 // Get pointer to B-tree
1470 const GXmlElement* btree = m_rsp_chunk_datalayout->element("btree");
1471
1472 // Convert 3D indices into flat 1D index
1473 int index = flat_index(idir, ieng, ipol);
1474
1475 // Search B-tree structure
1476 while (true) {
1477
1478 // Get number of keys in B-tree
1479 int nkeys = btree->elements("key");
1480
1481 // If there is more than one key then start bi-section search
1482 if (nkeys > 0) {
1483
1484 // Initialise key range
1485 int ikey_min = 0;
1486 int ikey_max = nkeys;
1487
1488 // Set initial key as mid-point
1489 int ikey = nkeys / 2;
1490
1491 // Do bi-section search
1492 while (ikey_min != ikey_max) {
1493
1494 // Get key
1495 key = btree->element("key", ikey);
1496
1497 // Get key index
1498 int idir_key = gammalib::toint(key->attribute("index0"));
1499 int ieng_key = gammalib::toint(key->attribute("index1"));
1500 int ipol_key = (m_src_pols.size() == 0) ?
1501 0 : gammalib::toint(key->attribute("index2"));
1502 int index_key = flat_index(idir_key, ieng_key, ipol_key);
1503
1504 // If index_key is larger than index the chunk resides before
1505 if (index_key > index) {
1506 ikey_max = ikey;
1507 ikey = ikey_min + (ikey_max-ikey_min) / 2;
1508 if (ikey == ikey_max) {
1509 break;
1510 }
1511 }
1512
1513 // If index_key is smaller than index the chunk resides after
1514 else if (index_key < index) {
1515 ikey_min = ikey;
1516 ikey = ikey_min + (ikey_max-ikey_min) / 2;
1517 if (ikey == ikey_min) {
1518 break;
1519 }
1520 }
1521
1522 // ... otherwise the index was found and we break
1523 else {
1524 break;
1525 }
1526
1527 // If interval is zero then key was found and we break
1528 if (ikey_min == ikey_max) {
1529 break;
1530 }
1531
1532 } // endwhile: did bi-section search
1533
1534 // If key does not contain another B-tree we are done
1535 if (key->elements("btree") == 0) {
1536 break;
1537 }
1538
1539 // ... otherwise get the B-tree and continue
1540 else {
1541 btree = key->element("btree");
1542 }
1543
1544 } // endif: there were keys in element
1545
1546 } // endwhile: searched B-tree
1547
1548 // Check that key was found
1549 if (key == NULL) {
1550 std::string msg = "Response chunk with index "+gammalib::str(index)+
1551 " not found. This should never happen if the HDF5 "
1552 "file is correct.";
1554 }
1555
1556 // Check that correct chunk was found
1557 int idir_key = gammalib::toint(key->attribute("index0"));
1558 int ieng_key = gammalib::toint(key->attribute("index1"));
1559 int ipol_key = (m_src_pols.size() == 0) ?
1560 0 : gammalib::toint(key->attribute("index2"));
1561 int index_key = flat_index(idir_key, ieng_key, ipol_key);
1562 if (index_key != index) {
1563 std::string msg = "Bad response chunk with index " +
1564 gammalib::str(index_key) +
1565 " found while index " +
1566 gammalib::str(index)+" was requested. This "
1567 "should never happen, the code is not doing "
1568 "what it is expected to do.";
1570 }
1571
1572 // Return key
1573 return key;
1574}
1575
1576
1577/***********************************************************************//**
1578 * @brief Get response chunk vector index for specified chunk index
1579 *
1580 * @param[in] idir True photon direction index (0,...,src_dirs.npix()-1).
1581 * @param[in] ieng True photon energy index (0,...,src_ebounds.size()-1).
1582 * @param[in] ipol True polarisation angle index (0,...,src_polarisations.size()-2).
1583 * @return Response chunk vector index (-1 if chunk index was not found).
1584 *
1585 * Returns the response vector index for a specified chunk @p index. If the
1586 * chunk index does not exist in the response vectors a value of -1 is
1587 * returned.
1588 ***************************************************************************/
1589int GCOSResponse::get_response_chunk_index(const int& idir,
1590 const int& ieng,
1591 const int& ipol) const
1592{
1593 // Initialise index with invalid value
1594 int inx = -1;
1595
1596 // Get number of response chunk indices
1597 int n = m_rsp_chunk_indices.size();
1598
1599 // Continue only if there are chunk indices
1600 if (n > 0) {
1601
1602 // Determine storage index
1603 int index = flat_index(idir, ieng, ipol);
1604
1605 // Search specified chunk index
1606 for (int i = 0; i < n; ++i) {
1607 if (m_rsp_chunk_indices[i] == index) {
1608 inx = i;
1609 break;
1610 }
1611 }
1612
1613 } // endif: there were chunk indices
1614
1615 // Return index
1616 return inx;
1617}
1618
1619
1620/***********************************************************************//**
1621 * @brief Convert 3D index into flat 1D index
1622 *
1623 * @param[in] idir True photon direction index (0,...,src_dirs.npix()-1).
1624 * @param[in] ieng True photon energy index (0,...,src_ebounds.size()-1).
1625 * @param[in] ipol True polarisation angle index (0,...,src_polarisations.size()-1).
1626 * @return Flat index.
1627 *
1628 * Returns the flattened index of a specified true photon direction index
1629 * @p idir, true photon energy index @p ieng, and true polarisation angle
1630 * index @p ipol. If no source polarisations exists, the value of @p ipol
1631 * is ignored.
1632 *
1633 * The method does not check the validity of the input indices, this needs
1634 * to be done by the client.
1635 ***************************************************************************/
1636int GCOSResponse::flat_index(const int& idir,
1637 const int& ieng,
1638 const int& ipol) const
1639{
1640 // Determine number of energy boundaries and polarisation boundaries
1641 int nengs = m_src_ebounds.size();
1642 int npols = m_src_pols.size();
1643
1644 // Determine flat index
1645 int index = (npols == 0) ? idir * nengs + ieng
1646 : (idir * nengs + ieng) * npols + ipol;
1647
1648 // Return flat index
1649 return index;
1650}
#define G_IRF
#define G_EBOUNDS
#define G_NROI
COSI observation class definition.
#define G_READ_RESPONSE_MATRIX
#define G_LOAD_RESPONSE_CHUNK
#define G_READ_NDARRAY
#define G_GET_RESPONSE_CHUNK_KEY
#define G_READ_BOUNDS
#define G_READ_EBOUNDS
COSI instrument response function class definition.
Exception handler interface definition.
FITS binary table class definition.
FITS table double column class interface definition.
FITS table long integer column class interface definition.
FITS table string column class interface definition.
FITS table unsigned short integer column class interface definition.
FITS file class interface definition.
Point source spatial model class interface definition.
#define G_LOAD
Source class definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
@ VERBOSE
Definition GTypemaps.hpp:38
double min(const GVector &vector)
Computes minimum vector element.
Definition GVector.cpp:954
double max(const GVector &vector)
Computes maximum vector element.
Definition GVector.cpp:983
Boundaries container class.
Definition GBounds.hpp:59
void read(const GFitsTable &table)
Read boundaries from FITS table.
Definition GBounds.cpp:662
void clear(void)
Clear boundaries.
Definition GBounds.cpp:262
const std::string & unit(void) const
Return boundary units.
Definition GBounds.hpp:178
void append(const double &min, const double &max)
Append interval.
Definition GBounds.cpp:294
double max(void) const
Return maximum value of all intervals.
Definition GBounds.cpp:806
double min(void) const
Return minimum value of all intervals.
Definition GBounds.cpp:781
void write(GFits &file, const std::string &extname=gammalib::extname_bounds) const
Write boundaries into FITS object.
Definition GBounds.cpp:703
int size(void) const
Return number of boundaries.
Definition GBounds.hpp:154
int index(const double &value) const
Returns bin index for a value.
Definition GBounds.cpp:758
COSI event bin class.
virtual const GCOSInstDir & dir(void) const
Return instrument direction of event bin.
virtual const GEnergy & energy(void) const
Return energy of event bin.
void phi(const double &phi)
Set event Compton scatter angle.
void dir(const GSkyDir &dir)
Set event scatter direction in celestial coordinates.
COSI observation class.
const GCOSSpaceCraft & spacecraft(void) const
Return space craft information.
COSI instrument response function class.
const GEbounds & obs_ebounds(void) const
Return measured event energy boundaries.
const GXmlElement * m_rsp_chunk_dataspace
Data space.
const GXmlElement * get_response_chunk_key(const int &idir, const int &ieng=0, const int &ipol=0) const
GCOSResponse(void)
Void constructor.
GBounds m_obs_phis
Boundaries for observed scatter angles.
GHdf5 m_hdf5
HDF5 file.
virtual void clear(void)
Clear instance.
virtual double irf(const GEvent &event, const GPhoton &photon, const GObservation &obs) const
GEbounds m_src_ebounds
Energy boundaries for source photons.
void copy_members(const GCOSResponse &rsp)
const GXmlElement * m_rsp_chunk_datatype
Data type.
void read_response_matrix(FILE *fptr, const GXmlElement *dataspace, const GXmlElement *datatype, const GXmlElement *datalayout, const GXmlElement *datafilter)
GEbounds read_ebounds(FILE *fptr, const GXmlElement *dataspace, const GXmlElement *datatype, const GXmlElement *datalayout, const std::string &unit)
int get_response_chunk(const int &idir, const int &ieng=0, const int &ipol=0) const
const GHealpix & obs_dirs(void) const
Return HealPix projection of measured scatter directions.
void free_members(void)
virtual double nroi(const GModelSky &model, const GEnergy &obsEng, const GTime &obsTime, const GObservation &obs) const
const GXmlElement * m_rsp_chunk_datafilter
Data filter.
const GNdarray & eff_areas(void) const
Return effective area array.
virtual std::string print(const GChatter &chatter=NORMAL) const
Print content of object.
void set_response_chunk_pointers(void)
const GHealpix & src_dirs(void) const
Return HealPix projection of true photon directions.
const GXmlElement * m_rsp_chunk_datalayout
Data layout.
GNdarray read_ndarray(FILE *fptr, const GXmlElement *dataspace, const GXmlElement *datatype, const GXmlElement *datalayout)
void save(const GFilename &filename, const bool &clobber=false) const
void init_members(void)
GEbounds m_obs_ebounds
Energy boundaries for observed events.
GFilename m_rspfile
Response file name.
GBounds read_bounds(FILE *fptr, const GXmlElement *dataspace, const GXmlElement *datatype, const GXmlElement *datalayout)
virtual GCOSResponse & operator=(const GCOSResponse &rsp)
Assignment operator.
void read(const GFits &fits)
GHealpix m_obs_dirs
Sky projection for observed events.
GNdarray m_eff_areas
Array of effective areas.
virtual ~GCOSResponse(void)
Destructor.
int get_response_chunk_index(const int &idir, const int &ieng=0, const int &ipol=0) const
GHealpix m_src_dirs
Sky projection for source photons.
void write(GFits &fits) const
int flat_index(const int &idir, const int &ieng=0, const int &ipol=0) const
std::vector< int > m_rsp_chunk_indices
Loaded indices.
void read_response(FILE *fptr)
virtual GEbounds ebounds(const GEnergy &obsEnergy) const
const GEbounds & src_ebounds(void) const
Return true photon energy boundaries.
virtual GCOSResponse * clone(void) const
Clone instance.
GBounds m_src_pols
Boundaries for observed polarisation.
const GBounds & obs_phis(void) const
Return measured scatter angle boundaries.
void load(const GFilename &filename)
std::vector< std::vector< uint16_t > > m_rsp_chunks
Loaded chunks.
const double & livetime(void) const
Return livetime in seconds.
Energy boundaries container class.
Definition GEbounds.hpp:60
int index(const GEnergy &eng) const
Returns energy bin index for a given energy.
Definition GEbounds.cpp:948
const GEnergy & emax(void) const
Return maximum energy of all intervals.
Definition GEbounds.hpp:199
void read(const GFitsTable &table)
Read energy boundaries from FITS table.
Definition GEbounds.cpp:761
void append(const GEnergy &emin, const GEnergy &emax)
Append energy interval.
Definition GEbounds.cpp:301
int size(void) const
Return number of energy boundaries.
Definition GEbounds.hpp:163
void clear(void)
Clear energy boundaries.
Definition GEbounds.cpp:268
bool is_empty(void) const
Signal if there are no energy boundaries.
Definition GEbounds.hpp:175
const GEnergy & emin(void) const
Return minimum energy of all intervals.
Definition GEbounds.hpp:187
void write(GFits &file, const std::string &extname=gammalib::extname_ebounds, const std::string &unit="keV") const
Write energy boundaries into FITS object.
Definition GEbounds.cpp:816
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
double keV(void) const
Return energy in keV.
Definition GEnergy.cpp:327
Abstract interface for the event classes.
Definition GEvent.hpp:71
Filename class.
Definition GFilename.hpp:62
bool is_fits(void) const
Checks whether file is a FITS file.
std::string url(void) const
Return Uniform Resource Locator (URL)
void clear(void)
Clear file name.
FITS binary table class.
const std::string & extname(void) const
Return extension name.
Definition GFitsHDU.hpp:162
Abstract interface for FITS table column.
virtual int integer(const int &row, const int &inx=0) const =0
void number(const int &number)
Set number of elements in column.
virtual double real(const int &row, const int &inx=0) const =0
virtual std::string string(const int &row, const int &inx=0) const =0
void nrows(const int &nrows)
Set number of rows in column.
FITS table double column.
FITS table long integer column.
FITS table string column.
FITS table unsigned short integer column.
Abstract interface for FITS table.
GFitsTableCol * append(const GFitsTableCol &column)
Append column to the table.
bool is_empty(void) const
Signals if the FITS table has no row or columns.
const int & nrows(void) const
Return number of rows in table.
FITS file class.
Definition GFits.hpp:63
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition GFits.cpp:1293
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition GFits.cpp:678
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition GFits.cpp:482
const GXmlElement * xml_hdf5_entry(const std::string &name) const
Return XML element to entry tag.
Definition GHdf5.cpp:371
void clear(void)
Clear instance.
Definition GHdf5.cpp:267
void read(FILE *fptr)
Read data from HDF5 file into instance.
Definition GHdf5.cpp:338
bool is_empty(void) const
Signals if HDF5 instance is empty.
Definition GHdf5.hpp:225
HealPix projection class interface definition.
Definition GHealpix.hpp:50
virtual void clear(void)
Clear object.
Definition GHealpix.cpp:255
virtual void write(GFitsHDU &hdu) const
Write Healpix definition into FITS HDU.
Definition GHealpix.cpp:356
std::string ordering(void) const
Returns ordering parameter.
Definition GHealpix.cpp:511
const int & nside(void) const
Returns number of divisions of the side of each base pixel.
Definition GHealpix.hpp:207
const int & npix(void) const
Returns number of pixels.
Definition GHealpix.hpp:196
virtual void read(const GFitsHDU &hdu)
Read Healpix definition from FITS header.
Definition GHealpix.cpp:287
Sky model class.
N-dimensional array class.
Definition GNdarray.hpp:44
int size(void) const
Return number of elements in array.
Definition GNdarray.hpp:294
void clear(void)
Clear array.
Definition GNdarray.cpp:527
Abstract observation base class.
Class that handles photons.
Definition GPhoton.hpp:47
const GSkyDir & dir(void) const
Return photon sky direction.
Definition GPhoton.hpp:110
const GEnergy & energy(void) const
Return photon energy.
Definition GPhoton.hpp:122
Abstract instrument response base class.
Definition GResponse.hpp:77
void init_members(void)
Initialise class members.
void free_members(void)
Delete class members.
virtual GResponse & operator=(const GResponse &rsp)
Assignment operator.
Time class.
Definition GTime.hpp:55
XML element node class.
const GXmlAttribute * attribute(const int &index) const
Return attribute.
virtual GXmlElement * element(const int &index)
Return pointer to GXMLElement child.
Definition GXmlNode.cpp:640
virtual int elements(void) const
Return number of GXMLElement children of node.
Definition GXmlNode.cpp:586
const GXmlElement * xml_msg_datalayout(const GXmlElement *header)
Return "DataStorageLayout" message XML element.
Definition GHdf5.hpp:298
std::string fread_data_chunk(FILE *fptr, const GXmlElement *chunk, const GXmlElement *dataspace, const GXmlElement *datatype, const GXmlElement *datalayout, const GXmlElement *datafilter=NULL)
Read data chunk.
Definition GHdf5.cpp:3172
int data_to_int(const char *data, const GXmlElement *datatype)
Convert data into integer value.
Definition GHdf5.cpp:3558
const GXmlElement * xml_msg_datafilter(const GXmlElement *header)
Return "DataStorageFilterPipeline" message XML element.
Definition GHdf5.hpp:319
std::string fread_data(FILE *fptr, const int &nbytes)
Read data from HDF5 file.
Definition GHdf5.cpp:2984
double data_to_double(const char *data, const GXmlElement *datatype)
Convert data into double precision floating point value.
Definition GHdf5.cpp:3639
const GXmlElement * xml_msg_datatype(const GXmlElement *header)
Return "Datatype" message XML element.
Definition GHdf5.hpp:277
const GXmlElement * xml_msg_attribute(const GXmlElement *header, const std::string &name, const int &number=0)
Return "Attribute" message XML element with specified name.
Definition GHdf5.cpp:4193
const GXmlElement * xml_msg_dataspace(const GXmlElement *header)
Return "Dataspace" message XML element.
Definition GHdf5.hpp:257
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1136
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition GTools.hpp:203
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition GTools.hpp:220
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508
int toint(const std::string &arg)
Convert string into integer value.
Definition GTools.cpp:814
std::string expand_env(const std::string &arg)
Expand environment variables in string.
Definition GTools.cpp:233