Coverage for python/lsst/sims/catUtils/exampleCatalogDefinitions/phoSimCatalogExamples.py : 33%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Instance Catalog"""
2from __future__ import print_function
3from builtins import range
4import numpy as np
5from lsst.sims.utils import SpecMap, defaultSpecMap
6from lsst.sims.catalogs.definitions import InstanceCatalog
7from lsst.sims.catalogs.decorators import compound, cached
8from lsst.sims.utils import arcsecFromRadians, _observedFromICRS, altAzPaFromRaDec
9from lsst.sims.catUtils.mixins import (EBVmixin, PhoSimAstrometryStars,
10 PhoSimAstrometryGalaxies,
11 PhoSimAstrometrySSM,
12 FrozenSNCat)
14__all__ = ["write_phoSim_header", "PhosimInputBase",
15 "PhoSimCatalogPoint", "PhoSimCatalogZPoint", "PhoSimCatalogSN",
16 "PhoSimCatalogSersic2D", "PhoSimCatalogSSM", "PhoSimSpecMap",
17 "DefaultPhoSimHeaderMap", 'DefaultPhoSimInstanceCatalogCols']
20PhoSimSpecMap = SpecMap(fileDict=defaultSpecMap.fileDict,
21 dirDict={'(^lte)': 'starSED/phoSimMLT'})
24# This is a dict of transformations mapping from data in OpSim to header
25# values expected by PhoSim. The dict is keyed on the names of PhoSim
26# parameters. The values of the dict are either straight values, in which
27# case those values are written to the PhoSim InstanceCatalog header, or tuples
28# containing the name of the OpSim column which should be use to calculate
29# the PhoSim parameter and any transformation needed to go from OpSim
30# units to PhoSim units.
31#
32# PhoSim header parameters are documented here
33# https://bitbucket.org/phosim/phosim_release/wiki/Instance%20Catalog
34#
35# OpSim columns are documented here
36# https://www.lsst.org/scientists/simulations/opsim/summary-table-column-descriptions-v335
38DefaultPhoSimHeaderMap = {'rottelpos': ('rotTelPos', np.degrees), 38 ↛ exitline 38 didn't jump to the function exit
39 'obshistid': ('obsHistID', None),
40 'moonra': ('moonRA', np.degrees),
41 'moondec': ('moonDec', np.degrees),
42 'moonphase': ('moonPhase', None),
43 'moonalt': ('moonAlt', np.degrees),
44 'dist2moon': ('dist2Moon', np.degrees),
45 'sunalt': ('sunAlt', np.degrees),
46 'seeing': ('rawSeeing', None),
47 'vistime': ('visitExpTime', lambda x: x + 3.0),
48 'nsnap': 2,
49 'seed': ('obsHistID', None),
50 'opsim_version': 3}
52# This variable contains all of the columns in a phosim Instance Catalog
53# Can be used to reconstruct the information encoded in a phosim instance
54# Catalog
55DefaultPhoSimInstanceCatalogCols = ('object', 'uniqueID', 'RA', 'DEC'\
56 , 'MAG_NORM', 'SED_NAME', 'REDSHIFT'\
57 , 'GAMMA1', 'GAMMA2', 'MU', 'DELTA_RA'\
58 , 'DELTA_DEC', 'SOURCE_TYPE'\
59 , 'source_pars', 'DUST_REST_NAME'\
60 , 'dust_pars_1a', 'dust_pars_1b'\
61 , 'DUST_LAB_NAME', 'dust_pars_2a'\
62 , 'dust_pars_2b')
64def evaluate_phosim_header(param, phosim_header_map, obs):
65 """
66 Find the value of a PhoSim header parameter given a PhoSimHeaderMap,
67 and an ObservationMetaData
69 @param [in] param is the name of the header parameter desired.
71 @param [in] phosim_header_map is a phoSimHeaderMap dict
73 @param [in] obs is an ObservationMetaData
75 @param [out] returns the value of the PhoSim header parameter
76 """
78 if param not in phosim_header_map:
79 return None
81 if isinstance(phosim_header_map[param], tuple):
82 transform = phosim_header_map[param]
83 if obs.OpsimMetaData is None:
84 return None
86 if transform[0] not in obs.OpsimMetaData:
87 return None
89 if transform[1] is None:
90 return obs.OpsimMetaData[transform[0]]
92 return transform[1](obs.OpsimMetaData[transform[0]])
94 return phosim_header_map[param]
97def write_phoSim_header(obs, file_handle, phosim_header_map):
98 """
99 Write the data contained in an ObservationMetaData as a header in a
100 PhoSim InstanceCatalog.
102 obs is the ObservationMetaData
104 file_handle points to the catalog being written.
105 """
107 if phosim_header_map is None:
108 raw_opsim_contents = ""
110 if obs.OpsimMetaData is not None:
111 sorted_opsim_metadata = list(obs.OpsimMetaData.keys())
112 sorted_opsim_metadata.sort()
113 for tag in sorted_opsim_metadata:
114 raw_opsim_contents += "%s\n" % tag
116 raise RuntimeError("You have tried to write a PhoSim InstanceCatalog without specifying "
117 "a phoSimHeaderMap in your call to write_catalog().\n"
118 "\n"
119 "A phoSimHeaderMap is a dict that maps between columns stored in the "
120 "OpSim database from which an ObservationMetaData was created and "
121 "header parameters expected by PhoSim. The dict is keyed on the names of PhoSim "
122 "parameters. The values of the dict are either straight values, in which "
123 "case those values are written to the PhoSim InstanceCatalog header, or tuples "
124 "containing the name of the OpSim column which should be use to calculate "
125 "the PhoSim parameter and any transformation needed to go from OpSim "
126 "units to PhoSim units (the transform is 'None' if no transform is needed).\n"
127 "\n"
128 "To add a phoSimHeaderMap, simple assign it to the 'phoSimHeaderMap' "
129 "member variable of your catalog with (for instance):\n"
130 "\n"
131 "myCat.phoSimHeaderMap = my_phosim_header_map\n"
132 "\n"
133 "Before calling write_catalog()\n"
134 "\n"
135 "The header parameters expected by PhoSim can be found at\n"
136 "\n"
137 "https://bitbucket.org/phosim/phosim_release/wiki/Instance%20Catalog\n"
138 "\n"
139 "The contents of the OpSim database's Summary table can be found at\n"
140 "\n"
141 "https://www.lsst.org/scientists/simulations/opsim/summary-table-column-descriptions-v335\n"
142 "\n"
143 "If you do not wish to use any of these columns, you can just pass in an empty "
144 "dict. If you want to use a pre-made mapping, use the DefaultPhoSimHeaderMap "
145 "imported from lsst.sims.catUtils.exampleCatalogDefinitions\n"
146 "\n"
147 "Note: do not specify ra, dec, alt, az, mjd, filter, or rotSkyPos in your "
148 "phoSimHeaderMap. These are handled directly by the ObservationMetaData "
149 "to ensure self-consistency.\n"
150 "\n"
151 "For reference, the OpSim columns you can choose to map (i.e. those contained "
152 "in your ObservationMetaData) are:\n\n" +
153 raw_opsim_contents +
154 "\n(Even if your ObservationMetaData contains no extra OpSim Columns "
155 "you may wish to consider adding default PhoSim parameters through "
156 "the phoSimHeaderMap)")
158 try:
160 # PhoSim wants the MJD at the middle of the visit (i.e. between the two exposures
161 # in our two-snap model). OpSim gives the MJD at the start of the visit.
162 # below we calculate the change in MJD necessary to transform the OpSim value
163 # into the PhoSim value
164 vistime = evaluate_phosim_header('vistime', phosim_header_map, obs)
165 if vistime is not None:
166 delta_t = 0.5*vistime
167 else:
168 delta_t = 16.5 # half of the default 33 seconds
170 file_handle.write('rightascension %.17f\n' % obs.pointingRA)
171 file_handle.write('declination %.17f\n' % obs.pointingDec)
172 file_handle.write('mjd %.17f\n' % (obs.mjd.TAI + delta_t/86400.0))
173 alt, az, pa = altAzPaFromRaDec(obs.pointingRA, obs.pointingDec, obs, includeRefraction=False)
174 file_handle.write('altitude %.17f\n' % alt)
175 file_handle.write('azimuth %.17f\n' % az)
176 file_handle.write('filter %d\n' %
177 {'u': 0, 'g': 1, 'r': 2, 'i': 3, 'z': 4, 'y': 5}[obs.bandpass])
179 file_handle.write('rotskypos %.17f\n' % obs.rotSkyPos)
180 except:
181 print("\n\n")
182 print("The ObservationMetaData you tried to write a PhoSim header from")
183 print("lacks one of the required parameters")
184 print("(pointingRA, pointingDec, mjd, bandpass, rotSkyPos)\n")
185 raise
187 # sort the header map keys so that PhoSim headers generated with the same
188 # map always have parameters in the same order.
189 sorted_header_keys = list(phosim_header_map.keys())
190 sorted_header_keys.sort()
192 if (obs.OpsimMetaData is not None and 'opsim_version' in obs.OpsimMetaData and
193 'opsim_version' in phosim_header_map):
194 if obs.OpsimMetaData['opsim_version'] != phosim_header_map['opsim_version']:
195 raise RuntimeError('Your ObservationMetaData was built from '
196 + 'a v%d OpSim database, but your phosim_header_map ' % obs.OpsimMetaData['opsim_version']
197 + 'is expecting a v%d OpSim database. Your InstanceCatalog ' % phosim_header_map['opsim_version']
198 + 'header is not going to make any sense if you proceed, so '
199 + 'we are throwing this error.')
201 for name in sorted_header_keys:
202 if name == 'opsim_version':
203 continue
204 val = evaluate_phosim_header(name, phosim_header_map, obs)
205 if val is not None:
206 if isinstance(val, float) or isinstance(val, np.float):
207 file_handle.write('%s %.7f\n' % (name, val))
208 elif isinstance(val, int) or isinstance(val, np.int):
209 file_handle.write('%s %d\n' % (name, val))
210 elif isinstance(val, int):
211 file_handle.write('%s %ld\n' % (name, val))
212 else:
213 file_handle.write('%s %s\n' % (name, str(val)))
216class PhosimInputBase(InstanceCatalog):
218 override_formats = {'raPhoSim': '%.16g', 'decPhoSim': '%.16g'}
220 filtMap = dict([(c, i) for i, c in enumerate('ugrizy')])
222 specFileMap = PhoSimSpecMap
224 cannot_be_null = ['sedFilepath']
226 delimiter = " "
228 @cached
229 def get_prefix(self):
230 chunkiter = range(len(self.column_by_name(self.refIdCol)))
231 return np.array(['object' for i in chunkiter], dtype=(str, 6))
233 @cached
234 def get_sedFilepath(self):
235 return np.array([self.specFileMap[k] if k in self.specFileMap else None
236 for k in self.column_by_name('sedFilename')])
238 @cached
239 def get_spatialmodel(self):
240 chunkiter = range(len(self._current_chunk))
241 return np.array([self.spatialModel for i in chunkiter], dtype=(str, 8))
243 @cached
244 def get_phoSimMagNorm(self):
245 """
246 This getter returns the magnitude normalization expected by PhoSim (the magnitude at
247 500 nm).
249 To account for variability, the getter adds the delta_lsst_x column from the Variability
250 mixin where 'x' is the bandpass defined by self.observation_metadata.bandpass (assuming
251 that self.observation_metadata.bandpass is not list-like; if it is list-like, then no
252 variability is added to the magNorm value).
254 Redshift is currently ignored. That may or may not be appropriate. This requires
255 further investigation into the behavior of PhoSim.
256 """
258 try:
259 string_class = basestring
260 except:
261 string_class = str
263 magNorm = self.column_by_name('magNorm')
264 varName = None
265 if self.obs_metadata is not None:
266 if self.obs_metadata.bandpass is not None:
267 if isinstance(self.obs_metadata.bandpass, string_class):
268 varName = 'delta_lsst_' + self.obs_metadata.bandpass
270 if varName is not None and varName in self._all_available_columns:
271 magNorm_out = magNorm + self.column_by_name(varName)
272 return magNorm_out
273 else:
274 return magNorm
276 def write_header(self, file_handle):
277 if not hasattr(self, 'phoSimHeaderMap'):
278 self.phoSimHeaderMap = None
280 write_phoSim_header(self.obs_metadata, file_handle, self.phoSimHeaderMap)
283class PhoSimCatalogPoint(PhosimInputBase, PhoSimAstrometryStars):
285 catalog_type = 'phoSim_catalog_POINT'
287 column_outputs = ['prefix', 'uniqueId', 'raPhoSim', 'decPhoSim', 'phoSimMagNorm', 'sedFilepath',
288 'redshift', 'gamma1', 'gamma2', 'kappa', 'raOffset', 'decOffset',
289 'spatialmodel', 'internalExtinctionModel',
290 'galacticExtinctionModel', 'galacticAv', 'galacticRv']
292 default_columns = [('redshift', 0., float), ('gamma1', 0., float), ('gamma2', 0., float),
293 ('kappa', 0., float), ('raOffset', 0., float), ('decOffset', 0., float),
294 ('galacticExtinctionModel', 'CCM', (str, 3)), ('galacticRv', 3.1, float),
295 ('internalExtinctionModel', 'none', (str, 4))]
297 default_formats = {'S': '%s', 'f': '%.9g', 'i': '%i'}
299 spatialModel = "point"
301 transformations = {'raPhoSim': np.degrees, 'decPhoSim': np.degrees}
304class PhoSimCatalogZPoint(PhosimInputBase, PhoSimAstrometryGalaxies, EBVmixin):
306 catalog_type = 'phoSim_catalog_ZPOINT'
308 column_outputs = ['prefix', 'uniqueId', 'raPhoSim', 'decPhoSim', 'phoSimMagNorm', 'sedFilepath',
309 'redshift', 'gamma1', 'gamma2', 'kappa', 'raOffset', 'decOffset',
310 'spatialmodel', 'internalExtinctionModel',
311 'galacticExtinctionModel', 'galacticAv', 'galacticRv']
313 default_columns = [('gamma1', 0., float), ('gamma2', 0., float), ('kappa', 0., float),
314 ('raOffset', 0., float), ('decOffset', 0., float),
315 ('spatialmodel', 'ZPOINT', (str, 6)),
316 ('galacticExtinctionModel', 'CCM', (str, 3)),
317 ('galacticAv', 0.1, float), ('galacticRv', 3.1, float),
318 ('internalExtinctionModel', 'none', (str, 4))]
320 default_formats = {'S': '%s', 'f': '%.9g', 'i': '%i'}
322 spatialModel = "point"
324 transformations = {'raPhoSim': np.degrees, 'decPhoSim': np.degrees}
327class PhoSimCatalogSN(PhoSimCatalogZPoint, FrozenSNCat, EBVmixin):
328 """
329 Mixin for PhoSim Instance Catalogs in PhoSim
330 """
331 catalog_type = 'phoSim_SNcatalog'
332 writeSEDFile = True
334 def get_sedFilepath(self):
335 return self.column_by_name('TsedFilepath')
337 def get_phoSimMagNorm(self):
338 return self.column_by_name('magNorm')
341class PhoSimCatalogSersic2D(PhoSimCatalogZPoint):
343 catalog_type = 'phoSim_catalog_SERSIC2D'
345 column_outputs = ['prefix', 'uniqueId', 'raPhoSim', 'decPhoSim', 'phoSimMagNorm', 'sedFilepath',
346 'redshift', 'gamma1', 'gamma2', 'kappa', 'raOffset', 'decOffset',
347 'spatialmodel', 'majorAxis', 'minorAxis', 'positionAngle', 'sindex',
348 'internalExtinctionModel', 'internalAv', 'internalRv',
349 'galacticExtinctionModel', 'galacticAv', 'galacticRv',]
351 default_columns = [('gamma1', 0., float), ('gamma2', 0., float), ('kappa', 0., float),
352 ('raOffset', 0., float), ('decOffset', 0., float),
353 ('galacticAv', 0.1, float), ('galacticRv', 3.1, float),
354 ('galacticExtinctionModel', 'CCM', (str, 3)),
355 ('internalExtinctionModel', 'CCM', (str, 3)), ('internalAv', 0., float),
356 ('internalRv', 3.1, float)]
358 default_formats = {'S': '%s', 'f': '%.9g', 'i': '%i'}
360 spatialModel = "sersic2d"
362 transformations = {'raPhoSim': np.degrees, 'decPhoSim': np.degrees, 'positionAngle': np.degrees,
363 'majorAxis': arcsecFromRadians, 'minorAxis': arcsecFromRadians}
365 @compound('raPhoSim', 'decPhoSim')
366 def get_phoSimCoordinates(self):
367 """Getter for RA, Dec coordinates expected by PhoSim.
369 These are observed RA, Dec coordinates with the effects of nutation, aberration,
370 and precession subtracted out by the PhosimInputBase._dePrecess() method.
371 This preserves the relative effects of nutation, aberration, and precession while
372 re-aligning the catalog with the boresite RA, Dec so that astrometric solutions
373 make sense."""
375 ra = self.column_by_name('raJ2000')
376 dec = self.column_by_name('decJ2000')
377 raObs, decObs = _observedFromICRS(ra, dec, includeRefraction = False, obs_metadata=self.obs_metadata,
378 epoch=self.db_obj.epoch)
380 return self._dePrecess(raObs, decObs, self.obs_metadata)
383class PhoSimCatalogSSM(PhosimInputBase, PhoSimAstrometrySSM):
385 catalog_type = 'phoSim_catalog_SSM'
387 column_outputs = ['prefix', 'uniqueId', 'raPhoSim', 'decPhoSim', 'phoSimMagNorm', 'sedFilepath',
388 'redshift', 'gamma1', 'gamma2', 'kappa', 'raOffset', 'decOffset',
389 'spatialmodel', 'internalExtinctionModel', 'galacticExtinctionModel']
391 default_columns = [('redshift', 0., float), ('gamma1', 0., float), ('gamma2', 0., float),
392 ('kappa', 0., float), ('raOffset', 0., float), ('decOffset', 0., float),
393 ('galacticExtinctionModel', 'none', (str, 4)),
394 ('internalExtinctionModel', 'none', (str, 4))]
396 default_formats = {'S': '%s', 'f': '%.9g', 'i': '%i'}
398 spatialModel = "point"
400 transformations = {'raPhoSim': np.degrees, 'decPhoSim': np.degrees}