You need to sign in before continuing.
Commit 28cac9e0d87fd68e7517b144e39c9ceb7626b09d
1 parent
b815cccb
Add initial csviscube version
Showing
6 changed files
with
441 additions
and
0 deletions
cscripts/Makefile.am
| ... | ... | @@ -37,6 +37,7 @@ pythonscripts = $(builddir)/__init__.py \ |
| 37 | 37 | $(srcdir)/cstsdist.py \ |
| 38 | 38 | $(srcdir)/cstsmapmerge.py \ |
| 39 | 39 | $(srcdir)/cstsmapsplit.py \ |
| 40 | + $(srcdir)/csviscube.py \ | |
| 40 | 41 | $(srcdir)/csworkflow.py \ |
| 41 | 42 | $(srcdir)/csfindobs.py \ |
| 42 | 43 | $(srcdir)/csiactcopy.py \ |
| ... | ... | @@ -63,6 +64,7 @@ parfiles = $(srcdir)/cscaldb.par \ |
| 63 | 64 | $(srcdir)/cstsdist.par \ |
| 64 | 65 | $(srcdir)/cstsmapmerge.par \ |
| 65 | 66 | $(srcdir)/cstsmapsplit.par \ |
| 67 | + $(srcdir)/csviscube.par \ | |
| 66 | 68 | $(srcdir)/csworkflow.par \ |
| 67 | 69 | $(srcdir)/csfindobs.par \ |
| 68 | 70 | $(srcdir)/csiactcopy.par \ |
| ... | ... | @@ -86,6 +88,7 @@ linkscripts = cscaldb \ |
| 86 | 88 | cstsdist \ |
| 87 | 89 | cstsmapmerge \ |
| 88 | 90 | cstsmapsplit \ |
| 91 | + csviscube \ | |
| 89 | 92 | csworkflow \ |
| 90 | 93 | csfindobs \ |
| 91 | 94 | csiactcopy \ |
| ... | ... | @@ -111,6 +114,7 @@ testscripts = $(srcdir)/tests/__init__.py \ |
| 111 | 114 | $(top_srcdir)/test/test_cstsdist.py \ |
| 112 | 115 | $(top_srcdir)/test/test_cstsmapmerge.py \ |
| 113 | 116 | $(top_srcdir)/test/test_cstsmapsplit.py \ |
| 117 | + $(top_srcdir)/test/test_csviscube.py \ | |
| 114 | 118 | $(top_srcdir)/test/test_csworkflow.py \ |
| 115 | 119 | $(top_srcdir)/test/test_csfindobs.py \ |
| 116 | 120 | $(top_srcdir)/test/test_csiactcopy.py \ | ... | ... |
cscripts/__init__.py.in
| ... | ... | @@ -40,6 +40,7 @@ __all__ = [ |
| 40 | 40 | "cstsdist", |
| 41 | 41 | "cstsmapmerge", |
| 42 | 42 | "cstsmapsplit", |
| 43 | + "csviscube", | |
| 43 | 44 | "csworkflow", |
| 44 | 45 | "obsutils", |
| 45 | 46 | "modutils", |
| ... | ... | @@ -69,6 +70,7 @@ from cscripts.csspec import csspec |
| 69 | 70 | from cscripts.cstsdist import cstsdist |
| 70 | 71 | from cscripts.cstsmapmerge import cstsmapmerge |
| 71 | 72 | from cscripts.cstsmapsplit import cstsmapsplit |
| 73 | +from cscripts.csviscube import csviscube | |
| 72 | 74 | from cscripts.csworkflow import csworkflow |
| 73 | 75 | from cscripts import obsutils |
| 74 | 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 | 41 | import test_cstsdist |
| 42 | 42 | import test_cstsmapmerge |
| 43 | 43 | import test_cstsmapsplit |
| 44 | +import test_csviscube | |
| 44 | 45 | import test_csworkflow |
| 45 | 46 | import test_csiactcopy |
| 46 | 47 | import test_csiactdata |
| ... | ... | @@ -124,6 +125,7 @@ def test(installed=False, debug=False): |
| 124 | 125 | test_cstsdist.Test(), |
| 125 | 126 | test_cstsmapmerge.Test(), |
| 126 | 127 | test_cstsmapsplit.Test(), |
| 128 | + test_csviscube.Test(), | |
| 127 | 129 | test_csworkflow.Test()] |
| 128 | 130 | |
| 129 | 131 | # Append tests for Python 2.6+ (the IACT cscripts depend on the json | ... | ... |