Commit 6418ebd17599adcb809da65422ad1d9ca35e6ac9

Authored by Jürgen Knödlseder
2 parents a8577c15 797a7544

Merge branch '1264-obsutils-under-ctools' into integration

Conflicts:
	scripts/cspull.py
	scripts/obsutils.py
.gitignore
... ... @@ -44,8 +44,8 @@ doc/man
44 44 src/ctools-setup
45 45 pyext/build/
46 46 pyext/setup.py
47   -pyext/ctools.py
48   -pyext/ctools_wrap.cpp
  47 +pyext/ctools/ctools.py
  48 +pyext/ctools/ctools_wrap.cpp
49 49 src/ctbin/ctbin
50 50 src/ctlike/ctlike
51 51 src/ctmodel/ctmodel
... ...
configure.ac
... ... @@ -630,7 +630,7 @@ else
630 630  
631 631 # Do we have wrappers (ctools_wrap.cpp and ctools.py)?
632 632 has_wrappers="yes"
633   - AC_CHECK_FILES([pyext/ctools_wrap.cpp pyext/ctools.py],,
  633 + AC_CHECK_FILES([pyext/ctools/ctools_wrap.cpp pyext/ctools/ctools.py],,
634 634 [has_wrappers="no"])
635 635 if test "x$has_wrappers" = "xno"; then
636 636 AC_MSG_WARN([Python wrapper missing. Requires swig for wrapper generation.])
... ... @@ -669,7 +669,7 @@ if test "x$enable_python" = "xyes"; then
669 669  
670 670 else
671 671 if test "x$has_python_wrap" = "xno" -a "x$has_swig" = "xno"; then
672   - AC_MSG_WARN([Neither pyext/ctools_wrap.cpp and pyext/ctools.py nor swig has not been found])
  672 + AC_MSG_WARN([Neither pyext/ctools/ctools_wrap.cpp and pyext/ctools/ctools.py nor swig has not been found])
673 673 AC_MSG_WARN([Python binding for ctools cannot be built])
674 674 AC_MSG_WARN([Either use a ctools distribution with ctools_wrap.cpp and ctools.py shipped or install swig on your system])
675 675 AC_MSG_WARN([swig can be obtained from http://www.swig.org/])
... ...
pyext/Makefile.am
... ... @@ -30,7 +30,7 @@
30 30 if WITH_PYTHON
31 31  
32 32 # Set SWIG interface filename
33   -SWIG_INTERFACE = ctools.i
  33 +SWIG_INTERFACE = ctools/ctools.i
34 34  
35 35 # Dependencies. Obtain the list of dependencies by executing ls.
36 36 DEPFILES = $(shell ls *.Pi 2> /dev/null)
... ... @@ -39,7 +39,7 @@ include $(DEPFILES)
39 39 # Define the targets that should be built before anything else.
40 40 # These are honored by 'make all', 'make check' and 'make install'.
41 41 if HAVE_SWIG
42   - BUILT_SOURCES = ctools_wrap.cpp ctools.py
  42 + BUILT_SOURCES = ctools/ctools_wrap.cpp ctools/ctools.py
43 43 endif
44 44  
45 45 # Set files to be removed in clean. We remove only the SWIG interface files
... ... @@ -59,7 +59,7 @@ all-local: build
59 59 # of this specific directory. So we build one instance for checking, the other
60 60 # for installation. If we could get the name built by distutils we could avoid
61 61 # this.
62   -build: ctools_wrap.cpp ctools.py
  62 +build: ctools/ctools_wrap.cpp ctools/ctools.py
63 63 $(PYTHON) setup.py build
64 64  
65 65 install-exec-local:
... ... @@ -70,9 +70,9 @@ clean-local:
70 70  
71 71 # Rule for building Python wrappers using SWIG.
72 72 if HAVE_SWIG
73   -ctools.py: ctools_wrap.cpp
74   -ctools_wrap.cpp: $(SWIG_INTERFACE)
75   - if $(SWIGCOMPILE) -MMD -MF "ctools.Tpi" -o ctools_wrap.cpp $<; \
  73 +ctools/ctools.py: ctools/ctools_wrap.cpp
  74 +ctools/ctools_wrap.cpp: $(SWIG_INTERFACE)
  75 + if $(SWIGCOMPILE) -MMD -MF "ctools.Tpi" -o ctools/ctools_wrap.cpp $<; \
76 76 then mv -f "ctools.Tpi" "ctools.Pi"; else rm -f "ctools.Tpi"; exit 1; fi
77 77 endif # HAVE_SWIG
78 78  
... ...
pyext/ctools/__init__.py 0 → 100644
  1 +from ctools import *
  2 +import obsutils
  3 +
... ...
pyext/ctools.i renamed to pyext/ctools/ctools.i
pyext/ctools/obsutils.py 0 → 100755
  1 +# ==========================================================================
  2 +# This script provides a number of functions that are useful for handling
  3 +# CTA observations.
  4 +#
  5 +# Copyright (C) 2011-2014 Juergen Knoedlseder
  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 +import ctools
  22 +import gammalib
  23 +
  24 +
  25 +# ===================== #
  26 +# Simulate observations #
  27 +# ===================== #
  28 +def sim(obs, log=False, debug=False, edisp=False, seed=0, nbins=0,
  29 + binsz=0.05, npix=200, proj="TAN", coord="GAL"):
  30 + """
  31 + Simulate events for all observations in the container.
  32 +
  33 + Parameters:
  34 + obs - Observation container
  35 + Keywords:
  36 + log - Create log file(s)
  37 + debug - Create console dump?
  38 + edisp - Apply energy dispersion?
  39 + seed - Seed value for simulations (default: 0)
  40 + nbins - Number of energy bins (default: 0=unbinned)
  41 + binsz - Pixel size for binned simulation (deg/pixel)
  42 + npix - Number of pixels in X and Y for binned simulation
  43 + """
  44 + # Allocate ctobssim application and set parameters
  45 + sim = ctools.ctobssim(obs)
  46 + sim["seed"].integer(seed)
  47 +
  48 + # Optionally open the log file
  49 + if log:
  50 + sim.logFileOpen()
  51 +
  52 + # Optionally switch-on debugging model
  53 + if debug:
  54 + sim["debug"].boolean(True)
  55 +
  56 + # Run ctobssim application. This will loop over all observations in the
  57 + # container and simulation the events for each observation. Note that
  58 + # events are not added together, they still apply to each observation
  59 + # separately.
  60 + sim.run()
  61 +
  62 + # Binned option?
  63 + if nbins > 0:
  64 +
  65 + # Determine common energy boundaries for observations
  66 + emin = None
  67 + emax = None
  68 + for run in sim.obs():
  69 + run_emin = run.events().ebounds().emin().TeV()
  70 + run_emax = run.events().ebounds().emax().TeV()
  71 + if emin == None:
  72 + emin = run_emin
  73 + elif run_emin > emin:
  74 + emin = run_emin
  75 + if emax == None:
  76 + emax = run_emax
  77 + elif run_emax > emax:
  78 + emax = run_emax
  79 +
  80 + # Allocate ctbin application and set parameters
  81 + bin = ctools.ctbin(sim.obs())
  82 + bin["ebinalg"].string("LOG")
  83 + bin["emin"].real(emin)
  84 + bin["emax"].real(emax)
  85 + bin["enumbins"].integer(nbins)
  86 + bin["usepnt"].boolean(True) # Use pointing for map centre
  87 + bin["nxpix"].integer(npix)
  88 + bin["nypix"].integer(npix)
  89 + bin["binsz"].real(binsz)
  90 + bin["coordsys"].string(coord)
  91 + bin["proj"].string(proj)
  92 +
  93 + # Optionally open the log file
  94 + if log:
  95 + bin.logFileOpen()
  96 +
  97 + # Optionally switch-on debugging model
  98 + if debug:
  99 + bin["debug"].boolean(True)
  100 +
  101 + # Run ctbin application. This will loop over all observations in
  102 + # the container and bin the events in counts maps
  103 + bin.run()
  104 +
  105 + # Make a deep copy of the observation that will be returned
  106 + # (the ctbin object will go out of scope one the function is
  107 + # left)
  108 + obs = bin.obs().copy()
  109 +
  110 + else:
  111 +
  112 + # Make a deep copy of the observation that will be returned
  113 + # (the ctobssim object will go out of scope one the function is
  114 + # left)
  115 + obs = sim.obs().copy()
  116 +
  117 + # Delete the simulation
  118 + del sim
  119 +
  120 + # Return observation container
  121 + return obs
  122 +
  123 +
  124 +# ================ #
  125 +# Fit observations #
  126 +# ================ #
  127 +def fit(obs, log=False, debug=False, edisp=False):
  128 + """
  129 + Perform maximum likelihood fitting of observations in the container.
  130 +
  131 + Parameters:
  132 + obs - Observation container
  133 + Keywords:
  134 + log - Create log file(s)
  135 + debug - Create screen dump
  136 + edisp - Apply energy dispersion?
  137 + """
  138 + # Allocate ctlike application
  139 + like = ctools.ctlike(obs)
  140 +
  141 + # Optionally open the log file
  142 + if log:
  143 + like.logFileOpen()
  144 +
  145 + # Optionally switch-on debugging model
  146 + if debug:
  147 + like["debug"].boolean(True)
  148 +
  149 + # Optionally apply energy dispersion
  150 + like["edisp"].boolean(edisp)
  151 +
  152 + # Run ctlike application.
  153 + like.run()
  154 +
  155 + # Return observations
  156 + return like
  157 +
  158 +
  159 +# ================= #
  160 +# Create counts map #
  161 +# ================= #
  162 +def cntmap(obs, proj="TAN", coord="GAL", xval=0.0, yval=0.0, \
  163 + binsz=0.05, nxpix=200, nypix=200, \
  164 + outname="cntmap.fits"):
  165 + """
  166 + Creates a counts map by combining the events of all observations.
  167 + The counts map will be a summed map over all energies.
  168 +
  169 + Parameters:
  170 + obs - Observation container
  171 + Keywords:
  172 + proj - Projection type (e.g. TAN, CAR, STG, ...) (default: TAN)
  173 + coord - Coordinate type (GAL, CEL) (default: GAL)
  174 + xval - Reference longitude value [deg] (default: 0.0)
  175 + yval - Reference latitude value [deg] (default: 0.0)
  176 + binsz - Pixel size [deg/pixel] (default: 0.05)
  177 + nxpix - Number of pixels in X direction (default: 200)
  178 + nypix - Number of pixels in Y direction (default: 200)
  179 + outname - Counts map FITS filename (default: cntmap.fits)
  180 + """
  181 + # Allocate counts map
  182 + map = gammalib.GSkymap(proj, coord, xval, yval, -binsz, binsz, nxpix, nypix, 1)
  183 +
  184 + # Set maximum pixel number
  185 + maxpixel = nxpix * nypix
  186 +
  187 + # Fill all observations
  188 + for run in obs:
  189 +
  190 + # Loop over all events
  191 + for event in run.events():
  192 +
  193 + # Determine sky pixel
  194 + skydir = gammalib.GCTAInstDir(event.dir()).dir()
  195 + pixel = map.dir2inx(skydir)
  196 +
  197 + # Set pixel
  198 + if pixel < maxpixel:
  199 + map[pixel] += event.counts()
  200 +
  201 + # Save sky map. The clobber flag is set to True, so any existing FITS
  202 + # file will be overwritten.
  203 + map.save(outname, True)
  204 +
  205 + # Return counts map
  206 + return map
  207 +
  208 +
  209 +# ================ #
  210 +# Create model map #
  211 +# ================ #
  212 +def modmap(obs, eref=0.1, proj="TAN", coord="GAL", xval=0.0, yval=0.0, \
  213 + binsz=0.05, nxpix=200, nypix=200, \
  214 + outname="modmap.fits"):
  215 + """
  216 + Make model map for a given reference energy by combining all observations.
  217 + The model map will be evaluated for a given reference energy 'eref' and will
  218 + be given in units of [counts/(sr MeV s)].
  219 +
  220 + Parameters:
  221 + obs - Observation container
  222 + Keywords:
  223 + eref - Reference energy for which model is created [TeV] (default: 0.1)
  224 + proj - Projection type (e.g. TAN, CAR, STG, ...) (default: TAN)
  225 + coord - Coordinate type (GAL, CEL) (default: GAL)
  226 + xval - Reference longitude value [deg] (default: 0.0)
  227 + yval - Reference latitude value [deg] (default: 0.0)
  228 + binsz - Pixel size [deg/pixel] (default: 0.05)
  229 + nxpix - Number of pixels in X direction (default: 200)
  230 + nypix - Number of pixels in Y direction (default: 200)
  231 + outname - Model map FITS filename (default: modmap.fits)
  232 + """
  233 + # Allocate model map
  234 + map = gammalib.GSkymap(proj, coord, xval, yval, -binsz, binsz, nxpix, nypix, 1)
  235 +
  236 + # Set reference energy, time and direction. The time is not initialised and is
  237 + # in fact not used (as the IRF is assumed to be time independent for now).
  238 + # The sky direction is set later using the pixel values.
  239 + energy = gammalib.GEnergy()
  240 + time = gammalib.GTime()
  241 + instdir = gammalib.GCTAInstDir()
  242 + energy.TeV(eref)
  243 +
  244 + # Loop over all map pixels
  245 + for pixel in range(map.npix()):
  246 +
  247 + # Get sky direction
  248 + skydir = map.inx2dir(pixel)
  249 + instdir.dir(skydir)
  250 +
  251 + # Create event atom for map pixel
  252 + atom = gammalib.GCTAEventAtom()
  253 + atom.dir(instdir)
  254 + atom.energy(energy)
  255 + atom.time(time)
  256 +
  257 + # Initialise model value
  258 + value = 0.0
  259 +
  260 + # Loop over all observations
  261 + for run in obs:
  262 + value += obs.models().eval(atom, run)
  263 +
  264 + # Set map value
  265 + map[pixel] = value
  266 +
  267 + # Save sky map
  268 + map.save(outname, True)
  269 +
  270 + # Return model map
  271 + return map
  272 +
  273 +
  274 +# ===================================== #
  275 +# Set one CTA observation (old version) #
  276 +# ===================================== #
  277 +def set(pntdir, tstart=0.0, duration=1800.0, deadc=0.95, \
  278 + emin=0.1, emax=100.0, rad=5.0, \
  279 + irf="cta_dummy_irf", caldb="dummy"):
  280 + """
  281 + Obsolete function, use set_obs instead.
  282 + """
  283 + # Print warning
  284 + print("Warning: obsutils.set is obsolete, use obsutils.set_obs instead.")
  285 +
  286 + # Call new function
  287 + obs_cta = set_obs(pntdir, tstart=tstart, duration=duration, deadc=deadc, \
  288 + emin=emin, emax=emax, rad=rad, \
  289 + irf=irf, caldb=caldb)
  290 +
  291 + # Return CTA observation
  292 + return obs_cta
  293 +
  294 +
  295 +# ======================= #
  296 +# Set one CTA observation #
  297 +# ======================= #
  298 +def set_obs(pntdir, tstart=0.0, duration=1800.0, deadc=0.95, \
  299 + emin=0.1, emax=100.0, rad=5.0, \
  300 + irf="cta_dummy_irf", caldb="dummy", id="000000"):
  301 + """
  302 + Returns a single CTA observation containing an empty CTA event list.
  303 + By looping over this function you can add CTA observations to the
  304 + observation container.
  305 +
  306 + Parameters:
  307 + pntdir - Pointing direction [GSkyDir]
  308 + Keywords:
  309 + tstart - Start time [seconds] (default: 0.0)
  310 + duration - Duration of observation [seconds] (default: 1800.0)
  311 + deadc - Deadtime correction factor (default: 0.95)
  312 + emin - Minimum event energy [TeV] (default: 0.1)
  313 + emax - Maximum event energy [TeV] (default: 100.0)
  314 + rad - ROI radius used for analysis [deg] (default: 5.0)
  315 + irf - Instrument response function (default: cta_dummy_irf)
  316 + caldb - Calibration database path (default: "dummy")
  317 + """
  318 + # Allocate CTA observation
  319 + obs_cta = gammalib.GCTAObservation()
  320 +
  321 + # Set calibration database
  322 + db = gammalib.GCaldb()
  323 + if (gammalib.dir_exists(caldb)):
  324 + db.rootdir(caldb)
  325 + else:
  326 + db.open("cta", caldb)
  327 +
  328 + # Set pointing direction
  329 + pnt = gammalib.GCTAPointing()
  330 + pnt.dir(pntdir)
  331 + obs_cta.pointing(pnt)
  332 +
  333 + # Set ROI
  334 + roi = gammalib.GCTARoi()
  335 + instdir = gammalib.GCTAInstDir()
  336 + instdir.dir(pntdir)
  337 + roi.centre(instdir)
  338 + roi.radius(rad)
  339 +
  340 + # Set GTI
  341 + gti = gammalib.GGti()
  342 + gti.append(gammalib.GTime(tstart), gammalib.GTime(tstart+duration))
  343 +
  344 + # Set energy boundaries
  345 + ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, "TeV"), \
  346 + gammalib.GEnergy(emax, "TeV"))
  347 +
  348 + # Allocate event list
  349 + events = gammalib.GCTAEventList()
  350 + events.roi(roi)
  351 + events.gti(gti)
  352 + events.ebounds(ebounds)
  353 + obs_cta.events(events)
  354 +
  355 + # Set instrument response
  356 + obs_cta.response(irf, db)
  357 +
  358 + # Set ontime, livetime, and deadtime correction factor
  359 + obs_cta.ontime(duration)
  360 + obs_cta.livetime(duration*deadc)
  361 + obs_cta.deadc(deadc)
  362 + obs_cta.id(id)
  363 +
  364 + # Return CTA observation
  365 + return obs_cta
  366 +
  367 +
  368 +# ============================ #
  369 +# Set list of CTA observations #
  370 +# ============================ #
  371 +def set_obs_list(obsdeflist, tstart=0.0, duration=1800.0, deadc=0.95, \
  372 + emin=0.1, emax=100.0, rad=5.0, \
  373 + irf="cta_dummy_irf", caldb="dummy"):
  374 + """
  375 + Returns an observation container filled with a list of CTA observations.
  376 + The list is defined by the obsdeflist parameter which is a dictionnary
  377 + containing the mandatory keywords 'ra' and 'dec' that specify the
  378 + pointing direction for a given observation. Optional keyword give control
  379 + over other observation proposerties, such as duration, deadtime correction,
  380 + energy range, etc. If an optional keyword is not specified, the function
  381 + keyword is used instead.
  382 +
  383 + Parameters:
  384 + obsdeflist - Observation definition list [{'ra': x.xx, 'dec': x.xx}]
  385 + The directory can take the following optional keywords:
  386 + - duration: Duration of CTA observation [seconds]
  387 + - deadc: Deadtime correction factor
  388 + - emin: Minimum event energy [TeV]
  389 + - emax: Maximum event energy [TeV]
  390 + - rad: ROI radius used for analysis [deg]
  391 + - irf: Instrument response function
  392 + - caldb: Calibration database path
  393 + Optional keywords overwrite keywords specified in the
  394 + function call.
  395 + Keywords:
  396 + tstart - Start time [seconds] (default: 0.0)
  397 + duration - Duration of one CTA observation [seconds] (default: 1800.0)
  398 + deadc - Deadtime correction factor (default: 0.95)
  399 + emin - Minimum event energy [TeV] (default: 0.1)
  400 + emax - Maximum event energy [TeV] (default: 100.0)
  401 + rad - ROI radius used for analysis [deg] (default: 5.0)
  402 + irf - Instrument response function (default: cta_dummy_irf)
  403 + caldb - Calibration database path (default: "dummy")
  404 + """
  405 + # Initialise empty observation container
  406 + obs = gammalib.GObservations()
  407 +
  408 + # Initialise first time and identifier
  409 + obs_start = tstart
  410 + obs_id = 1
  411 +
  412 + # Loop over observation definition list
  413 + for obsdef in obsdeflist:
  414 +
  415 + # Set pointing direction
  416 + pntdir = gammalib.GSkyDir()
  417 + pntdir.radec_deg(obsdef['ra'], obsdef['dec'])
  418 +
  419 + # Set duration (use default if not found in definition list)
  420 + if 'duration' in obsdef:
  421 + obs_duration = obsdef['duration']
  422 + else:
  423 + obs_duration = duration
  424 +
  425 + # Set emin (use default if not found in definition list)
  426 + if 'emin' in obsdef:
  427 + obs_emin = obsdef['emin']
  428 + else:
  429 + obs_emin = emin
  430 +
  431 + # Set emax (use default if not found in definition list)
  432 + if 'emax' in obsdef:
  433 + obs_emax = obsdef['emax']
  434 + else:
  435 + obs_emax = emax
  436 +
  437 + # Set radius (use default if not found in definition list)
  438 + if 'rad' in obsdef:
  439 + obs_rad = obsdef['rad']
  440 + else:
  441 + obs_rad = rad
  442 +
  443 + # Set deadc (use default if not found in definition list)
  444 + if 'deadc' in obsdef:
  445 + obs_deadc = obsdef['deadc']
  446 + else:
  447 + obs_deadc = deadc
  448 +
  449 + # Set caldb (use default if not found in definition list)
  450 + if 'caldb' in obsdef:
  451 + obs_caldb = obsdef['caldb']
  452 + else:
  453 + obs_caldb = caldb
  454 +
  455 + # Set irf (use default if not found in definition list)
  456 + if 'irf' in obsdef:
  457 + obs_irf = obsdef['irf']
  458 + else:
  459 + obs_irf = irf
  460 +
  461 + # Generate identifier string
  462 + id = "%6.6d" % obs_id
  463 +
  464 + # Set CTA observation
  465 + obs_cta = set_obs(pntdir, tstart=obs_start, duration=obs_duration, \
  466 + deadc=obs_deadc, emin=obs_emin, emax=obs_emax, \
  467 + rad=obs_rad, irf=obs_irf, caldb=obs_caldb, id=id)
  468 +
  469 + # Append to container
  470 + obs.append(obs_cta)
  471 +
  472 + # Update start time and identifier
  473 + obs_start += obs_duration
  474 + obs_id += 1
  475 +
  476 + # Return observation container
  477 + return obs
  478 +
  479 +
  480 +# ============================ #
  481 +# Set CTA observation patterns #
  482 +# ============================ #
  483 +def set_obs_patterns(pattern, ra=83.6331, dec=22.0145, offset=1.5):
  484 + """
  485 + Sets a number of standard patterns.
  486 +
  487 + Parameters:
  488 + pattern - Observation pattern. Possible options are:
  489 + - "single": single pointing
  490 + - "four": four pointings 'offset' around pattern centre
  491 +
  492 + Keywords:
  493 + ra - Right Ascension of pattern centre [deg] (default: 83.6331)
  494 + dec - Declination of pattern centre [deg] (default: 22.0145)
  495 + offset - Offset from pattern centre [deg] (default: 1.5)
  496 + """
  497 + # Initialise observation definition list
  498 + obsdeflist = []
  499 +
  500 + # Add patterns
  501 + if pattern == "single":
  502 + obsdef = {'ra': ra, 'dec': dec}
  503 + obsdeflist.append(obsdef)
  504 + elif pattern == "four":
  505 + # Set pattern centre
  506 + centre = gammalib.GSkyDir()
  507 + centre.radec_deg(ra, dec)
  508 +
  509 + # Append pointings
  510 + for phi in [0.0, 90.0, 180.0, 270.0]:
  511 + pntdir = centre.copy()
  512 + pntdir.rotate_deg(phi, offset)
  513 + obsdeflist.append({'ra': pntdir.ra_deg(), 'dec': pntdir.dec_deg()})
  514 + else:
  515 + print("Warning: Observation pattern '"+str(pattern)+"' not recognized.")
  516 +
  517 + # Return observation definition list
  518 + return obsdeflist
... ...
pyext/setup.py.in
... ... @@ -121,8 +121,8 @@ setup(name=&#39;@PACKAGE_NAME@&#39;,
121 121 author='Juergen Knoedlseder',
122 122 author_email='@PACKAGE_BUGREPORT@',
123 123 url='http://gammalib.sourceforge.net/',
124   - py_modules=['ctools'],
125   - ext_modules=[Extension('_ctools', ['ctools_wrap.cpp'],
  124 + py_modules=['ctools.ctools', 'ctools.obsutils'],
  125 + ext_modules=[Extension('ctools._ctools', ['ctools/ctools_wrap.cpp'],
126 126 include_dirs=include_dirs,
127 127 library_dirs=library_dirs,
128 128 libraries=libraries,
... ...
scripts/Makefile.am
... ... @@ -5,8 +5,8 @@ binscripts = cta_kb_bkgrates.py cta_root2irf.py cta_make_nodes.py
5 5  
6 6 # Scripts to be distributed in pythondir
7 7 pythonscripts = cspull.py cssens.py cstsdist.py cscaldb.py \
8   - obsutils.py \
9   - lsmodel.py
  8 + lsmodel.py \
  9 + obsutils.py
10 10  
11 11 # Scripts for which symbolic links will be created in bindir
12 12 linkscripts = cspull cssens cstsdist cscaldb \
... ...
scripts/cssens.py
... ... @@ -35,7 +35,6 @@
35 35 # ==========================================================================
36 36 from ctools import *
37 37 from gammalib import *
38   -import obsutils
39 38 import sys
40 39 import csv
41 40 import math
... ...
scripts/cstsdist.py
... ... @@ -21,7 +21,6 @@
21 21 # ==========================================================================
22 22 from ctools import *
23 23 from gammalib import *
24   -import obsutils
25 24 import sys
26 25 import csv
27 26 import math
... ...
scripts/obsutils.py 100755 → 100644
1   -# ==========================================================================
2   -# This script provides a number of functions that are useful for handling
3   -# CTA observations.
4   -#
5   -# Copyright (C) 2011-2014 Juergen Knoedlseder
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   -import ctools
22   -import gammalib
  1 +print "The obsutils are moved under the ctools module."
  2 +print "Please update your script to import from ctools, i.e:"
  3 +print "from ctools import obsutils"
23 4  
24   -
25   -# ===================== #
26   -# Simulate observations #
27   -# ===================== #
28   -def sim(obs, log=False, debug=False, edisp=False, seed=0, nbins=0,
29   - binsz=0.05, npix=200, proj="TAN", coord="GAL"):
30   - """
31   - Simulate events for all observations in the container.
32   -
33   - Parameters:
34   - obs - Observation container
35   - Keywords:
36   - log - Create log file(s)
37   - debug - Create console dump?
38   - edisp - Apply energy dispersion?
39   - seed - Seed value for simulations (default: 0)
40   - nbins - Number of energy bins (default: 0=unbinned)
41   - binsz - Pixel size for binned simulation (deg/pixel)
42   - npix - Number of pixels in X and Y for binned simulation
43   - """
44   - # Allocate ctobssim application and set parameters
45   - sim = ctools.ctobssim(obs)
46   - sim["seed"].integer(seed)
47   -
48   - # Optionally open the log file
49   - if log:
50   - sim.logFileOpen()
51   -
52   - # Optionally switch-on debugging model
53   - if debug:
54   - sim["debug"].boolean(True)
55   -
56   - # Run ctobssim application. This will loop over all observations in the
57   - # container and simulation the events for each observation. Note that
58   - # events are not added together, they still apply to each observation
59   - # separately.
60   - sim.run()
61   -
62   - # Binned option?
63   - if nbins > 0:
64   -
65   - # Determine common energy boundaries for observations
66   - emin = None
67   - emax = None
68   - for run in sim.obs():
69   - run_emin = run.events().ebounds().emin().TeV()
70   - run_emax = run.events().ebounds().emax().TeV()
71   - if emin == None:
72   - emin = run_emin
73   - elif run_emin > emin:
74   - emin = run_emin
75   - if emax == None:
76   - emax = run_emax
77   - elif run_emax > emax:
78   - emax = run_emax
79   -
80   - # Allocate ctbin application and set parameters
81   - bin = ctools.ctbin(sim.obs())
82   - bin["ebinalg"].string("LOG")
83   - bin["emin"].real(emin)
84   - bin["emax"].real(emax)
85   - bin["enumbins"].integer(nbins)
86   - bin["usepnt"].boolean(True) # Use pointing for map centre
87   - bin["nxpix"].integer(npix)
88   - bin["nypix"].integer(npix)
89   - bin["binsz"].real(binsz)
90   - bin["coordsys"].string(coord)
91   - bin["proj"].string(proj)
92   -
93   - # Optionally open the log file
94   - if log:
95   - bin.logFileOpen()
96   -
97   - # Optionally switch-on debugging model
98   - if debug:
99   - bin["debug"].boolean(True)
100   -
101   - # Run ctbin application. This will loop over all observations in
102   - # the container and bin the events in counts maps
103   - bin.run()
104   -
105   - # Make a deep copy of the observation that will be returned
106   - # (the ctbin object will go out of scope one the function is
107   - # left)
108   - obs = bin.obs().copy()
109   -
110   - else:
111   -
112   - # Make a deep copy of the observation that will be returned
113   - # (the ctobssim object will go out of scope one the function is
114   - # left)
115   - obs = sim.obs().copy()
116   -
117   - # Delete the simulation
118   - del sim
119   -
120   - # Return observation container
121   - return obs
122   -
123   -
124   -# ================ #
125   -# Fit observations #
126   -# ================ #
127   -def fit(obs, log=False, debug=False, edisp=False):
128   - """
129   - Perform maximum likelihood fitting of observations in the container.
130   -
131   - Parameters:
132   - obs - Observation container
133   - Keywords:
134   - log - Create log file(s)
135   - debug - Create screen dump
136   - edisp - Apply energy dispersion?
137   - """
138   - # Allocate ctlike application
139   - like = ctools.ctlike(obs)
140   -
141   - # Optionally open the log file
142   - if log:
143   - like.logFileOpen()
144   -
145   - # Optionally switch-on debugging model
146   - if debug:
147   - like["debug"].boolean(True)
148   -
149   - # Optionally apply energy dispersion
150   - like["edisp"].boolean(edisp)
151   -
152   - # Run ctlike application.
153   - like.run()
154   -
155   - # Return observations
156   - return like
157   -
158   -
159   -# ================= #
160   -# Create counts map #
161   -# ================= #
162   -def cntmap(obs, proj="TAN", coord="GAL", xval=0.0, yval=0.0, \
163   - binsz=0.05, nxpix=200, nypix=200, \
164   - outname="cntmap.fits"):
165   - """
166   - Creates a counts map by combining the events of all observations.
167   - The counts map will be a summed map over all energies.
168   -
169   - Parameters:
170   - obs - Observation container
171   - Keywords:
172   - proj - Projection type (e.g. TAN, CAR, STG, ...) (default: TAN)
173   - coord - Coordinate type (GAL, CEL) (default: GAL)
174   - xval - Reference longitude value [deg] (default: 0.0)
175   - yval - Reference latitude value [deg] (default: 0.0)
176   - binsz - Pixel size [deg/pixel] (default: 0.05)
177   - nxpix - Number of pixels in X direction (default: 200)
178   - nypix - Number of pixels in Y direction (default: 200)
179   - outname - Counts map FITS filename (default: cntmap.fits)
180   - """
181   - # Allocate counts map
182   - map = gammalib.GSkymap(proj, coord, xval, yval, -binsz, binsz, nxpix, nypix, 1)
183   -
184   - # Set maximum pixel number
185   - maxpixel = nxpix * nypix
186   -
187   - # Fill all observations
188   - for run in obs:
189   -
190   - # Loop over all events
191   - for event in run.events():
192   -
193   - # Determine sky pixel
194   - skydir = gammalib.GCTAInstDir(event.dir()).dir()
195   - pixel = map.dir2inx(skydir)
196   -
197   - # Set pixel
198   - if pixel < maxpixel:
199   - map[pixel] += event.counts()
200   -
201   - # Save sky map. The clobber flag is set to True, so any existing FITS
202   - # file will be overwritten.
203   - map.save(outname, True)
204   -
205   - # Return counts map
206   - return map
207   -
208   -
209   -# ================ #
210   -# Create model map #
211   -# ================ #
212   -def modmap(obs, eref=0.1, proj="TAN", coord="GAL", xval=0.0, yval=0.0, \
213   - binsz=0.05, nxpix=200, nypix=200, \
214   - outname="modmap.fits"):
215   - """
216   - Make model map for a given reference energy by combining all observations.
217   - The model map will be evaluated for a given reference energy 'eref' and will
218   - be given in units of [counts/(sr MeV s)].
219   -
220   - Parameters:
221   - obs - Observation container
222   - Keywords:
223   - eref - Reference energy for which model is created [TeV] (default: 0.1)
224   - proj - Projection type (e.g. TAN, CAR, STG, ...) (default: TAN)
225   - coord - Coordinate type (GAL, CEL) (default: GAL)
226   - xval - Reference longitude value [deg] (default: 0.0)
227   - yval - Reference latitude value [deg] (default: 0.0)
228   - binsz - Pixel size [deg/pixel] (default: 0.05)
229   - nxpix - Number of pixels in X direction (default: 200)
230   - nypix - Number of pixels in Y direction (default: 200)
231   - outname - Model map FITS filename (default: modmap.fits)
232   - """
233   - # Allocate model map
234   - map = gammalib.GSkymap(proj, coord, xval, yval, -binsz, binsz, nxpix, nypix, 1)
235   -
236   - # Set reference energy, time and direction. The time is not initialised and is
237   - # in fact not used (as the IRF is assumed to be time independent for now).
238   - # The sky direction is set later using the pixel values.
239   - energy = gammalib.GEnergy()
240   - time = gammalib.GTime()
241   - instdir = gammalib.GCTAInstDir()
242   - energy.TeV(eref)
243   -
244   - # Loop over all map pixels
245   - for pixel in range(map.npix()):
246   -
247   - # Get sky direction
248   - skydir = map.inx2dir(pixel)
249   - instdir.dir(skydir)
250   -
251   - # Create event atom for map pixel
252   - atom = gammalib.GCTAEventAtom()
253   - atom.dir(instdir)
254   - atom.energy(energy)
255   - atom.time(time)
256   -
257   - # Initialise model value
258   - value = 0.0
259   -
260   - # Loop over all observations
261   - for run in obs:
262   - value += obs.models().eval(atom, run)
263   -
264   - # Set map value
265   - map[pixel] = value
266   -
267   - # Save sky map
268   - map.save(outname, True)
269   -
270   - # Return model map
271   - return map
272   -
273   -
274   -# ===================================== #
275   -# Set one CTA observation (old version) #
276   -# ===================================== #
277   -def set(pntdir, tstart=0.0, duration=1800.0, deadc=0.95, \
278   - emin=0.1, emax=100.0, rad=5.0, \
279   - irf="cta_dummy_irf", caldb="dummy"):
280   - """
281   - Obsolete function, use set_obs instead.
282   - """
283   - # Print warning
284   - print("Warning: obsutils.set is obsolete, use obsutils.set_obs instead.")
285   -
286   - # Call new function
287   - obs_cta = set_obs(pntdir, tstart=tstart, duration=duration, deadc=deadc, \
288   - emin=emin, emax=emax, rad=rad, \
289   - irf=irf, caldb=caldb)
290   -
291   - # Return CTA observation
292   - return obs_cta
293   -
294   -
295   -# ======================= #
296   -# Set one CTA observation #
297   -# ======================= #
298   -def set_obs(pntdir, tstart=0.0, duration=1800.0, deadc=0.95, \
299   - emin=0.1, emax=100.0, rad=5.0, \
300   - irf="cta_dummy_irf", caldb="dummy", id="000000"):
301   - """
302   - Returns a single CTA observation containing an empty CTA event list.
303   - By looping over this function you can add CTA observations to the
304   - observation container.
305   -
306   - Parameters:
307   - pntdir - Pointing direction [GSkyDir]
308   - Keywords:
309   - tstart - Start time [seconds] (default: 0.0)
310   - duration - Duration of observation [seconds] (default: 1800.0)
311   - deadc - Deadtime correction factor (default: 0.95)
312   - emin - Minimum event energy [TeV] (default: 0.1)
313   - emax - Maximum event energy [TeV] (default: 100.0)
314   - rad - ROI radius used for analysis [deg] (default: 5.0)
315   - irf - Instrument response function (default: cta_dummy_irf)
316   - caldb - Calibration database path (default: "dummy")
317   - """
318   - # Allocate CTA observation
319   - obs_cta = gammalib.GCTAObservation()
320   -
321   - # Set calibration database
322   - db = gammalib.GCaldb()
323   - if (gammalib.dir_exists(caldb)):
324   - db.rootdir(caldb)
325   - else:
326   - db.open("cta", caldb)
327   -
328   - # Set pointing direction
329   - pnt = gammalib.GCTAPointing()
330   - pnt.dir(pntdir)
331   - obs_cta.pointing(pnt)
332   -
333   - # Set ROI
334   - roi = gammalib.GCTARoi()
335   - instdir = gammalib.GCTAInstDir()
336   - instdir.dir(pntdir)
337   - roi.centre(instdir)
338   - roi.radius(rad)
339   -
340   - # Set GTI
341   - gti = gammalib.GGti()
342   - gti.append(gammalib.GTime(tstart), gammalib.GTime(tstart+duration))
343   -
344   - # Set energy boundaries
345   - ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, "TeV"), \
346   - gammalib.GEnergy(emax, "TeV"))
347   -
348   - # Allocate event list
349   - events = gammalib.GCTAEventList()
350   - events.roi(roi)
351   - events.gti(gti)
352   - events.ebounds(ebounds)
353   - obs_cta.events(events)
354   -
355   - # Set instrument response
356   - obs_cta.response(irf, db)
357   -
358   - # Set ontime, livetime, and deadtime correction factor
359   - obs_cta.ontime(duration)
360   - obs_cta.livetime(duration*deadc)
361   - obs_cta.deadc(deadc)
362   - obs_cta.id(id)
363   -
364   - # Return CTA observation
365   - return obs_cta
366   -
367   -
368   -# ============================ #
369   -# Set list of CTA observations #
370   -# ============================ #
371   -def set_obs_list(obsdeflist, tstart=0.0, duration=1800.0, deadc=0.95, \
372   - emin=0.1, emax=100.0, rad=5.0, \
373   - irf="cta_dummy_irf", caldb="dummy"):
374   - """
375   - Returns an observation container filled with a list of CTA observations.
376   - The list is defined by the obsdeflist parameter which is a dictionnary
377   - containing the mandatory keywords 'ra' and 'dec' that specify the
378   - pointing direction for a given observation. Optional keyword give control
379   - over other observation proposerties, such as duration, deadtime correction,
380   - energy range, etc. If an optional keyword is not specified, the function
381   - keyword is used instead.
382   -
383   - Parameters:
384   - obsdeflist - Observation definition list [{'ra': x.xx, 'dec': x.xx}]
385   - The directory can take the following optional keywords:
386   - - duration: Duration of CTA observation [seconds]
387   - - deadc: Deadtime correction factor
388   - - emin: Minimum event energy [TeV]
389   - - emax: Maximum event energy [TeV]
390   - - rad: ROI radius used for analysis [deg]
391   - - irf: Instrument response function
392   - - caldb: Calibration database path
393   - Optional keywords overwrite keywords specified in the
394   - function call.
395   - Keywords:
396   - tstart - Start time [seconds] (default: 0.0)
397   - duration - Duration of one CTA observation [seconds] (default: 1800.0)
398   - deadc - Deadtime correction factor (default: 0.95)
399   - emin - Minimum event energy [TeV] (default: 0.1)
400   - emax - Maximum event energy [TeV] (default: 100.0)
401   - rad - ROI radius used for analysis [deg] (default: 5.0)
402   - irf - Instrument response function (default: cta_dummy_irf)
403   - caldb - Calibration database path (default: "dummy")
404   - """
405   - # Initialise empty observation container
406   - obs = gammalib.GObservations()
407   -
408   - # Initialise first time and identifier
409   - obs_start = tstart
410   - obs_id = 1
411   -
412   - # Loop over observation definition list
413   - for obsdef in obsdeflist:
414   -
415   - # Set pointing direction
416   - pntdir = gammalib.GSkyDir()
417   - pntdir.radec_deg(obsdef['ra'], obsdef['dec'])
418   -
419   - # Set duration (use default if not found in definition list)
420   - if 'duration' in obsdef:
421   - obs_duration = obsdef['duration']
422   - else:
423   - obs_duration = duration
424   -
425   - # Set emin (use default if not found in definition list)
426   - if 'emin' in obsdef:
427   - obs_emin = obsdef['emin']
428   - else:
429   - obs_emin = emin
430   -
431   - # Set emax (use default if not found in definition list)
432   - if 'emax' in obsdef:
433   - obs_emax = obsdef['emax']
434   - else:
435   - obs_emax = emax
436   -
437   - # Set radius (use default if not found in definition list)
438   - if 'rad' in obsdef:
439   - obs_rad = obsdef['rad']
440   - else:
441   - obs_rad = rad
442   -
443   - # Set deadc (use default if not found in definition list)
444   - if 'deadc' in obsdef:
445   - obs_deadc = obsdef['deadc']
446   - else:
447   - obs_deadc = deadc
448   -
449   - # Set caldb (use default if not found in definition list)
450   - if 'caldb' in obsdef:
451   - obs_caldb = obsdef['caldb']
452   - else:
453   - obs_caldb = caldb
454   -
455   - # Set irf (use default if not found in definition list)
456   - if 'irf' in obsdef:
457   - obs_irf = obsdef['irf']
458   - else:
459   - obs_irf = irf
460   -
461   - # Generate identifier string
462   - id = "%6.6d" % obs_id
463   -
464   - # Set CTA observation
465   - obs_cta = set_obs(pntdir, tstart=obs_start, duration=obs_duration, \
466   - deadc=obs_deadc, emin=obs_emin, emax=obs_emax, \
467   - rad=obs_rad, irf=obs_irf, caldb=obs_caldb, id=id)
468   -
469   - # Append to container
470   - obs.append(obs_cta)
471   -
472   - # Update start time and identifier
473   - obs_start += obs_duration
474   - obs_id += 1
475   -
476   - # Return observation container
477   - return obs
478   -
479   -
480   -# ============================ #
481   -# Set CTA observation patterns #
482   -# ============================ #
483   -def set_obs_patterns(pattern, ra=83.6331, dec=22.0145, offset=1.5):
484   - """
485   - Sets a number of standard patterns.
486   -
487   - Parameters:
488   - pattern - Observation pattern. Possible options are:
489   - - "single": single pointing
490   - - "four": four pointings 'offset' around pattern centre
491   -
492   - Keywords:
493   - ra - Right Ascension of pattern centre [deg] (default: 83.6331)
494   - dec - Declination of pattern centre [deg] (default: 22.0145)
495   - offset - Offset from pattern centre [deg] (default: 1.5)
496   - """
497   - # Initialise observation definition list
498   - obsdeflist = []
499   -
500   - # Add patterns
501   - if pattern == "single":
502   - obsdef = {'ra': ra, 'dec': dec}
503   - obsdeflist.append(obsdef)
504   - elif pattern == "four":
505   - # Set pattern centre
506   - centre = gammalib.GSkyDir()
507   - centre.radec_deg(ra, dec)
508   -
509   - # Append pointings
510   - for phi in [0.0, 90.0, 180.0, 270.0]:
511   - pntdir = centre.copy()
512   - pntdir.rotate_deg(phi, offset)
513   - obsdeflist.append({'ra': pntdir.ra_deg(), 'dec': pntdir.dec_deg()})
514   - else:
515   - print("Warning: Observation pattern '"+str(pattern)+"' not recognized.")
516   -
517   - # Return observation definition list
518   - return obsdeflist
  5 +from ctools.obsutils import *
... ...
test/Makefile.am
... ... @@ -8,7 +8,7 @@ MACOSX_DEPLOYMENT_TARGET=@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@
8 8 if WITH_PYTHON
9 9 TEST_CSCRIPTS = test_cscripts.sh
10 10 TEST_PYTHON_SCRIPT = test_python.py
11   - TEST_PYTHON_ENV = PYTHONPATH=$(top_builddir)/pyext:$(top_builddir)/pyext/build/ctools:$(top_builddir)/scripts:$(pythondir):$(pyexecdir):$(PYTHONPATH)
  11 + TEST_PYTHON_ENV = PYTHONPATH=$(top_builddir)/pyext:$(top_builddir)/pyext/build/ctools/ctools:$(top_builddir)/scripts:$(pythondir):$(pyexecdir):$(PYTHONPATH)
12 12 endif
13 13  
14 14 # Set test environment
... ...