Commit 57ce6beb85279b6b95735246f15e4b5edc910fed

Authored by Jürgen Knödlseder
2 parents 59d3205f 1898bd89

Merge branch '3436-off-runs' into integration

cscripts/Makefile.am
... ... @@ -182,6 +182,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \
182 182 $(top_srcdir)/test/data/crab_offaxis1.fits.gz \
183 183 $(top_srcdir)/test/data/crab_offaxis2.fits.gz \
184 184 $(top_srcdir)/test/data/crab_exclusion.fits.gz \
  185 + $(top_srcdir)/test/data/background_events.fits.gz \
185 186 $(top_srcdir)/test/data/crab_src_reg.reg \
186 187 $(top_srcdir)/test/data/crab_bkg_reg.reg \
187 188 $(top_srcdir)/test/data/tsmap_0.fits \
... ...
cscripts/csphagen.par
... ... @@ -17,6 +17,7 @@
17 17 # Input and output parameters
18 18 #============================
19 19 inobs, f, a, obs.xml,,, "Input event list or observation definition XML file"
  20 +inobsoff, f, a, obs_off.xml,,, "Input Off event list or observation definition XML file"
20 21 inmodel, f, a, NONE,,, "Input model definition XML file (if NONE, use point source)"
21 22 srcname, s, a, Crab,,, "Source name"
22 23 caldb, s, a, prod2,,, "Calibration database"
... ... @@ -46,10 +47,10 @@ width, r, a, 0.2,0,180, "Width of source region rectangle (deg)"
46 47 height, r, a, 0.2,0,180, "Height of source region rectangle (deg)"
47 48 posang, r, a, 0.0,-360,360, "Position angle of source region rectangle (deg)"
48 49 srcregfile, f, a, on.reg,,, "Source region file (ds9 or FITS WCS)"
49   -bkgmethod, s, a, REFLECTED,REFLECTED|CUSTOM,, "Method for background estimation"
  50 +bkgmethod, s, a, REFLECTED,REFLECTED|CUSTOM|OFF,, "Method for background estimation"
50 51 bkgregfile, f, a, bkg.reg,,, "Background regions file (ds9 or FITS WCS)"
51   -bkgregmin, i, h, 2,1,1000, "Minimum number of background regions"
52   -bkgregskip, i, h, 1,0,10, "Number of background regions to skip next to On region"
  52 +bkgregmin, i, h, 2,1,1000, "Minimum number of background regions for REFLECTED method"
  53 +bkgregskip, i, h, 1,0,10, "Number of background regions to skip next to On region for REFLECTED method"
53 54 use_model_bkg, b, h, yes,,, "Use background model?"
54 55 maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation"
55 56 stack, b, a, no,,, "Stack multiple observations into single PHA, ARF and RMF files?"
... ...
cscripts/csphagen.py
... ... @@ -3,7 +3,7 @@
3 3 # Computes the PHA spectra for source/background and ARF/RMF files using the
4 4 # reflected region method
5 5 #
6   -# Copyright (C) 2017-2020 Luigi Tibaldo
  6 +# Copyright (C) 2017-2021 Luigi Tibaldo
7 7 #
8 8 # This program is free software: you can redistribute it and/or modify
9 9 # it under the terms of the GNU General Public License as published by
... ... @@ -43,6 +43,7 @@ class csphagen(ctools.csobservation):
43 43 self._init_csobservation(self.__class__.__name__, ctools.__version__, argv)
44 44  
45 45 # Initialise other variables
  46 + self._obs_off = gammalib.GObservations()
46 47 self._ebounds = gammalib.GEbounds()
47 48 self._etruebounds = gammalib.GEbounds()
48 49 self._src_dir = gammalib.GSkyDir()
... ... @@ -74,6 +75,7 @@ class csphagen(ctools.csobservation):
74 75 """
75 76 # Set pickled dictionary
76 77 state = {'base' : ctools.csobservation.__getstate__(self),
  78 + 'obs_off' : self._obs_off,
77 79 'ebounds' : self._ebounds,
78 80 'etruebounds' : self._etruebounds,
79 81 'src_dir' : self._src_dir,
... ... @@ -103,6 +105,7 @@ class csphagen(ctools.csobservation):
103 105 Pickled instance
104 106 """
105 107 ctools.csobservation.__setstate__(self, state['base'])
  108 + self._obs_off = state['obs_off']
