GammaLib 2.1.0.dev
Loading...
Searching...
No Matches
GSPIObservation.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GSPIObservation.cpp - INTEGRAL/SPI observation class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2020-2024 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 GSPIObservation.cpp
23 * @brief INTEGRAL/SPI observation class implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <typeinfo>
32#include "GException.hpp"
34#include "GSPIObservation.hpp"
35#include "GSPITools.hpp"
36#include "GSPIEventCube.hpp"
37
38/* __ Globals ____________________________________________________________ */
40const GObservationRegistry g_obs_spi_registry(&g_obs_spi_seed);
41
42/* __ Method name definitions ____________________________________________ */
43#define G_RESPONSE "GSPIObservation::response(GResponse&)"
44#define G_READ "GSPIObservation::read(GXmlElement&)"
45#define G_READ_FITS "GSPIObservation::read(GFits&)"
46#define G_WRITE "GSPIObservation::write(GXmlElement&)"
47
48/* __ Macros _____________________________________________________________ */
49
50/* __ Coding definitions _________________________________________________ */
51
52/* __ Debug definitions __________________________________________________ */
53
54
55/*==========================================================================
56 = =
57 = Constructors/destructors =
58 = =
59 ==========================================================================*/
60
61/***********************************************************************//**
62 * @brief Void constructor
63 *
64 * Creates an empty INTEGRAL/SPI observation.
65 ***************************************************************************/
67{
68 // Initialise members
70
71 // Return
72 return;
73}
74
75
76/***********************************************************************//**
77 * @brief XML constructor
78 *
79 * @param[in] xml XML element.
80 *
81 * Constructs an INTEGRAL/SPI observation from the information that is found
82 * in the XML element.
83 ***************************************************************************/
85{
86 // Initialise members
88
89 // Read XML
90 read(xml);
91
92 // Return
93 return;
94}
95
96
97/***********************************************************************//**
98 * @brief Observation Group constructor
99 *
100 * @param[in] filename Observation Group FITS file name.
101 *
102 * Constructs an INTEGRAL/SPI observation from an Observation Group.
103 ***************************************************************************/
105{
106 // Initialise members
107 init_members();
108
109 // Load data
110 load(filename);
111
112 // Return
113 return;
114}
115
116
117/***********************************************************************//**
118 * @brief Copy constructor
119 *
120 * @param[in] obs INTEGRAL/SPI observation.
121 *
122 * Creates INTEGRAL/SPI observation by copying from an existing INTEGRAL/SPI
123 * observation.
124 ***************************************************************************/
126{
127 // Initialise members
128 init_members();
129
130 // Copy members
131 copy_members(obs);
132
133 // Return
134 return;
135}
136
137
138/***********************************************************************//**
139 * @brief Destructor
140 ***************************************************************************/
142{
143 // Free members
144 free_members();
145
146 // Return
147 return;
148}
149
150
151/*==========================================================================
152 = =
153 = Operators =
154 = =
155 ==========================================================================*/
156
157/***********************************************************************//**
158 * @brief Assignment operator
159 *
160 * @param[in] obs INTEGRAL/SPI observation.
161 * @return INTEGRAL/SPI observation.
162 *
163 * Assign INTEGRAL/SPI observation to this object. The assignment performs
164 * a deep copy of all information, hence the original object from which the
165 * assignment was made can be destroyed after this operation without any loss
166 * of information.
167 ***************************************************************************/
169{
170 // Execute only if object is not identical
171 if (this != &obs) {
172
173 // Copy base class members
174 this->GObservation::operator=(obs);
175
176 // Free members
177 free_members();
178
179 // Initialise members
180 init_members();
181
182 // Copy members
183 copy_members(obs);
184
185 } // endif: object was not identical
186
187 // Return this object
188 return *this;
189}
190
191
192/*==========================================================================
193 = =
194 = Public methods =
195 = =
196 ==========================================================================*/
197
198/***********************************************************************//**
199 * @brief Clear INTEGRAL/SPI observation
200 *
201 * Clears INTEGRAL/SPI observation by resetting all class members to an
202 * initial state. Any information that was present before will be lost.
203 ***************************************************************************/
205{
206 // Free members
207 free_members();
209
210 // Initialise members
212 init_members();
213
214 // Return
215 return;
216}
217
218
219/***********************************************************************//**
220 * @brief Clone INTEGRAL/SPI observation
221 *
222 * @return Pointer to deep copy of INTEGRAL/SPI observation.
223 ***************************************************************************/
225{
226 return new GSPIObservation(*this);
227}
228
229
230/***********************************************************************//**
231 * @brief Set INTEGRAL/SPI response function
232 *
233 * @param[in] rsp INTEGRAL/SPI response function.
234 *
235 * @exception GException::invalid_argument
236 * Specified response is not a INTEGRAL/SPI response.
237 *
238 * Sets the response function for the observation.
239 ***************************************************************************/
241{
242 // Get pointer on INTEGRAL/SPI response
243 const GSPIResponse* spirsp = dynamic_cast<const GSPIResponse*>(&rsp);
244 if (spirsp == NULL) {
245 std::string cls = std::string(typeid(&rsp).name());
246 std::string msg = "Response of type \""+cls+"\" is "
247 "not a INTEGRAL/SPI response. Please specify a "
248 "INTEGRAL/SPI response as argument.";
250 }
251
252 // Clone response function
253 m_response = *spirsp;
254
255 // Return
256 return;
257}
258
259
260/***********************************************************************//**
261 * @brief Read INTEGRAL/SPI observation from XML element
262 *
263 * @param[in] xml XML element.
264 *
265 * Reads information for a INTEGRAL/SPI observation from an XML element.
266 * The expected format of the XML element is
267 *
268 * <observation name="Crab" id="0044" instrument="SPI">
269 * <parameter name="ObservationGroup" file="og_spi.fits"/>
270 * </observation>
271 *
272 * In addition, a response group can be specified using
273 *
274 * <observation name="Crab" id="0044" instrument="SPI">
275 * <parameter name="ObservationGroup" file="og_spi.fits"/>
276 * <parameter name="ResponseGroup" file="spi_irf_grp.fits"/>
277 * </observation>
278 *
279 * If a response group is found the file is loaded and the response is set.
280 * Optionally, an energy attribute can be specified in units of keV, leading
281 * to the setup of a line response
282 *
283 * <observation name="Crab" id="0044" instrument="SPI">
284 * <parameter name="ObservationGroup" file="og_spi.fits"/>
285 * <parameter name="ResponseGroup" file="spi_irf_grp.fits" energy="511"/>
286 * </observation>
287 *
288 * Alternatively, a response file can be specified, which will be directly
289 * loaded in the SPI response class.
290 *
291 * <observation name="Crab" id="0044" instrument="SPI">
292 * <parameter name="ObservationGroup" file="og_spi.fits"/>
293 * <parameter name="ResponseFile" file="irf.fits"/>
294 * </observation>
295 ***************************************************************************/
297{
298 // Clear observation
299 clear();
300
301 // Extract instrument name
302 m_instrument = xml.attribute("instrument");
303
304 // Get expanded Observation Group file name
305 m_filename = gammalib::xml_get_attr(G_READ, xml, "ObservationGroup", "file");
307
308 // Load Observation Group
310
311 // If a response group is specified then get expanded file name and
312 // load it
313 if (gammalib::xml_has_par(xml, "ResponseGroup")) {
314
315 // Get parameter
316 const GXmlElement* par = gammalib::xml_get_par(G_READ, xml, "ResponseGroup");
317
318 // Get expanded filename
319 m_rsp_grpname = par->attribute("file");
321
322 // Set response group name
324
325 // If we have an energy attribute then extract line energy and set
326 // response as line response
327 if (par->has_attribute("energy")) {
328 double value = gammalib::todouble(par->attribute("energy"));
329 GEnergy energy(value, "keV");
330 m_response.set(*this, energy);
331 }
332
333 // ... otherwise set response as continnum response
334 else {
335 m_response.set(*this);
336 }
337
338 } // endif: reponse group specified
339
340 // ... otherwise if a response file is specified then load the response
341 // from the response file
342 else if (gammalib::xml_has_par(xml, "ResponseFile")) {
343
344 // Get expanded response file name
345 m_rsp_filename = gammalib::xml_get_attr(G_READ, xml, "ResponseFile", "file");
347
348 // Load response
350
351 } // endelse: reponse file was specified
352
353 // Return
354 return;
355}
356
357
358/***********************************************************************//**
359 * @brief Write INTEGRAL/SPI observation into XML element
360 *
361 * @param[in] xml XML element.
362 *
363 * Writes information for a INTEGRAL/SPI observation into an XML element.
364 * In case that no response information is available the method writes the
365 * format
366 *
367 * <observation name="Crab" id="0044" instrument="SPI">
368 * <parameter name="ObservationGroup" file="og_spi.fits"/>
369 * </observation>
370 *
371 * If a response group file is defined the method writes the format
372 *
373 * <observation name="Crab" id="0044" instrument="SPI">
374 * <parameter name="ObservationGroup" file="og_spi.fits"/>
375 * <parameter name="ResponseGroup" file="spi_irf_grp.fits" energy="511"/>
376 * </observation>
377 *
378 * The energy attribute is optional, and is only written in case that the
379 * IRF is a line IRF.
380 *
381 * Otherwise, if a response file is defined the method writes the format
382 *
383 * <observation name="Crab" id="0044" instrument="SPI">
384 * <parameter name="ObservationGroup" file="og_spi.fits"/>
385 * <parameter name="ResponseFile" file="irf.fits"/>
386 * </observation>
387 ***************************************************************************/
389{
390 // Allocate XML element pointer
391 GXmlElement* par;
392
393 // Set ObservationGroup parameter
394 par = gammalib::xml_need_par(G_WRITE, xml, "ObservationGroup");
396
397 // If we have a response group filename then write it as response
398 // group
399 if (!m_rsp_grpname.is_empty()) {
400 par = gammalib::xml_need_par(G_WRITE, xml, "ResponseGroup");
402 if (m_response.energy_keV() > 0.0) {
403 par->attribute("energy", gammalib::str(m_response.energy_keV()));
404 }
405 }
406
407 // ... otherwise if we have a response file then write the response
408 // file
409 else if (!m_rsp_filename.is_empty()) {
410 par = gammalib::xml_need_par(G_WRITE, xml, "ResponseFile");
412 }
413
414 // ... otherwise if we have a response group filename in the response
415 // then write it as response group
416 else if (!m_response.rspname().is_empty()) {
417 par = gammalib::xml_need_par(G_WRITE, xml, "ResponseGroup");
419 if (m_response.energy_keV() > 0.0) {
420 par->attribute("energy", gammalib::str(m_response.energy_keV()));
421 }
422 }
423
424 // Return
425 return;
426}
427
428
429/***********************************************************************//**
430 * @brief Read Observation Group from FITS file
431 *
432 * @param[in] fits FITS file.
433 *
434 * Reads Observation Group from a FITS file.
435 ***************************************************************************/
437{
438 // Delete any existing event container (do not call clear() as we do not
439 // want to delete the response function)
440 if (m_events != NULL) delete m_events;
441
442 // Allocate new event cube
444
445 // Assign event cube as the observation's event container
447
448 // Read in Observation Group
449 events->read(fits);
450
451 // Store ontime and livetime and compute deadtime correction
452 m_ontime = events->ontime();
453 m_livetime = events->livetime();
454 m_deadc = (m_ontime > 0.0) ? m_livetime/m_ontime : 1.0;
455
456 // Return
457 return;
458}
459
460
461/***********************************************************************//**
462 * @brief Load Observation Group
463 *
464 * @param[in] filename Observation Group FITS file name.
465 *
466 * Loads data from an Observation Group FITS file into an INTEGRAL/SPI
467 * observation.
468 ***************************************************************************/
469void GSPIObservation::load(const GFilename& filename)
470{
471 #pragma omp critical(GSPIObservation_load)
472 {
473 // Store event filename
474 m_filename = filename;
475
476 // Open FITS file
477 GFits fits(filename);
478
479 // Read data
480 read(fits);
481
482 // Close FITS file
483 fits.close();
484 }
485
486 // Return
487 return;
488}
489
490
491/***********************************************************************//**
492 * @brief Return gradient step size for a given model parameter
493 *
494 * @param[in] par Model parameter
495 * @return Gradient step size
496 *
497 * Returns the step size that is used for numerical gradient computation
498 * for a given model parameter @p par.
499 *
500 * Following a detailed analysis
501 * (see https://gitlab.in2p3.fr/gammalib/gammalib/-/issues/13)
502 * the step size is set to 0.002 for all parameters except of position
503 * angles (PA) which will have a step size of 0.02. At these step sizes
504 * numerical computation of parameter gradients is no longer perturbed by
505 * numerical noise.
506 ***************************************************************************/
508{
509 // Set step size
510 double step = (par.name() == "PA") ? 0.02 : 0.002;
511
512 // Return step size
513 return step;
514}
515
516
517/***********************************************************************//**
518 * @brief Print observation information
519 *
520 * @param[in] chatter Chattiness.
521 * @return String containing INTEGRAL/SPI observation information.
522 ***************************************************************************/
523std::string GSPIObservation::print(const GChatter& chatter) const
524{
525 // Initialise result string
526 std::string result;
527
528 // Continue only if chatter is not silent
529 if (chatter != SILENT) {
530
531 // Append header
532 result.append("=== GSPIObservation ===");
533
534 // Append standard information for observation
535 result.append("\n"+gammalib::parformat("Name")+name());
536 result.append("\n"+gammalib::parformat("Observation group filename"));
537 result.append(m_filename.url());
538 if (!m_rsp_grpname.is_empty()) {
539 result.append("\n"+gammalib::parformat("Response group filename"));
540 result.append(m_rsp_grpname.url());
541 }
542 if (!m_rsp_filename.is_empty()) {
543 result.append("\n"+gammalib::parformat("Response filename"));
544 result.append(m_rsp_filename.url());
545 }
546 result.append("\n"+gammalib::parformat("Identifier")+id());
547 result.append("\n"+gammalib::parformat("Instrument")+instrument());
548 result.append("\n"+gammalib::parformat("Statistic")+statistic());
549 result.append("\n"+gammalib::parformat("Ontime"));
550 result.append(gammalib::str(ontime())+" sec");
551 result.append("\n"+gammalib::parformat("Livetime"));
552 result.append(gammalib::str(livetime())+" sec");
553 result.append("\n"+gammalib::parformat("Deadtime correction"));
554 result.append(gammalib::str(m_deadc));
555
556 // Append detailed information
557 GChatter reduced_chatter = gammalib::reduce(chatter);
558 if (reduced_chatter > SILENT) {
559
560 // Append response
561 result.append("\n"+m_response.print(reduced_chatter));
562
563 // Append events
564 if (m_events != NULL) {
565 result.append("\n"+m_events->print(reduced_chatter));
566 }
567
568 } // endif: appended detailed information
569
570 } // endif: chatter was not silent
571
572 // Return result
573 return result;
574}
575
576
577/*==========================================================================
578 = =
579 = Private methods =
580 = =
581 ==========================================================================*/
582
583/***********************************************************************//**
584 * @brief Initialise class members
585 *
586 * Initialises all members of a SPI observations.
587 ***************************************************************************/
589{
590 // Initialise members
591 m_instrument = "SPI";
596 m_ontime = 0.0;
597 m_livetime = 0.0;
598 m_deadc = 0.0;
599
600 // Return
601 return;
602}
603
604
605/***********************************************************************//**
606 * @brief Copy class members
607 *
608 * @param[in] obs INTEGRAL/SPI observation.
609 ***************************************************************************/
611{
612 // Copy members
618 m_ontime = obs.m_ontime;
620 m_deadc = obs.m_deadc;
621
622 // Return
623 return;
624}
625
626
627/***********************************************************************//**
628 * @brief Delete class members
629 ***************************************************************************/
631{
632 // Return
633 return;
634}
#define G_WRITE
#define G_READ
#define G_RESPONSE
Exception handler interface definition.
Observation registry class definition.
INTEGRAL/SPI event bin container class definition.
const GSPIObservation g_obs_spi_seed
INTEGRAL/SPI observation class definition.
Definition of INTEGRAL/SPI tools.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
virtual void read(const GFits &file)=0
virtual std::string print(const GChatter &chatter=NORMAL) const =0
Print content of object.
Filename class.
Definition GFilename.hpp:62
std::string url(void) const
Return Uniform Resource Locator (URL)
bool is_empty(void) const
Signal if filename is empty.
void clear(void)
Clear file name.
FITS file class.
Definition GFits.hpp:63
void close(void)
Close FITS file.
Definition GFits.cpp:1342
Model parameter class.
Definition GModelPar.hpp:87
Interface definition for the observation registry class.
Abstract observation base class.
const std::string & statistic(void) const
Return optimizer statistic.
const std::string & name(void) const
Return observation name.
virtual GEvents * events(void)
Return events.
void init_members(void)
Initialise class members.
virtual GObservation & operator=(const GObservation &obs)
Assignment operator.
GEvents * m_events
Pointer to event container.
void free_members(void)
Delete class members.
const std::string & name(void) const
Return parameter name.
Abstract instrument response base class.
Definition GResponse.hpp:77
INTEGRAL/SPI event bin container class.
INTEGRAL/SPI observation class.
double m_deadc
Deadtime correction.
double m_ontime
Ontime (sec)
void load(const GFilename &filename)
Load Observation Group.
GSPIObservation(void)
Void constructor.
void copy_members(const GSPIObservation &obs)
Copy class members.
virtual double ontime(void) const
Return ontime.
virtual void read(const GXmlElement &xml)
Read INTEGRAL/SPI observation from XML element.
virtual ~GSPIObservation(void)
Destructor.
virtual std::string print(const GChatter &chatter=NORMAL) const
Print observation information.
void init_members(void)
Initialise class members.
virtual double livetime(void) const
Return livetime.
GFilename m_rsp_filename
Response FITS filename (optional)
virtual void write(GXmlElement &xml) const
Write INTEGRAL/SPI observation into XML element.
virtual GSPIObservation * clone(void) const
Clone INTEGRAL/SPI observation.
GSPIResponse m_response
Response functions.
virtual GSPIObservation & operator=(const GSPIObservation &obs)
Assignment operator.
GFilename m_rsp_grpname
Response group FITS filename (optional)
virtual const GSPIResponse * response(void) const
Return pointer to response function.
virtual void clear(void)
Clear INTEGRAL/SPI observation.
double m_livetime
Livetime (sec)
virtual double grad_step_size(const GModelPar &par) const
Return gradient step size for a given model parameter.
std::string m_instrument
Instrument name.
GFilename m_filename
OG FITS filename.
virtual std::string instrument(void) const
Return instrument.
void free_members(void)
Delete class members.
INTEGRAL/SPI instrument response function class.
virtual std::string print(const GChatter &chatter=NORMAL) const
Print INTEGRAL/SPI response information.
void load(const GFilename &filename)
Load SPI response from file.
void rspname(const GFilename &rspname)
Set response name.
virtual void clear(void)
Clear instance.
const double & energy_keV(void) const
Return line IRF energy in keV.
void set(const GSPIObservation &obs, const GEnergy &energy=GEnergy())
Set response for a specific observation.
XML element node class.
const GXmlAttribute * attribute(const int &index) const
Return attribute.
bool has_attribute(const std::string &name) const
Check if element has a given attribute.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1162
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508
const GXmlElement * xml_get_par(const std::string &origin, const GXmlElement &xml, const std::string &name)
Return pointer to parameter with given name in XML element.
Definition GTools.cpp:1708
double todouble(const std::string &arg)
Convert string into double precision value.
Definition GTools.cpp:945
std::string xml_get_attr(const std::string &origin, const GXmlElement &xml, const std::string &name, const std::string &attribute)
Return attribute value for a given parameter in XML element.
Definition GTools.cpp:1757
GXmlElement * xml_need_par(const std::string &origin, GXmlElement &xml, const std::string &name)
Return pointer to parameter with given name in XML element.
Definition GTools.cpp:1656
GFilename xml_file_reduce(const GXmlElement &xml, const std::string &filename)
Reduce file name provided for writing as XML attribute.
Definition GTools.cpp:1965
GChatter reduce(const GChatter &chatter)
Reduce chattiness by one level.
Definition GTypemaps.hpp:65
bool xml_has_par(const GXmlElement &xml, const std::string &name)
Checks if parameter with given name in XML element exists.
Definition GTools.cpp:1615
GFilename xml_file_expand(const GXmlElement &xml, const std::string &filename)
Expand file name provided as XML attribute for loading.
Definition GTools.cpp:1908