GammaLib  2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GModelSpectralTable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GModelSpectralTable.cpp - Spectral table model class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2019-2021 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 GModelSpectralTable.cpp
23  * @brief Spectral table model class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cmath>
32 #include "GException.hpp"
33 #include "GTools.hpp"
34 #include "GMath.hpp"
35 #include "GRan.hpp"
36 #include "GEnergy.hpp"
37 #include "GFits.hpp"
38 #include "GFitsBinTable.hpp"
39 #include "GFitsTableStringCol.hpp"
40 #include "GFitsTableLongCol.hpp"
41 #include "GFitsTableFloatCol.hpp"
42 #include "GModelSpectralTable.hpp"
46 
47 /* __ Constants __________________________________________________________ */
48 
49 /* __ Globals ____________________________________________________________ */
51 const GModelSpectralRegistry g_spectral_table_registry(&g_spectral_table_seed);
52 
53 /* __ Method name definitions ____________________________________________ */
54 #define G_CONST "GModelSpectralTable(GEbounds&, GModelSpectralTablePars&, "\
55  "GNdarray&)"
56 #define G_FLUX "GModelSpectralTable::flux(GEnergy&, GEnergy&)"
57 #define G_EFLUX "GModelSpectralTable::eflux(GEnergy&, GEnergy&)"
58 #define G_MC "GModelSpectralTable::mc(GEnergy&, GEnergy&, GTime&, GRan&)"
59 #define G_READ "GModelSpectralTable::read(GXmlElement&)"
60 #define G_WRITE "GModelSpectralTable::write(GXmlElement&)"
61 #define G_LOAD "GModelSpectralTable::load(GFilename&)"
62 #define G_TABLE_PAR "GModelSpectralTable::table_par(int&)"
63 #define G_LOAD_PAR "GModelSpectralTable::load_par(GFits&)"
64 #define G_PAR_INDEX "GModelSpectralTable::par_index(std::string&)"
65 #define G_UPDATE "GModelSpectralTable::update()"
66 
67 /* __ Macros _____________________________________________________________ */
68 
69 /* __ Coding definitions _________________________________________________ */
70 
71 /* __ Debug definitions __________________________________________________ */
72 //#define G_DEBUG_LOAD_SPEC //!< Debug load_spec() method
73 //#define G_DEBUG_UPDATE //!< Debug update() method
74 
75 
76 /*==========================================================================
77  = =
78  = Constructors/destructors =
79  = =
80  ==========================================================================*/
81 
82 /***********************************************************************//**
83  * @brief Void constructor
84  ***************************************************************************/
86 {
87  // Initialise members
88  init_members();
89 
90  // Return
91  return;
92 }
93 
94 
95 /***********************************************************************//**
96  * @brief File constructor
97  *
98  * @param[in] filename File name of table model.
99  * @param[in] norm Normalization factor.
100  *
101  * Constructs a spectral table model from a FITS file. See the load() method
102  * for more information about the expected structure of the FITS file.
103  ***************************************************************************/
105  const double& norm) :
107 {
108  // Initialise members
109  init_members();
110 
111  // Load table
112  load(filename);
113 
114  // Set normalization as the scale factor and limit change in
115  // normalisation to a factor of 1000
117  m_norm.scale(norm);
118  m_norm.value(norm);
119  m_norm.range(0.0,1.0e3*norm);
120 
121  // Return
122  return;
123 }
124 
125 
126 /***********************************************************************//**
127  * @brief Table model constructor
128  *
129  * @param[in] ebounds Energy boundaries.
130  * @param[in] pars Table model parameters.
131  * @param[in] spectra Spectra.
132  *
133  * Constructs a spectral table model from energy boundaries, table model
134  * parameters, and spectra.
135  ***************************************************************************/
137  const GModelSpectralTablePars& pars,
138  const GNdarray& spectra) :
140 {
141  // Initialise members
142  init_members();
143 
144  // Check consistency of spectra dimension
145  if (spectra.dim() != pars.size()+1) {
146  std::string msg = "Spectra array has "+gammalib::str(spectra.dim())+
147  " dimensions but an array with "+
148  gammalib::str(pars.size()+1)+" dimensions is "
149  "expected. Please specify a spectra array with the "
150  "correct dimension.";
152  }
153 
154  // Check consistency of parameter dimensions
155  for (int i = 0; i < pars.size(); ++i) {
156  int npars = pars[i]->size();
157  int nspec = spectra.shape()[i];
158  if (npars != nspec) {
159  std::string msg = "Parameter \""+pars[i]->par().name()+"\" has "+
160  gammalib::str(npars)+" values but there are "+
161  gammalib::str(nspec)+" spectra for table axis "+
162  gammalib::str(i)+". Please specify a spectra "
163  "array with the correct number of spectra.";
165  }
166  }
167 
168  // Check consistency of energy bins
169  int nebins = ebounds.size();
170  int nspec = spectra.shape()[pars.size()];
171  if (nebins != nspec) {
172  std::string msg = "Spectra array has "+gammalib::str(nspec)+" energy "
173  "bins but there are "+gammalib::str(nebins)+
174  " energy boundaries. Please specify a spectra "
175  "array with the correct number of energy bins.";
177  }
178 
179  // Set members
180  m_ebounds = ebounds;
181  m_table_pars = pars;
182  m_spectra = spectra;
183 
184  // Set energy nodes
186 
187  // Set parameter pointers
189 
190  // Return
191  return;
192 }
193 
194 
195 /***********************************************************************//**
196  * @brief XML constructor
197  *
198  * @param[in] xml XML element.
199  *
200  * Constructs a spectral table model by extracting information from an XML
201  * element. See the read() method for more information about the expected
202  * structure of the XML element.
203  ***************************************************************************/
206 {
207  // Initialise members
208  init_members();
209 
210  // Read information from XML element
211  read(xml);
212 
213  // Return
214  return;
215 }
216 
217 
218 /***********************************************************************//**
219  * @brief Copy constructor
220  *
221  * @param[in] model Table model.
222  ***************************************************************************/
224  GModelSpectral(model)
225 {
226  // Initialise members
227  init_members();
228 
229  // Copy members
230  copy_members(model);
231 
232  // Return
233  return;
234 }
235 
236 
237 /***********************************************************************//**
238  * @brief Destructor
239  ***************************************************************************/
241 {
242  // Free members
243  free_members();
244 
245  // Return
246  return;
247 }
248 
249 
250 /*==========================================================================
251  = =
252  = Operators =
253  = =
254  ==========================================================================*/
255 
256 /***********************************************************************//**
257  * @brief Assignment operator
258  *
259  * @param[in] model Table model.
260  * @return Table model.
261  ***************************************************************************/
263 {
264  // Execute only if object is not identical
265  if (this != &model) {
266 
267  // Copy base class members
268  this->GModelSpectral::operator=(model);
269 
270  // Free members
271  free_members();
272 
273  // Initialise members
274  init_members();
275 
276  // Copy members
277  copy_members(model);
278 
279  } // endif: object was not identical
280 
281  // Return
282  return *this;
283 }
284 
285 
286 /*==========================================================================
287  = =
288  = Public methods =
289  = =
290  ==========================================================================*/
291 
292 /***********************************************************************//**
293  * @brief Clear table model
294 ***************************************************************************/
296 {
297  // Free class members (base and derived classes, derived class first)
298  free_members();
300 
301  // Initialise members
303  init_members();
304 
305  // Return
306  return;
307 }
308 
309 
310 /***********************************************************************//**
311  * @brief Clone table model
312 ***************************************************************************/
314 {
315  // Clone table model
316  return new GModelSpectralTable(*this);
317 }
318 
319 
320 /***********************************************************************//**
321  * @brief Evaluate spectral table model
322  *
323  * @param[in] srcEng True photon energy.
324  * @param[in] srcTime True photon arrival time.
325  * @param[in] gradients Compute gradients?
326  * @return Model value (ph/cm2/s/MeV).
327  *
328  * Evaluates
329  *
330  * \f[
331  * S_{\rm E}(E | t) = {\tt m\_norm} \times
332  * \left( w_l F_l(p) + w_r F_r(p) \right)
333  * \f]
334  *
335  * where
336  * - \f${\tt m\_norm}\f$ is the normalization factor of the spectral table
337  * model,
338  * - \f$w_l\f$ is the weight of the spectral vector \f$F_l(p)\f$ with the
339  * largest energy \f$E_l\f$ that satisfies \f$E<E_l\f$, and
340  * - \f$w_r\f$ is the weight of the spectral vector \f$F_r(p)\f$ with the
341  * smallest energy \f$E_r\f$ that satisfies \f$E>E_r\f$.
342  *
343  * The weights are computed using
344  *
345  * \f[
346  * w_r = \frac{\log_{10} E - \log_{10} E_l}{\log_{10} E_r - \log_{10} E_l}
347  * \f]
348  *
349  * and \f$w_l = 1 - w_r\f$.
350  *
351  * If @p gradient is true, the method also computes the parameter gradients
352  * using
353  *
354  * \f[
355  * \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_norm}} =
356  * \frac{S_{\rm E}(E | t)}{{\tt m\_norm}}
357  * \f]
358  *
359  * and
360  *
361  * \f[
362  * \frac{\delta S_{\rm E}(E | t)}{\delta p} =
363  * {\tt m\_norm} \times
364  * \left( w_l \frac{\delta F_l(p)}{\delta p} +
365  * w_r \frac{\delta F_r(p)}{\delta p} \right)
366  * \f]
367  *
368  * for all other parameters.
369  *
370  * For the computation of \f$F_l(p)\f$, \f$F_r(p)\f$,
371  * \f$\frac{\delta F_l(p)}{\delta p}\f$, and
372  * \f$\frac{\delta F_r(p)}{\delta p}\f$ see the update() method.
373  ***************************************************************************/
374 double GModelSpectralTable::eval(const GEnergy& srcEng,
375  const GTime& srcTime,
376  const bool& gradients) const
377 {
378  // Update spectral function
379  update();
380 
381  // Interpolate function in log10 energy
382  m_log_nodes.set_value(srcEng.log10MeV());
383  double wgt_left = m_log_nodes.wgt_left();
384  double wgt_right = m_log_nodes.wgt_right();
385  int inx_left = m_log_nodes.inx_left();
386  int inx_right = m_log_nodes.inx_right();
387  double func = wgt_left * m_lin_values(inx_left,0) +
388  wgt_right * m_lin_values(inx_right,0);
389 
390  // Compute function value
391  double value = m_norm.value() * func;
392 
393  // Optionally compute gradients
394  if (gradients) {
395 
396  // Compute partial derivative of function normalisation
397  double g_norm = (m_norm.is_free()) ? m_norm.scale() * func : 0.0;
398 
399  // Set normalisation gradient
400  m_norm.factor_gradient(g_norm);
401 
402  // Compute partial derivatives of all other free parameters
403  int dim = m_table_pars.size();
404  for (int i = 0; i < dim; ++i) {
405 
406  // Initialise gradient
407  double grad = 0.0;
408 
409  // Get reference to model parameter (circumvent const correctness)
410  GModelPar& par =
411  const_cast<GModelSpectralTablePar*>(m_table_pars[i])->par();
412 
413  // If parameter is free then compute gradient
414  if (par.is_free()) {
415  grad = (wgt_left * m_lin_values(inx_left,i+1) +
416  wgt_right * m_lin_values(inx_right,i+1)) *
417  par.scale() * m_norm.value();
418  }
419 
420  // Set gradient
421  par.factor_gradient(grad);
422 
423  } // endfor: looped over all parameters
424 
425  } // endif: gradient computation was requested
426 
427  // Compile option: Check for NaN/Inf
428  #if defined(G_NAN_CHECK)
429  if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
430  std::cout << "*** ERROR: GModelSpectralTable::eval";
431  std::cout << "(srcEng=" << srcEng;
432  std::cout << ", srcTime=" << srcTime << "):";
433  std::cout << " NaN/Inf encountered";
434  std::cout << " (value=" << value;
435  std::cout << ", norm=" << norm();
436  std::cout << ", func=" << func;
437  std::cout << ")" << std::endl;
438  }
439  #endif
440 
441  // Return
442  return value;
443 }
444 
445 
446 /***********************************************************************//**
447  * @brief Returns model photon flux between [emin, emax] (units: ph/cm2/s)
448  *
449  * @param[in] emin Minimum photon energy.
450  * @param[in] emax Maximum photon energy.
451  * @return Photon flux (ph/cm2/s).
452  *
453  * Computes
454  *
455  * \f[
456  * \int_{\tt emin}^{\tt emax} S_{\rm E}(E | t) dE
457  * \f]
458  *
459  * where
460  * - [@p emin, @p emax] is an energy interval, and
461  * - \f$S_{\rm E}(E | t)\f$ is the spectral model (ph/cm2/s/MeV).
462  ***************************************************************************/
464  const GEnergy& emax) const
465 {
466  // Initialise flux
467  double flux = 0.0;
468 
469  // Compute only if integration range is valid
470  if (emin < emax) {
471 
472  // Update spectral function and flux cache
473  update();
474  update_flux();
475 
476  // Get energy range in MeV
477  double e_min = emin.MeV();
478  double e_max = emax.MeV();
479 
480  // Determine left node index for minimum energy
481  m_lin_nodes.set_value(e_min);
482  int inx_emin = m_lin_nodes.inx_left();
483 
484  // Determine left node index for maximum energy
485  m_lin_nodes.set_value(e_max);
486  int inx_emax = m_lin_nodes.inx_left();
487 
488  // If both energies are within the same nodes then simply
489  // integrate over the energy interval using the appropriate power
490  // law parameters
491  if (inx_emin == inx_emax) {
492  flux = m_prefactor[inx_emin] *
494  e_max,
495  m_epivot[inx_emin],
496  m_gamma[inx_emin]);
497  }
498 
499  // ... otherwise integrate over the nodes where emin and emax
500  // resides and all the remaining nodes
501  else {
502 
503  // If we are requested to extrapolate beyond first node,
504  // the use the first nodes lower energy and upper energy
505  // boundary for the initial integration.
506  int i_start = (e_min < m_lin_nodes[0]) ? inx_emin : inx_emin+1;
507 
508  // Integrate from emin to the node boundary
509  flux = m_prefactor[inx_emin] *
511  m_lin_nodes[i_start],
512  m_epivot[inx_emin],
513  m_gamma[inx_emin]);
514 
515  // Integrate over all nodes between
516  for (int i = i_start; i < inx_emax; ++i) {
517  flux += m_flux[i];
518  }
519 
520  // Integrate from node boundary to emax
521  flux += m_prefactor[inx_emax] *
523  e_max,
524  m_epivot[inx_emax],
525  m_gamma[inx_emax]);
526 
527  } // endelse: emin and emax not between same nodes
528 
529  // Multiply flux by normalisation factor
530  flux *= norm();
531 
532  } // endif: integration range was valid
533 
534  // Return
535  return flux;
536 }
537 
538 
539 /***********************************************************************//**
540  * @brief Returns model energy flux between [emin, emax] (units: erg/cm2/s)
541  *
542  * @param[in] emin Minimum photon energy.
543  * @param[in] emax Maximum photon energy.
544  * @return Energy flux (erg/cm2/s).
545  *
546  * Computes
547  *
548  * \f[
549  * \int_{\tt emin}^{\tt emax} S_{\rm E}(E | t) E \, dE
550  * \f]
551  *
552  * where
553  * - [@p emin, @p emax] is an energy interval, and
554  * - \f$S_{\rm E}(E | t)\f$ is the spectral model (ph/cm2/s/MeV).
555  ***************************************************************************/
557  const GEnergy& emax) const
558 {
559  // Initialise flux
560  double eflux = 0.0;
561 
562  // Compute only if integration range is valid
563  if (emin < emax) {
564 
565  // Update spectral function and flux cache
566  update();
567  update_flux();
568 
569  // Get energy range in MeV
570  double e_min = emin.MeV();
571  double e_max = emax.MeV();
572 
573  // Determine left node index for minimum energy
574  m_lin_nodes.set_value(e_min);
575  int inx_emin = m_lin_nodes.inx_left();
576 
577  // Determine left node index for maximum energy
578  m_lin_nodes.set_value(e_max);
579  int inx_emax = m_lin_nodes.inx_left();
580 
581  // If both energies are within the same nodes then simply
582  // integrate over the energy interval using the appropriate power
583  // law parameters
584  if (inx_emin == inx_emax) {
585  eflux = m_prefactor[inx_emin] *
587  e_max,
588  m_epivot[inx_emin],
589  m_gamma[inx_emin]) *
591  }
592 
593  // ... otherwise integrate over the nodes where emin and emax
594  // resides and all the remaining nodes
595  else {
596 
597  // If we are requested to extrapolate beyond first node,
598  // the use the first nodes lower energy and upper energy
599  // boundary for the initial integration.
600  int i_start = (e_min < m_lin_nodes[0]) ? inx_emin : inx_emin+1;
601 
602  // Integrate from emin to the node boundary
603  eflux = m_prefactor[inx_emin] *
605  m_lin_nodes[i_start],
606  m_epivot[inx_emin],
607  m_gamma[inx_emin]) *
609 
610  // Integrate over all nodes between
611  for (int i = i_start; i < inx_emax; ++i) {
612  eflux += m_eflux[i];
613  }
614 
615  // Integrate from node boundary to emax
616  eflux += m_prefactor[inx_emax] *
618  e_max,
619  m_epivot[inx_emax],
620  m_gamma[inx_emax]) *
622 
623  } // endelse: emin and emax not between same nodes
624 
625  // Multiply flux by normalisation factor
626  eflux *= norm();
627 
628  } // endif: integration range was valid
629 
630  // Return flux
631  return eflux;
632 }
633 
634 
635 /***********************************************************************//**
636  * @brief Returns MC energy between [emin, emax]
637  *
638  * @param[in] emin Minimum photon energy.
639  * @param[in] emax Maximum photon energy.
640  * @param[in] time True photon arrival time.
641  * @param[in,out] ran Random number generator.
642  * @return Energy.
643  *
644  * Returns Monte Carlo energy by randomly drawing from a broken power law
645  * defined by the file function.
646  ***************************************************************************/
648  const GEnergy& emax,
649  const GTime& time,
650  GRan& ran) const
651 {
652  // Check energy interval
654 
655  // Allocate energy
656  GEnergy energy;
657 
658  // Update spectral function and flux cache
659  update();
660  update_flux();
661  update_mc(emin, emax);
662 
663  // Determine in which bin we reside
664  int inx = 0;
665  if (m_mc_cum.size() > 1) {
666  double u = ran.uniform();
667  for (inx = m_mc_cum.size()-1; inx > 0; --inx) {
668  if (m_mc_cum[inx-1] <= u) {
669  break;
670  }
671  }
672  }
673 
674  // Get random energy for specific bin
675  if (m_mc_exp[inx] != 0.0) {
676  double e_min = m_mc_min[inx];
677  double e_max = m_mc_max[inx];
678  double u = ran.uniform();
679  double eng = (u > 0.0)
680  ? std::exp(std::log(u * (e_max - e_min) + e_min) / m_mc_exp[inx])
681  : 0.0;
682  energy.MeV(eng);
683  }
684  else {
685  double e_min = m_mc_min[inx];
686  double e_max = m_mc_max[inx];
687  double u = ran.uniform();
688  double eng = std::exp(u * (e_max - e_min) + e_min);
689  energy.MeV(eng);
690  }
691 
692  // Return energy
693  return energy;
694 }
695 
696 
697 /***********************************************************************//**
698  * @brief Read model from XML element
699  *
700  * @param[in] xml XML element containing power law model information.
701  *
702  * Reads the spectral information from an XML element. The format of the XML
703  * elements is
704  *
705  * <spectrum type="TableModel" file="..">
706  * <parameter name="Normalization" scale=".." value=".." min=".." max=".." free=".."/>
707  * </spectrum>
708  *
709  * Optionally, values for the model table parameters can also be provided.
710  ***************************************************************************/
712 {
713  // Load table model from file (do this first since method calls clear())
714  load(gammalib::xml_file_expand(xml, xml.attribute("file")));
715 
716  // Get normalisation parameter pointer
718 
719  // Read normalisation parameter
720  m_norm.read(*norm);
721 
722  // Optionally read all table parameters
723  for (int i = 0; i < m_table_pars.size(); ++i) {
724  GModelPar& par = m_table_pars[i]->par();
725  if (gammalib::xml_has_par(xml, par.name())) {
726  const GXmlElement* element =
727  gammalib::xml_get_par(G_READ, xml, par.name());
728  par.read(*element);
729  }
730  }
731 
732  // Return
733  return;
734 }
735 
736 
737 /***********************************************************************//**
738  * @brief Write model into XML element
739  *
740  * @param[in] xml XML element into which model information is written.
741  *
742  * Writes the spectral information into an XML element. The format of the XML
743  * element is
744  *
745  * <spectrum type="FileFunction" file="..">
746  * <parameter name="Normalization" scale=".." value=".." min=".." max=".." free=".."/>
747  * </spectrum>
748  *
749  * In addition, the method writes the model table parameters into the XML
750  * file.
751  *
752  * Note that the function nodes will not be written since they will not be
753  * altered by any method.
754  ***************************************************************************/
756 {
757  // Verify model type
759 
760  // Get normalisation parameter
762 
763  // Write normalisation parameter
764  m_norm.write(*norm);
765 
766  // Write all table parameters
767  for (int i = 0; i < m_table_pars.size(); ++i) {
768  const GModelPar& par = m_table_pars[i]->par();
769  GXmlElement* element = gammalib::xml_need_par(G_WRITE, xml, par.name());
770  par.write(*element);
771  }
772 
773  // Set file attribute
775 
776  // Return
777  return;
778 }
779 
780 
781 /***********************************************************************//**
782  * @brief Load table from file
783  *
784  * @param[in] filename File name.
785  *
786  * Loads table model from FITS file. The format of the FITS file complies
787  * with https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/ogip_92_009.html
788  ***************************************************************************/
790 {
791  // Clear table model
792  clear();
793 
794  // Set filename
796 
797  // Continue only if filename is not empty
798  if (!filename.is_empty()) {
799 
800  // Open FITS file
801  GFits fits(filename);
802 
803  // Load data from extensions
804  load_par(fits);
805  load_eng(fits);
806  load_spec(fits);
807 
808  // Close FITS file
809  fits.close();
810 
811  // Set energy nodes
813 
814  // Set parameter pointers
816 
817  } // endif: filename was not empty
818 
819  // Return
820  return;
821 }
822 
823 
824 /***********************************************************************//**
825  * @brief Save table into file
826  *
827  * @param[in] filename File name.
828  * @param[in] clobber Overwrite existing file?
829  *
830  * Save the table model into a FITS file. The FITS file will contain three
831  * binary table extensions:
832  *
833  * * PARAMETERS - Table model parameters
834  * * ENERGIES - Table model energies
835  * * SPECTRA - Table model spectra
836  *
837  * The format of the FITS file complies with
838  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/ogip_92_009.html
839  ***************************************************************************/
841  const bool& clobber) const
842 {
843  // Create FITS file
844  GFits fits;
845 
846  // Create binary tables
847  GFitsBinTable par_table = create_par_table();
848  GFitsBinTable eng_table = create_eng_table();
849  GFitsBinTable spec_table = create_spec_table();
850 
851  // Append binary tables
852  fits.append(par_table);
853  fits.append(eng_table);
854  fits.append(spec_table);
855 
856  // Set keywords in primary header
857  GFitsHDU* primary = fits[0];
858  primary->card("CONTENT", "MODEL", "Spectrum file");
859  primary->card("FILENAME", filename.url(), "FITS file name");
860  primary->card("ORIGIN", PACKAGE_NAME, "Origin of FITS file");
861  primary->card("MODLNAME", "model", "Model name");
862  primary->card("MODLUNIT", "photons/cm^2/s/MeV", "Model units");
863  primary->card("REDSHIFT", false, "If true then redshift will be included as a par");
864  primary->card("ADDMODEL", true, "If true then this is an additive table model");
865  primary->card("HDUCLASS", "OGIP", "Format conforms to OGIP standard");
866  primary->card("HDUCLAS1", "XSPEC TABLE MODEL", "Model spectra for XSPEC");
867  primary->card("HDUVERS", "1.0.0", "Version of format");
868 
869  // Save to FITS file
870  fits.saveto(filename, clobber);
871 
872  // Set filename
874 
875  // Return
876  return;
877 }
878 
879 
880 /***********************************************************************//**
881  * @brief Return reference to table parameter
882  *
883  * @param[in] index Table parameter index [0,...,size()[.
884  * @return Reference to table parameter.
885  ***************************************************************************/
887 {
888  // Raise exception if index is out of range
889  if (index < 0 || index >= size()) {
890  throw GException::out_of_range(G_TABLE_PAR, "Table parameter index",
891  index, size());
892  }
893 
894  // Return reference
895  return *(m_table_pars[index]);
896 }
897 
898 
899 /***********************************************************************//**
900  * @brief Return const reference to table parameter
901  *
902  * @param[in] index Table parameter index [0,...,size()[.
903  * @return Const reference to table parameter.
904  ***************************************************************************/
906 {
907  // Raise exception if index is out of range
908  if (index < 0 || index >= size()) {
909  throw GException::out_of_range(G_TABLE_PAR, "Table parameter index",
910  index, size());
911  }
912 
913  // Return reference
914  return *(m_table_pars[index]);
915 }
916 
917 
918 /***********************************************************************//**
919  * @brief Return reference to table parameter
920  *
921  * @param[in] name Table parameter name.
922  * @return Reference to table parameter.
923  ***************************************************************************/
925 {
926  // Get index from name
927  int index = par_index(name);
928 
929  // Return reference
930  return *(m_table_pars[index]);
931 }
932 
933 
934 /***********************************************************************//**
935  * @brief Return const reference to table parameter
936  *
937  * @param[in] name Table parameter name.
938  * @return Const reference to table parameter.
939  ***************************************************************************/
940 const GModelSpectralTablePar& GModelSpectralTable::table_par(const std::string& name) const
941 {
942  // Get index from name
943  int index = par_index(name);
944 
945  // Return reference
946  return *(m_table_pars[index]);
947 }
948 
949 
950 /***********************************************************************//**
951  * @brief Print table model information
952  *
953  * @param[in] chatter Chattiness.
954  * @return String containing table model information.
955  ***************************************************************************/
956 std::string GModelSpectralTable::print(const GChatter& chatter) const
957 {
958  // Initialise result string
959  std::string result;
960 
961  // Continue only if chatter is not silent
962  if (chatter != SILENT) {
963 
964  // Append header
965  result.append("=== GModelSpectralTable ===");
966 
967  // Append information
968  result.append("\n"+gammalib::parformat("Table file"));
969  result.append(m_filename.url());
970  result.append("\n"+gammalib::parformat("Number of parameters"));
971  result.append(gammalib::str(this->size()));
972  for (int i = 0; i < size(); ++i) {
973  result.append("\n"+m_pars[i]->print(chatter));
974  }
975 
976  // Append parameter value intervals
977  for (int i = 0; i < m_table_pars.size(); ++i) {
978  std::string name = m_table_pars[i]->par().name();
979  int size = m_table_pars[i]->values().size();
980  result.append("\n"+gammalib::parformat(name+" values"));
981  result.append(gammalib::str(size));
982  if (size > 0) {
983  double min = m_table_pars[i]->values()[0];
984  double max = m_table_pars[i]->values()[0];
985  for (int k = 0; k < size; ++k) {
986  double value = m_table_pars[i]->values()[k];
987  if (value < min) {
988  min = value;
989  }
990  if (value > max) {
991  max = value;
992  }
993  }
994  result.append(" ["+gammalib::str(min)+", "+gammalib::str(max)+"]");
995  }
996  if (m_table_pars[i]->method() == 0) {
997  result.append(" (linear)");
998  }
999  else if (m_table_pars[i]->method() == 1) {
1000  result.append(" (logarithmic)");
1001  }
1002  }
1003 
1004  // Append energy boundaries
1005  result.append("\n"+gammalib::parformat("Energies"));
1006  result.append(gammalib::str(m_ebounds.size()));
1007  result.append(" [");
1008  result.append(m_ebounds.emin().print());
1009  result.append(", ");
1010  result.append(m_ebounds.emax().print());
1011  result.append("]");
1012 
1013  // Append shape of spectra array
1014  int nspectra = 0;
1015  int nebins = 0;
1016  if (m_spectra.dim() > 0) {
1017  nspectra = 1;
1018  for (int i = 0; i < m_spectra.dim()-1; ++i) {
1019  nspectra *= m_spectra.shape()[i];
1020  }
1021  nebins = m_spectra.shape()[m_spectra.dim()-1];
1022  }
1023  result.append("\n"+gammalib::parformat("Spectra array dimension"));
1024  result.append(gammalib::str(m_spectra.dim()));
1025  result.append("\n"+gammalib::parformat("Number of spectra"));
1026  result.append(gammalib::str(nspectra));
1027  result.append("\n"+gammalib::parformat("Number of spectral bins"));
1028  result.append(gammalib::str(nebins));
1029 
1030  } // endif: chatter was not silent
1031 
1032  // Return result
1033  return result;
1034 }
1035 
1036 
1037 /*==========================================================================
1038  = =
1039  = Private methods =
1040  = =
1041  ==========================================================================*/
1042 
1043 /***********************************************************************//**
1044  * @brief Initialise class members
1045  ***************************************************************************/
1047 {
1048  // Initialise normalisation
1049  m_norm.clear();
1050  m_norm.name("Normalization");
1051  m_norm.scale(1.0);
1052  m_norm.value(1.0);
1053  m_norm.range(0.0,1000.0);
1054  m_norm.free();
1055  m_norm.gradient(0.0);
1056  m_norm.has_grad(true);
1057 
1058  // Initialize other members
1059  m_table_pars.clear();
1060  m_spectra.clear();
1061  m_ebounds.clear();
1062  m_filename.clear();
1063 
1064  // Initialize cache
1065  m_npars = 0;
1066  m_nebins = 0;
1067  m_last_values.clear();
1068  m_lin_nodes.clear();
1069  m_log_nodes.clear();
1070  m_lin_values.clear();
1071  m_log_values.clear();
1072 
1073  // Initialise flux cache
1074  m_prefactor.clear();
1075  m_gamma.clear();
1076  m_epivot.clear();
1077  m_flux.clear();
1078  m_eflux.clear();
1079 
1080  // Initialise MC cache
1081  m_mc_emin.clear();
1082  m_mc_emax.clear();
1083  m_mc_cum.clear();
1084  m_mc_min.clear();
1085  m_mc_max.clear();
1086  m_mc_exp.clear();
1087 
1088  // Set parameter pointer(s)
1089  set_par_pointers();
1090 
1091  // Return
1092  return;
1093 }
1094 
1095 
1096 /***********************************************************************//**
1097  * @brief Copy class members
1098  *
1099  * @param[in] model Table model.
1100  ***************************************************************************/
1102 {
1103  // Copy members
1104  m_norm = model.m_norm;
1105  m_table_pars = model.m_table_pars;
1106  m_spectra = model.m_spectra;
1107  m_ebounds = model.m_ebounds;
1108  m_filename = model.m_filename;
1109 
1110  // Copy cache
1111  m_npars = model.m_npars;
1112  m_nebins = model.m_nebins;
1113  m_last_values = model.m_last_values;
1114  m_lin_nodes = model.m_lin_nodes;
1115  m_log_nodes = model.m_log_nodes;
1116  m_lin_values = model.m_lin_values;
1117  m_log_values = model.m_log_values;
1118 
1119  // Copy flux cache
1120  m_prefactor = model.m_prefactor;
1121  m_gamma = model.m_gamma;
1122  m_epivot = model.m_epivot;
1123  m_flux = model.m_flux;
1124  m_eflux = model.m_eflux;
1125 
1126  // Copy MC cache
1127  m_mc_emin = model.m_mc_emin;
1128  m_mc_emax = model.m_mc_emax;
1129  m_mc_cum = model.m_mc_cum;
1130  m_mc_min = model.m_mc_min;
1131  m_mc_max = model.m_mc_max;
1132  m_mc_exp = model.m_mc_exp;
1133 
1134  // Set energy nodes
1135  set_energy_nodes();
1136 
1137  // Set parameter pointer(s)
1138  set_par_pointers();
1139 
1140  // Return
1141  return;
1142 }
1143 
1144 
1145 /***********************************************************************//**
1146  * @brief Delete class members
1147  ***************************************************************************/
1149 {
1150  // Return
1151  return;
1152 }
1153 
1154 
1155 /***********************************************************************//**
1156  * @brief Set parameter pointers
1157  ***************************************************************************/
1159 {
1160  // Clear pointers
1161  m_pars.clear();
1162 
1163  // Put normalisation parameter on stack
1164  m_pars.push_back(&m_norm);
1165 
1166  // Put table model parameters on stack
1167  for (int i = 0; i < m_table_pars.size(); ++i) {
1168  m_pars.push_back(&(m_table_pars[i]->par()));
1169  }
1170 
1171  // Return
1172  return;
1173 }
1174 
1175 
1176 /***********************************************************************//**
1177  * @brief Set energy nodes from energy boundaries
1178  ***************************************************************************/
1180 {
1181  // Determine number of energy bins
1182  int nebins = m_ebounds.size();
1183 
1184  // Continue only if there are energy bins
1185  if (nebins > 0) {
1186 
1187  // Initialise vectors for values
1188  m_lin_nodes = GNodeArray();
1189  m_log_nodes = GNodeArray();
1190  m_lin_values = GNdarray(nebins, 1);
1191  m_log_values = GNdarray(nebins, 1);
1192 
1193  // Compute node values
1194  for (int i = 0; i < nebins; ++i) {
1195  double energy_MeV = m_ebounds.elogmean(i).MeV();
1196  double log10_energy_MeV = std::log10(energy_MeV);
1197  m_lin_nodes.append(energy_MeV);
1198  m_log_nodes.append(log10_energy_MeV);
1199  }
1200 
1201  } // endif: there were energy bins
1202 
1203  // Return
1204  return;
1205 }
1206 
1207 
1208 /***********************************************************************//**
1209  * @brief Create PARAMETERS FITS table
1210  *
1211  * @return Binary FITS table containing table model parameters
1212  *
1213  * The method creates a binary FITS table that is compliant with
1214  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/node6.html
1215  ***************************************************************************/
1217 {
1218  // Determine number of parameters
1219  int nrows = m_table_pars.size();
1220 
1221  // Determine maximum number of parameters values
1222  int ncols = 0;
1223  for (int i = 0; i < nrows; ++i) {
1224  int n = m_table_pars[i]->size();
1225  if (n > ncols) {
1226  ncols = n;
1227  }
1228  }
1229 
1230  // Create table columns
1231  GFitsTableStringCol col_name("NAME", nrows, 12);
1232  GFitsTableLongCol col_method("METHOD", nrows);
1233  GFitsTableFloatCol col_initial("INITIAL", nrows);
1234  GFitsTableFloatCol col_delta("DELTA", nrows);
1235  GFitsTableFloatCol col_minimum("MINIMUM", nrows);
1236  GFitsTableFloatCol col_bottom("BOTTOM", nrows);
1237  GFitsTableFloatCol col_top("TOP", nrows);
1238  GFitsTableFloatCol col_maximum("MAXIMUM", nrows);
1239  GFitsTableLongCol col_numbvals("NUMBVALS", nrows);
1240  GFitsTableFloatCol col_value("VALUE", nrows, ncols);
1241 
1242  // Fill columns
1243  for (int i = 0; i < nrows; ++i) {
1244 
1245  // Get model parameter
1246  const GModelPar& par = m_table_pars[i]->par();
1247 
1248  // Set parameter name and initial value
1249  col_name(i) = par.name();
1250  col_method(i) = m_table_pars[i]->method();
1251  col_initial(i) = (float)par.value();
1252 
1253  // Handle free/fixed parameter attribute
1254  if (par.is_fixed()) {
1255  col_delta(i) = -1.0;
1256  }
1257  else {
1258  col_delta(i) = 1.0; // Dummy value
1259  }
1260 
1261  // Set number of parameters
1262  col_numbvals(i) = m_table_pars[i]->size();
1263 
1264  // Set parameter values
1265  double min_value = 0.0;
1266  double max_value = 0.0;
1267  if (m_table_pars[i]->size() > 0) {
1268  min_value = m_table_pars[i]->values()[0];
1269  max_value = m_table_pars[i]->values()[0];
1270  for (int k = 0; k < m_table_pars[i]->size(); ++k) {
1271  double value = m_table_pars[i]->values()[k];
1272  if (value < min_value) {
1273  min_value = value;
1274  }
1275  if (value > max_value) {
1276  max_value = value;
1277  }
1278  col_value(i,k) = value;
1279  }
1280  }
1281 
1282  // Handle parameter minimum
1283  if (par.has_min()) {
1284  if (par.min() < min_value) {
1285  col_minimum(i) = (float)par.min(); // Hard minimum limit
1286  col_bottom(i) = min_value; // Soft minimum limit
1287  }
1288  else {
1289  col_minimum(i) = (float)par.min(); // Hard minimum limit
1290  col_bottom(i) = (float)par.min(); // Soft minimum limit
1291  }
1292  }
1293  else {
1294  col_minimum(i) = min_value; // Hard minimum limit
1295  col_bottom(i) = min_value; // Soft minimum limit
1296  }
1297 
1298  // Handle parameter maximum
1299  if (par.has_max()) {
1300  if (par.max() > max_value) {
1301  col_top(i) = max_value; // Soft maximum limit
1302  col_maximum(i) = (float)par.max(); // Hard maximum limit
1303  }
1304  else {
1305  col_top(i) = (float)par.max(); // Soft maximum limit
1306  col_maximum(i) = (float)par.max(); // Hard maximum limit
1307  }
1308  }
1309  else {
1310  col_top(i) = max_value; // Soft maximum limit
1311  col_maximum(i) = max_value; // Soft maximum limit
1312  }
1313 
1314  } // endfor: looped over parameters
1315 
1316  // Create binary table
1317  GFitsBinTable table;
1318 
1319  // Append columns to FITS table
1320  table.append(col_name);
1321  table.append(col_method);
1322  table.append(col_initial);
1323  table.append(col_delta);
1324  table.append(col_minimum);
1325  table.append(col_bottom);
1326  table.append(col_top);
1327  table.append(col_maximum);
1328  table.append(col_numbvals);
1329  table.append(col_value);
1330 
1331  // Set table extension name
1332  table.extname("PARAMETERS");
1333 
1334  // Set table keywords
1335  table.card("HDUCLASS", "OGIP", "Format conforms to OGIP standard");
1336  table.card("HDUCLAS1", "XSPEC TABLE MODEL", "Model spectra for XSPEC");
1337  table.card("HDUCLAS2", "PARAMETERS", "Extension containing parameter info");
1338  table.card("HDUVERS", "1.0.0", "Version of format");
1339  table.card("NINTPARM", nrows, "Number of interpolation parameters");
1340  table.card("NADDPARM", 0, "Number of additional parameters");
1341 
1342  // Return table
1343  return table;
1344 }
1345 
1346 
1347 /***********************************************************************//**
1348  * @brief Create ENERGIES FITS table
1349  *
1350  * @return Binary FITS table containing table model energies
1351  *
1352  * The method creates a binary FITS table that is compliant with
1353  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/node7.html
1354  ***************************************************************************/
1356 {
1357  // Create table columns
1358  GFitsTableFloatCol col_lo("ENERG_LO", m_ebounds.size());
1359  GFitsTableFloatCol col_hi("ENERG_HI", m_ebounds.size());
1360 
1361  // Fill energy boundary columns
1362  for (int i = 0; i < m_ebounds.size(); ++i) {
1363  col_lo(i) = m_ebounds.emin(i).keV();
1364  col_hi(i) = m_ebounds.emax(i).keV();
1365  }
1366 
1367  // Set energy units
1368  col_lo.unit("keV");
1369  col_hi.unit("keV");
1370 
1371  // Create binary table
1372  GFitsBinTable table;
1373 
1374  // Append columns to FITS table
1375  table.append(col_lo);
1376  table.append(col_hi);
1377 
1378  // Set table extension name
1379  table.extname("ENERGIES");
1380 
1381  // Set table keywords
1382  table.card("HDUCLASS", "OGIP", "Format conforms to OGIP standard");
1383  table.card("HDUCLAS1", "XSPEC TABLE MODEL", "Model spectra for XSPEC");
1384  table.card("HDUCLAS2", "ENERGIES", "Extension containing energy bin info");
1385  table.card("HDUVERS", "1.0.0", "Version of format");
1386 
1387  // Return table
1388  return table;
1389 }
1390 
1391 
1392 /***********************************************************************//**
1393  * @brief Create SPECTRA FITS table
1394  *
1395  * @return Binary FITS table containing table model spectra
1396  *
1397  * The method creates a binary FITS table that is compliant with
1398  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/node8.html
1399  ***************************************************************************/
1401 {
1402  // Compute number of rows
1403  int nrows = 1;
1404  for (int i = 0; i < m_spectra.dim()-1; ++i) {
1405  nrows *= m_spectra.shape()[i];
1406  }
1407 
1408  // Compute number of parameters
1409  int npars = m_table_pars.size();
1410 
1411  // Compute number of energy bins
1412  int nebins = m_ebounds.size();
1413 
1414  // Create columns
1415  GFitsTableFloatCol col_pars("PARAMVAL", nrows, npars);
1416  GFitsTableFloatCol col_spec("INTPSPEC", nrows, nebins);
1417 
1418  // Set units
1419  col_spec.unit("ph cm-2 s-1 MeV-1");
1420 
1421  // Fill columns
1422  std::vector<int> inx(npars+1,0);
1423  for (int i = 0; i < nrows; ++i) {
1424 
1425  // Set parameter values
1426  for (int k = 0; k < npars; ++k) {
1427  col_pars(i,k) = m_table_pars[k]->values()[inx[k]];
1428  }
1429 
1430  // Set current index
1431  std::vector<int> index = inx;
1432 
1433  // Loop over energy bins
1434  for (int k = 0; k < nebins; ++k, ++index[npars]) {
1435  col_spec(i,k) = m_spectra(index);
1436  }
1437 
1438  // Increment parameter index. Last parameter index is changing fastest
1439  int ipar = npars-1;
1440  do {
1441  inx[ipar] += 1;
1442  if (inx[ipar] < m_spectra.shape()[ipar]) {
1443  break;
1444  }
1445  else {
1446  inx[ipar] = 0;
1447  ipar--;
1448  }
1449  } while (ipar >= 0);
1450 
1451  } // endfor: looped over rows
1452 
1453  // Create binary table
1454  GFitsBinTable table;
1455 
1456  // Append columns to FITS table
1457  table.append(col_pars);
1458  table.append(col_spec);
1459 
1460  // Set table extension name
1461  table.extname("SPECTRA");
1462 
1463  // Set table keywords
1464  table.card("HDUCLASS", "OGIP", "Format conforms to OGIP standard");
1465  table.card("HDUCLAS1", "XSPEC TABLE MODEL", "Model spectra for XSPEC");
1466  table.card("HDUCLAS2", "MODEL SPECTRA", "Extension containing model spectra");
1467  table.card("HDUVERS", "1.0.0", "Version of format");
1468 
1469  // Return table
1470  return table;
1471 }
1472 
1473 
1474 /***********************************************************************//**
1475  * @brief Load data from PARAMETERS extension
1476  *
1477  * @param[in] fits FITS file.
1478  *
1479  * @exception GException::invalid_value
1480  * Non-positive parameter value encountered for logarithmic
1481  * parameters.
1482  *
1483  * The method loads data from the PARAMETERS binary table. The format of the
1484  * table needs to be compliant with
1485  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/node6.html
1486  ***************************************************************************/
1488 {
1489  // Get PARAMETERS extension
1490  const GFitsTable& table = *fits.table("PARAMETERS");
1491 
1492  // Get number of parameters
1493  int npars = table.integer("NAXIS2");
1494 
1495  // Loop over all parameters
1496  for (int i = 0; i < npars; ++i) {
1497 
1498  // Set model parameter
1499  GModelPar par(table["NAME"]->string(i), table["INITIAL"]->real(i));
1500 
1501  // Apply hard minimum and maximum as parameter range. Note that the
1502  // minimum and maximum apply to the value factor, hence in case of
1503  // a negative scale factor the minimum becomes the maximum and vice
1504  // versa. This is actually a bug in GammaLib, see
1505  // https://cta-redmine.irap.omp.eu/issues/3072
1506  double min = table["MINIMUM"]->real(i) / par.scale();
1507  double max = table["MAXIMUM"]->real(i) / par.scale();
1508  if (min > max) {
1509  par.factor_range(max, min);
1510  }
1511  else {
1512  par.factor_range(min, max);
1513  }
1514 
1515  // Fix or free parameter according to DELTA value
1516  if (table["DELTA"]->real(i) < 0.0) {
1517  par.fix();
1518  par.has_grad(false);
1519  }
1520  else {
1521  par.free();
1522  par.has_grad(true);
1523  }
1524 
1525  // Set vector of parameter values
1526  std::vector<double> values;
1527  for (int k = 0; k < table["NUMBVALS"]->integer(i); ++k) {
1528 
1529  // Extract value
1530  double value = table["VALUE"]->real(i,k);
1531 
1532  // If method is logarithmic then store log of the parameter
1533  // values in the spectral table parameters
1534  /*
1535  if (table["METHOD"]->integer(i) == 1) {
1536  if (value > 0.0) {
1537  value = std::log(value);
1538  }
1539  else {
1540  std::string msg = "Non-positive value "+gammalib::str(value)+
1541  " encountered for logarithmic parameter "
1542  "\""+table["NAME"]->string(i)+"\". Please "
1543  "provide only positive parameter "
1544  "values for logarithmic parameters.";
1545  throw GException::invalid_value(G_LOAD_PAR, msg);
1546  }
1547  }
1548  */
1549 
1550  // Append value
1551  values.push_back(value);
1552 
1553  } // endfor: looped over parameter values
1554 
1555  // Set table model parameter
1556  GModelSpectralTablePar table_model_par(par, values);
1557 
1558  // Set interpolation method
1559  table_model_par.method(table["METHOD"]->integer(i));
1560 
1561  // Append table model parameter
1562  m_table_pars.append(table_model_par);
1563 
1564  } // endfor: looped over all parameters
1565 
1566  // Return
1567  return;
1568 }
1569 
1570 
1571 /***********************************************************************//**
1572  * @brief Load data from ENERGIES extension
1573  *
1574  * @param[in] fits FITS file.
1575  *
1576  * The method loads data from the ENERGIES binary table. The format of the
1577  * table needs to be compliant with
1578  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/node7.html
1579  *
1580  * If energy units are provided in the ENERGIES extension the method decodes
1581  * the units and interprets the energy values correspondingly.
1582  ***************************************************************************/
1584 {
1585  // Get ENERGIES extension
1586  const GFitsTable& table = *fits.table("ENERGIES");
1587 
1588  // Get number of energy bins
1589  int nebins = table.integer("NAXIS2");
1590 
1591  // Extract energy boundary information from FITS table
1592  if (nebins > 0) {
1593 
1594  // Get units
1595  std::string emin_unit = table["ENERG_LO"]->unit();
1596  std::string emax_unit = table["ENERG_HI"]->unit();
1597  if (emin_unit.empty()) {
1598  emin_unit = "keV";
1599  }
1600  if (emax_unit.empty()) {
1601  emax_unit = "keV";
1602  }
1603 
1604  // Read energy boundaries and append bins
1605  for (int i = 0; i < nebins; ++i) {
1606  GEnergy emin(table["ENERG_LO"]->real(i), emin_unit);
1607  GEnergy emax(table["ENERG_HI"]->real(i), emax_unit);
1608  m_ebounds.append(emin, emax);
1609  }
1610 
1611  } // endif: there were energy bins
1612 
1613  // Return
1614  return;
1615 }
1616 
1617 
1618 /***********************************************************************//**
1619  * @brief Load data from SPECTRA extension
1620  *
1621  * @param[in] fits FITS file.
1622  *
1623  * The method loads data from the SPECTRA binary table. The format of the
1624  * table needs to be compliant with
1625  * https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_92_009/node8.html
1626  ***************************************************************************/
1628 {
1629  // Get number of parameters and energy bins
1630  int npars = fits.table("PARAMETERS")->integer("NAXIS2");
1631  int nebins = fits.table("ENERGIES")->integer("NAXIS2");
1632 
1633  // Setup dimension of spectra array
1634  std::vector<int> naxis(npars+1, 0);
1635  for (int i = 0; i < npars; ++i) {
1636  naxis[i] = (*fits.table("PARAMETERS"))["NUMBVALS"]->integer(i);
1637  }
1638  naxis[npars] = nebins;
1639 
1640  // Setup spectra array
1641  m_spectra = GNdarray(naxis);
1642 
1643  // Get SPECTRA extension
1644  const GFitsTable& table = *fits.table("SPECTRA");
1645 
1646  // Get number of rows
1647  int nrows = table.integer("NAXIS2");
1648 
1649  // Loop over rows
1650  for (int i = 0; i < nrows; ++i) {
1651 
1652  // Initialise spectra array index of first energy bin
1653  std::vector<int> index(npars+1, 0);
1654 
1655  // Setup spectra array index
1656  std::vector<double> parval(npars, 0.0);
1657  for (int k = 0; k < npars; ++k) {
1658 
1659  // Get reference to node array
1660  const GNodeArray& nodes = m_table_pars[k]->values();
1661 
1662  // Set interpolation value
1663  nodes.set_value(table["PARAMVAL"]->real(i,k));
1664 
1665  // Get best index
1666  int inx = (nodes.wgt_left() > nodes.wgt_right()) ? nodes.inx_left()
1667  : nodes.inx_right();
1668 
1669  // Store index
1670  index[k] = inx;
1671 
1672  } // endfor: looped over parameters
1673 
1674  // Debug: dump vector array
1675  #if defined(G_DEBUG_LOAD_SPEC)
1676  std::cout << i << ": (";
1677  for (int k = 0; k < index.size(); ++k) {
1678  if (k > 0) {
1679  std::cout << ", ";
1680  }
1681  std::cout << index[k];
1682  }
1683  std::cout << ")" << std::endl;
1684  #endif
1685 
1686  // Loop over energy bins and store spectrum
1687  for (int k = 0; k < nebins; ++k, ++index[npars]) {
1688  m_spectra(index) = table["INTPSPEC"]->real(i,k);
1689  }
1690 
1691  } // endfor: looped over rows
1692 
1693  // Return
1694  return;
1695 }
1696 
1697 
1698 /***********************************************************************//**
1699  * @brief Return index for parameter name
1700  *
1701  * @param[in] name Parameter name.
1702  * @return Parameter index.
1703  *
1704  * @exception GException::invalid_argument
1705  * Parameter name not found in spectral table.
1706  ***************************************************************************/
1707 int GModelSpectralTable::par_index(const std::string& name) const
1708 {
1709  // Get parameter index
1710  int index = 0;
1711  for (; index < size(); ++index) {
1712  if (m_table_pars[index]->par().name() == name) {
1713  break;
1714  }
1715  }
1716 
1717  // Throw exception if parameter name was not found
1718  if (index >= size()) {
1719  std::string msg = "Parameter name \""+name+"\" not found in spectral "
1720  "table. Please specify one of the following parameter "
1721  "names:";
1722  for (int i = 0; i < size(); ++i) {
1723  if (i > 0) {
1724  msg += ",";
1725  }
1726  msg += " \""+m_table_pars[i]->par().name()+"\"";
1727  }
1729  }
1730 
1731  // Return index
1732  return index;
1733 }
1734 
1735 
1736 /***********************************************************************//**
1737  * @brief Update cache for spectral table model computation
1738  *
1739  * Update interval vectors for function values and gradients. An update is
1740  * performed in case that some of the parameter values have changed. The
1741  * method sets the following cache members:
1742  *
1743  * m_npars (number of model parameters)
1744  * m_nebins (number of energy bins in spectra)
1745  * m_last_values (last model parameter values)
1746  * m_lin_values (function values)
1747  *
1748  * The array \f${\tt m\_lin\_values}\f$ holds the vector \f$F(E|p)\f$ as
1749  * function of energy \f$E\f$, computed for the current set of parameters
1750  * \f$p\f$. The computation is done using the N-dimensional linear
1751  * interpolation
1752  *
1753  * \f[
1754  * F(E|p) = \sum_{k=1}^M \left( \prod_{i=1}^N w_x(p) \right) F_p(E)
1755  * \f]
1756  *
1757  * where
1758  * - \f$M=2^N\f$ is the number of parameter combinations,
1759  * - \f$N\f$ is the number of table model parameters,
1760  * - \f$w_x(p) = \{w_0^l, w_0^r, w_1^l, w_1^r, ...\}\f$
1761  * is an array of subsequently arranged left and right weighting factors
1762  * for the linear interpolation of parameters, where \f$w_0^l\f$ and
1763  * \f$w_0^r\f$ are the left and right weighting factors for the first
1764  * parameter, \f$w_1^l\f$ and \f$w_1^r\f$ are the left and right weighting
1765  * factors for the second parameter, and so on,
1766  * - \f$x=2 k + i/2^k \mod 2\f$ is the index in the array of
1767  * weighting factors for parameter combination \f$k\f$ and parameter
1768  * \f$i\f$, and
1769  * - \f$F_p(E)\f$ are the table model spectra, computed for a grid of
1770  * possible parameter values.
1771  *
1772  * For each parameter \f$i\f$, the weighting factors \f$w_i^l(p)\f$
1773  * and \f$w_i^r(p)\f$ are computed using
1774  *
1775  * \f[
1776  * w_i^r(p) = \frac{p - p_l}{p_r - p_l}
1777  * \f]
1778  *
1779  * and \f$w_i^l(p) = 1 - w_i^r(p)\f$, where
1780  * \f$p_l\f$ is the largest parameter value that satisfies \f$p<p_l\f$ and
1781  * \f$p_r\f$ is the smallest parameter value that satisfies \f$p>p_r\f$.
1782  *
1783  * The method also computes the gradients
1784  *
1785  * \f[
1786  * \frac{\delta F(E|p)}{\delta p} = \sum_{k=1}^M \left(
1787  * \frac{\delta w_{x_p}(p)}{\delta p} \prod_{i=1 \land i \neq i_p}^N
1788  * w_x(p) \right) F_p(E)
1789  * \f]
1790  *
1791  * where
1792  * \f$x_p = 2 k + i_p/2^k \mod 2\f$.
1793  ***************************************************************************/
1795 {
1796  // Get number of parameters and number of energy bins
1798  m_nebins = ebounds().size();
1799 
1800  // Initialise update flag
1801  bool need_update = false;
1802 
1803  // If dimension of last cached parameter values differ from dimension
1804  // of spectral table then reallocate cache and request update
1805  if (m_last_values.size() != m_npars) {
1806  m_last_values = std::vector<double>(m_npars, 0.0);
1807  need_update = true;
1808  }
1809 
1810  // ... otherwise check if some parameter values have changed
1811  else {
1812  for (int i = 0; i < m_npars; ++i) {
1813  if (m_table_pars[i]->par().value() != m_last_values[i]) {
1814  need_update = true;
1815  break;
1816  }
1817  }
1818  }
1819 
1820  // Continue only if update is required
1821  if (need_update) {
1822 
1823  // Debug option: write header
1824  #if defined(G_DEBUG_UPDATE)
1825  std::cout << "GModelSpectralTable::update() required" << std::endl;
1826  #endif
1827 
1828  // Initialise vectors for weights, weight gradients and indices
1829  std::vector<double> weights(2*m_npars, 0.0);
1830  std::vector<double> weight_gradients(2*m_npars, 0.0);
1831  std::vector<int> indices(2*m_npars, 0);
1832 
1833  // Loop over all parameters and extract left and right weights,
1834  // weight gradients and indices and put them into single arrays
1835  for (int i = 0; i < m_npars; ++i) {
1836 
1837  // Get pointers to node array and parameter
1838  const GNodeArray* nodes = &(m_table_pars[i]->values());
1839  const GModelPar* par = &(m_table_pars[i]->par());
1840 
1841  // Get parameter value
1842  double value = par->value();
1843 
1844  // Cache parameter value
1845  m_last_values[i] = value;
1846 
1847  // Use log of value for logarithmic parameters
1848  /*
1849  if (m_table_pars[i]->method() == 1) {
1850  if (value > 0.0) {
1851  value = std::log(value);
1852  }
1853  else {
1854  std::string msg = "Non-positive value "+gammalib::str(value)+
1855  " encountered for logarithmic parameter "
1856  "\""+m_table_pars[i]->par().name()+"\".";
1857  throw GException::invalid_value(G_UPDATE, msg);
1858  }
1859  }
1860  */
1861 
1862  // Set values for node array
1863  nodes->set_value(value);
1864 
1865  // Compute left and right indices
1866  int il = 2*i;
1867  int ir = il + 1;
1868 
1869  // Push back weigths, weight gradients and indices
1870  weights[il] = nodes->wgt_left();
1871  weights[ir] = nodes->wgt_right();
1872  weight_gradients[il] = nodes->wgt_grad_left();
1873  weight_gradients[ir] = nodes->wgt_grad_right();
1874  indices[il] = nodes->inx_left();
1875  indices[ir] = nodes->inx_right();
1876 
1877  // Debug option: print weights and indices
1878  #if defined(G_DEBUG_UPDATE)
1879  std::cout << " wgt_l=" << weights[il];
1880  std::cout << " wgt_r=" << weights[ir];
1881  std::cout << " wgt_grad_l=" << weight_gradients[il];
1882  std::cout << " wgt_grad_r=" << weight_gradients[ir];
1883  std::cout << " inx_l=" << indices[il];
1884  std::cout << " inx_r=" << indices[ir] << std::endl;
1885  #endif
1886 
1887  } // endfor: looped over all parameters
1888 
1889  // Compute number of combinations
1890  int combinations = 1 << m_npars;
1891 
1892  // Initialise 2d arrays for values and gradients
1893  m_lin_values = GNdarray(m_nebins, m_npars+1);
1894  m_log_values = GNdarray(m_nebins, m_npars+1);
1895 
1896  // Debug option: initial sum of weights
1897  #if defined(G_DEBUG_UPDATE)
1898  double weight_sum = 0.0;
1899  #endif
1900 
1901  // Loop over combinations
1902  for (int i = 0; i < combinations; ++i) {
1903 
1904  // Debug option: start printing combination
1905  #if defined(G_DEBUG_UPDATE)
1906  std::cout << " " << i << ": ";
1907  #endif
1908 
1909  // Initialise weight and gradient weights
1910  double weight = 1.0;
1911  std::vector<double> grad_weight(m_npars, 1.0);
1912 
1913  // Initialise index vector (including the energy dimension)
1914  std::vector<int> index_shape(m_npars+1,0);
1915 
1916  // Loop over dimensions
1917  for (int k = 0, div = 1; k < m_npars; ++k, div *= 2) {
1918 
1919  // Compute index for each dimension
1920  int index = i/div % 2 + k * 2;
1921 
1922  // Update weight
1923  weight *= weights[index];
1924 
1925  // Add index
1926  index_shape[k] = indices[index];
1927 
1928  // Update gradient weights
1929  for (int j = 0; j < m_npars; ++j) {
1930  if (k == j) {
1931  grad_weight[j] *= weight_gradients[index];
1932  }
1933  else {
1934  grad_weight[j] *= weights[index];
1935  }
1936  } // endfor: update gradient weights
1937 
1938  // Debug option: print index and weight for current dimension
1939  #if defined(G_DEBUG_UPDATE)
1940  std::cout << index;
1941  std::cout << " (" << weights[index];
1942  std::cout << " @ " << indices[index] << ") ";
1943  #endif
1944 
1945  } // endfor: looped over dimensions
1946 
1947  // Debug option: print total weight and weight gradient
1948  #if defined(G_DEBUG_UPDATE)
1949  std::cout << ": wgt=" << weight;
1950  std::cout << " [";
1951  for (int k = 0; k < m_npars; ++k) {
1952  if (k > 0) {
1953  std::cout << ",";
1954  }
1955  std::cout << grad_weight[k];
1956  }
1957  std::cout << "]" << std::endl;
1958  weight_sum += weight;
1959  #endif
1960 
1961  // Compute interpolated value and gradient
1962  for (int iebin = 0; iebin < m_nebins; ++iebin) {
1963 
1964  // Set energy index
1965  index_shape[m_npars] = iebin;
1966 
1967  // Get spectral value
1968  double value = m_spectra(index_shape);
1969 
1970  // Compute contribution and store in value slot
1971  m_lin_values(iebin,0) += weight * value;
1972 
1973  // Compute gradients and store in gradient slots
1974  for (int j = 0; j < m_npars; ++j) {
1975  m_lin_values(iebin,j+1) += grad_weight[j] * value;
1976  }
1977 
1978  } // endfor: looped over all energy bins
1979 
1980  } // endfor: looped over combinations
1981 
1982  // Compute log10 values of function values
1983  /*
1984  for (int iebin = 0; iebin < m_nebins; ++iebin) {
1985  if (m_lin_values(iebin,0) > 0.0) {
1986  m_log_values(iebin,0) = std::log10(m_lin_values(iebin,0));
1987  }
1988  else {
1989  m_log_values(iebin,0) = -1000.0; // Set to a tiny value
1990  }
1991  }
1992  */
1993 
1994  // Debug option: print sum of weights
1995  #if defined(G_DEBUG_UPDATE)
1996  std::cout << " sum(wgt)=" << weight_sum << std::endl;
1997  #endif
1998 
1999  } // endif: updated requested
2000 
2001  // Return
2002  return;
2003 }
2004 
2005 
2006 /***********************************************************************//**
2007  * @brief Update flux cache
2008  ***************************************************************************/
2010 {
2011  // Clear any existing values
2012  m_prefactor.clear();
2013  m_gamma.clear();
2014  m_epivot.clear();
2015  m_flux.clear();
2016  m_eflux.clear();
2017 
2018  // Loop over all nodes-1
2019  for (int i = 0; i < m_lin_nodes.size()-1; ++i) {
2020 
2021  // Get energies and function values
2022  double emin = m_lin_nodes[i];
2023  double emax = m_lin_nodes[i+1];
2024  double fmin = m_lin_values(i,0);
2025  double fmax = m_lin_values(i+1,0);
2026 
2027  // Compute pivot energy (MeV). We use here the geometric mean of
2028  // the node boundaries.
2029  double epivot = std::sqrt(emin*emax);
2030 
2031  // Compute spectral index
2032  double gamma = std::log(fmin/fmax) / std::log(emin/emax);
2033 
2034  // Compute power law normalisation
2035  double prefactor = fmin / std::pow(emin/epivot, gamma);
2036 
2037  // Compute photon flux between nodes
2038  double flux = prefactor *
2039  gammalib::plaw_photon_flux(emin, emax, epivot, gamma);
2040 
2041  // Compute energy flux between nodes
2042  double eflux = prefactor *
2043  gammalib::plaw_energy_flux(emin, emax, epivot, gamma);
2044 
2045  // Convert energy flux from MeV/cm2/s to erg/cm2/s
2046  eflux *= gammalib::MeV2erg;
2047 
2048  // Push back values on pre-computation cache
2049  m_prefactor.push_back(prefactor);
2050  m_gamma.push_back(gamma);
2051  m_epivot.push_back(epivot);
2052  m_flux.push_back(flux);
2053  m_eflux.push_back(eflux);
2054 
2055  } // endfor: looped over all nodes
2056 
2057  // Return
2058  return;
2059 }
2060 
2061 
2062 /***********************************************************************//**
2063  * @brief Update MC cache
2064  *
2065  * @param[in] emin Minimum energy.
2066  * @param[in] emax Maximum energy.
2067  *
2068  * This method sets up an array of indices and the cumulative distribution
2069  * function needed for MC simulations.
2070  ***************************************************************************/
2072  const GEnergy& emax) const
2073 {
2074  // Check if we need to update the cache
2075  if (emin != m_mc_emin || emax != m_mc_emax) {
2076 
2077  // Store new energy interval
2078  m_mc_emin = emin;
2079  m_mc_emax = emax;
2080 
2081  // Initialise cache
2082  m_mc_cum.clear();
2083  m_mc_min.clear();
2084  m_mc_max.clear();
2085  m_mc_exp.clear();
2086 
2087  // Get energy range in MeV
2088  double e_min = emin.MeV();
2089  double e_max = emax.MeV();
2090 
2091  // Continue only if e_max > e_min
2092  if (e_max > e_min) {
2093 
2094  // Allocate flux
2095  double flux;
2096 
2097  // Determine left node index for minimum energy
2098  m_lin_nodes.set_value(e_min);
2099  int inx_emin = m_lin_nodes.inx_left();
2100 
2101  // Determine left node index for maximum energy
2102  m_lin_nodes.set_value(e_max);
2103  int inx_emax = m_lin_nodes.inx_left();
2104 
2105  // If both energies are within the same node then just
2106  // add this one node on the stack
2107  if (inx_emin == inx_emax) {
2108  flux = m_prefactor[inx_emin] *
2110  e_max,
2111  m_epivot[inx_emin],
2112  m_gamma[inx_emin]);
2113  m_mc_cum.push_back(flux);
2114  m_mc_min.push_back(e_min);
2115  m_mc_max.push_back(e_max);
2116  m_mc_exp.push_back(m_gamma[inx_emin]);
2117  }
2118 
2119  // ... otherwise integrate over the nodes where emin and emax
2120  // resides and all the remaining nodes
2121  else {
2122 
2123  // If we are requested to extrapolate beyond first node,
2124  // the use the first nodes lower energy and upper energy
2125  // boundary for the initial integration.
2126  int i_start = (e_min < m_lin_nodes[0]) ? inx_emin : inx_emin+1;
2127 
2128  // Add emin to the node boundary
2129  flux = m_prefactor[inx_emin] *
2131  m_lin_nodes[i_start],
2132  m_epivot[inx_emin],
2133  m_gamma[inx_emin]);
2134  m_mc_cum.push_back(flux);
2135  m_mc_min.push_back(e_min);
2136  m_mc_max.push_back(m_lin_nodes[i_start]);
2137  m_mc_exp.push_back(m_gamma[inx_emin]);
2138 
2139  // Add all nodes between
2140  for (int i = i_start; i < inx_emax; ++i) {
2141  flux = m_flux[i];
2142  m_mc_cum.push_back(flux);
2143  m_mc_min.push_back(m_lin_nodes[i]);
2144  m_mc_max.push_back(m_lin_nodes[i+1]);
2145  m_mc_exp.push_back(m_gamma[i]);
2146  }
2147 
2148  // Add node boundary to emax
2149  flux = m_prefactor[inx_emax] *
2151  e_max,
2152  m_epivot[inx_emax],
2153  m_gamma[inx_emax]);
2154  m_mc_cum.push_back(flux);
2155  m_mc_min.push_back(m_lin_nodes[inx_emax]);
2156  m_mc_max.push_back(e_max);
2157  m_mc_exp.push_back(m_gamma[inx_emax]);
2158 
2159  } // endelse: emin and emax not between same nodes
2160 
2161  // Build cumulative distribution
2162  for (int i = 1; i < m_mc_cum.size(); ++i) {
2163  m_mc_cum[i] += m_mc_cum[i-1];
2164  }
2165  double norm = m_mc_cum[m_mc_cum.size()-1];
2166  if (norm > 0.0) {
2167  for (int i = 0; i < m_mc_cum.size(); ++i) {
2168  m_mc_cum[i] /= norm;
2169  }
2170  }
2171 
2172  // Set MC values
2173  for (int i = 0; i < m_mc_cum.size(); ++i) {
2174 
2175  // Compute exponent
2176  double exponent = m_mc_exp[i] + 1.0;
2177 
2178  // Exponent dependend computation
2179  if (std::abs(exponent) > 1.0e-11) {
2180 
2181  // If the exponent is too small then use lower energy
2182  // boundary
2183  if (exponent < -50.0) {
2184  m_mc_exp[i] = 0.0;
2185  m_mc_min[i] = std::log(m_mc_min[i]);
2186  m_mc_max[i] = m_mc_min[i];
2187  }
2188 
2189  // ... otherwise if exponent is too large then use
2190  // upper energy boundary
2191  else if (exponent > +50.0) {
2192  m_mc_exp[i] = 0.0;
2193  m_mc_min[i] = std::log(m_mc_max[i]);
2194  m_mc_max[i] = m_mc_min[i];
2195  }
2196 
2197  // ... otherwise use transformation formula
2198  else {
2199  m_mc_exp[i] = exponent;
2200  m_mc_min[i] = std::pow(m_mc_min[i], exponent);
2201  m_mc_max[i] = std::pow(m_mc_max[i], exponent);
2202  }
2203  }
2204  else {
2205  m_mc_exp[i] = 0.0;
2206  m_mc_min[i] = std::log(m_mc_min[i]);
2207  m_mc_max[i] = std::log(m_mc_max[i]);
2208  }
2209 
2210  } // endfor: set MC values
2211 
2212  } // endif: e_max > e_min
2213 
2214  } // endif: Update was required
2215 
2216  // Return
2217  return;
2218 }
void unit(const std::string &unit)
Set column unit.
int size(void) const
Return number of nodes in node array.
Definition: GNodeArray.hpp:192
#define G_PAR_INDEX
GModelSpectralTablePar * append(const GModelSpectralTablePar &par)
Append table model parameter to container.
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition: GFits.cpp:482
GModelSpectralTable(void)
Void constructor.
std::vector< double > m_mc_max
Upper boundary for MC.
const double & factor_gradient(void) const
Return parameter factor gradient.
double norm(const GVector &vector)
Computes vector norm.
Definition: GVector.cpp:864
Node array class.
Definition: GNodeArray.hpp:60
GFitsBinTable create_spec_table(void) const
Create SPECTRA FITS table.
Energy value class definition.
const std::string & name(void) const
Return parameter name.
Random number generator class definition.
std::vector< double > m_mc_exp
Exponent for MC.
void clear(void)
Clear array.
Definition: GNdarray.cpp:527
Abstract spectral model base class.
GModelPar m_norm
Normalization factor.
double gradient(void) const
Return parameter gradient.
GModelSpectralTablePars m_table_pars
Table model parameters.
GEnergy m_mc_emax
Maximum energy.
int size(void) const
Return number of parameters.
const double & wgt_grad_left(void) const
Returns left node weight gradient.
Definition: GNodeArray.hpp:294
GVector abs(const GVector &vector)
Computes absolute of vector elements.
Definition: GVector.cpp:1253
virtual GModelSpectral & operator=(const GModelSpectral &model)
Assignment operator.
void write(GXmlElement &xml) const
Set or update parameter attributes in XML element.
Definition: GModelPar.cpp:351
virtual void read(const GXmlElement &xml)
Read model from XML element.
Abstract FITS extension base class.
Definition: GFitsHDU.hpp:51
int size(void) const
Return number of energy boundaries.
Definition: GEbounds.hpp:163
GNdarray m_lin_values
Function values and grad&#39;s.
void append(const GEnergy &emin, const GEnergy &emax)
Append energy interval.
Definition: GEbounds.cpp:301
virtual GEnergy mc(const GEnergy &emin, const GEnergy &emax, const GTime &time, GRan &ran) const
Returns MC energy between [emin, emax].
std::vector< GModelPar * > m_pars
Parameter pointers.
const GEbounds & ebounds(void) const
Return reference to energy boundaries.
void factor_range(const double &min, const double &max)
Set minimum and maximum parameter boundary factors.
bool is_empty(void) const
Signal if filename is empty.
Definition: GFilename.hpp:160
XML element node class.
Definition: GXmlElement.hpp:48
const double & wgt_left(void) const
Returns left node weight.
Definition: GNodeArray.hpp:264
virtual double flux(const GEnergy &emin, const GEnergy &emax) const
Returns model photon flux between emin, emax
Spectral model registry class definition.
double max(void) const
Return parameter maximum boundary.
void clear(void)
Clear node array.
Definition: GNodeArray.cpp:235
virtual double eflux(const GEnergy &emin, const GEnergy &emax) const
Returns model energy flux between emin, emax
Random number generator class.
Definition: GRan.hpp:44
GFitsTableCol * append(const GFitsTableCol &column)
Append column to the table.
Definition: GFitsTable.hpp:147
void free_members(void)
Delete class members.
virtual ~GModelSpectralTable(void)
Destructor.
double norm(void) const
Return normalization factor.
double MeV(void) const
Return energy in MeV.
Definition: GEnergy.cpp:321
const int & method(void) const
Return reference to table model parameter interpolation method.
Time class.
Definition: GTime.hpp:55
void set_energy_nodes(void)
Set energy nodes from energy boundaries.
Gammalib tools definition.
void update_mc(const GEnergy &emin, const GEnergy &emax) const
Update MC cache.
const std::vector< int > & shape(void) const
Return shape of array.
Definition: GNdarray.hpp:307
FITS table float column class interface definition.
GNodeArray m_log_nodes
log10(Energy) nodes of function
FITS file class.
Definition: GFits.hpp:63
std::vector< double > m_epivot
Power-law pivot energies.
FITS file class interface definition.
void set_value(const double &value) const
Set indices and weighting factors for interpolation.
Definition: GNodeArray.cpp:587
std::vector< double > m_flux
Photon fluxes.
double min(const GVector &vector)
Computes minimum vector element.
Definition: GVector.cpp:886
GFilename xml_file_reduce(const GXmlElement &xml, const std::string &filename)
Reduce file name provided for writing as XML attribute.
Definition: GTools.cpp:1946
void init_members(void)
Initialise class members.
double min(void) const
Return parameter minimum boundary.
double plaw_energy_flux(const double &emin, const double &emax, const double &epivot, const double &gamma)
Compute energy flux between two energies for a power law.
Definition: GTools.cpp:1248
bool is_free(void) const
Signal if parameter is free.
Spectral table model parameter container class.
double log10MeV(void) const
Return log10 of energy in MeV.
Definition: GEnergy.cpp:423
std::vector< double > m_prefactor
Power-law normalisations.
void update_flux(void) const
Update flux cache.
void set_par_pointers(void)
Set parameter pointers.
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition: GTools.hpp:201
std::vector< double > m_mc_cum
Cumulative distribution.
FITS table string column.
void load(const GFilename &filename)
Load table from file.
const double & scale(void) const
Return parameter scale.
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition: GTools.hpp:184
void load_eng(const GFits &fits)
Load data from ENERGIES extension.
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition: GFits.cpp:1293
Model parameter class.
Definition: GModelPar.hpp:87
double real(const std::string &keyname) const
Return card value as double precision.
Definition: GFitsHDU.hpp:423
GFitsBinTable create_eng_table(void) const
Create ENERGIES FITS table.
void copy_members(const GModelSpectralTable &model)
Copy class members.
GVector sqrt(const GVector &vector)
Computes square root of vector elements.
Definition: GVector.cpp:1358
GNodeArray m_lin_nodes
Energy nodes of function.
bool has_grad(void) const
Signal if parameter gradient is computed analytically.
const GFilename & filename(void) const
Return file name.
const double & wgt_right(void) const
Returns right node weight.
Definition: GNodeArray.hpp:279
Energy boundaries container class.
Definition: GEbounds.hpp:60
#define G_CONST
const GXmlAttribute * attribute(const int &index) const
Return attribute.
std::string print(const GChatter &chatter=NORMAL) const
Print energy.
Definition: GEnergy.cpp:748
std::vector< double > m_last_values
Last parameter values.
void free(void)
Free a parameter.
virtual double eval(const GEnergy &srcEng, const GTime &srcTime=GTime(), const bool &gradients=false) const
Evaluate spectral table model.
Filename class.
Definition: GFilename.hpp:62
const GEnergy & emin(void) const
Return minimum energy of all intervals.
Definition: GEbounds.hpp:187
virtual void clear(void)
Clear table model.
void fix(void)
Fix a parameter.
GNdarray m_spectra
Spectra.
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:1637
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition: GVector.cpp:1274
Spectral table model parameter class definition.
double uniform(void)
Returns random double precision floating value in range 0 to 1.
Definition: GRan.cpp:242
void check_energy_interval(const std::string &origin, const GEnergy &emin, const GEnergy &emax)
Checks energy interval.
Definition: GException.cpp:364
const GModelSpectralTable g_spectral_table_seed
void clear(void)
Clear parameter.
int m_nebins
Number of energy bins.
GFilename m_filename
Filename of table.
FITS table string column class interface definition.
std::vector< double > m_gamma
Power-law indices.
Abstract interface for FITS table.
Definition: GFitsTable.hpp:44
bool is_fixed(void) const
Signal if parameter is fixed.
Spectral table model class.
GEbounds m_ebounds
Energy boundaries.
void remove_range(void)
Removes minimum and maximum boundary.
GChatter
Definition: GTypemaps.hpp:33
int size(void) const
Return number of table model parameters in container.
GEnergy elogmean(const int &index) const
Returns logarithmic mean energy for a given energy interval.
Definition: GEbounds.cpp:1126
int integer(const std::string &keyname) const
Return card value as integer.
Definition: GFitsHDU.hpp:436
const int & inx_left(void) const
Returns left node index.
Definition: GNodeArray.hpp:235
GModelSpectralTablePar & table_par(const int &index)
Return reference to table parameter.
int par_index(const std::string &name) const
Return index for parameter name.
bool has_max(void) const
Signal if parameter has maximum boundary.
std::vector< double > m_mc_min
Lower boundary for MC.
N-dimensional array class.
Definition: GNdarray.hpp:44
const std::string & extname(void) const
Return extension name.
Definition: GFitsHDU.hpp:162
void clear(void)
Clear energy boundaries.
Definition: GEbounds.cpp:268
double max(const GVector &vector)
Computes maximum vector element.
Definition: GVector.cpp:915
void load_par(const GFits &fits)
Load data from PARAMETERS extension.
Interface definition for the spectral model registry class.
FITS table long integer column.
bool xml_has_par(const GXmlElement &xml, const std::string &name)
Checks if parameter with given name in XML element exists.
Definition: GTools.cpp:1596
const int & inx_right(void) const
Returns right node index.
Definition: GNodeArray.hpp:249
virtual GModelSpectralTable * clone(void) const
Clone table model.
virtual std::string type(void) const
Return model type.
Spectral table model parameter container class definition.
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
GNdarray m_log_values
log10(Function) values and grad&#39;s
void clear(void)
Clear file name.
Definition: GFilename.cpp:188
void init_members(void)
Initialise class members.
#define G_MC
int m_npars
Number of parameters.
double keV(void) const
Return energy in keV.
Definition: GEnergy.cpp:306
const double MeV2erg
Definition: GTools.hpp:45
void range(const double &min, const double &max)
Set minimum and maximum parameter boundaries.
void read(const GXmlElement &xml)
Extract parameter attributes from XML element.
Definition: GModelPar.cpp:229
double value(void) const
Return parameter value.
FITS binary table class.
void clear(void)
Clear table model parameters.
bool has_min(void) const
Signal if parameter has minimum boundary.
GVector pow(const GVector &vector, const double &power)
Computes tanh of vector elements.
Definition: GVector.cpp:1422
std::vector< double > m_eflux
Energy fluxes.
Exception handler interface definition.
FITS table long integer column class interface definition.
Spectral table model class definition.
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition: GFits.cpp:678
FITS binary table class definition.
GVector exp(const GVector &vector)
Computes exponential of vector elements.
Definition: GVector.cpp:1232
FITS table float column.
#define G_READ
const GEnergy & emax(void) const
Return maximum energy of all intervals.
Definition: GEbounds.hpp:199
void save(const GFilename &filename, const bool &clobber=false) const
Save table into file.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
void append(const double &node)
Append one node to array.
Definition: GNodeArray.cpp:351
GEnergy m_mc_emin
Minimum energy.
GFitsBinTable create_par_table(void) const
Create PARAMETERS FITS table.
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition: GFitsHDU.hpp:259
void close(void)
Close FITS file.
Definition: GFits.cpp:1342
int dim(void) const
Return dimension of array.
Definition: GNdarray.hpp:279
void update(void) const
Update cache for spectral table model computation.
virtual GModelSpectralTable & operator=(const GModelSpectralTable &model)
Assignment operator.
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:1689
Spectral table model parameter class.
void free_members(void)
Delete class members.
const double & wgt_grad_right(void) const
Returns right node weight gradient.
Definition: GNodeArray.hpp:309
void clear(void)
Clear instance.
Definition: GEnergy.cpp:261
virtual void write(GXmlElement &xml) const
Write model into XML element.
#define G_TABLE_PAR
void load_spec(const GFits &fits)
Load data from SPECTRA extension.
virtual std::string print(const GChatter &chatter=NORMAL) const
Print table model information.
#define G_WRITE
Mathematical function definitions.
GVector log10(const GVector &vector)
Computes base10 logarithm of vector elements.
Definition: GVector.cpp:1295
Class that handles energies in a unit independent way.
Definition: GEnergy.hpp:48
double plaw_photon_flux(const double &emin, const double &emax, const double &epivot, const double &gamma)
Compute photon flux between two energies for a power law.
Definition: GTools.cpp:1200
GFilename xml_file_expand(const GXmlElement &xml, const std::string &filename)
Expand file name provided as XML attribute for loading.
Definition: GTools.cpp:1889
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489
void xml_check_type(const std::string &origin, GXmlElement &xml, const std::string &type)
Checks the model type.
Definition: GTools.cpp:1819