106 109 self._ebounds = state['ebounds']
107 110 self._etruebounds = state['etruebounds']
108 111 self._src_dir = state['src_dir']
... ... @@ -205,21 +208,17 @@ class csphagen(ctools.csobservation):
205 208 # Return region container
206 209 return regs
207 210  
208   - def _get_parameters_bkgmethod_reflected(self):
  211 + def _get_source_parameters(self):
209 212 """
210   - Get parameters for REFLECTED background method
  213 + Get parameters to define source/On region
211 214 """
  215 +
212 216 # Get source shape
213 217 self._srcshape = self['srcshape'].string()
214 218  
215 219 # Query source direction
216 220 self._query_src_direction()
217 221  
218   - # Query minimum number of background regions and number of
219   - # background regions to skip next to On region
220   - self['bkgregmin'].integer()
221   - self['bkgregskip'].integer()
222   -
223 222 # If source shape is a circle the append GSkyRegionCircle
224 223 if self._srcshape == 'CIRCLE':
225 224  
... ... @@ -240,8 +239,21 @@ class csphagen(ctools.csobservation):
240 239 self._reg_height,
241 240 self._reg_posang))
242 241  
243   - # Query usage of background model
244   - self['use_model_bkg'].boolean()
  242 + # Return
  243 + return
  244 +
  245 + def _get_parameters_bkgmethod_reflected(self):
  246 + """
  247 + Get parameters for REFLECTED background method
  248 + """
  249 +
  250 + # Query parameters for source/On region definition
  251 + self._get_source_parameters()
  252 +
  253 + # Query minimum number of background regions and
  254 + # number of background regions to skip next to On region
  255 + self['bkgregmin'].integer()
  256 + self['bkgregskip'].integer()
245 257  
246 258 # Return
247 259 return
... ... @@ -284,6 +296,76 @@ class csphagen(ctools.csobservation):
284 296 # Return
285 297 return
286 298  
  299 + def _get_parameters_bkgmethod_off(self):
  300 + """
  301 + Get parameters for OFF background method
  302 +
  303 + Raises
  304 + ------
  305 + RuntimeError
  306 + On and Off observations must have same size
  307 + RuntimeError
  308 + Off observations must be event lists
  309 + """
  310 +
  311 + # Set up Off observations. If there are no Off observations in the
  312 + # container then load them via user parameters
  313 + if self.obs_off().is_empty():
  314 +
  315 + # Get Off observation file name
  316 + filename = self['inobsoff'].filename()
  317 +
  318 + # If Off observation is a FITS file then load observation and
  319 + # append it to the Off observation container
  320 + if gammalib.GFilename(filename).is_fits():
  321 + self._obs_off.append(gammalib.GCTAObservation(filename))
  322 +
  323 + # ... otherwise load XML file into Off observation container
  324 + else:
  325 + self._obs_off.load(filename)
  326 +
  327 + # Check that size of On and Off observations are the same, otherwise
  328 + # raise error
  329 + if self.obs().size() != self.obs_off().size():
  330 + raise RuntimeError('On and Off observations must have the same size')
  331 +
  332 + # Loop through observations
  333 + for obs in self.obs_off():
  334 +
  335 + # Check that observation is event list, otherwise throw error
  336 + if obs.eventtype() != "EventList":
  337 + raise RuntimeError('Off observations must be event lists')
  338 +
  339 + # Check that they have response, otherwise assign based on user parameter
  340 + if obs.has_response() == False:
  341 +
  342 + # Get database and IRF
  343 + database = self["caldb"].string()
  344 + irf = self["irf"].string()
  345 +
  346 + # Create an XML element for response
  347 + parameter = "parameter name=\"Calibration\"" +\
  348 + " database=\"" + database + "\"" +\
  349 + " response=\"" + irf + "\""
  350 + xml = gammalib.GXmlElement()
  351 + xml.append(parameter)
  352 +
  353 + # Create CTA response
  354 + response = gammalib.GCTAResponseIrf(xml)
  355 +
  356 + # Attach response to observation
  357 + obs.response(response)
  358 +
  359 + # Add models from Off observations to model container
  360 + for model in self.obs_off().models():
  361 + self._models.append(model)
  362 +
  363 + # Query parameters for source/On region definition
  364 + self._get_source_parameters()
  365 +
  366 + # Return
  367 + return
  368 +
