Commit 28cac9e0d87fd68e7517b144e39c9ceb7626b09d

Authored by Jürgen Knödlseder
1 parent b815cccb

Add initial csviscube version

cscripts/Makefile.am
@@ -37,6 +37,7 @@ pythonscripts = $(builddir)/__init__.py \ @@ -37,6 +37,7 @@ pythonscripts = $(builddir)/__init__.py \
37 $(srcdir)/cstsdist.py \ 37 $(srcdir)/cstsdist.py \
38 $(srcdir)/cstsmapmerge.py \ 38 $(srcdir)/cstsmapmerge.py \
39 $(srcdir)/cstsmapsplit.py \ 39 $(srcdir)/cstsmapsplit.py \
  40 + $(srcdir)/csviscube.py \
40 $(srcdir)/csworkflow.py \ 41 $(srcdir)/csworkflow.py \
41 $(srcdir)/csfindobs.py \ 42 $(srcdir)/csfindobs.py \
42 $(srcdir)/csiactcopy.py \ 43 $(srcdir)/csiactcopy.py \
@@ -63,6 +64,7 @@ parfiles = $(srcdir)/cscaldb.par \ @@ -63,6 +64,7 @@ parfiles = $(srcdir)/cscaldb.par \
63 $(srcdir)/cstsdist.par \ 64 $(srcdir)/cstsdist.par \
64 $(srcdir)/cstsmapmerge.par \ 65 $(srcdir)/cstsmapmerge.par \
65 $(srcdir)/cstsmapsplit.par \ 66 $(srcdir)/cstsmapsplit.par \
  67 + $(srcdir)/csviscube.par \
66 $(srcdir)/csworkflow.par \ 68 $(srcdir)/csworkflow.par \
67 $(srcdir)/csfindobs.par \ 69 $(srcdir)/csfindobs.par \
68 $(srcdir)/csiactcopy.par \ 70 $(srcdir)/csiactcopy.par \
@@ -86,6 +88,7 @@ linkscripts = cscaldb \ @@ -86,6 +88,7 @@ linkscripts = cscaldb \
86 cstsdist \ 88 cstsdist \
87 cstsmapmerge \ 89 cstsmapmerge \
88 cstsmapsplit \ 90 cstsmapsplit \
  91 + csviscube \
89 csworkflow \ 92 csworkflow \
90 csfindobs \ 93 csfindobs \
91 csiactcopy \ 94 csiactcopy \
@@ -111,6 +114,7 @@ testscripts = $(srcdir)/tests/__init__.py \ @@ -111,6 +114,7 @@ testscripts = $(srcdir)/tests/__init__.py \
111 $(top_srcdir)/test/test_cstsdist.py \ 114 $(top_srcdir)/test/test_cstsdist.py \
112 $(top_srcdir)/test/test_cstsmapmerge.py \ 115 $(top_srcdir)/test/test_cstsmapmerge.py \
113 $(top_srcdir)/test/test_cstsmapsplit.py \ 116 $(top_srcdir)/test/test_cstsmapsplit.py \
  117 + $(top_srcdir)/test/test_csviscube.py \
