GammaLib 2.1.0.dev
Loading...
Searching...
No Matches
GCOMDris.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GCOMDris.cpp - COMPTEL Data Space container class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2023 by Juergen Knodlseder *
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 GCOMDris.hpp
23 * @brief COMPTEL Data Space container class implementation
24 * @author Juergen Knodlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <fstream>
32#include <iostream>
33#include "GException.hpp"
34#include "GMath.hpp"
35#include "GFits.hpp"
36#include "GFitsImageDouble.hpp"
37#include "GFitsImageShort.hpp"
38#include "GFitsTableLongCol.hpp"
40#include "GOptimizerPars.hpp"
41#include "GOptimizerPar.hpp"
42#include "GOptimizerLM.hpp"
43#include "GFft.hpp"
44#include "GCOMTools.hpp"
45#include "GCOMSupport.hpp"
46#include "GCOMDri.hpp"
47#include "GCOMDris.hpp"
48#include "GCOMOad.hpp"
49#include "GCOMOads.hpp"
50#include "GCOMTim.hpp"
51#include "GCOMStatus.hpp"
52#include "GCOMObservation.hpp"
53#include "GCOMEventList.hpp"
54#include "GCOMEventAtom.hpp"
55#include "GCOMSelection.hpp"
56
57/* __ Method name definitions ____________________________________________ */
58#define G_AT "GCOMDris::at(int&)"
59#define G_INSERT "GCOMDris::insert(int&, GCOMDri&)"
60#define G_REMOVE "GCOMDris::remove(int&)"
61#define G_COMPUTE_DRWS "GCOMDris::compute_drws(GCOMObservation&,"\
62 " GCOMSelection&, double&, double&, std::string&)"
63
64/* __ Macros _____________________________________________________________ */
65
66/* __ Coding definitions _________________________________________________ */
67#define G_COMPUTE_DRWS_EHACORR //!< Correct Earth horizon cut
68
69/* __ Debug definitions __________________________________________________ */
70//#define G_DEBUG_COMPUTE_DRWS
71//#define G_DEBUG_SAVE_WORKING_ARRAYS
72
73/* __ Constants __________________________________________________________ */
74
75
76/*==========================================================================
77 = =
78 = Constructors/destructors =
79 = =
80 ==========================================================================*/
81
82/***********************************************************************//**
83 * @brief Void constructor
84 *
85 * Constructs an empty Data Space container.
86 ***************************************************************************/
88{
89 // Initialise class members
91
92 // Return
93 return;
94}
95
96
97/***********************************************************************//**
98 * @brief Copy constructor
99 *
100 * @param[in] dris Data Space container.
101 ***************************************************************************/
103{
104 // Initialise class members
105 init_members();
106
107 // Copy members
108 copy_members(dris);
109
110 // Return
111 return;
112}
113
114
115/***********************************************************************//**
116 * @brief Destructor
117 ***************************************************************************/
119{
120 // Free members
121 free_members();
122
123 // Return
124 return;
125}
126
127
128/*==========================================================================
129 = =
130 = Operators =
131 = =
132 ==========================================================================*/
133
134/***********************************************************************//**
135 * @brief Assignment operator
136 *
137 * @param[in] dris Data Space container.
138 * @return Data Space container.
139 ***************************************************************************/
141{
142 // Execute only if object is not identical
143 if (this != &dris) {
144
145 // Free members
146 free_members();
147
148 // Initialise private members
149 init_members();
150
151 // Copy members
152 copy_members(dris);
153
154 } // endif: object was not identical
155
156 // Return this object
157 return *this;
158}
159
160
161/*==========================================================================
162 = =
163 = Public methods =
164 = =
165 ==========================================================================*/
166
167/***********************************************************************//**
168 * @brief Clear Data Space container
169 ***************************************************************************/
171{
172 // Free members
173 free_members();
174
175 // Initialise private members
176 init_members();
177
178 // Return
179 return;
180}
181
182
183/***********************************************************************//**
184 * @brief Clone Data Space container
185 *
186 * @return Pointer to deep copy of Data Space container.
187 ***************************************************************************/
189{
190 return new GCOMDris(*this);
191}
192
193
194/***********************************************************************//**
195 * @brief Return reference to Data Space
196 *
197 * @param[in] index Data Space index [0,...,size()-1].
198 *
199 * @exception GException::out_of_range
200 * Data Space index is out of range.
201 *
202 * Returns a reference to the Data Space with the specified @p index.
203 ***************************************************************************/
204GCOMDri& GCOMDris::at(const int& index)
205{
206 // Raise exception if index is out of range
207 if (index < 0 || index >= size()) {
208 throw GException::out_of_range(G_AT, "Data Space index",
209 index, size());
210 }
211
212 // Return reference
213 return m_dris[index];
214}
215
216
217/***********************************************************************//**
218 * @brief Return reference to Data Space (const version)
219 *
220 * @param[in] index Data Space index [0,...,size()-1].
221 *
222 * @exception GException::out_of_range
223 * Data Space index is out of range.
224 *
225 * Returns a reference to the Data Space with the specified @p index.
226 ***************************************************************************/
227const GCOMDri& GCOMDris::at(const int& index) const
228{
229 // Raise exception if index is out of range
230 if (index < 0 || index >= size()) {
231 throw GException::out_of_range(G_AT, "Data Space index",
232 index, size());
233 }
234
235 // Return reference
236 return m_dris[index];
237}
238
239
240/***********************************************************************//**
241 * @brief Append Data Space to container
242 *
243 * @param[in] dri Data Space.
244 * @return Reference to appended Data Space.
245 *
246 * Appends Data Space to the container by making a deep copy of the Data
247 * Space.
248 ***************************************************************************/
250{
251 // Append dri to list
252 m_dris.push_back(dri);
253
254 // Return reference
255 return m_dris[size()-1];
256}
257
258
259/***********************************************************************//**
260 * @brief Insert Data Space into container
261 *
262 * @param[in] index Data Space index (0,...,size()-1).
263 * @param[in] dri Data Space.
264 *
265 * @exception GException::out_of_range
266 * Data Space index is out of range.
267 *
268 * Inserts a Data Space into the container before the Data Space with the
269 * specified @p index.
270 ***************************************************************************/
271GCOMDri& GCOMDris::insert(const int& index, const GCOMDri& dri)
272{
273 // Compile option: raise exception if index is out of range
274 #if defined(G_RANGE_CHECK)
275 if (is_empty()) {
276 if (index > 0) {
277 throw GException::out_of_range(G_INSERT, "Data Space index",
278 index, size());
279 }
280 }
281 else {
282 if (index < 0 || index >= size()) {
283 throw GException::out_of_range(G_INSERT, "Data Space index",
284 index, size());
285 }
286 }
287 #endif
288
289 // Inserts Data Space
290 m_dris.insert(m_dris.begin()+index, dri);
291
292 // Return reference
293 return m_dris[index];
294}
295
296
297/***********************************************************************//**
298 * @brief Remove Data Space from container
299 *
300 * @param[in] index Data Space index (0,...,size()-1).
301 *
302 * @exception GException::out_of_range
303 * Data Space index is out of range.
304 *
305 * Remove Data Space of specified @p index from container.
306 ***************************************************************************/
307void GCOMDris::remove(const int& index)
308{
309 // Compile option: raise exception if index is out of range
310 #if defined(G_RANGE_CHECK)
311 if (index < 0 || index >= size()) {
312 throw GException::out_of_range(G_REMOVE, "Data Space index",
313 index, size());
314 }
315 #endif
316
317 // Erase Data Space from container
318 m_dris.erase(m_dris.begin() + index);
319
320 // Return
321 return;
322}
323
324
325/***********************************************************************//**
326 * @brief Append Data Space container
327 *
328 * @param[in] dris Data Space container.
329 *
330 * Append Data Space container to the container.
331 ***************************************************************************/
332void GCOMDris::extend(const GCOMDris& dris)
333{
334 // Do nothing if Data Space container is empty
335 if (!dris.is_empty()) {
336
337 // Get size. Note that we extract the size first to avoid an
338 // endless loop that arises when a container is appended to
339 // itself.
340 int num = dris.size();
341
342 // Reserve enough space
343 reserve(size() + num);
344
345 // Loop over all elements and append them to container
346 for (int i = 0; i < num; ++i) {
347 m_dris.push_back(dris[i]);
348 }
349
350 } // endif: Data Space container was not empty
351
352 // Return
353 return;
354}
355
356
357/***********************************************************************//**
358 * @brief Compute background weighting cubes
359 *
360 * @param[in] obs COMPTEL observation.
361 * @param[in] select Selection set.
362 * @param[in] zetamin Minimum Earth horizon - Phibar cut (deg).
363 * @param[in] timebin Time binning for rate determination (sec).
364 * @param[in] method Method to compute DRWs ("energy", "phibar" or "vetorate").
365 *
366 * @exception GException::invalid_value
367 * DRW with mismatching definition encountered in container.
368 * @exception GException::invalid_argument
369 * No event list found in COMPTEL observation.
370 *
371 * Compute DRW cubes for a COMPTEL observation. DRW cubes are event-rate
372 * weighted geometry cubes which were multiplied by the solid angle of
373 * the (Chi,Psi) pixels.
374 *
375 * The DRW cubes are normalised so that the sum of each Phibar layer equals
376 * to unity.
377 ***************************************************************************/
379 const GCOMSelection& select,
380 const double& zetamin,
381 const double& timebin,
382 const std::string& method)
383{
384 // Continue only if there are DRWs in container
385 if (!is_empty()) {
386
387 // Debug
388 #if defined(G_DEBUG_COMPUTE_DRWS)
389 std::cout << "Initialisation" << std::endl;
390 std::cout << "--------------" << std::endl;
391 #endif
392
393 // Get pointer to first DRW in container (for convenient access to
394 // members of GCOMDri; we won't use it for modifications, hence we
395 // can declare it as constant)
396 const GCOMDri* dri = &(m_dris[0]);
397
398 // Initialise all DRWs
399 for (int i = 0; i < size(); ++i) {
400
401 // Throw an exception if the DRW definition differs
402 if (dri->m_dri != m_dris[i].m_dri) {
403 std::string msg = "Mismatch between definition of DRW "+
404 gammalib::str(i)+" and the first DRW. "
405 "Method only works on DRWs with identical "
406 "definitions.";
408 }
409
410 // Initialise superpacket statistics
411 m_dris[i].init_statistics();
412
413 // Store selection set so that handling of failed PMT flag is
414 // correctly ritten into the FITS header
415 m_dris[i].m_selection = select;
416
417 // Set all DRG bins to zero
418 m_dris[i].init_cube();
419
420 // Set DRW header method parameter
421 m_dris[i].m_drw_method = method;
422
423 // Debug
424 #if defined(G_DEBUG_COMPUTE_DRWS)
425 std::cout << m_dris[i].print() << std::endl;
426 #endif
427
428 } // endfor: looped over all DRWs
429
430 // Get pointer to event list. Throw an exception if the observation
431 // did not contain an event list
432 const GCOMEventList* events = dynamic_cast<const GCOMEventList*>(obs.events());
433 if (events == NULL) {
434 std::string msg = "No event list found in COMPTEL observation. Please "
435 "specify an observation that contains an event list.";
437 }
438
439 // Method A: energy-dependent weighting
440 if (method == "energy") {
441 compute_drws_energy(obs, events, select, zetamin, timebin);
442 }
443
444 // Method B: Phibar-dependent weighting
445 else if (method == "phibar") {
446 compute_drws_phibar(obs, events, select, zetamin, timebin);
447 }
448
449 // Method C: Veto rate weighting
450 else if (method == "vetorate") {
451 compute_drws_vetorate(obs, events, select, zetamin);
452 }
453
454 // Normalise Phibar layers of all DRWs to unity
455 for (int i = 0; i < size(); ++i) {
456
457 // Loop over Phibar layers
458 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
459
460 // Compute sum in Phibar layer
461 double sum = 0.0;
462 int istart = iphibar * dri->m_dri.npix();
463 int istop = istart + dri->m_dri.npix();
464 for (int inx = istart; inx < istop; ++inx) {
465 sum += m_dris[i][inx];
466 }
467
468 // Normalise by sum
469 if (sum != 0.0) {
470 for (int inx = istart; inx < istop; ++inx) {
471 m_dris[i][inx] /= sum;
472 }
473 }
474
475 } // endfor: looped over Phibar layers
476
477 // Set the Good Time interval for DRWs
478 if (m_dris[i].m_num_used_superpackets > 0) {
479 m_dris[i].m_gti = GGti(dri->m_tstart, dri->m_tstop);
480 }
481
482 } // endfor: looped over DRWs
483
484 } // endif: there were DRWs in container
485
486 // Return
487 return;
488}
489
490
491/***********************************************************************//**
492 * @brief Print Data Space container
493 *
494 * @param[in] chatter Chattiness.
495 * @return String containing Data Space container information.
496 ***************************************************************************/
497std::string GCOMDris::print(const GChatter& chatter) const
498{
499 // Initialise result string
500 std::string result;
501
502 // Continue only if chatter is not silent
503 if (chatter != SILENT) {
504
505 // Append header
506 result.append("=== GCOMDris ===");
507
508 // Append container information
509 result.append("\n"+gammalib::parformat("Number of DRIs"));
510 result.append(gammalib::str(size()));
511
512 } // endif: chatter was not silent
513
514 // Return result
515 return result;
516}
517
518
519/*==========================================================================
520 = =
521 = Private methods =
522 = =
523 ==========================================================================*/
524
525/***********************************************************************//**
526 * @brief Initialise class members
527 ***************************************************************************/
529{
530 // Initialise members
531 m_dris.clear();
532
533 // Initialise working arrays
539 m_wrk_use_sp.clear();
540
541 // Return
542 return;
543}
544
545
546/***********************************************************************//**
547 * @brief Copy class members
548 *
549 * @param[in] dris Data Space container.
550 ***************************************************************************/
552{
553 // Copy members
554 m_dris = dris.m_dris;
555
556 // Copy working arrays
561 m_wrk_rate = dris.m_wrk_rate;
563
564 // Return
565 return;
566}
567
568
569/***********************************************************************//**
570 * @brief Delete class members
571 ***************************************************************************/
573{
574 // Return
575 return;
576}
577
578
579/***********************************************************************//**
580 * @brief Compute background weighting cubes using energy dependent rates
581 *
582 * @param[in] obs COMPTEL observation.
583 * @param[in] events COMPTEL event list.
584 * @param[in] select Selection set.
585 * @param[in] zetamin Minimum Earth horizon - Phibar cut (deg).
586 * @param[in] timebin Time binning for rate determination (sec).
587 *
588 * Compute DRW cubes for a COMPTEL observation. DRW cubes are event-rate
589 * weighted geometry cubes which were multiplied by the solid angle of
590 * the (Chi,Psi) pixels.
591 *
592 * For this method the event rates depend on time and energy. Three
593 * hard-wired energy ranges are defined for which the event rate is
594 * determined:
595 *
596 * E < 1.8 MeV
597 * 1.8 < E < 4.3 MeV
598 * E > 4.3 MeV
599 *
600 * For each superpacket, the event rates are linearly interpolated to the
601 * relevant superpacket time.
602 *
603 * The DRW cubes are normalised so that the sum of each Phibar layer equals
604 * to unity.
605 ***************************************************************************/
607 const GCOMEventList* events,
608 const GCOMSelection& select,
609 const double& zetamin,
610 const double& timebin)
611{
612 // Debug
613 #if defined(G_DEBUG_COMPUTE_DRWS)
614 std::cout << "GCOMDris::compute_drws_energy" << std::endl;
615 std::cout << "=============================" << std::endl;
616 #endif
617
618 // Initialise D1 & D2 module status
619 GCOMStatus status;
620
621 // Get pointer to first DRW in container (for convenient access to members
622 // of GCOMDri; we won't use it for modifications, hence we can declare it
623 // as constant)
624 const GCOMDri* dri = &(m_dris[0]);
625
626 // Debug
627 #if defined(G_DEBUG_COMPUTE_DRWS)
628 std::cout << "Determine energy-dependent event rates" << std::endl;
629 std::cout << "--------------------------------------" << std::endl;
630 #endif
631
632 // Initialise current time and vectors for computation of node function
633 // and vectors for energy-dependent rate interpolation
634 double time;
635 double rate0 = 0.0;
636 double rate1 = 0.0;
637 double rate2 = 0.0;
638 GNodeArray times;
639 std::vector<double> rates0;
640 std::vector<double> rates1;
641 std::vector<double> rates2;
642 bool set = false;
643
644 // Set time of first event
645 if (events->size() > 0) {
646 time = (*events)[0]->time().secs();
647 }
648
649 // Determine energy-dependent event rates. The standard event selection
650 // criteria and earth horizon cut is applied so that the event rate
651 // corresponds to the current selection, yet we do not consider any
652 // Good Time Intervals or OAD time information as we later only access
653 // relevant times (YET THERE COULD BE AN ISSUE WITH A GLITCH). Rates
654 // are computed for three hard-coded energy intervals.
655 for (int i_evt = 0; i_evt < events->size(); ++i_evt) {
656
657 // Get pointer to event
658 const GCOMEventAtom* event = (*events)[i_evt];
659
660 // Apply event selection
661 if (!select.use_event(*event)) {
662 continue;
663 }
664
665 // Apply Earth horizon cut. Note that we need to correct later
666 // for this cut to remove the additional time variation coming
667 // from the cut.
668 if ((event->eha() - event->phibar()) < zetamin) {
669 continue;
670 }
671
672 // If time exceeds time bin then add rates
673 while (event->time().secs() > (time + timebin)) {
674
675 // If rates are not all zero then append rates to vectors
676 if (set) {
677
678 // Append time at mid-point of interval
679 times.append(time + 0.5 * timebin);
680
681 // Append rates
682 rates0.push_back(rate0);
683 rates1.push_back(rate1);
684 rates2.push_back(rate2);
685
686 // Debug
687 #if defined(G_DEBUG_COMPUTE_DRWS)
688 std::cout << "t=" << time + 0.5 * timebin;
689 std::cout << " r0=" << rate0;
690 std::cout << " r1=" << rate1;
691 std::cout << " r2=" << rate2;
692 std::cout << std::endl;
693 #endif
694
695 // Initialise rates for new accumulation
696 rate0 = 0.0;
697 rate1 = 0.0;
698 rate2 = 0.0;
699 set = false;
700
701 } // endif: there were rates to append
702
703 // Increment time
704 time += timebin;
705
706 } // endwhile: event time was after the current integration time stop
707
708 // Accumulate rates
709 double energy = event->energy().MeV();
710 if (energy < 1.8) {
711 rate0 += 1.0;
712 set = true;
713 }
714 else if (energy < 4.3) {
715 rate1 += 1.0;
716 set = true;
717 }
718 else {
719 rate2 += 1.0;
720 set = true;
721 }
722
723 } // endfor: looped over events
724
725 // Append remaining rates
726 if (set) {
727
728 // Append time at mid-point of interval
729 times.append(time + 0.5 * timebin);
730
731 // Append rates
732 rates0.push_back(rate0);
733 rates1.push_back(rate1);
734 rates2.push_back(rate2);
735
736 } // endif: appended remaining rates
737
738 // Debug
739 #if defined(G_DEBUG_COMPUTE_DRWS)
740 std::cout << "Compute DRWs" << std::endl;
741 std::cout << "------------" << std::endl;
742 #endif
743
744 // Allocate geometry factor and Earth horizon angle working arrays
745 std::vector<double> geometry(dri->m_dri.npix());
746 std::vector<double> eha(dri->m_dri.npix());
747
748 // Get Good Time Intervals. If a pulsar selection is specified then
749 // reduce the Good Time Intervals to the validity intervals of the
750 // pulsar emphemerides.
751 GCOMTim tim = obs.tim();
752 if (select.has_pulsar()) {
753 tim.reduce(select.pulsar().validity());
754 }
755
756 // Loop over Orbit Aspect Data
757 for (int i_oad = 0; i_oad < obs.oads().size(); ++i_oad) {
758
759 // Get reference to Orbit Aspect Data of superpacket
760 const GCOMOad &oad = obs.oads()[i_oad];
761
762 // Check superpacket usage for all DRWs so that their statistics
763 // get correctly updated
764 bool skip = false;
765 for (int i = 0; i < size(); ++i) {
766 if (!m_dris[i].use_superpacket(oad, tim, select)) {
767 skip = true;
768 }
769 }
770 if (skip) {
771 continue;
772 }
773
774 // Get Orbit Aspect Data mid-time in seconds
775 double time = oad.tstart().secs() + 0.5 * (oad.tstop() - oad.tstart());
776
777 // Compute energy-dependent rates
778 std::vector<double> rates;
779 for (int i = 0; i < size(); ++i) {
780 if (m_dris[i].m_ebounds.emin().MeV() > 4.3) {
781 rates.push_back(times.interpolate(time, rates2));
782 }
783 else if (m_dris[i].m_ebounds.emin().MeV() > 1.8) {
784 rates.push_back(times.interpolate(time, rates1));
785 }
786 else {
787 rates.push_back(times.interpolate(time, rates0));
788 }
789 }
790
791 // Debug
792 #if defined(G_DEBUG_COMPUTE_DRWS)
793 std::cout << "time=" << time;
794 for (int i = 0; i < size(); ++i) {
795 std::cout << " r[" << i << "]=" << rates[i];
796 }
797 std::cout << std::endl;
798 #endif
799
800 // Prepare Earth horizon angle computation. The celestial system
801 // is reinterpreted as the COMPTEL coordinate system, where the
802 // 90 degrees - zenith angle becomes the declination and the azimuth
803 // angle becomes Right Ascension. This allows us later to use the
804 // GSkyDir::dist method to compute the distance between the geocentre
805 // and the telescope Z-axis.
806 double theta_geocentre = double(oad.gcel());
807 double phi_geocentre = double(oad.gcaz());
808 GSkyDir geocentre_comptel;
809 geocentre_comptel.radec_deg(phi_geocentre, 90.0-theta_geocentre);
810
811 // Compute solid-angle-corrected geometry factors and Earth horizon
812 // angles for all (Chi,Psi) pixels
813 for (int index = 0; index < dri->m_dri.npix(); ++index) {
814
815 // Get sky direction and solid angle for (Chi, Psi)
816 GSkyDir sky = dri->m_dri.inx2dir(index);
817 double omega = dri->m_dri.solidangle(index);
818
819 // Convert sky direction to COMPTEL coordinates
820 double theta = oad.theta(sky);
821 double phi = oad.phi(sky);
822
823 // Compute geometric factor summed over all D1, D2 times
824 // the solid angle of the weighting cube pixel
825 geometry[index] = dri->compute_geometry(oad.tjd(), theta, phi,
826 select, status) * omega;
827
828 // Compute Earth horizon angle as the distance between the Earth
829 // centre in COMPTEL coordinates and the (Chi, Psi) pixel in
830 // COMPTEL coordinates minus the radius of the Earth.
831 GSkyDir chipsi_comptel;
832 chipsi_comptel.radec_deg(phi, 90.0-theta);
833 eha[index] = geocentre_comptel.dist_deg(chipsi_comptel) - oad.georad();
834
835 } // endfor: computed solid-angle-corrected geometry factors
836
837 // Loop over all DRWs
838 for (int i = 0; i < size(); ++i) {
839
840 // Compute Earth horizon cut correction factor
841 double rates_sum_all = 0.0;
842 double rates_sum_cut = 0.0;
843 double ehacorr = 1.0;
844 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
845
846 // Sum product of geometry and rates for all pixels
847 for (int index = 0; index < dri->m_dri.npix(); ++index) {
848 rates_sum_all += geometry[index] * rates[i];
849 }
850
851 // Sum product of geometry and rates for pixels passing
852 // the Earth horizon cut
853 double ehamin = double(iphibar) * dri->m_phibin + zetamin;
854 for (int index = 0; index < dri->m_dri.npix(); ++index) {
855 if (eha[index] >= ehamin) {
856 rates_sum_cut += geometry[index] * rates[i];
857 }
858 }
859
860 } // endfor: looped over Phibar
861
862 // If something passes the Earth horizon cut then compute the
863 // correction factor now
864 if (rates_sum_cut != 0.0) {
865 ehacorr = rates_sum_all / rates_sum_cut;
866 }
867
868 // Loop over all Phibar layers
869 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
870
871 // Compute minimum Earth horizon angle for Phibar layer
872 double ehamin = double(iphibar) * dri->m_phibin + zetamin;
873
874 // Loop over all (Chi,Psi) pixels
875 for (int index = 0; index < dri->m_dri.npix(); ++index) {
876
877 // If Earth horizon angle is equal or larger than the minimum
878 // requirement then add up the rate weighted geometry factor
879 if (eha[index] >= ehamin) {
880
881 // Get data space index
882 int inx = index + iphibar * dri->m_dri.npix();
883
884 // Store product as DRW value
885 m_dris[i][inx] += geometry[index] * rates[i] * ehacorr;
886
887 } // endif: Earth horizon cut passed
888
889 } // endfor: looped over (Chi, Psi)
890
891 } // endfor: looped over Phibar
892
893 } // endfor: looped over all DRWs
894
895 } // endfor: looped over Orbit Aspect Data
896
897 // Return
898 return;
899}
900
901
902/***********************************************************************//**
903 * @brief Compute background weighting cubes using Phibar dependent rates
904 *
905 * @param[in] obs COMPTEL observation.
906 * @param[in] events COMPTEL event list.
907 * @param[in] select Selection set.
908 * @param[in] zetamin Minimum Earth horizon - Phibar cut (deg).
909 * @param[in] timebin Time binning for rate determination (sec).
910 *
911 * Compute DRW cubes for a COMPTEL observation. DRW cubes are event-rate
912 * weighted geometry cubes which were multiplied by the solid angle of
913 * the (Chi,Psi) pixels.
914 *
915 * For this method the event rates depend on time and Phibar angle. For
916 * each superpacket, the event rates are linearly interpolated to the
917 * relevant superpacket time.
918 *
919 * The DRW cubes are normalised so that the sum of each Phibar layer equals
920 * to unity.
921 ***************************************************************************/
923 const GCOMEventList* events,
924 const GCOMSelection& select,
925 const double& zetamin,
926 const double& timebin)
927{
928 // Set constants
929 const double ehacorrmax = 10.0; //!< Maximum EHA correction factor
930
931 // Debug
932 #if defined(G_DEBUG_COMPUTE_DRWS)
933 std::cout << "GCOMDris::compute_drws_phibar" << std::endl;
934 std::cout << "=============================" << std::endl;
935 #endif
936
937 // Initialise D1 & D2 module status
938 GCOMStatus status;
939
940 // Get pointer to first DRW in container (for convenient access to members
941 // of GCOMDri; we won't use it for modifications, hence we can declare it
942 // as constant)
943 const GCOMDri* dri = &(m_dris[0]);
944
945 // Debug
946 #if defined(G_DEBUG_COMPUTE_DRWS)
947 std::cout << "Determine Phibar-dependent event rates" << std::endl;
948 std::cout << "--------------------------------------" << std::endl;
949 #endif
950
951 // Initialise current time and vectors for computation of node function
952 // and vectors for energy-dependent rate interpolation
953 double time;
954 std::vector<double> rate(dri->nphibar());
955 GNodeArray times;
956 std::vector<std::vector<double> > rates(dri->nphibar());
957 bool set = false;
958
959 // Set time of first event
960 if (events->size() > 0) {
961 time = (*events)[0]->time().secs();
962 }
963
964 // Determine energy-dependent event rates. The standard event selection
965 // criteria and earth horizon cut is applied so that the event rate
966 // corresponds to the current selection, yet we do not consider any
967 // Good Time Intervals or OAD time information as we later only access
968 // relevant times.
969 for (int i_evt = 0; i_evt < events->size(); ++i_evt) {
970
971 // Get pointer to event
972 const GCOMEventAtom* event = (*events)[i_evt];
973
974 // Apply event selection
975 if (!select.use_event(*event)) {
976 continue;
977 }
978
979 // Apply Earth horizon cut. Note that we need to correct later
980 // for this cut to remove the additional time variation coming
981 // from the cut.
982 #if defined(G_COMPUTE_DRWS_EHACORR)
983 if ((event->eha() - event->phibar()) < zetamin) {
984 continue;
985 }
986 #endif
987
988 // If time exceeds time bin then add rates
989 while (event->time().secs() > (time + timebin)) {
990
991 // If rates are set then append rates to vectors
992 if (set) {
993
994 // Append time at mid-point of interval
995 times.append(time + 0.5 * timebin);
996
997 // Append rates and initialise for new accumulation
998 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
999 rates[iphibar].push_back(rate[iphibar]);
1000 rate[iphibar] = 0.0;
1001 }
1002
1003 // Signal that rates were not yet set
1004 set = false;
1005
1006 } // endif: there were rates to append
1007
1008 // Increment time
1009 time += timebin;
1010
1011 } // endwhile: event time was after the current integration time stop
1012
1013 // Accumulate rates
1014 int iphibar = int((event->phibar() - dri->phimin()) / dri->phibin());
1015 if (iphibar >= 0 and iphibar < dri->nphibar()) {
1016 rate[iphibar] += 1.0;
1017 set = true;
1018 }
1019
1020 } // endfor: looped over events
1021
1022 // Append remaining rates
1023 if (set) {
1024
1025 // Append time at mid-point of interval
1026 times.append(time + 0.5 * timebin);
1027
1028 // Append rates
1029 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
1030 rates[iphibar].push_back(rate[iphibar]);
1031 }
1032
1033 } // endif: appended remaining rates
1034
1035 // Debug: write debug file
1036 #if defined(G_DEBUG_COMPUTE_DRWS)
1037 std::ofstream debugfile;
1038 debugfile.open("compute_drws_phibar_tbin_rates.csv");
1039 debugfile.precision(12);
1040 for (int i = 0; i < times.size(); ++i) {
1041 debugfile << times[i];
1042 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
1043 debugfile << "," << rates[iphibar][i];
1044 }
1045 debugfile << std::endl;
1046 }
1047 debugfile.close();
1048 #endif
1049
1050 // Debug
1051 #if defined(G_DEBUG_COMPUTE_DRWS)
1052 std::cout << "Compute DRWs" << std::endl;
1053 std::cout << "------------" << std::endl;
1054 #endif
1055
1056 // Allocate geometry factor and Earth horizon angle working arrays
1057 std::vector<double> geometry(dri->m_dri.npix());
1058 std::vector<double> eha(dri->m_dri.npix());
1059
1060 // Get Good Time Intervals. If a pulsar selection is specified then
1061 // reduce the Good Time Intervals to the validity intervals of the
1062 // pulsar emphemerides.
1063 GCOMTim tim = obs.tim();
1064 if (select.has_pulsar()) {
1065 tim.reduce(select.pulsar().validity());
1066 }
1067
1068 // Debug: open debug files file
1069 #if defined(G_DEBUG_COMPUTE_DRWS)
1070 std::ofstream debugfile1;
1071 std::ofstream debugfile2;
1072 debugfile1.open("compute_drws_phibar_sp_rates.csv");
1073 debugfile2.open("compute_drws_phibar_sp_ehacorr.csv");
1074 debugfile1.precision(12);
1075 debugfile2.precision(12);
1076 #endif
1077
1078 // Loop over Orbit Aspect Data
1079 for (int i_oad = 0; i_oad < obs.oads().size(); ++i_oad) {
1080
1081 // Get reference to Orbit Aspect Data of superpacket
1082 const GCOMOad &oad = obs.oads()[i_oad];
1083
1084 // Check superpacket usage for all DRWs so that their statistics
1085 // get correctly updated
1086 bool skip = false;
1087 for (int i = 0; i < size(); ++i) {
1088 if (!m_dris[i].use_superpacket(oad, tim, select)) {
1089 skip = true;
1090 }
1091 }
1092 if (skip) {
1093 continue;
1094 }
1095
1096 // Get Orbit Aspect Data mid-time in seconds
1097 double time = oad.tstart().secs() + 0.5 * (oad.tstop() - oad.tstart());
1098
1099 // Compute Phibar-dependent rates by interpolation of precomputed
1100 // rate vectors. Make sure that rates never become negative.
1101 std::vector<double> rate_oad(dri->nphibar());
1102 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
1103 rate_oad[iphibar] = times.interpolate(time, rates[iphibar]);
1104 if (rate_oad[iphibar] < 0.0) {
1105 rate_oad[iphibar] = 0.0;
1106 }
1107 }
1108
1109 // Prepare Earth horizon angle computation. The celestial system
1110 // is reinterpreted as the COMPTEL coordinate system, where the
1111 // 90 degrees - zenith angle becomes the declination and the azimuth
1112 // angle becomes Right Ascension. This allows us later to use the
1113 // GSkyDir::dist method to compute the distance between the geocentre
1114 // and the telescope Z-axis.
1115 double theta_geocentre = double(oad.gcel());
1116 double phi_geocentre = double(oad.gcaz());
1117 GSkyDir geocentre_comptel;
1118 geocentre_comptel.radec_deg(phi_geocentre, 90.0-theta_geocentre);
1119
1120 // Compute solid-angle-corrected geometry factors and Earth horizon
1121 // angles for all (Chi,Psi) pixels
1122 for (int index = 0; index < dri->m_dri.npix(); ++index) {
1123
1124 // Get sky direction and solid angle for (Chi, Psi)
1125 GSkyDir sky = dri->m_dri.inx2dir(index);
1126 double omega = dri->m_dri.solidangle(index);
1127
1128 // Convert sky direction to COMPTEL coordinates
1129 double theta = oad.theta(sky);
1130 double phi = oad.phi(sky);
1131
1132 // Compute geometric factor summed over all D1, D2 times
1133 // the solid angle of the weighting cube pixel
1134 geometry[index] = dri->compute_geometry(oad.tjd(), theta, phi,
1135 select, status) * omega;
1136
1137 // Compute Earth horizon angle as the distance between the Earth
1138 // centre in COMPTEL coordinates and the (Chi, Psi) pixel in
1139 // COMPTEL coordinates minus the radius of the Earth.
1140 GSkyDir chipsi_comptel;
1141 chipsi_comptel.radec_deg(phi, 90.0-theta);
1142 eha[index] = geocentre_comptel.dist_deg(chipsi_comptel) - oad.georad();
1143
1144 } // endfor: computed solid-angle-corrected geometry factors
1145
1146 // Debug: write time in debug files
1147 #if defined(G_DEBUG_COMPUTE_DRWS)
1148 debugfile1 << time;
1149 debugfile2 << time;
1150 #endif
1151
1152 // Correct rates for Earth horizon cut
1153 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
1154
1155 // Compute minimum Earth Horizon angle for Phibar layer
1156 double ehamin = double(iphibar) * dri->m_phibin + zetamin;
1157
1158 // Initialise sums
1159 double rates_sum_all = 0.0;
1160 double rates_sum_cut = 0.0;
1161
1162 // Sum geometry for all pixels and pixels passing the Earth horizon cut
1163 for (int index = 0; index < dri->m_dri.npix(); ++index) {
1164 rates_sum_all += geometry[index];
1165 if (eha[index] >= ehamin) {
1166 rates_sum_cut += geometry[index];
1167 }
1168 }
1169
1170 // Compute Earth horizon cut correction, limited to a maximum of ehacorrmax
1171 double ehacorr = (rates_sum_cut != 0.0) ? rates_sum_all / rates_sum_cut : ehacorrmax;
1172 if (ehacorr > ehacorrmax) {
1173 ehacorr = ehacorrmax;
1174 }
1175
1176 // Correct rate
1177 #if defined(G_COMPUTE_DRWS_EHACORR)
1178 rate_oad[iphibar] *= ehacorr;
1179 #endif
1180
1181 // Debug: write rate and correction in debug files
1182 #if defined(G_DEBUG_COMPUTE_DRWS)
1183 debugfile1 << "," << rate_oad[iphibar];
1184 debugfile2 << "," << ehacorr;
1185 #endif
1186
1187 } // endfor: looped over Phibar
1188
1189 // Debug: write linefeeds in debug files
1190 #if defined(G_DEBUG_COMPUTE_DRWS)
1191 debugfile1 << std::endl;
1192 debugfile2 << std::endl;
1193 #endif
1194
1195 // Loop over all DRWs
1196 for (int i = 0; i < size(); ++i) {
1197
1198 // Loop over all Phibar layers
1199 for (int iphibar = 0; iphibar < dri->nphibar(); ++iphibar) {
1200
1201 // Compute minimum Earth horizon angle for Phibar layer
1202 double ehamin = double(iphibar) * dri->m_phibin + zetamin;
1203
1204 // Loop over all (Chi,Psi) pixels
1205 for (int index = 0; index < dri->m_dri.npix(); ++index) {
1206
1207 // If Earth horizon angle is equal or larger than the minimum
1208 // requirement then add up the rate weighted geometry factor
1209 if (eha[index] >= ehamin) {
1210
1211 // Get data space index
1212 int inx = index + iphibar * dri->m_dri.npix();
1213
1214 // Store product as DRW value
1215 m_dris[i][inx] += geometry[index] * rate_oad[iphibar];
1216
1217 } // endif: Earth horizon cut passed
1218
1219 } // endfor: looped over (Chi, Psi)
1220
1221 } // endfor: looped over Phibar
1222
1223 } // endfor: looped over all DRWs
1224
1225 } // endfor: looped over Orbit Aspect Data
1226
1227 // Debug: close debug files
1228 #if defined(G_DEBUG_COMPUTE_DRWS)
1229 debugfile1.close();
1230 debugfile2.close();
1231 #endif
1232
1233 // Return
1234 return;
1235}
1236
1237
1238/***********************************************************************//**
1239 * @brief Compute background weighting cubes using veto rates
1240 *
1241 * @param[in] obs COMPTEL observation.
1242 * @param[in] events COMPTEL event list.
1243 * @param[in] select Selection set.
1244 * @param[in] zetamin Minimum Earth horizon - Phibar cut (deg).
1245 *
1246 * Compute DRW cubes for a COMPTEL observation. DRW cubes are event-rate
1247 * weighted geometry cubes which were multiplied by the solid angle of
1248 * the (Chi,Psi) pixels.
1249 ***************************************************************************/
1251 const GCOMEventList* events,
1252 const GCOMSelection& select,
1253 const double& zetamin)
1254{
1255 // Debug
1256 #if defined(G_DEBUG_COMPUTE_DRWS)
1257 std::cout << "GCOMDris::compute_drws_vetorate" << std::endl;
1258 std::cout << "===============================" << std::endl;
1259 #endif
1260
1261 // Setup working arrays
1262 vetorate_setup(obs, events, select, zetamin);
1263
1264 // Debug: Save working arrays in FITS file
1265 #if defined(G_DEBUG_SAVE_WORKING_ARRAYS)
1266 vetorate_save("debug_gcomdris_compute_drws_vetorate.fits");
1267 vetorate_load("debug_gcomdris_compute_drws_vetorate.fits");
1268 #endif
1269
1270 // Initialise fprompt for all energies
1271 for (int i = 0; i < size(); ++i) {
1272 m_dris[i].m_drw_fprompt = 0.5; //!< Initial fprompt value
1273 }
1274
1275 // Fit working arrays to determine f_prompt parameter
1276 vetorate_fit();
1277 for (int iter = 0; iter < 4; ++iter) { //!< Perform 4 iterations
1279 vetorate_fit();
1280 }
1281
1282 // Generate DRWs
1283 vetorate_generate(obs, select, zetamin);
1284
1285 // Finish DRWs
1286 vetorate_finish(obs);
1287
1288 // Return
1289 return;
1290}
1291
1292
1293/***********************************************************************//**
1294 * @brief Setup working arrays for vetorate computation
1295 *
1296 * @param[in] obs COMPTEL observation.
1297 * @param[in] events COMPTEL event list.
1298 * @param[in] select Selection set.
1299 * @param[in] zetamin Minimum Earth horizon - Phibar cut (deg).
1300 *
1301 * Setup working arrays for vetorate DRW computation. There are five working
1302 * arrays:
1303 *
1304 * @c m_wrk_counts is a 3D working array spanned by superpacket index,
1305 * phibar layer and energy bin, containing the number of events passing the
1306 * event selections for the specified bin.
1307 *
1308 * @c m_wrk_ehacutcorr is a 2D working array spanned by superpacket index
1309 * and phibar layer, containing the fraction of the solid-angle weighted
1310 * geometry that passes the Earth horizon cut for the specified bin. This
1311 * array represents the response to a given incident event rate.
1312 *
1313 * @c m_wrk_vetorate is a 1D working array, containing the veto rate as
1314 * function of superpacket index.
1315 *
1316 * @c m_wrk_activrate is a 2D working array, containing the activation rate
1317 * as function of superpacket index and energy range.
1318 *
1319 * @c m_wrk_use_sp is a 1D working array, signalling the usage of a given
1320 * superpacket.
1321 *
1322 * Note that the method also sets the superpacket usage and event selection
1323 * statistics of all DRWs.
1324 ***************************************************************************/
1326 const GCOMEventList* events,
1327 const GCOMSelection& select,
1328 const double& zetamin)
1329{
1330 // Debug
1331 #if defined(G_DEBUG_COMPUTE_DRWS)
1332 std::cout << "GCOMDris::vetorate_setup" << std::endl;
1333 std::cout << "========================" << std::endl;
1334 #endif
1335
1336 // Initialise D1 & D2 module status
1337 GCOMStatus status;
1338
1339 // Get pointer to first DRW (assume their definition is identical)
1340 const GCOMDri* dri0 = &(m_dris[0]);
1341
1342 // Extract dimensions
1343 int noads = obs.oads().size();
1344 int npix = dri0->m_dri.npix();
1345 int nphibar = dri0->nphibar();
1346 int neng = size();
1347
1348 // Debug
1349 #if defined(G_DEBUG_COMPUTE_DRWS)
1350 std::cout << "Number of OAD superpackets: " << noads << std::endl;
1351 std::cout << "Number of pixels: " << npix << std::endl;
1352 std::cout << "Number of Phibar layers: " << nphibar << std::endl;
1353 std::cout << "Number of energies: " << neng << std::endl;
1354 #endif
1355
1356 // Define working arrays
1357 m_wrk_counts = GNdarray(noads, nphibar, neng);
1358 m_wrk_ehacutcorr = GNdarray(noads, nphibar);
1359 m_wrk_vetorate = GNdarray(noads);
1360 m_wrk_activrate = GNdarray(noads, neng);
1361 m_wrk_use_sp = std::vector<bool>(noads);
1362
1363 // Initialise superpacket usage array
1364 for (int i_oad = 0; i_oad < noads; ++i_oad) {
1365 m_wrk_use_sp[i_oad] = false;
1366 }
1367
1368 // Allocate further working arrays
1369 GNdarray geo(npix);
1370 GNdarray eha(npix);
1371
1372 // Setup node array and rate vector for veto rate interpolation
1373 const GCOMHkd& hdk = obs.hkds()["SCV2M"];
1374 GNodeArray veto_times;
1375 std::vector<double> veto_rates;
1376 veto_times.reserve(hdk.size());
1377 veto_rates.reserve(hdk.size());
1378 for (int i = 0; i < hdk.size(); ++i) {
1379 double rate = hdk.value(i);
1380 if (rate > 0.0) {
1381 veto_times.append(hdk.time(i).secs());
1382 veto_rates.push_back(rate);
1383 }
1384 }
1385
1386 // Setup sky directions and solid angles for all (Chi, Psi)
1387 std::vector<GSkyDir> skys;
1388 std::vector<double> omegas;
1389 for (int index = 0; index < dri0->m_dri.npix(); ++index) {
1390 GSkyDir sky = dri0->m_dri.inx2dir(index);
1391 double omega = dri0->m_dri.solidangle(index);
1392 skys.push_back(sky);
1393 omegas.push_back(omega);
1394 }
1395
1396 // Debug
1397 #if defined(G_DEBUG_COMPUTE_DRWS)
1398 std::cout << "Setup node array for veto rate interpolation: ";
1399 std::cout << veto_times.size() << " times, ";
1400 std::cout << veto_rates.size() << " rates" << std::endl;
1401 #endif
1402
1403 // Get Good Time Intervals. If a pulsar selection is specified then
1404 // reduce the Good Time Intervals to the validity intervals of the
1405 // pulsar emphemerides.
1406 GCOMTim tim = obs.tim();
1407 if (select.has_pulsar()) {
1408 tim.reduce(select.pulsar().validity());
1409 }
1410
1411 // Initialise event and superpacket counters
1412 int i_evt = 0;
1413 int i_sp = 0;
1414
1415 // Signal that loop should be terminated
1416 bool terminate = false;
1417
1418 // Loop over Orbit Aspect Data
1419 for (int i_oad = 0; i_oad < noads; ++i_oad) {
1420
1421 // Get reference to Orbit Aspect Data of superpacket
1422 const GCOMOad &oad = obs.oads()[i_oad];
1423
1424 // Check superpacket usage for all DRWs so that their statistics
1425 // get correctly updated
1426 for (int i = 0; i < size(); ++i) {
1427 if (m_dris[i].use_superpacket(oad, tim, select)) {
1428 m_wrk_use_sp[i_oad] = true;
1429 }
1430 }
1431 if (!m_wrk_use_sp[i_oad]) {
1432 continue;
1433 }
1434
1435 // Get Orbit Aspect Data mid-time in seconds
1436 double time = oad.tstart().secs() + 0.5 * (oad.tstop() - oad.tstart());
1437
1438 // Prepare Earth horizon angle computation. The celestial system
1439 // is reinterpreted as the COMPTEL coordinate system, where the
1440 // 90 degrees - zenith angle becomes the declination and the azimuth
1441 // angle becomes Right Ascension. This allows us later to use the
1442 // GSkyDir::dist method to compute the distance between the geocentre
1443 // and the telescope Z-axis.
1444 double theta_geocentre = double(oad.gcel());
1445 double phi_geocentre = double(oad.gcaz());
1446 GSkyDir geocentre_comptel;
1447 geocentre_comptel.radec_deg(phi_geocentre, 90.0-theta_geocentre);
1448
1449 // Compute solid-angle-corrected geometry factors and Earth horizon
1450 // angles for all (Chi,Psi) pixels
1451 double total_geo = 0.0;
1452 for (int index = 0; index < npix; ++index) {
1453
1454 // Convert sky direction to COMPTEL coordinates
1455 double theta = oad.theta(skys[index]);
1456 double phi = oad.phi(skys[index]);
1457
1458 // Compute geometric factor summed over all D1, D2 times
1459 // the solid angle of the weighting cube pixel
1460 double geometry = dri0->compute_geometry(oad.tjd(), theta, phi,
1461 select, status) * omegas[index];
1462
1463 // Store and sum up geometric factor
1464 geo(index) = geometry;
1465 total_geo += geometry;
1466
1467 // Compute Earth horizon angle as the distance between the Earth
1468 // centre in COMPTEL coordinates and the (Chi, Psi) pixel in
1469 // COMPTEL coordinates minus the radius of the Earth.
1470 GSkyDir chipsi_comptel;
1471 chipsi_comptel.radec_deg(phi, 90.0-theta);
1472 eha(index) = geocentre_comptel.dist_deg(chipsi_comptel) - oad.georad();
1473
1474 } // endfor: looped over (Chi, Psi)
1475
1476 // Compute Earth horizon cut correction factor for all Phibar layers
1477 if (total_geo > 0.0) {
1478 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1479 double ehamin = double(iphibar) * dri0->m_phibin + zetamin;
1480 double accepted_geo = 0.0;
1481 for (int index = 0; index < npix; ++index) {
1482 if (eha(index) >= ehamin) {
1483 accepted_geo += geo(index);
1484 }
1485 }
1486 m_wrk_ehacutcorr(i_sp, iphibar) = accepted_geo / total_geo;
1487 }
1488 }
1489
1490 // Compute veto rate and activation rates for superpacket time if rates
1491 // are available. The activation rates are initialised with a constant
1492 // rate.
1493 if (!veto_times.is_empty()) {
1494 double value = veto_times.interpolate(time, veto_rates);
1495 if (value > 0.0) {
1496 m_wrk_vetorate(i_sp) = value;
1497 for (int ieng = 0; ieng < neng; ++ieng) {
1498 m_wrk_activrate(i_sp, ieng) = 1.0;
1499 }
1500 }
1501 }
1502
1503 // Collect all events within superpacket and store result in 3D array.
1504 // Break if the end of the event list was reached.
1505 for (; i_evt < events->size(); ++i_evt) {
1506
1507 // Get pointer to event
1508 const GCOMEventAtom* event = (*events)[i_evt];
1509
1510 // Break loop if the end of the superpacket was reached
1511 if (event->time() > oad.tstop()) {
1512 break;
1513 }
1514
1515 // Determine energy bin
1516 int ienergy = -1;
1517 for (int i = 0; i < size(); ++i) {
1518 if ((event->energy() >= m_dris[i].m_ebounds.emin()) &&
1519 (event->energy() <= m_dris[i].m_ebounds.emax())) {
1520 ienergy = i;
1521 break;
1522 }
1523 }
1524
1525 // Skip event if it is not contained in any energy bin
1526 if (ienergy == -1) {
1527 continue;
1528 }
1529
1530 // Get pointer to appropriate DRW
1531 const GCOMDri* dri = &(m_dris[ienergy]);
1532
1533 // Check GTIs if the DRW has GTIs
1534 if (dri->gti().size() > 0) {
1535
1536 // Skip event if it lies before the GTI start
1537 if (event->time() < dri->gti().tstart()) {
1538 continue;
1539 }
1540
1541 // Break Orbit Aspect Data loop if event lies after the GTI stop
1542 else if (event->time() > dri->gti().tstop()) {
1543 terminate = true;
1544 break;
1545 }
1546
1547 } // endif: DRW had GTIs
1548
1549 // Skip event if it lies before the superpacket start
1550 if (event->time() < oad.tstart()) {
1551 continue;
1552 }
1553
1554 // Apply event selection
1555 if (!dri->m_selection.use_event(*event)) {
1556 continue;
1557 }
1558
1559 // Compute Compton scatter angle index. Skip if it's invalid.
1560 int iphibar = int((event->phibar() - dri->m_phimin) / dri->m_phibin);
1561 if (iphibar < 0) {
1562 continue;
1563 }
1564 else if (iphibar >= dri->nphibar()) {
1565 continue;
1566 }
1567
1568 // Check for Earth horizon angle. There is a constant EHA limit
1569 // over a Phibar layer to be compliant with the DRG.
1570 double ehamin = double(iphibar) * dri->m_phibin + zetamin;
1571 if (event->eha() < ehamin) {
1572 continue;
1573 }
1574
1575 // Now fill the event in the 3D counts array
1576 m_wrk_counts(i_sp, iphibar, ienergy) += 1.0;
1577
1578 } // endfor: collected events
1579
1580 // Debug
1581 #if defined(G_DEBUG_COMPUTE_DRWS)
1582 std::cout << "Superpacket " << i_oad;
1583 std::cout << " i_sp=" << i_sp;
1584 std::cout << " time=" << time;
1585 std::cout << " vetorate=" << m_wrk_vetorate(i_oad);
1586 std::cout << " total_geo=" << total_geo;
1587 std::cout << std::endl;
1588 #endif
1589
1590 // Increment superpacket counter
1591 i_sp++;
1592
1593 // Break Orbit Aspect Data loop if termination was signalled or if there
1594 // are no more events
1595 if (terminate || i_evt >= events->size()) {
1596 break;
1597 }
1598
1599 } // endfor: looped over Orbit Aspect Data
1600
1601 // Copy results in shortened Ndarrays
1602 GNdarray wrk_counts(i_sp, nphibar, neng);
1603 GNdarray wrk_ehacutcorr(i_sp, nphibar);
1604 GNdarray wrk_vetorate(i_sp);
1605 GNdarray wrk_activrate(i_sp, neng);
1606 for (int i = 0; i < i_sp; ++i) {
1607 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1608 for (int ieng = 0; ieng < neng; ++ieng) {
1609 wrk_counts(i, iphibar, ieng) = m_wrk_counts(i, iphibar, ieng);
1610 }
1611 wrk_ehacutcorr(i, iphibar) = m_wrk_ehacutcorr(i, iphibar);
1612 }
1613 for (int ieng = 0; ieng < neng; ++ieng) {
1614 wrk_activrate(i, ieng) = m_wrk_activrate(i, ieng);
1615 }
1616 wrk_vetorate(i) = m_wrk_vetorate(i);
1617 }
1618 m_wrk_counts = wrk_counts;
1619 m_wrk_ehacutcorr = wrk_ehacutcorr;
1620 m_wrk_vetorate = wrk_vetorate;
1621 m_wrk_activrate = wrk_activrate;
1622
1623 // Return
1624 return;
1625}
1626
1627
1628/***********************************************************************//**
1629 * @brief Fit working arrays for vetorate computation
1630 *
1631 * Determines the f_prompt parameter for each energy bin using a maximum
1632 * log-likelihood fit using the data in the working arrays for each energy
1633 * bin. Based on the fitted f_prompt parameter the method also computes the
1634 * expected background rate time variation for each energy bin.
1635 ***************************************************************************/
1637{
1638 // Set constants
1639 const double norm = 1.0; // Model normalisation
1640
1641 // Debug
1642 #if defined(G_DEBUG_COMPUTE_DRWS)
1643 std::cout << "GCOMDris::vetorate_fit" << std::endl;
1644 std::cout << "======================" << std::endl;
1645 #endif
1646
1647 // Extract dimension from working arrays
1648 int nsp = m_wrk_counts.shape()[0];
1649 int nphibar = m_wrk_counts.shape()[1];
1650 int neng = m_wrk_counts.shape()[2];
1651
1652 // Debug
1653 #if defined(G_DEBUG_COMPUTE_DRWS)
1654 std::cout << "Number of used superpackets: " << nsp << std::endl;
1655 std::cout << "Number of Phibar layers: " << nphibar << std::endl;
1656 std::cout << "Number of energies: " << neng << std::endl;
1657 #endif
1658
1659 // Setup rate result array
1660 m_wrk_rate = GNdarray(nsp, neng);
1661
1662 // Normalise veto rates
1663 double nvetorate = 0.0;
1664 for (int isp = 0; isp < nsp; ++isp) {
1665 if (m_wrk_vetorate(isp) > 0.0) {
1666 nvetorate += m_wrk_vetorate(isp);
1667 }
1668 }
1669 if (nvetorate > 0.0) {
1670 for (int isp = 0; isp < nsp; ++isp) {
1671 m_wrk_vetorate(isp) /= nvetorate;
1672 }
1673 }
1674
1675 // Normalise activation rates
1676 for (int ieng = 0; ieng < neng; ++ieng) {
1677 double nactivrate = 0.0;
1678 for (int isp = 0; isp < nsp; ++isp) {
1679 if (m_wrk_activrate(isp, ieng) > 0.0) {
1680 nactivrate += m_wrk_activrate(isp, ieng);
1681 }
1682 }
1683 if (nactivrate > 0.0) {
1684 for (int isp = 0; isp < nsp; ++isp) {
1685 m_wrk_activrate(isp, ieng) /= nactivrate;
1686 }
1687 }
1688 }
1689
1690 // Loop over energy bins
1691 for (int ieng = 0; ieng < neng; ++ieng) {
1692
1693 // Set fprompt parameter
1694 GOptimizerPars pars;
1695 GOptimizerPar par("fprompt", m_dris[ieng].m_drw_fprompt);
1696 par.range(0.0, 1.0);
1697 par.factor_gradient(1.0); // To avoid zero gradient log message
1698 pars.append(par);
1699
1700 // Set fit function
1701 GCOMDris::likelihood fct(this, ieng, norm);
1702
1703 // Optimize function and compute errors
1704 #if defined(G_DEBUG_COMPUTE_DRWS)
1705 GLog log;
1706 log.cout(true);
1707 GOptimizerLM opt(&log);
1708 #else
1709 GOptimizerLM opt;
1710 #endif
1711 opt.eps(0.1);
1712 opt.max_iter(500);
1713 opt.optimize(fct, pars);
1714 opt.errors(fct, pars);
1715
1716 // Retrieve logL
1717 double logL = opt.value();
1718
1719 // Recover fprompt parameter and errors
1720 double fprompt = pars[0]->value();
1721 double e_fprompt = pars[0]->error();
1722 double factiv = 1.0 - fprompt;
1723
1724 // Set resulting rate vector for this energy bin
1725 for (int isp = 0; isp < nsp; ++isp) {
1726 m_wrk_rate(isp, ieng) = fprompt * m_wrk_vetorate(isp) +
1727 factiv * m_wrk_activrate(isp, ieng);
1728 }
1729
1730 // Set DRW header members
1731 m_dris[ieng].m_drw_status = opt.status_string();
1732 m_dris[ieng].m_drw_fprompt = fprompt;
1733 m_dris[ieng].m_drw_e_fprompt = e_fprompt;
1734 m_dris[ieng].m_drw_iter = opt.iter();
1735
1736 // Debug: Print fit results
1737 #if defined(G_DEBUG_COMPUTE_DRWS)
1738 std::cout << ieng;
1739 std::cout << " logL=" << logL;
1740 std::cout << " fprompt=" << fprompt << " +/- " << e_fprompt;
1741 std::cout << std::endl;
1742 #endif
1743
1744 } // endfor: looped over energy bins
1745
1746 // Return
1747 return;
1748}
1749
1750
1751/***********************************************************************//**
1752 * @brief Update activation rate
1753 ***************************************************************************/
1755{
1756 // Set constants
1757 const double norm = 1.0; // Model normalisation
1758 const double t_hour = 1.0; // Smoothing duration (hours)
1759
1760 // Debug
1761 #if defined(G_DEBUG_COMPUTE_DRWS)
1762 std::cout << "GCOMDris::vetorate_update_activ" << std::endl;
1763 std::cout << "===============================" << std::endl;
1764 #endif
1765
1766 // Extract dimension from working arrays
1767 int nsp = m_wrk_counts.shape()[0];
1768 int nphibar = m_wrk_counts.shape()[1];
1769 int neng = m_wrk_counts.shape()[2];
1770
1771 // Setup Gaussian smoothing kernel
1772 double nsmooth = 3600.0/16.384 * t_hour;
1773 double weight = -0.5 / (nsmooth * nsmooth);
1774 GNdarray kernel(nsp);
1775 double sum = 0.0;
1776 for (int isp = 0; isp < nsp/2; ++isp) {
1777 double kvalue = std::exp(weight*isp*isp);
1778 if (kvalue <= 0.0) {
1779 break;
1780 }
1781 kernel(isp) = kvalue;
1782 sum += kvalue;
1783 if (isp > 0) {
1784 kernel(nsp-isp) = kvalue;
1785 sum += kvalue;
1786 }
1787 }
1788 if (sum > 0.0) {
1789 for (int isp = 0; isp < nsp; ++isp) {
1790 kernel(isp) /= sum;
1791 }
1792 }
1793 GFft fft_kernel(kernel);
1794
1795 // Loop over energy bins
1796 for (int ieng = 0; ieng < neng; ++ieng) {
1797
1798 // Compute model normalised to number of events for each Phibar
1799 GNdarray model(nsp, nphibar);
1800 GNdarray n(nphibar);
1801 for (int isp = 0; isp < nsp; ++isp) {
1802 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1803 model(isp, iphibar) = m_wrk_rate(isp, ieng) * m_wrk_ehacutcorr(isp, iphibar);
1804 }
1805 }
1806 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1807 double nevents = 0.0;
1808 double nmodel = 0.0;
1809 for (int isp = 0; isp < nsp; ++isp) {
1810 if (model(isp, iphibar) > 0.0) {
1811 nevents += m_wrk_counts(isp, iphibar, ieng);
1812 nmodel += model(isp, iphibar);
1813 }
1814 }
1815 if (nmodel > 0.0) {
1816 n(iphibar) = norm * (nevents / nmodel);
1817 for (int isp = 0; isp < nsp; ++isp) {
1818 model(isp, iphibar) *= n(iphibar);
1819 }
1820 }
1821 }
1822
1823 // Compute residual and Earth horizon correction cut by summing over
1824 // all Phibar layers
1825 GNdarray residual(nsp);
1826 GNdarray ehacutcorr(nsp);
1827 for (int isp = 0; isp < nsp; ++isp) {
1828 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1829 residual(isp) += m_wrk_counts(isp, iphibar, ieng) - model(isp, iphibar);
1830 ehacutcorr(isp) += n(iphibar) * m_wrk_ehacutcorr(isp, iphibar);
1831 }
1832 }
1833
1834 // Smooth residual
1835 GFft fft_residual(residual);
1836 fft_residual = fft_residual * fft_kernel;
1837 residual = fft_residual.backward();
1838
1839 // Smooth ehacutcorr
1840 GFft fft_ehacutcorr(ehacutcorr);
1841 fft_ehacutcorr = fft_ehacutcorr * fft_kernel;
1842 ehacutcorr = fft_ehacutcorr.backward();
1843
1844 // Derive indicent residual background rate
1845 for (int isp = 0; isp < nsp; ++isp) {
1846 if (ehacutcorr(isp) > 0.0) {
1847 residual(isp) /= ehacutcorr(isp);
1848 }
1849 }
1850
1851 // Update activation rate
1852 for (int isp = 0; isp < nsp; ++isp) {
1853 double activ = m_wrk_activrate(isp, ieng);
1854 if (activ > 0.0) {
1855 double update = activ + residual(isp);
1856 if (update < 0.05 * activ) { //!< Cap reduction at 5%
1857 update = 0.05 * activ;
1858 }
1859 m_wrk_activrate(isp, ieng) = update;
1860 }
1861 }
1862
1863 // Normalise activation rate
1864 double nactivrate = 0.0;
1865 for (int isp = 0; isp < nsp; ++isp) {
1866 nactivrate += m_wrk_activrate(isp, ieng);
1867 }
1868 if (nactivrate > 0.0) {
1869 for (int isp = 0; isp < nsp; ++isp) {
1870 m_wrk_activrate(isp, ieng) /= nactivrate;
1871 }
1872 }
1873
1874 } // endfor: looped over all energy bins
1875
1876 // Return
1877 return;
1878}
1879
1880
1881/***********************************************************************//**
1882 * @brief Generate DRWs
1883 *
1884 * @param[in] obs COMPTEL observation.
1885 * @param[in] select Selection set.
1886 * @param[in] zetamin Minimum Earth horizon - Phibar cut (deg).
1887 ***************************************************************************/
1889 const GCOMSelection& select,
1890 const double& zetamin)
1891{
1892 // Debug
1893 #if defined(G_DEBUG_COMPUTE_DRWS)
1894 std::cout << "GCOMDris::vetorate_generate" << std::endl;
1895 std::cout << "===========================" << std::endl;
1896 #endif
1897
1898 // Initialise D1 & D2 module status
1899 GCOMStatus status;
1900
1901 // Get pointer to first DRW (assume their definition is identical)
1902 const GCOMDri* dri0 = &(m_dris[0]);
1903
1904 // Extract dimensions
1905 int noads = obs.oads().size();
1906 int npix = dri0->m_dri.npix();
1907 int nphibar = dri0->nphibar();
1908 int neng = size();
1909
1910 // Debug
1911 #if defined(G_DEBUG_COMPUTE_DRWS)
1912 std::cout << "Number of OAD superpackets: " << noads << std::endl;
1913 std::cout << "Number of pixels: " << npix << std::endl;
1914 std::cout << "Number of Phibar layers: " << nphibar << std::endl;
1915 std::cout << "Number of energies: " << neng << std::endl;
1916 #endif
1917
1918 // Allocate geometry factor and Earth horizon angle working arrays
1919 GNdarray geometry(npix);
1920 GNdarray eha(npix);
1921
1922 // Setup sky directions and solid angles for all (Chi, Psi)
1923 std::vector<GSkyDir> skys;
1924 std::vector<double> omegas;
1925 for (int index = 0; index < npix; ++index) {
1926 GSkyDir sky = dri0->m_dri.inx2dir(index);
1927 double omega = dri0->m_dri.solidangle(index);
1928 skys.push_back(sky);
1929 omegas.push_back(omega);
1930 }
1931
1932 // Get Good Time Intervals. If a pulsar selection is specified then
1933 // reduce the Good Time Intervals to the validity intervals of the
1934 // pulsar emphemerides.
1935 GCOMTim tim = obs.tim();
1936 if (select.has_pulsar()) {
1937 tim.reduce(select.pulsar().validity());
1938 }
1939
1940 // Initialise superpacket counter
1941 int i_sp = 0;
1942
1943 // Loop over Orbit Aspect Data
1944 for (int i_oad = 0; i_oad < noads; ++i_oad) {
1945
1946 // Get reference to Orbit Aspect Data of superpacket
1947 const GCOMOad &oad = obs.oads()[i_oad];
1948
1949 // Skip not-to-be-used superpackets
1950 if (!m_wrk_use_sp[i_oad]) {
1951 continue;
1952 }
1953
1954 // Prepare Earth horizon angle computation. The celestial system
1955 // is reinterpreted as the COMPTEL coordinate system, where the
1956 // 90 degrees - zenith angle becomes the declination and the azimuth
1957 // angle becomes Right Ascension. This allows us later to use the
1958 // GSkyDir::dist method to compute the distance between the geocentre
1959 // and the telescope Z-axis.
1960 double theta_geocentre = double(oad.gcel());
1961 double phi_geocentre = double(oad.gcaz());
1962 GSkyDir geocentre_comptel;
1963 geocentre_comptel.radec_deg(phi_geocentre, 90.0-theta_geocentre);
1964
1965 // Compute solid-angle-corrected geometry factors and Earth horizon
1966 // angles for all (Chi,Psi) pixels
1967 for (int index = 0; index < npix; ++index) {
1968
1969 // Convert sky direction to COMPTEL coordinates
1970 double theta = oad.theta(skys[index]);
1971 double phi = oad.phi(skys[index]);
1972
1973 // Compute geometric factor summed over all D1, D2 times
1974 // the solid angle of the weighting cube pixel
1975 geometry(index) = dri0->compute_geometry(oad.tjd(), theta, phi,
1976 select, status) * omegas[index];
1977
1978 // Compute Earth horizon angle as the distance between the Earth
1979 // centre in COMPTEL coordinates and the (Chi, Psi) pixel in
1980 // COMPTEL coordinates minus the radius of the Earth.
1981 GSkyDir chipsi_comptel;
1982 chipsi_comptel.radec_deg(phi, 90.0-theta);
1983 eha(index) = geocentre_comptel.dist_deg(chipsi_comptel) - oad.georad();
1984
1985 } // endfor: computed solid-angle-corrected geometry factors
1986
1987 // Loop over all DRWs
1988 for (int i = 0; i < size(); ++i) {
1989
1990 // Get pointer to appropriate DRW
1991 const GCOMDri* dri = &(m_dris[i]);
1992
1993 // Loop over all Phibar layers
1994 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1995
1996 // Compute minimum Earth horizon angle for Phibar layer
1997 double ehamin = double(iphibar) * dri->m_phibin + zetamin;
1998
1999 // Loop over all (Chi,Psi) pixels
2000 for (int index = 0; index < npix; ++index) {
2001
2002 // If Earth horizon angle is equal or larger than the minimum
2003 // requirement then add up the rate weighted geometry factor
2004 if (eha(index) >= ehamin) {
2005
2006 // Get data space index
2007 int inx = index + iphibar * npix;
2008
2009 // Store geometry weighted with rate as DRW
2010 m_dris[i][inx] += geometry(index) * m_wrk_rate(i_sp, i);
2011
2012 } // endif: Earth horizon cut passed
2013
2014 } // endfor: looped over (Chi, Psi)
2015
2016 } // endfor: looped over Phibar
2017
2018 // Set binary table information
2019
2020 } // endfor: looped over all DRWs
2021
2022 // Increment superpacket counter
2023 i_sp++;
2024
2025 } // endfor: looped over Orbit Aspect Data
2026
2027 // Return
2028 return;
2029}
2030
2031
2032/***********************************************************************//**
2033 * @brief Finish DRWs
2034 *
2035 * @param[in] obs COMPTEL observation.
2036 *
2037 * Set DRW binary tables
2038 ***************************************************************************/
2040{
2041 // Debug
2042 #if defined(G_DEBUG_COMPUTE_DRWS)
2043 std::cout << "GCOMDris::vetorate_finish" << std::endl;
2044 std::cout << "=========================" << std::endl;
2045 #endif
2046
2047 // Extract dimensions
2048 int noads = obs.oads().size();
2049
2050 // Compute number of to-be-used superpackets
2051 int nsp = 0;
2052 for (int i_oad = 0; i_oad < noads; ++i_oad) {
2053 if (m_wrk_use_sp[i_oad]) {
2054 nsp++;
2055 }
2056 }
2057
2058 // Debug
2059 #if defined(G_DEBUG_COMPUTE_DRWS)
2060 std::cout << "Number of OAD superpackets: " << noads << std::endl;
2061 std::cout << "Number of used superpackets: " << nsp << std::endl;
2062 #endif
2063
2064 // Setup DRW binary tables
2065 for (int i = 0; i < size(); ++i) {
2066
2067 // Get pointer to DRW
2068 GCOMDri* dri = &(m_dris[i]);
2069
2070 // Allocate columns
2071 GFitsTableLongCol tjd("TJD", nsp);
2072 GFitsTableLongCol tics("TICS", nsp);
2073 GFitsTableDoubleCol rate("RATE", nsp);
2074 GFitsTableDoubleCol vetorate("VETORATE", nsp);
2075 GFitsTableDoubleCol activrate("ACTIVRATE", nsp);
2076
2077 // Initialise superpacket counter
2078 int isp = 0;
2079
2080 // Loop over Orbit Aspect Data
2081 for (int i_oad = 0; i_oad < noads; ++i_oad) {
2082
2083 // Get reference to Orbit Aspect Data of superpacket
2084 const GCOMOad &oad = obs.oads()[i_oad];
2085
2086 // Skip not-to-be-used superpackets
2087 if (!m_wrk_use_sp[i_oad]) {
2088 continue;
2089 }
2090
2091 // Fill table columns
2092 tjd(isp) = oad.tjd();
2093 tics(isp) = oad.tics();
2094 rate(isp) = m_wrk_rate(isp, i);
2095 vetorate(isp) = m_wrk_vetorate(isp);
2096 activrate(isp) = m_wrk_activrate(isp, i);
2097
2098 // Increment superpacket counter
2099 isp++;
2100
2101 } // endfor: looped over Orbit Aspect Data
2102
2103 // Append columns to binary table
2104 dri->m_drw_table.clear();
2105 dri->m_drw_table.extname("RATES");
2106 dri->m_drw_table.append(tjd);
2107 dri->m_drw_table.append(tics);
2108 dri->m_drw_table.append(rate);
2109 dri->m_drw_table.append(vetorate);
2110 dri->m_drw_table.append(activrate);
2111
2112 } // endfor: looped over DRWs
2113
2114 // Return
2115 return;
2116}
2117
2118
2119/***********************************************************************//**
2120 * @brief Save working arrays for vetorate computation
2121 *
2122 * @param[in] filename FITS filename.
2123 *
2124 * Save working arrays for vetorate DRW computation in FITS file.
2125 ***************************************************************************/
2126void GCOMDris::vetorate_save(const GFilename& filename) const
2127{
2128 // Debug
2129 #if defined(G_DEBUG_COMPUTE_DRWS)
2130 std::cout << "GCOMDris::vetorate_save" << std::endl;
2131 std::cout << "=======================" << std::endl;
2132 #endif
2133
2134 // Extract dimension from working arrays
2135 int noads = m_wrk_use_sp.size();
2136 int nsp = m_wrk_counts.shape()[0];
2137 int nphibar = m_wrk_counts.shape()[1];
2138 int neng = m_wrk_counts.shape()[2];
2139
2140 // Allocate FITS images
2141 GFitsImageDouble image_counts(nsp, nphibar, neng);
2142 GFitsImageDouble image_ehacutcorr(nsp, nphibar);
2143 GFitsImageDouble image_vetorate(nsp);
2144 GFitsImageDouble image_activrate(nsp, neng);
2145 GFitsImageShort image_use_sp(noads);
2146
2147 // Fill FITS images from working arrays
2148 for (int isp = 0; isp < nsp; ++isp) {
2149 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
2150 for (int ieng = 0; ieng < neng; ++ieng) {
2151 image_counts(isp, iphibar, ieng) = m_wrk_counts(isp, iphibar, ieng);
2152 }
2153 image_ehacutcorr(isp, iphibar) = m_wrk_ehacutcorr(isp, iphibar);
2154 }
2155 for (int ieng = 0; ieng < neng; ++ieng) {
2156 image_activrate(isp, ieng) = m_wrk_activrate(isp, ieng);
2157 }
2158 image_vetorate(isp) = m_wrk_vetorate(isp);
2159 }
2160 for (int ioad = 0; ioad < noads; ++ioad) {
2161 image_use_sp(ioad) = (m_wrk_use_sp[ioad]) ? 1 : 0;
2162 }
2163
2164 // Set image attributes
2165 image_counts.extname("COUNTS");
2166 image_ehacutcorr.extname("EHACUTCORR");
2167 image_vetorate.extname("VETORATE");
2168 image_activrate.extname("ACTIVRATE");
2169 image_use_sp.extname("SPUSAGE");
2170
2171 // Create FITS file and append images
2172 GFits fits;
2173 fits.append(image_counts);
2174 fits.append(image_ehacutcorr);
2175 fits.append(image_vetorate);
2176 fits.append(image_activrate);
2177 fits.append(image_use_sp);
2178
2179 // Save FITS file
2180 fits.saveto(filename, true);
2181
2182 // Return
2183 return;
2184}
2185
2186
2187/***********************************************************************//**
2188 * @brief Load working arrays for vetorate computation
2189 *
2190 * @param[in] filename FITS filename.
2191 *
2192 * Load working arrays for vetorate DRW computation from FITS file.
2193 ***************************************************************************/
2195{
2196 // Debug
2197 #if defined(G_DEBUG_COMPUTE_DRWS)
2198 std::cout << "GCOMDris::vetorate_load" << std::endl;
2199 std::cout << "=======================" << std::endl;
2200 #endif
2201
2202 // Open FITS file
2203 GFits fits(filename);
2204
2205 // Get images
2206 const GFitsImage* image_counts = fits.image("COUNTS");
2207 const GFitsImage* image_ehacutcorr = fits.image("EHACUTCORR");
2208 const GFitsImage* image_vetorate = fits.image("VETORATE");
2209 const GFitsImage* image_activrate = fits.image("ACTIVRATE");
2210 const GFitsImage* image_use_sp = fits.image("SPUSAGE");
2211
2212 // Extract dimensions
2213 int noads = image_use_sp->naxes(0);
2214 int nsp = image_counts->naxes(0);
2215 int nphibar = image_counts->naxes(1);
2216 int neng = image_counts->naxes(2);
2217
2218 // Debug
2219 #if defined(G_DEBUG_COMPUTE_DRWS)
2220 std::cout << "Number of OAD superpackets: " << noads << std::endl;
2221 std::cout << "Number of used superpackets: " << nsp << std::endl;
2222 std::cout << "Number of Phibar layers: " << nphibar << std::endl;
2223 std::cout << "Number of energies: " << neng << std::endl;
2224 #endif
2225
2226 // Allocate working array
2227 m_wrk_counts = GNdarray(nsp, nphibar, neng);
2228 m_wrk_ehacutcorr = GNdarray(nsp, nphibar);
2229 m_wrk_vetorate = GNdarray(nsp);
2230 m_wrk_activrate = GNdarray(nsp, neng);
2231 m_wrk_use_sp = std::vector<bool>(noads);
2232
2233 // Extract data from images
2234 for (int isp = 0; isp < nsp; ++isp) {
2235 for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
2236 for (int ieng = 0; ieng < neng; ++ieng) {
2237 m_wrk_counts(isp, iphibar, ieng) = image_counts->pixel(isp, iphibar, ieng);
2238 }
2239 m_wrk_ehacutcorr(isp, iphibar) = image_ehacutcorr->pixel(isp, iphibar);
2240 }
2241 for (int ieng = 0; ieng < neng; ++ieng) {
2242 m_wrk_activrate(isp, ieng) = image_activrate->pixel(isp, ieng);
2243 }
2244 m_wrk_vetorate(isp) = image_vetorate->pixel(isp);
2245 }
2246 for (int ioad = 0; ioad < noads; ++ioad) {
2247 m_wrk_use_sp[ioad] = (image_use_sp->pixel(ioad) == 1);
2248 }
2249
2250 // Close FITS file
2251 fits.close();
2252
2253 // Return
2254 return;
2255}
2256
2257
2258/***********************************************************************//**
2259 * @brief Log-likelihood function constructor
2260 *
2261 * @param[in] dris Parent calling likelihood function.
2262 * @param[in] ieng DRW energy bin.
2263 * @param[in] norm Normalisation constant.
2264 *
2265 * Constructs the log-likelihood function that is used to determine the
2266 * value of f_prompt.
2267 ***************************************************************************/
2269 const int& ieng,
2270 const double& norm) : GOptimizerFunction()
2271{
2272 // Store arguments
2273 m_this = dris;
2274 m_ieng = ieng;
2275 m_norm = norm;
2276
2277 // Set gradient and curvature members
2278 m_gradient = GVector(1);
2280
2281 // Extract dimension from working array
2284
2285 // Multiply veto and constant rate by EHA cut correction. Rates are zero
2286 // in case that the vetorate is zero.
2290 for (int isp = 0; isp < m_nsp; ++isp) {
2291 double vetorate = m_this->m_wrk_vetorate(isp);
2292 if (vetorate > 0.0) {
2293 double activrate = m_this->m_wrk_activrate(isp, ieng);
2294 for (int iphibar = 0; iphibar < m_nphibar; ++iphibar) {
2295 m_vetorate(isp, iphibar) = vetorate * m_this->m_wrk_ehacutcorr(isp, iphibar);
2296 m_activrate(isp, iphibar) = activrate * m_this->m_wrk_ehacutcorr(isp, iphibar);
2297 m_diffrate(isp, iphibar) = m_vetorate(isp, iphibar) - m_activrate(isp, iphibar);
2298 }
2299 }
2300 }
2301
2302 // Compute rate sums
2306 for (int iphibar = 0; iphibar < m_nphibar; ++iphibar) {
2307 for (int isp = 0; isp < m_nsp; ++isp) {
2308 m_vetorate_sum(iphibar) += m_vetorate(isp, iphibar);
2309 m_activrate_sum(iphibar) += m_activrate(isp, iphibar);
2310 }
2311 m_diffrate_sum(iphibar) = m_vetorate_sum(iphibar) - m_activrate_sum(iphibar);
2312 }
2313
2314 // Return
2315 return;
2316}
2317
2318
2319/***********************************************************************//**
2320 * @brief Log-likelihood function evaluation
2321 *
2322 * @param[in] pars Function parameters.
2323 *
2324 * Computes the log-likelihood function, its gradient and its curvature at
2325 * the specified function parameters.
2326 ***************************************************************************/
2328{
2329 // Recover parameters
2330 double fprompt = pars[0]->value();
2331 double factiv = 1.0 - fprompt;
2332
2333 // Allocate phibar normalisation arrays
2334 GNdarray nevents(m_nphibar);
2335 GNdarray nmodel(m_nphibar);
2336 GNdarray norm(m_nphibar);
2337
2338 // Compute model
2339 GNdarray model = fprompt * m_vetorate + factiv * m_activrate;
2340
2341 // Compute Phibar normalised model
2342 GNdarray model_norm = model;
2343 for (int iphibar = 0; iphibar < m_nphibar; ++iphibar) {
2344 for (int isp = 0; isp < m_nsp; ++isp) {
2345 if (model(isp, iphibar) > 0.0) {
2346 nevents(iphibar) += m_this->m_wrk_counts(isp, iphibar, m_ieng);
2347 nmodel(iphibar) += model(isp, iphibar);
2348 }
2349 }
2350 if (nmodel(iphibar) > 0.0) {
2351 norm(iphibar) = m_norm * nevents(iphibar) / nmodel(iphibar);
2352 for (int isp = 0; isp < m_nsp; ++isp) {
2353 model_norm(isp, iphibar) *= norm(iphibar);
2354 }
2355 }
2356 }
2357
2358 // Compute LogL
2359 m_value = 0.0;
2360 for (int isp = 0; isp < m_nsp; ++isp) {
2361 for (int iphibar = 0; iphibar < m_nphibar; ++iphibar) {
2362 if (model_norm(isp, iphibar) > 0.0) {
2363 m_value -= m_this->m_wrk_counts(isp, iphibar, m_ieng) *
2364 std::log(model_norm(isp, iphibar)) - model_norm(isp, iphibar);
2365 }
2366 }
2367 }
2368
2369 // Evaluate gradient and curvature
2370 for (int iphibar = 0; iphibar < m_nphibar; ++iphibar) {
2371
2372 // Precompute normalisation gradient
2373 double nmodel2 = nmodel(iphibar) * nmodel(iphibar);
2374 double g_norm = -m_norm * nevents(iphibar) / nmodel2 * m_diffrate_sum(iphibar);
2375
2376 // Loop over superpackets
2377 for (int isp = 0; isp < m_nsp; ++isp) {
2378
2379 // Continue only if model is positive
2380 if (model_norm(isp, iphibar) > 0.0) {
2381
2382 // Pre computation
2383 double fb = m_this->m_wrk_counts(isp, iphibar, m_ieng) / model_norm(isp, iphibar);
2384 double fc = (1.0 - fb);
2385 double fa = fb / model_norm(isp, iphibar);
2386
2387 // Compute parameter gradient
2388 double g = norm(iphibar) * m_diffrate(isp, iphibar) + g_norm * model(isp, iphibar);
2389
2390 // Update gradient
2391 m_gradient[0] += fc * g;
2392
2393 // Update curvature
2394 m_curvature(0,0) += fa * g * g;
2395
2396 } // endif: model was positive
2397
2398 } // endfor: looped over superpackets
2399
2400 } // endfor: looped over Phibar
2401
2402 // Return
2403 return;
2404}
#define G_AT
COMPTEL Data Space class definition.
#define G_COMPUTE_DRWS
Definition GCOMDris.cpp:61
COMPTEL Data Space container class definition.
COMPTEL event atom class definition.
COMPTEL event list class definition.
COMPTEL Orbit Aspect Data class definition.
COMPTEL Orbit Aspect Data container class definition.
COMPTEL observation class interface definition.
COMPTEL selection set class definition.
COMPTEL instrument status class definition.
Implementation of support function used by COMPTEL classes.
COMPTEL Good Time Intervals class definition.
Definition of COMPTEL tools.
Exception handler interface definition.
Fast Fourier transformation class interface definition.
Double precision FITS image class definition.
Short integer FITS image class definition.
#define G_INSERT
#define G_REMOVE
FITS table double column class interface definition.
FITS table long integer column class interface definition.
FITS file class interface definition.
Mathematical function definitions.
Levenberg Marquardt optimizer class interface definition.
Optimizer parameter class interface definition.
Optimizer parameters base class definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
double norm(const GVector &vector)
Computes vector norm.
Definition GVector.cpp:932
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition GVector.cpp:1342
double sum(const GVector &vector)
Computes vector sum.
Definition GVector.cpp:1012
COMPTEL Data Space class.
Definition GCOMDri.hpp:62
GFitsBinTable m_drw_table
DRW binary table to append to the FITS file.
Definition GCOMDri.hpp:173
double m_phibin
Phibar binsize (deg)
Definition GCOMDri.hpp:160
const GGti & gti(void) const
Return Good Time Intervals of DRI cube.
Definition GCOMDri.hpp:344
GTime m_tstart
Selection start time.
Definition GCOMDri.hpp:165
double compute_geometry(const int &tjd, const double &theta, const double &phi, const GCOMSelection &select, const GCOMStatus &status) const
Compute DRG geometry factor.
Definition GCOMDri.cpp:1506
const double & phibin(void) const
Return Compton scatter angle bin of DRI cube.
Definition GCOMDri.hpp:383
const double & phimin(void) const
Return minimum Compton scatter angle of DRI cube.
Definition GCOMDri.hpp:371
GSkyMap m_dri
Data cube.
Definition GCOMDri.hpp:156
GCOMSelection m_selection
Selection parameters.
Definition GCOMDri.hpp:181
double m_phimin
Phibar minimum (deg)
Definition GCOMDri.hpp:159
GTime m_tstop
Selection stop time.
Definition GCOMDri.hpp:166
int nphibar(void) const
Return number of Phibar bins.
Definition GCOMDri.hpp:266
int m_nsp
Number of superpackets.
Definition GCOMDris.hpp:104
int m_nphibar
Number of phibar layers.
Definition GCOMDris.hpp:105
GMatrixSparse m_curvature
Curvature matrix.
Definition GCOMDris.hpp:114
int m_ieng
DRW energy bin.
Definition GCOMDris.hpp:102
GNdarray m_vetorate
Vetorate array multiplied by EHA cut correction.
Definition GCOMDris.hpp:106
GVector m_gradient
Gradient vector.
Definition GCOMDris.hpp:113
GNdarray m_activrate_sum
Time integrated activation rate array.
Definition GCOMDris.hpp:110
GNdarray m_vetorate_sum
Time integrated vetorate array.
Definition GCOMDris.hpp:109
GNdarray m_diffrate
Vetorate - activation rate array.
Definition GCOMDris.hpp:108
GNdarray m_diffrate_sum
Time integrated difference rate array.
Definition GCOMDris.hpp:111
likelihood(GCOMDris *dris, const int &ieng, const double &norm)
Log-likelihood function constructor.
double m_norm
Normalisation value.
Definition GCOMDris.hpp:103
GNdarray m_activrate
Activation rate array multiplied by EHA cut correction.
Definition GCOMDris.hpp:107
GCOMDris * m_this
Pointer to GCOMDris object.
Definition GCOMDris.hpp:115
virtual void eval(const GOptimizerPars &pars)
Log-likelihood function evaluation.
COMPTEL Data Space container class.
Definition GCOMDris.hpp:56
GCOMDri & append(const GCOMDri &dri)
Append Data Space to container.
Definition GCOMDris.cpp:249
void compute_drws_phibar(const GCOMObservation &obs, const GCOMEventList *events, const GCOMSelection &select, const double &zetamin, const double &timebin)
Compute background weighting cubes using Phibar dependent rates.
Definition GCOMDris.cpp:922
void vetorate_save(const GFilename &filename) const
Save working arrays for vetorate computation.
GCOMDris(void)
Void constructor.
Definition GCOMDris.cpp:87
void init_members(void)
Initialise class members.
Definition GCOMDris.cpp:528
GCOMDris & operator=(const GCOMDris &dris)
Assignment operator.
Definition GCOMDris.cpp:140
void extend(const GCOMDris &dris)
Append Data Space container.
Definition GCOMDris.cpp:332
virtual ~GCOMDris(void)
Destructor.
Definition GCOMDris.cpp:118
GNdarray m_wrk_activrate
2D activation rate array
Definition GCOMDris.hpp:157
std::vector< GCOMDri > m_dris
Data space instances.
Definition GCOMDris.hpp:151
void copy_members(const GCOMDris &dris)
Copy class members.
Definition GCOMDris.cpp:551
std::string print(const GChatter &chatter=NORMAL) const
Print Data Space container.
Definition GCOMDris.cpp:497
GCOMDri & insert(const int &index, const GCOMDri &dri)
Insert Data Space into container.
Definition GCOMDris.cpp:271
void clear(void)
Clear Data Space container.
Definition GCOMDris.cpp:170
bool is_empty(void) const
Signals if there are no Data space instances in container.
Definition GCOMDris.hpp:226
void vetorate_setup(const GCOMObservation &obs, const GCOMEventList *events, const GCOMSelection &select, const double &zetamin)
Setup working arrays for vetorate computation.
void vetorate_generate(const GCOMObservation &obs, const GCOMSelection &select, const double &zetamin)
Generate DRWs.
void compute_drws_energy(const GCOMObservation &obs, const GCOMEventList *events, const GCOMSelection &select, const double &zetamin, const double &timebin)
Compute background weighting cubes using energy dependent rates.
Definition GCOMDris.cpp:606
void compute_drws(const GCOMObservation &obs, const GCOMSelection &select=GCOMSelection(), const double &zetamin=5.0, const double &timebin=300.0, const std::string &method="phibar")
Compute background weighting cubes.
Definition GCOMDris.cpp:378
std::vector< bool > m_wrk_use_sp
1D superpacket usage array
Definition GCOMDris.hpp:159
void vetorate_finish(const GCOMObservation &obs)
Finish DRWs.
GCOMDris * clone(void) const
Clone Data Space container.
Definition GCOMDris.cpp:188
int size(void) const
Return number of Data space instances in container.
Definition GCOMDris.hpp:211
void remove(const int &index)
Remove Data Space from container.
Definition GCOMDris.cpp:307
GNdarray m_wrk_counts
3D event cube array
Definition GCOMDris.hpp:154
GNdarray m_wrk_ehacutcorr
2D geometry response array
Definition GCOMDris.hpp:155
void vetorate_load(const GFilename &filename)
Load working arrays for vetorate computation.
void compute_drws_vetorate(const GCOMObservation &obs, const GCOMEventList *events, const GCOMSelection &select, const double &zetamin)
Compute background weighting cubes using veto rates.
void vetorate_update_activ(void)
Update activation rate.
GNdarray m_wrk_vetorate
1D vetorates array
Definition GCOMDris.hpp:156
GCOMDri & at(const int &index)
Return reference to Data Space.
Definition GCOMDris.cpp:204
void free_members(void)
Delete class members.
Definition GCOMDris.cpp:572
GNdarray m_wrk_rate
2D rate array
Definition GCOMDris.hpp:158
void reserve(const int &num)
Reserves space for Data space instances in container.
Definition GCOMDris.hpp:240
void vetorate_fit(void)
Fit working arrays for vetorate computation.
COMPTEL event atom class.
COMPTEL event list class.
virtual int size(void) const
Return number of events in list.
COMPTEL Housekeeping Data container class.
Definition GCOMHkd.hpp:49
const GTime & time(const int &index) const
Return reference to Housekeeping Data time.
Definition GCOMHkd.cpp:304
const double & value(const int &index) const
Return reference to Housekeeping Data value.
Definition GCOMHkd.cpp:356
int size(void) const
Return number of Housekeeping Data in container.
Definition GCOMHkd.hpp:112
COMPTEL Orbit Aspect Data class.
Definition GCOMOad.hpp:49
const int & tjd(void) const
Return Truncated Julian Days of Orbit Aspect Record.
Definition GCOMOad.hpp:196
const int & tics(void) const
Return tics of Orbit Aspect Record.
Definition GCOMOad.hpp:225
double theta(const GSkyDir &sky) const
Return zenith angle of sky direction in COMPTEL coordinates.
Definition GCOMOad.hpp:460
const float & gcaz(void) const
Return Geocentre azimuth angle.
Definition GCOMOad.hpp:254
const GTime & tstart(void) const
Return start time of superpacket.
Definition GCOMOad.hpp:136
const float & gcel(void) const
Return Geocentre zenith angle.
Definition GCOMOad.hpp:283
const float & georad(void) const
Return apparent radius of Earth.
Definition GCOMOad.hpp:312
const GTime & tstop(void) const
Return stop time of superpacket.
Definition GCOMOad.hpp:167
double phi(const GSkyDir &sky) const
Return azimuth angle of sky direction in COMPTEL coordinates.
Definition GCOMOad.hpp:475
int size(void) const
Return number of Orbit Aspect Data in container.
Definition GCOMOads.hpp:141
Interface class for COMPTEL observations.
const GCOMTim & tim(void) const
Return COMPTEL Good Time Intervals.
const GCOMOads & oads(void) const
Return Orbit Aspect Data.
const GCOMHkds & hkds(void) const
Return Housekeeping Data collection.
COMPTEL selection set class.
bool has_pulsar(void) const
Signals that pulsar selection should be performed.
bool use_event(const GCOMEventAtom &event) const
Check if event should be used.
const GPulsar & pulsar(void) const
Return pulsar.
COMPTEL instrument status class.
COMPTEL Good Time Intervals class.
Definition GCOMTim.hpp:50
void reduce(const GGti &gti)
Reduces Good Time Intervals to intersection with intervals.
Definition GCOMTim.hpp:129
Fast Fourier Transformation class.
Definition GFft.hpp:57
GNdarray backward(void) const
Backward Fast Fourier Transform.
Definition GFft.cpp:401
Filename class.
Definition GFilename.hpp:62
virtual void clear(void)
Clear binary table.
const std::string & extname(void) const
Return extension name.
Definition GFitsHDU.hpp:162
Double precision FITS image class.
Short integer FITS image class.
Abstract FITS image base class.
virtual double pixel(const int &ix) const =0
int naxes(const int &axis) const
Return dimension of an image axis.
FITS table double column.
FITS table long integer column.
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
GFitsImage * image(const int &extno)
Get pointer to image HDU.
Definition GFits.cpp:368
void close(void)
Close FITS file.
Definition GFits.cpp:1342
Good Time Interval class.
Definition GGti.hpp:63
const GTime & tstop(void) const
Returns latest stop time in Good Time Intervals.
Definition GGti.hpp:210
int size(void) const
Return number of Good Time Intervals.
Definition GGti.hpp:157
const GTime & tstart(void) const
Returns earliest start time in Good Time Intervals.
Definition GGti.hpp:197
Information logger interface definition.
Definition GLog.hpp:62
Sparse matrix class interface definition.
N-dimensional array class.
Definition GNdarray.hpp:44
void clear(void)
Clear array.
Definition GNdarray.cpp:527
const std::vector< int > & shape(void) const
Return shape of array.
Definition GNdarray.hpp:308
Node array class.
double interpolate(const double &value, const std::vector< double > &vector) const
Interpolate value.
bool is_empty(void) const
Signals if there are no nodes in node array.
void reserve(const int &num)
Reserves space for nodes in node array.
int size(void) const
Return number of nodes in node array.
void append(const double &node)
Append one node to array.
virtual GEvents * events(void)
Return events.
Optimizer function abstract base class.
Levenberg Marquardt optimizer class.
void eps(const double &eps)
Set requested absolute convergence precision.
std::string status_string(void) const
Set fit status string.
virtual int iter(void) const
Return number of iterations.
virtual void errors(GOptimizerFunction &fct, GOptimizerPars &pars)
Compute parameter uncertainties.
virtual void optimize(GOptimizerFunction &fct, GOptimizerPars &pars)
Optimize function parameters.
void max_iter(const int &max_iter)
Set maximum number of iterations.
virtual double value(void) const
Return function value.
Optimizer parameter class.
void range(const double &min, const double &max)
Set minimum and maximum parameter boundaries.
const double & factor_gradient(void) const
Return parameter factor gradient.
Optimizer parameter container class.
GOptimizerPar * append(const GOptimizerPar &par)
Append parameter to container.
GGti validity(void) const
Return validity intervals of pulsar ephemerides.
Definition GPulsar.cpp:286
Sky direction class.
Definition GSkyDir.hpp:62
void radec_deg(const double &ra, const double &dec)
Set equatorial sky direction (degrees)
Definition GSkyDir.cpp:245
double dist_deg(const GSkyDir &dir) const
Compute angular distance between sky directions in degrees.
Definition GSkyDir.hpp:288
double solidangle(const int &index) const
Returns solid angle of pixel.
Definition GSkyMap.cpp:1858
GSkyDir inx2dir(const int &index) const
Returns sky direction of pixel.
Definition GSkyMap.cpp:1331
const int & npix(void) const
Returns number of pixels.
Definition GSkyMap.hpp:383
const double & secs(void) const
Return time in seconds in native reference (TT)
Definition GTime.hpp:156
Vector class.
Definition GVector.hpp:46
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1162
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508