287 369 def _get_parameters_bkgmethod(self):
288 370 """
289 371 Get background method parameters
... ... @@ -296,9 +378,12 @@ class csphagen(ctools.csobservation):
296 378 self._get_parameters_bkgmethod_reflected()
297 379 elif bkgmethod == 'CUSTOM':
298 380 self._get_parameters_bkgmethod_custom()
  381 + elif bkgmethod == 'OFF':
  382 + self._get_parameters_bkgmethod_off()
299 383  
300 384 # Query parameters that are needed for all background methods
301 385 self['maxoffset'].real()
  386 + self['use_model_bkg'].boolean()
302 387  
303 388 # Return
304 389 return
... ... @@ -351,9 +436,13 @@ class csphagen(ctools.csobservation):
351 436 else:
352 437 self._has_exclusion = False
353 438  
354   - # Query remaining parameters
355   - self['use_model_bkg'].boolean()
356   - self['stack'].boolean()
  439 + # Get background method parameters (have to come after setting up of
  440 + # observations and models)
  441 + self._get_parameters_bkgmethod()
  442 +
  443 + # If there are multiple observations query whether to stack them
  444 + if self.obs().size() > 1:
  445 + self['stack'].boolean()
357 446  
358 447 # Query ahead output parameters
359 448 if (self._read_ahead()):
... ... @@ -361,15 +450,6 @@ class csphagen(ctools.csobservation):
361 450 self['outmodel'].filename()
362 451 self['prefix'].string()
363 452  
364   - # If there are no observations in container then get them from the
365   - # parameter file
366   - if self.obs().is_empty():
367   - self.obs(self._get_observations(False))
368   -
369   - # Get background method parameters (have to come after setting up of
370   - # observations)
371   - self._get_parameters_bkgmethod()
372   -
373 453 # Write input parameters into logger
374 454 self._log_parameters(gammalib.TERSE)
375 455  
... ... @@ -459,14 +539,6 @@ class csphagen(ctools.csobservation):
459 539 if self._srcshape == 'CIRCLE':
460 540 msg += ' (circle rad=%.3f).' % (self._rad)
461 541 self._log_string(gammalib.NORMAL, msg)
462   -
463   - # Skip observation if it is pointed too far from the source
464   - elif offset >= self['maxoffset'].real():
465   - msg = ' Skip because observation is pointed at %.3f deg >= '\
466   - '"maxoffset=%.3f" from source.' \
467   - % (offset, self['maxoffset'].real())
468   - self._log_string(gammalib.NORMAL, msg)
469   -
470 542 # ... otherwise
471 543 else:
472 544 posang = pnt_dir.posang_deg(self._src_dir)
... ... @@ -474,7 +546,6 @@ class csphagen(ctools.csobservation):
474 546  
475 547 # Determine number of background regions to skip
476 548 N_skip = self['bkgregskip'].integer()
477   - N_start = 1 + N_skip
478 549 N_lim = 1 + 2 * N_skip
479 550  
480 551 # Compute the angular separation of reflected regions wrt
... ... @@ -532,13 +603,82 @@ class csphagen(ctools.csobservation):
532 603 regions.append(region)
533 604 dphi += alpha
534 605  
535   - # Log number of reflected regions
536   - msg = ' Use %d reflected regions.' % (regions.size())
537   - self._log_string(gammalib.NORMAL, msg)
  606 + # Check again that we have enough background regions
  607 + # now that we have checked for overlap with exclusion region
  608 + if regions.size() >= self['bkgregmin'].integer():
  609 + # Log number of reflected regions
  610 + msg = ' Use %d reflected regions.' % (regions.size())
  611 + self._log_string(gammalib.NORMAL, msg)
  612 + # Otherwise log observation skipped and return empty region container
  613 + else:
  614 + msg = ' Skip because the number %d of regions' \
  615 + 'for background estimation not overlapping ' \
  616 + 'with the exclusion region is smaller than ' \
  617 + '"bkgregmin"=%d.' % (bkg_reg.size(), self['bkgregmin'].integer())
  618 + self._log_string(gammalib.NORMAL, msg)
  619 + regions = gammalib.GSkyRegions()
538 620  
539 621 # Return reflected regions
540 622 return regions
541 623  
  624 + def _instrument_regions(self, obs, obs_off):
  625 + """
  626 + Compute background regions for Off observation
  627 +
  628 + Calculate background region in Off observation that corresponds to the
  629 + source region in the On observation in instrument coordinates
  630 +
  631 + Parameters
  632 + ----------
  633 + obs : `~gammalib.GCTAObservation()`
  634 + On CTA observation
  635 + obs_off : `~gammalib.GCTAObservation()`
  636 + Off CTA observation
  637 +
  638 + Returns
  639 + -------
  640 + regions : `~gammalib.GSkyRegions`
  641 + Container with background region
  642 + """
  643 + # Initialise region container
  644 + regions = gammalib.GSkyRegions()
  645 +
  646 + # Convert source position in On observation to instrument coordinates
  647 + instdir = obs.pointing().instdir(self._src_dir)
  648 +
  649 + # Convert instrument position to sky direction for Off observation
  650 + off_dir = obs_off.pointing().skydir(instdir)
  651 +
  652 + # Build region according to shape specified by user
  653 + # If circle
  654 + if self._srcshape == 'CIRCLE':
  655 + region = gammalib.GSkyRegionCircle(off_dir, self._rad)
  656 +
  657 + # ... otherwise if rectangle
  658 + elif self._srcshape == 'RECT':
  659 + # Instrument coordinates take sky direction as reference
  660 + # so no need to change the position angle
  661 + region = gammalib.GSkyRegionRectangle(off_dir,
  662 + self._reg_width,
  663 + self._reg_height,
  664 + self._reg_posang)
  665 +
  666 + # Check if background region overlaps with exclusion region
  667 + is_valid = True
  668 + if self._has_exclusion:
  669 + if self._excl_reg.overlaps(region):
  670 + # Signal region overlap
  671 + msg = ' Background region overlaps with exclusion region.'
  672 + self._log_string(gammalib.EXPLICIT, msg)
  673 + is_valid = False
  674 +
  675 + # If region is valid then append it to container
  676 + if is_valid:
  677 + regions.append(region)
  678 +
  679 + # Return
  680 + return regions
  681 +
542 682 def _set_models(self, results):
543 683 """
544 684 Set models for On/Off fitting
... ... @@ -587,7 +727,7 @@ class csphagen(ctools.csobservation):
587 727 # observations
588 728 for result in results:
589 729 if model.is_valid(result['instrument'], result['id']):
590   - if result['bkg_reg'].size() >= self['bkgregmin'].integer():
  730 + if result['bkg_reg'].size() > 0:
