GammaLib 2.0.0
Loading...
Searching...
No Matches
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"
40#include "GFitsTableLongCol.hpp"
46
47/* __ Constants __________________________________________________________ */
48
49/* __ Globals ____________________________________________________________ */
51const 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
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
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
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 ***************************************************************************/
375 const GTime& srcTime,
376 const bool& gradients) const
377{
378 // Update spectral function
379 update();
380
381 // Interpolate function in log10 energy
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 ***************************************************************************/
940const 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 ***************************************************************************/
956std::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
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();
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)
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;
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
1136
1137 // Set parameter pointer(s)
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
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 ***************************************************************************/
1707int 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
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
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}
#define G_WRITE
#define G_READ
Energy value class definition.
Exception handler interface definition.
FITS binary table class definition.
FITS table float column class interface definition.
FITS table long integer column class interface definition.
FITS table string column class interface definition.
FITS file class interface definition.
Mathematical function definitions.
Spectral model registry class definition.
Spectral table model parameter class definition.
Spectral table model parameter container class definition.
const GModelSpectralTable g_spectral_table_seed
#define G_PAR_INDEX
#define G_TABLE_PAR
#define G_CONST
Spectral table model class definition.
Random number generator class definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
double norm(const GVector &vector)
Computes vector norm.
Definition GVector.cpp:864
double min(const GVector &vector)
Computes minimum vector element.
Definition GVector.cpp:886
double max(const GVector &vector)
Computes maximum vector element.
Definition GVector.cpp:915
Energy boundaries container class.
Definition GEbounds.hpp:60
const GEnergy & emax(void) const
Return maximum energy of all intervals.
Definition GEbounds.hpp:199
void append(const GEnergy &emin, const GEnergy &emax)
Append energy interval.
Definition GEbounds.cpp:301
int size(void) const
Return number of energy boundaries.
Definition GEbounds.hpp:163
void clear(void)
Clear energy boundaries.
Definition GEbounds.cpp:268
GEnergy elogmean(const int &index) const
Returns logarithmic mean energy for a given energy interval.
const GEnergy & emin(void) const
Return minimum energy of all intervals.
Definition GEbounds.hpp:187
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
double keV(void) const
Return energy in keV.
Definition GEnergy.cpp:306
double MeV(void) const
Return energy in MeV.
Definition GEnergy.cpp:321
double log10MeV(void) const
Return log10 of energy in MeV.
Definition GEnergy.cpp:423
std::string print(const GChatter &chatter=NORMAL) const
Print energy.
Definition GEnergy.cpp:748
void clear(void)
Clear instance.
Definition GEnergy.cpp:261
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 binary table class.
Abstract FITS extension base class.
Definition GFitsHDU.hpp:51
const std::string & extname(void) const
Return extension name.
Definition GFitsHDU.hpp:162
double real(const std::string &keyname) const
Return card value as double precision.
Definition GFitsHDU.hpp:423
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition GFitsHDU.hpp:259
int integer(const std::string &keyname) const
Return card value as integer.
Definition GFitsHDU.hpp:436
void unit(const std::string &unit)
Set column unit.
FITS table float column.
FITS table long integer column.
FITS table string column.
Abstract interface for FITS table.
GFitsTableCol * append(const GFitsTableCol &column)
Append column to the table.
FITS file class.
Definition GFits.hpp:63
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition GFits.cpp:1293
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition GFits.cpp:678
void close(void)
Close FITS file.
Definition GFits.cpp:1342
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition GFits.cpp:482
Model parameter class.
Definition GModelPar.hpp:87
void write(GXmlElement &xml) const
Set or update parameter attributes in XML element.
void read(const GXmlElement &xml)
Extract parameter attributes from XML element.
Interface definition for the spectral model registry class.
Spectral table model parameter class.
const int & method(void) const
Return reference to table model parameter interpolation method.
Spectral table model parameter container class.
GModelSpectralTablePar * append(const GModelSpectralTablePar &par)
Append table model parameter to container.
void clear(void)
Clear table model parameters.
int size(void) const
Return number of table model parameters in container.
Spectral table model class.
GModelSpectralTable(void)
Void constructor.
virtual void write(GXmlElement &xml) const
Write model into XML element.
std::vector< double > m_mc_cum
Cumulative distribution.
virtual double eval(const GEnergy &srcEng, const GTime &srcTime=GTime(), const bool &gradients=false) const
Evaluate spectral table model.
virtual void read(const GXmlElement &xml)
Read model from XML element.
std::vector< double > m_mc_min
Lower boundary for MC.
void save(const GFilename &filename, const bool &clobber=false) const
Save table into file.
GModelPar m_norm
Normalization factor.
void load_par(const GFits &fits)
Load data from PARAMETERS extension.
void load_spec(const GFits &fits)
Load data from SPECTRA extension.
GModelSpectralTablePar & table_par(const int &index)
Return reference to table parameter.
virtual std::string print(const GChatter &chatter=NORMAL) const
Print table model information.
GEnergy m_mc_emax
Maximum energy.
GEbounds m_ebounds
Energy boundaries.
GNodeArray m_log_nodes
log10(Energy) nodes of function
virtual GEnergy mc(const GEnergy &emin, const GEnergy &emax, const GTime &time, GRan &ran) const
Returns MC energy between [emin, emax].
std::vector< double > m_epivot
Power-law pivot energies.
virtual std::string type(void) const
Return model type.
void init_members(void)
Initialise class members.
GFilename m_filename
Filename of table.
void set_par_pointers(void)
Set parameter pointers.
int par_index(const std::string &name) const
Return index for parameter name.
void set_energy_nodes(void)
Set energy nodes from energy boundaries.
void free_members(void)
Delete class members.
std::vector< double > m_mc_exp
Exponent for MC.
virtual double eflux(const GEnergy &emin, const GEnergy &emax) const
Returns model energy flux between [emin, emax] (units: erg/cm2/s)
int m_nebins
Number of energy bins.
GNdarray m_log_values
log10(Function) values and grad's
GEnergy m_mc_emin
Minimum energy.
virtual ~GModelSpectralTable(void)
Destructor.
double norm(void) const
Return normalization factor.
GNodeArray m_lin_nodes
Energy nodes of function.
std::vector< double > m_flux
Photon fluxes.
void update_mc(const GEnergy &emin, const GEnergy &emax) const
Update MC cache.
void update(void) const
Update cache for spectral table model computation.
GNdarray m_lin_values
Function values and grad's.
GFitsBinTable create_eng_table(void) const
Create ENERGIES FITS table.
const GEbounds & ebounds(void) const
Return reference to energy boundaries.
virtual void clear(void)
Clear table model.
virtual GModelSpectralTable * clone(void) const
Clone table model.
void load(const GFilename &filename)
Load table from file.
std::vector< double > m_mc_max
Upper boundary for MC.
std::vector< double > m_last_values
Last parameter values.
void copy_members(const GModelSpectralTable &model)
Copy class members.
GFitsBinTable create_par_table(void) const
Create PARAMETERS FITS table.
void update_flux(void) const
Update flux cache.
std::vector< double > m_prefactor
Power-law normalisations.
int m_npars
Number of parameters.
void load_eng(const GFits &fits)
Load data from ENERGIES extension.
virtual GModelSpectralTable & operator=(const GModelSpectralTable &model)
Assignment operator.
std::vector< double > m_gamma
Power-law indices.
GModelSpectralTablePars m_table_pars
Table model parameters.
virtual double flux(const GEnergy &emin, const GEnergy &emax) const
Returns model photon flux between [emin, emax] (units: ph/cm2/s)
const GFilename & filename(void) const
Return file name.
GFitsBinTable create_spec_table(void) const
Create SPECTRA FITS table.
std::vector< double > m_eflux
Energy fluxes.
Abstract spectral model base class.
void free_members(void)
Delete class members.
virtual GModelSpectral & operator=(const GModelSpectral &model)
Assignment operator.
std::vector< GModelPar * > m_pars
Parameter pointers.
int size(void) const
Return number of parameters.
void init_members(void)
Initialise class members.
N-dimensional array class.
Definition GNdarray.hpp:44
void clear(void)
Clear array.
Definition GNdarray.cpp:527
int dim(void) const
Return dimension of array.
Definition GNdarray.hpp:279
const std::vector< int > & shape(void) const
Return shape of array.
Definition GNdarray.hpp:307
Node array class.
void set_value(const double &value) const
Set indices and weighting factors for interpolation.
const double & wgt_grad_left(void) const
Returns left node weight gradient.
const int & inx_right(void) const
Returns right node index.
const int & inx_left(void) const
Returns left node index.
const double & wgt_grad_right(void) const
Returns right node weight gradient.
const double & wgt_right(void) const
Returns right node weight.
const double & wgt_left(void) const
Returns left node weight.
void clear(void)
Clear node array.
int size(void) const
Return number of nodes in node array.
void append(const double &node)
Append one node to array.
bool is_free(void) const
Signal if parameter is free.
void free(void)
Free a parameter.
const double & scale(void) const
Return parameter scale.
bool has_grad(void) const
Signal if parameter gradient is computed analytically.
void range(const double &min, const double &max)
Set minimum and maximum parameter boundaries.
bool has_min(void) const
Signal if parameter has minimum boundary.
double max(void) const
Return parameter maximum boundary.
bool is_fixed(void) const
Signal if parameter is fixed.
double min(void) const
Return parameter minimum boundary.
void remove_range(void)
Removes minimum and maximum boundary.
const double & factor_gradient(void) const
Return parameter factor gradient.
void fix(void)
Fix a parameter.
bool has_max(void) const
Signal if parameter has maximum boundary.
void factor_range(const double &min, const double &max)
Set minimum and maximum parameter boundary factors.
double gradient(void) const
Return parameter gradient.
void clear(void)
Clear parameter.
double value(void) const
Return parameter value.
const std::string & name(void) const
Return parameter name.
Random number generator class.
Definition GRan.hpp:44
double uniform(void)
Returns random double precision floating value in range 0 to 1.
Definition GRan.cpp:242
Time class.
Definition GTime.hpp:55
XML element node class.
const GXmlAttribute * attribute(const int &index) const
Return attribute.
void check_energy_interval(const std::string &origin, const GEnergy &emin, const GEnergy &emax)
Checks energy interval.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition GTools.hpp:184
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
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition GTools.hpp:201
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
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
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
GFilename xml_file_reduce(const GXmlElement &xml, const std::string &filename)
Reduce file name provided for writing as XML attribute.
Definition GTools.cpp:1946
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 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 double MeV2erg
Definition GTools.hpp:45
GFilename xml_file_expand(const GXmlElement &xml, const std::string &filename)
Expand file name provided as XML attribute for loading.
Definition GTools.cpp:1889
void xml_check_type(const std::string &origin, GXmlElement &xml, const std::string &type)
Checks the model type.
Definition GTools.cpp:1819