114 $(top_srcdir)/test/test_csworkflow.py \ 118 $(top_srcdir)/test/test_csworkflow.py \
115 $(top_srcdir)/test/test_csfindobs.py \ 119 $(top_srcdir)/test/test_csfindobs.py \
116 $(top_srcdir)/test/test_csiactcopy.py \ 120 $(top_srcdir)/test/test_csiactcopy.py \
cscripts/__init__.py.in
@@ -40,6 +40,7 @@ __all__ = [ @@ -40,6 +40,7 @@ __all__ = [
40 "cstsdist", 40 "cstsdist",
41 "cstsmapmerge", 41 "cstsmapmerge",
42 "cstsmapsplit", 42 "cstsmapsplit",
  43 + "csviscube",
43 "csworkflow", 44 "csworkflow",
44 "obsutils", 45 "obsutils",
45 "modutils", 46 "modutils",
@@ -69,6 +70,7 @@ from cscripts.csspec import csspec @@ -69,6 +70,7 @@ from cscripts.csspec import csspec
69 from cscripts.cstsdist import cstsdist 70 from cscripts.cstsdist import cstsdist
70 from cscripts.cstsmapmerge import cstsmapmerge 71 from cscripts.cstsmapmerge import cstsmapmerge
71 from cscripts.cstsmapsplit import cstsmapsplit 72 from cscripts.cstsmapsplit import cstsmapsplit
  73 +from cscripts.csviscube import csviscube
72 from cscripts.csworkflow import csworkflow 74 from cscripts.csworkflow import csworkflow
73 from cscripts import obsutils 75 from cscripts import obsutils
74 from cscripts import modutils 76 from cscripts import modutils
cscripts/csviscube.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csviscube.par #
  8 +# Version: 1.0.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csviscube script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# General parameters
  19 +#===================
  20 +tmin, r, a, 0.0,,, "Start time (CTA MET in seconds)"
  21 +tmax, r, a, 31557600.0,,, "End time (CTA MET in seconds)"
  22 +geolon, r, a, 79.4041,,, "Geographic longitude of array (deg)"
  23 +geolat, r, a, -24.6272,,, "Geographic latitude of array (deg)"
  24 +sunzenith, r, h, 105.0,,, "Minimum Sun zenith angle for observations (deg)"
  25 +moonzenith, r, h, 90.0,,, "Minimum Moon zenith angle for observations (deg)"
  26 +outfile, f, a, viscube.fits,,, "Visibility cube output file"
  27 +
  28 +#
  29 +# Visibility cube parameters
  30 +#===========================
  31 +binsz, r, h, 1.0,,, "Image scale (in degrees/pixel)"
  32 +
  33 +#
  34 +# Standard parameters
  35 +#====================
  36 +chatter, i, h, 2,0,4, "Chattiness of output"
  37 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  38 +debug, b, h, no,,, "Debugging mode activated"
  39 +mode, s, h, ql,,, "Mode of automatic parameters"
  40 +logfile, f, h, csviscube.log,,, "Log filename"
cscripts/csviscube.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# Compute a visibility cube
  4 +#
  5 +# Copyright (C) 2016 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 sys
  22 +import glob
  23 +import os
  24 +import math
  25 +import gammalib
  26 +import ctools
  27 +
  28 +
  29 +# =============== #
  30 +# csviscube class #
  31 +# =============== #
  32 +class csviscube(ctools.cscript):
  33 + """
  34 + Compute a visibility cube
  35 + """
  36 +
  37 + # Constructor
  38 + def __init__(self, *argv):
  39 + """
  40 + Constructor.
  41 +
  42 + Parameters
  43 + ----------
  44 + argv : list of str
  45 + List of IRAF command line parameter strings of the form
  46 + ``parameter=3``.
  47 +
  48 + Raises
  49 + ------
  50 + TypeError
  51 + An invalid number of command line arguments was provided.
  52 + """
  53 + # Set name
  54 + self._name = 'csviscube'
  55 + self._version = '1.0.0'
  56 +
  57 + # Initialise application by calling the appropriate class
  58 + # constructor.
  59 + self._init_cscript(argv)
  60 +
  61 + # Return
  62 + return
  63 +
  64 +
  65 + # Private methods
  66 + def _get_parameters(self):
  67 + """
  68 + Get all parameters
  69 + """
  70 + # Query parameters
  71 + self['tmin'].real()
  72 + self['tmax'].real()
  73 + self['geolon'].real()
  74 + self['geolat'].real()
  75 + self['sunzenith'].real()
  76 + self['moonzenith'].real()
  77 + self['outfile'].filename()
  78 + self['binsz'].real()
  79 +
  80 + # Write input parameters into logger
  81 + self._log_parameters(gammalib.TERSE)
  82 +
  83 + # Return
  84 + return
  85 +
  86 + def _hour_angle_weight(self):
  87 + """
  88 + Compute hour angle weight vector
  89 +
  90 + Compute during which time the array was observing due to hour angle
  91 + constraints. We take here a very simple model where the night lasts
  92 + between 6 and 10 hours (or 90-150 degrees), with a sinusoidal variation
  93 + along the year. Every day the hour angle of the Sun set is advancing by
  94 + 4 minutes.
  95 + """
  96 + # Set number of hour angle bins
  97 + nsteps = 1000
  98 + d_min = 6.0/24.0*nsteps
  99 + d_max = 10.0/24.0*nsteps
  100 +
  101 + # Initialise hour angle array and weight
  102 + hours = [0.0 for i in range(nsteps)]
  103 + weight = 24.0/float(nsteps) # Weight per hour angle step
  104 +
  105 + # Loop over all days in one year
  106 + for day in range(nsteps):
  107 + h_set = day
  108 + h_rise = day + \
  109 + int(d_min + (d_max-d_min)*math.sin(day/float(nsteps)*gammalib.pi))
  110 + for i in range(h_set,h_rise):
  111 + if i >= nsteps:
  112 + hours[i-nsteps] += weight
  113 + else:
  114 + hours[i] += weight
  115 +
  116 + # Return
  117 + return hours
  118 +
  119 + def _visibility_cube(self):
  120 + """
  121 + Compute visibility cube
  122 +
  123 + Compute visibility cube by displacing the zenith angle map for all
  124 + hour angles. The visibility cube contains the number of hours a given
  125 + celestial position is visible under a given zenith angle interval.
  126 + Summing over all zenith angle intervals specifies for how long a given
  127 + celestial position will be visible.
  128 + """
  129 + # Compute zenith angle map
  130 + map = self._zenith_angle_map()
  131 +
  132 + # Set zenith angle range and bin size
  133 + dz = 1.0
  134 + nz = int(60.0/dz)
  135 +
  136 + # Initialise visibility cube
  137 + self._cube = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180,nz)
  138 +
  139 + # Setup hour angle weights. They specify for how many hours a given
  140 + # hour angle will be observed during one year.
  141 + hours = self._hour_angle_weight()
  142 +
  143 + # Compute normalisation factor
  144 + norm = 365.0/float(len(hours))
  145 +
  146 + # Loop over all hour angle weights
  147 + for h, weight in enumerate(hours):
  148 +
  149 + # Compute temporal shift
  150 + shift = 360.0/float(len(hours)) * float(h)
  151 +
  152 + # Initialise shifted zenith angle map
  153 + map_shift = gammalib.GSkyMap('CAR','CEL',shift,0.0,-1.0,1.0,360,180)
  154 +
  155 + # Merge zenith angle map into shifted map
  156 + map_shift += map
  157 +
  158 + # Loop over all pixels of shifted map
  159 + for i, zenith in enumerate(map_shift):
  160 + iz = int(zenith/dz)
  161 + if iz < nz:
  162 + self._cube[i,iz] += weight * norm
  163 +
  164 + # Return
  165 + return
  166 +
  167 + def _zenith_angle_map(self):
  168 + """
  169 + Compute zenith angle map
  170 +
  171 + The zenith angle of a position (ra,dec) depends on the declination and
  172 + the hour angle h and is given by
  173 +
  174 + zenith(h,dec) = arccos( sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(h) )
  175 +
  176 + The hour angle h (or local hour angle, LHA) is defined as the difference
  177 + between local siderial time (LST) and the Right Ascension
  178 +
  179 + h = LST - ra
  180 +
  181 + LST is the difference between the Greenwich siderial time (GST) and the
  182 + geographic longitude of the observer
  183 +
  184 + LST = GST - lon
  185 +
  186 + where lon is counted positive west from the meridian. Hence
  187 +
  188 + h = GST - lon - ra
  189 +
  190 + The map is computed for h=-ra which is equivalent to GST=lon (or LST=0).
  191 + In other words, the map corresponds to the time when ra=0 passes the
  192 + local meridian.
  193 + """
  194 + # Initialise zenith angle map
  195 + map = gammalib.GSkyMap('CAR','CEL',0.0,0.0,-1.0,1.0,360,180)
  196 +
  197 + # Set hour angle and declination vectors
  198 + hours = [float(i) for i in range(360)]
  199 + decs = [float(i)-89.5 for i in range(180)]
  200 +
  201 + # Get array geographic longitude and latitude
  202 + geolon = self['geolon'].real()
  203 + geolat = self['geolat'].real()
  204 +
  205 + # Precompute latitude terms
  206 + cos_lat = math.cos(geolat*gammalib.deg2rad)
  207 + sin_lat = math.sin(geolat*gammalib.deg2rad)
  208 +
  209 + # Loop over all declination and hour angles and compute the zenith
  210 + # angle. Store the zenith angle in the map
  211 + index = 0
  212 + for dec in decs:
  213 + cos_dec = math.cos(dec*gammalib.deg2rad)
  214 + sin_dec = math.sin(dec*gammalib.deg2rad)
  215 + for h in hours:
  216 + cos_h = math.cos(h*gammalib.deg2rad)
  217 + zenith = math.acos(sin_lat*sin_dec +
  218 + cos_lat*cos_dec*cos_h)*gammalib.rad2deg
  219 + map[index] = zenith
  220 + index += 1
  221 +
  222 + # Return zenith angle map
  223 + return map
  224 +
  225 +
  226 + # Public methods
  227 + def run(self):
  228 + """
  229 + Run the script
  230 + """
  231 + # Switch screen logging on in debug mode
  232 + if self._logDebug():
  233 + self._log.cout(True)
  234 +
  235 + # Get parameters
  236 + self._get_parameters()
  237 +
  238 + # Compute visibility cube
  239 + self._visibility_cube()
  240 +
  241 + # Return
  242 + return
  243 +
  244 + def save(self):
  245 + """
  246 + Save the visibility cube
  247 + """
  248 + # Write header
  249 + self._log_header1(gammalib.TERSE, 'Save visibility cube')
  250 +
  251 + # Get outfile parameter
  252 + outfile = self['outfile'].filename()
  253 +
  254 + # Log file name
  255 + self._log_value(gammalib.TERSE, 'Visibility cube file', outfile.url())
  256 +
  257 + # Save the visibility cube
  258 + self._cube.save(outfile, self['clobber'].boolean())
  259 +
  260 + # Return
  261 + return
  262 +
  263 + def execute(self):
  264 + """
  265 + Execute the script
  266 + """
  267 + # Open logfile
  268 + self.logFileOpen()
  269 +
  270 + # Run the script
  271 + self.run()
  272 +
  273 + # Save the cube
  274 + self.save()
  275 +
  276 + # Return
  277 + return
  278 +
  279 +
  280 +# ======================== #
  281 +# Main routine entry point #
  282 +# ======================== #
  283 +if __name__ == '__main__':
  284 +
  285 + # Create instance of application
  286 + app = csviscube(sys.argv)
  287 +
  288 + # Execute application
  289 + app.execute()
test/test_csviscube.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This scripts performs unit tests for the csviscube script
  4 +#
  5 +# Copyright (C) 2016 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 gammalib
  22 +import cscripts
  23 +from testing import test
  24 +
  25 +
  26 +# =============================== #
  27 +# Test class for csviscube script #
  28 +# =============================== #
  29 +class Test(test):
  30 + """
  31 + Test class for csviscube script
  32 + """
  33 +
  34 + # Constructor
  35 + def __init__(self):
  36 + """
  37 + Constructor.
  38 + """
  39 + # Call base class constructor
  40 + test.__init__(self)
  41 +
  42 + # Return
  43 + return
  44 +
  45 + # Set test functions
  46 + def set(self):
  47 + """
  48 + Set all test functions.
  49 + """
  50 + # Set test name
  51 + self.name('csviscube')
  52 +
  53 + # Append tests
  54 + self.append(self._test_cmd, 'Test csviscube on command line')
  55 + self.append(self._test_python, 'Test csviscube from Python')
  56 +
  57 + # Return
  58 + return
  59 +
  60 + # Test csviscube on command line
  61 + def _test_cmd(self):
  62 + """
  63 + Test csviscube on the command line.
  64 + """
  65 + # Set script name
  66 + csviscube = self._script('csviscube')
  67 +
  68 + # Setup csviscube command
  69 + cmd = csviscube+' tmin=0.0 tmax=31557600.0 geolon=79.4041'+\
  70 + ' geolat=-24.6272 outfile=csviscube_cmd1.fits'+\
  71 + ' logfile="csviscube_cmd1.log" chatter=1'
  72 +
  73 + # Check if execution of wrong command fails
  74 + self.test_assert(self._execute('command_that_does_not_exist') != 0,
  75 + 'Self test of test script')
  76 +
  77 + # Check if execution was successful
  78 + self.test_assert(self._execute(cmd) == 0,
  79 + 'Check successful execution from command line')
  80 +
  81 + # Return
  82 + return
  83 +
  84 + # Test csviscube from Python
  85 + def _test_python(self):
  86 + """
  87 + Test csviscube from Python.
  88 + """
  89 + # Set-up csviscube
  90 + viscube = cscripts.csviscube()
  91 + viscube['tmin'] = 0.0
  92 + viscube['tmax'] = 31557600.0
  93 + viscube['geolon'] = 79.4041
  94 + viscube['geolat'] = -24.6272
  95 + viscube['outfile'] = 'csviscube_py1.fits'
  96 + viscube['logfile'] = 'csviscube_py1.log'
  97 + viscube['chatter'] = 2
  98 +
  99 + # Run script
  100 + viscube.logFileOpen() # Make sure we get a log file
  101 + viscube.run()
  102 +
  103 + # Return
  104 + return
test/test_python_cscripts.py
@@ -41,6 +41,7 @@ import test_csspec @@ -41,6 +41,7 @@ import test_csspec
41 import test_cstsdist 41 import test_cstsdist
42 import test_cstsmapmerge 42 import test_cstsmapmerge
43 import test_cstsmapsplit 43 import test_cstsmapsplit
  44 +import test_csviscube
44 import test_csworkflow 45 import test_csworkflow
45 import test_csiactcopy 46 import test_csiactcopy
46 import test_csiactdata 47 import test_csiactdata
@@ -124,6 +125,7 @@ def test(installed=False, debug=False): @@ -124,6 +125,7 @@ def test(installed=False, debug=False):
124 test_cstsdist.Test(), 125 test_cstsdist.Test(),
125 test_cstsmapmerge.Test(), 126 test_cstsmapmerge.Test(),
126 test_cstsmapsplit.Test(), 127 test_cstsmapsplit.Test(),
  128 + test_csviscube.Test(),
127 test_csworkflow.Test()] 129 test_csworkflow.Test()]
128 130
129 # Append tests for Python 2.6+ (the IACT cscripts depend on the json 131 # Append tests for Python 2.6+ (the IACT cscripts depend on the json