591 731 use_model = True
592 732 break
593 733  
... ... @@ -699,7 +839,7 @@ class csphagen(ctools.csobservation):
699 839 # Return energy boundaries
700 840 return ebounds
701 841  
702   - def _set_background_regions(self, obs):
  842 + def _set_background_regions(self, obs, obs_off=None):
703 843 """
704 844 Set background regions for an observation
705 845  
... ... @@ -716,7 +856,7 @@ class csphagen(ctools.csobservation):
716 856 # Initialise empty background regions for this observation
717 857 bkg_reg = gammalib.GSkyRegions()
718 858  
719   - # If reflected background is requested then created reflected
  859 + # If reflected background is requested then create reflected
720 860 # background regions
721 861 if self['bkgmethod'].string() == 'REFLECTED':
722 862 bkg_reg = self._reflected_regions(obs)
... ... @@ -728,6 +868,11 @@ class csphagen(ctools.csobservation):
728 868 elif self['bkgmethod'].string() == 'CUSTOM':
729 869 bkg_reg = obs.off_regions().copy()
730 870  
  871 + # ... otherwise if dedicated Off runs are use then
  872 + # use background region that correspond to the same instrument coordinates
  873 + if self['bkgmethod'].string() == 'OFF':
  874 + bkg_reg = self._instrument_regions(obs,obs_off)
  875 +
731 876 # Return background regions
732 877 return bkg_reg
733 878  
... ... @@ -750,6 +895,13 @@ class csphagen(ctools.csobservation):
750 895 bkg_reg = None
751 896 obs = self.obs()[i]
752 897  
  898 + # Retrieve dedicated Off observation if it exists
  899 + if not self.obs_off().is_empty():
  900 + obs_off = self.obs_off()[i]
  901 + # Otherwise use the same as On
  902 + else:
  903 + obs_off = self.obs()[i]
  904 +
753 905 # Log header
754 906 self._log_header3(gammalib.NORMAL,'%s observation "%s"' % \
755 907 (obs.instrument(), obs.id()))
... ... @@ -768,32 +920,43 @@ class csphagen(ctools.csobservation):
768 920 msg = ' Background model not used, assume constant backround rate.'
769 921 self._log_string(gammalib.NORMAL, msg)
770 922  
771   - # Set background regions for this observation
772   - bkg_reg = self._set_background_regions(obs)
  923 + # Get offset angle of source
  924 + pnt_dir = obs.pointing().dir()
  925 + offset = pnt_dir.dist_deg(self._src_dir)
773 926  
774   - # If there are background regions then create On/Off observation
775   - # and append it to the output container
776   - if bkg_reg.size() >= self['bkgregmin'].integer():
  927 + # Skip observation if it is pointed too far from the source
  928 + if offset >= self['maxoffset'].real():
  929 + msg = ' Skip because observation is pointed at %.3f deg >= ' \
  930 + '"maxoffset=%.3f" from source.' \
  931 + % (offset, self['maxoffset'].real())
  932 + self._log_string(gammalib.NORMAL, msg)
  933 + # ... otherwise continue to process
  934 + else:
777 935  
778   - # Create On/Off observation
779   - onoff = gammalib.GCTAOnOffObservation(obs,
780   - self._models,
781   - self._srcname,
782   - self._etruebounds,
783   - self._ebounds,
784   - self._src_reg,
785   - bkg_reg,
786   - use_model_bkg)
  936 + # Set background regions for this observation
  937 + bkg_reg = self._set_background_regions(obs,obs_off)
787 938  
788   - # Set On/Off observation ID
789   - onoff.id(obs.id())
  939 + # If there are any background regions then create On/Off observation
  940 + # and append it to the output container
  941 + if bkg_reg.size() >= 0:
790 942  
791   - # Otherwise log observation skipped
792   - else:
793   - msg = ' Skip because the number %d of regions ' \
794   - 'for background estimation is smaller than ' \
795   - '"bkgregmin"=%d.' % (bkg_reg.size(), self['bkgregmin'].integer())
796   - self._log_string(gammalib.NORMAL, msg)
  943 + # Create On/Off observation
  944 + onoff = gammalib.GCTAOnOffObservation(obs, obs_off,
  945 + self._models,
  946 + self._srcname,
  947 + self._etruebounds,
  948 + self._ebounds,
  949 + self._src_reg,
  950 + bkg_reg,
  951 + use_model_bkg)
  952 +
  953 + # Set On/Off observation ID
  954 + onoff.id(obs.id())
  955 +
  956 + # Otherwise log observation skipped
  957 + else:
  958 + msg = ' Skip because no valid Off regions could be determined'
  959 + self._log_string(gammalib.NORMAL, msg)
797 960  
798 961 # Construct dictionary with results
799 962 result = {'onoff' : onoff,
... ... @@ -854,6 +1017,8 @@ class csphagen(ctools.csobservation):
854 1017  
855 1018 # Write observation into logger
856 1019 self._log_observations(gammalib.NORMAL, self.obs(), 'Observation')
  1020 + if not self.obs_off().is_empty():
  1021 + self._log_observations(gammalib.NORMAL, self._obs_off, 'Off Observation')
857 1022  
858 1023 # Set true energy bins
859 1024 self._etruebounds = self._etrue_ebounds()
... ... @@ -1032,6 +1197,27 @@ class csphagen(ctools.csobservation):
1032 1197 # Return
1033 1198 return self._excl_reg
1034 1199  
  1200 + def obs_off(self, obs=None):
  1201 + """
  1202 + Return and optionally set the Off observations
  1203 +
  1204 + Parameters
  1205 + ----------
  1206 + obs : `~gammalib.GCTAObservations`
  1207 + Off observations container
  1208 +
  1209 + Returns
  1210 + -------
  1211 + observation container : `~gammalib.GCTAObservations`
  1212 + Off observations container
  1213 + """
  1214 + # If an observation container is provided then set the Off observations ...
  1215 + if obs is not None:
  1216 + self._obs_off = obs
  1217 +
  1218 + # Return
  1219 + return self._obs_off
  1220 +
1035 1221  
1036 1222 # ======================== #
1037 1223 # Main routine entry point #
... ...
doc/source/users/reference_manual/csphagen.rst
... ... @@ -17,17 +17,16 @@ the OGIP format normally used in X-ray astronomy (PHA, ARF, RMF),
17 17 If the ``bkgmethod`` parameter is ``REFLECTED``, the script will use an On region
18 18 shape defined by the ``srcshape`` parameter and generate reflected regions by
19 19 placing regions of the same shape that are reflected with respect to the
20   -pointing direction. So far circular and rectangular regions are supported.
21   -Circular regions are defined by a center that is specified by the ``ra`` and
22   -``dec`` parameters if ``coordsys=CEL`` or the ``glon`` and ``glat`` parameters if
  20 +pointing direction within the same observation. So far circular and
  21 +rectangular regions are supported. Circular regions are defined by a
  22 +center that is specified by the ``ra`` and ``dec`` parameters if
  23 +``coordsys=CEL`` or the ``glon`` and ``glat`` parameters if
23 24 ``coordsys=GAL``, and a radius that is specified by the ``rad`` parameter.
24 25 Rectangular regions are defined by a center, again specified by either the
25 26 ``ra`` and ``dec`` or the ``glon`` and ``glat`` parameters, a ``width`` parameter,
26 27 a ``height`` parameter and a ``posang`` parameter that specified the position
27 28 angle measured counter clockwise from celestial North. Note that the ``height``
28   -axis is pointing in the direction of the position angle. Reflected regions are
29   -only generated for offset angles with respect to the pointing direction that are
30   -smaller than the value specified by the ``maxoffset`` parameter. If less than
  29 +axis is pointing in the direction of the position angle. If less than
31 30 ``bkgregmin`` reflected regions can be found because the offset angle between
32 31 source and pointing direction is too small, the observation will be skipped.
33 32 Regions to be excluded for background determination can be specified using a
... ... @@ -44,7 +43,7 @@ respectively. Both parameters accept either a ds9 region file or a sky map where
44 43 all non-zero pixel values define a region. In case that an observation definition
45 44 XML file is specified as input using the ``inobs``, background region files can
46 45 be specified separately for each observation using the ``OffRegions`` parameter
47   -in the format
  46 +in the format.
48 47  
49 48 .. code-block:: xml
50 49  
... ... @@ -53,6 +52,16 @@ in the format
53 52 ...
54 53 </observation>
55 54  
  55 +Finally, if ``bkgmethod=OFF`` is specified, the background regions
  56 +will be taken from independent Off observations given by the
  57 +``inobsoff`` parameter. The On region can be defined in the same way
  58 +as for ``bkgmethod=REFLECTED``, and the Off regions are the
  59 +corresponding regions in instrument coordinates from the Off observations.
  60 +
  61 +For all background estimation methods only observations with a
  62 +pointing direction at an offset smaller than ``maxoffset`` from the
  63 +source are used.
  64 +
56 65 :ref:`csphagen` supports multiprocessing. By default each event list will be treated
57 66 in parallel over as many processes as the number of CPUs available on your
58 67 machine. The maximum number of parallel processes can be set by the user through the
... ... @@ -78,6 +87,11 @@ General parameters
78 87 ``inobs [file]``
79 88 Input event list or observation definition XML file.
80 89  
  90 +``inobsoff [file]``
  91 + Input event list or observation definition XML file for dedicated
  92 + Off observations. The number of observations must be the same as
  93 + for ``inobs``.
  94 +
81 95 ``inmodel [file]``
82 96 Input model XML file (if ``NONE`` a point source at the centre of the
83 97 source region is used).
... ... @@ -172,7 +186,7 @@ General parameters
172 186 ``srcregfile [file]``
173 187 Source region file (ds9 or FITS WCS map).
174 188  
175   -``bkgmethod <REFLECTED|CUSTOM> [string]``
  189 +``bkgmethod <REFLECTED|CUSTOM|OFF> [string]``
176 190 Method for background estimation:
177 191  
178 192 - ``REFLECTED``: background is evaluated in regions with the same shape as
... ... @@ -184,18 +198,25 @@ General parameters
184 198 observations specified in the observation definition XML file the name of
185 199 the region file will be extracted from the ``OffRegions`` parameter that
186 200 needs to be specified for each observation in the observation definition
187   - XML file. Off region files can be either ds9 region files or FITS WCS maps.
  201 + XML file. Off region files can be either ds9 region files or
  202 + FITS WCS maps.
  203 +
  204 + - ``OFF``: background is evaluated from dedicated Off
  205 + observations in regions that correspond to the source region
  206 + in the On observations in instrument coordinates.
188 207  
189 208 ``bkgregfile [file]``
190 209 Background regions file (ds9 or FITS WCS map).
191 210  
192 211 ``(bkgregmin = 2) [integer]``
193   - Minimum number of background regions that are required for an observation.
  212 + Minimum number of background regions that are required for an
  213 + observation when the ``REFLECTED`` background estimation is used.
194 214 If this number of background regions is not available the observation is
195 215 skipped.
196 216  
197 217 ``(bkgregskip = 1) [integer]``
198   - Number of background regions that should be skipped next to the On regions.
  218 + Number of background regions that should be skipped next to the On
  219 + region when the ``REFLECTED`` background estimation is used.
199 220 Typically, one region is skipped so that the Off regions are taken sufficiently
200 221 distant from the On region, but in some cases it may be useful to keep the
201 222 background regions next to the On region.
... ...
test/data/background_events.fits.gz 0 → 100644
No preview for this file type
test/test_csphagen.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # This scripts performs unit tests for the csphagen script.
4 4 #
5   -# Copyright (C) 2017-2020 Luigi Tibaldo
  5 +# Copyright (C) 2017-2021 Luigi Tibaldo
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -46,6 +46,7 @@ class Test(test):
46 46 self._myevents1 = self._datadir + '/crab_offaxis1.fits'
47 47 self._myevents2 = self._datadir + '/crab_offaxis2.fits'
48 48 self._exclusion = self._datadir + '/crab_exclusion.fits'
  49 + self._offevents = self._datadir + '/background_events.fits'
49 50 self._nreg_with_excl = 5
50 51 self._nreg_wo_excl = 8
51 52 self._nreg_bkg_reg = 5
... ... @@ -414,6 +415,117 @@ class Test(test):
414 415 self._check_output('csphagen_py8', self._nbins, 9)
415 416 self._check_outobs('csphagen_py8_obs.xml', 1)
416 417  
  418 + # Setup csphagen for test with dedicated Off observations
  419 + phagen = cscripts.csphagen()
  420 + phagen['inobs'] = self._myevents1
  421 + phagen['inobsoff'] = self._offevents
  422 + phagen['inmodel'] = 'NONE'
  423 + phagen['caldb'] = self._caldb
  424 + phagen['irf'] = self._irf
  425 + phagen['srcshape'] = 'CIRCLE'
  426 + phagen['coordsys'] = 'CEL'
  427 + phagen['ra'] = 83.633
  428 + phagen['dec'] = 22.0145
  429 + phagen['rad'] = 0.2
  430 + phagen['ebinalg'] = 'LOG'
  431 + phagen['emin'] = 0.1
  432 + phagen['emax'] = 100.0
  433 + phagen['enumbins'] = self._nbins
  434 + phagen['bkgmethod'] = 'OFF'
  435 + phagen['etruemin'] = 0.05
  436 + phagen['etruemax'] = 150.0
  437 + phagen['etruebins'] = 5
  438 + phagen['stack'] = False
  439 + phagen['outobs'] = 'csphagen_py9_obs.xml'
  440 + phagen['outmodel'] = 'csphagen_py9_model.xml'
  441 + phagen['prefix'] = 'csphagen_py9'
  442 + phagen['logfile'] = 'csphagen_py9.log'
  443 + phagen['chatter'] = 2
  444 +
  445 + # Execute script
  446 + phagen.execute()
  447 +
  448 + # Check output
  449 + self._check_output('csphagen_py9', self._nbins, 1)
  450 + self._check_outobs('csphagen_py9_obs.xml', 1)
  451 +
  452 + # Setup csphagen for test with dedicated Off observations and
  453 + # exclusion region
  454 + phagen = cscripts.csphagen()
  455 + phagen['inobs'] = self._myevents1
  456 + phagen['inobsoff'] = self._offevents
  457 + phagen['inmodel'] = 'NONE'
  458 + phagen['caldb'] = self._caldb
  459 + phagen['irf'] = self._irf
  460 + phagen['srcshape'] = 'CIRCLE'
  461 + phagen['coordsys'] = 'CEL'
  462 + phagen['ra'] = 83.633
  463 + phagen['dec'] = 22.0145
  464 + phagen['rad'] = 0.2
  465 + phagen['ebinalg'] = 'LOG'
  466 + phagen['emin'] = 0.1
  467 + phagen['emax'] = 100.0
  468 + phagen['enumbins'] = self._nbins
  469 + phagen['inexclusion'] = self._exclusion
  470 + phagen['bkgmethod'] = 'OFF'
  471 + phagen['etruemin'] = 0.05
  472 + phagen['etruemax'] = 150.0
  473 + phagen['etruebins'] = 5
  474 + phagen['stack'] = False
  475 + phagen['outobs'] = 'csphagen_py10_obs.xml'
  476 + phagen['outmodel'] = 'csphagen_py10_model.xml'
  477 + phagen['prefix'] = 'csphagen_py10'
  478 + phagen['logfile'] = 'csphagen_py10.log'
  479 + phagen['chatter'] = 2
  480 +
  481 + # Execute script
  482 + phagen.execute()
  483 +
  484 + # Check output
  485 + self._check_output('csphagen_py10', self._nbins, 1)
  486 + self._check_outobs('csphagen_py10_obs.xml', 1)
  487 +
  488 + # Test with multiple input observations and dedicated Off runs
  489 + # Create Off observation container
  490 + obs_off = gammalib.GObservations()
  491 + for s, events in enumerate([self._offevents, self._offevents]):
  492 + run = gammalib.GCTAObservation(events)
  493 + run.id(str(s + 1))
  494 + run.response(self._irf, gammalib.GCaldb('cta', self._caldb))
  495 + obs_off.append(run)
  496 +
  497 + # Setup csphagen
  498 + phagen = cscripts.csphagen(obs)
  499 + phagen.obs_off(obs_off)
  500 + phagen['inmodel'] = 'NONE'
  501 + phagen['srcshape'] = 'CIRCLE'
  502 + phagen['coordsys'] = 'CEL'
  503 + phagen['ra'] = 83.633
  504 + phagen['dec'] = 22.0145
  505 + phagen['rad'] = 0.2
  506 + phagen['ebinalg'] = 'LOG'
  507 + phagen['emin'] = 0.1
  508 + phagen['emax'] = 100.0
  509 + phagen['enumbins'] = self._nbins
  510 + phagen['bkgmethod'] = 'OFF'
  511 + phagen['etruemin'] = 0.05
  512 + phagen['etruemax'] = 150.0
  513 + phagen['etruebins'] = 5
  514 + phagen['stack'] = False
  515 + phagen['outobs'] = 'csphagen_py11_obs.xml'
  516 + phagen['outmodel'] = 'csphagen_py11_model.xml'
  517 + phagen['prefix'] = 'csphagen_py11'
  518 + phagen['logfile'] = 'csphagen_py11.log'
  519 + phagen['chatter'] = 2
  520 +
  521 + # Execute script
  522 + phagen.execute()
  523 +
  524 + # Check output
  525 + self._check_output('csphagen_py11_1', self._nbins, 1)
  526 + self._check_output('csphagen_py11_2', self._nbins, 1)
  527 + self._check_outobs('csphagen_py11_obs.xml', 2)
  528 +
417 529 # Return
418 530 return
419 531  
... ...