Coverage for python/lsst/sims/GalSimInterface/galSimDetector.py : 21%

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
1from builtins import zip
2from builtins import object
3import re
4import copy
5from collections import namedtuple
6import warnings
7import astropy.time
8import astropy.coordinates
9from astropy._erfa import ErfaWarning
10import galsim
11import numpy as np
12import lsst.geom as LsstGeom
13from lsst.obs.lsstSim import LsstSimMapper
14from lsst.sims.utils import arcsecFromRadians
15from lsst.sims.GalSimInterface.wcsUtils import tanSipWcsFromDetector
16from lsst.sims.GalSimInterface import GalSimCameraWrapper
17from lsst.sims.photUtils import PhotometricParameters
19__all__ = ["GalSimDetector", "make_galsim_detector", "LsstObservatory"]
22class GalSim_afw_TanSipWCS(galsim.wcs.CelestialWCS):
23 """
24 This class uses methods from lsst.geom and meas_astrom to
25 fit a TAN-SIP WCS to an afw.cameraGeom.Detector and then wrap
26 that WCS into something that GalSim can parse.
28 For documentation on the TAN-SIP WCS see
30 Shupe and Hook (2008)
31 http://fits.gsfc.nasa.gov/registry/sip/SIP_distortion_v1_0.pdf
32 """
34 def __init__(self, detectorName, cameraWrapper, obs_metadata, epoch, photParams=None, wcs=None):
35 """
36 @param [in] detectorName is the name of the detector as stored
37 by afw
39 @param [in] cameraWrapper is an instantionat of a GalSimCameraWrapper
41 @param [in] obs_metadata is an instantiation of ObservationMetaData
42 characterizing the telescope pointing
44 @param [in] epoch is the epoch in Julian years of the equinox against
45 which RA and Dec are measured
47 @param [in] photParams is an instantiation of PhotometricParameters
48 (it will contain information about gain, exposure time, etc.)
50 @param [in] wcs is a kwarg that is used by the method _newOrigin().
51 The wcs kwarg in this constructor method should not be used by users.
52 """
54 if not isinstance(cameraWrapper, GalSimCameraWrapper):
55 raise RuntimeError("You must pass GalSim_afw_TanSipWCS an instantiation "
56 "of GalSimCameraWrapper or one of its daughter "
57 "classes")
59 if wcs is None:
60 self._tanSipWcs = tanSipWcsFromDetector(detectorName, cameraWrapper, obs_metadata, epoch)
61 else:
62 self._tanSipWcs = wcs
64 self.detectorName = detectorName
65 self.cameraWrapper = cameraWrapper
66 self.obs_metadata = obs_metadata
67 self.photParams = photParams
68 self.epoch = epoch
70 # this is needed to match the GalSim v1.5 API
71 self._color = None
73 self.fitsHeader = self._tanSipWcs.getFitsMetadata()
74 self.fitsHeader.set("EXTTYPE", "IMAGE")
76 if self.obs_metadata.bandpass is not None:
77 if (not isinstance(self.obs_metadata.bandpass, list) and not
78 isinstance(self.obs_metadata.bandpass, np.ndarray)):
79 self.fitsHeader.set("FILTER", self.obs_metadata.bandpass)
81 if self.obs_metadata.mjd is not None:
82 self.fitsHeader.set("MJD-OBS", self.obs_metadata.mjd.TAI)
83 mjd_obs = astropy.time.Time(self.obs_metadata.mjd.TAI, format='mjd')
84 self.fitsHeader.set('DATE-OBS', mjd_obs.isot)
86 if self.photParams is not None:
87 exptime = self.photParams.nexp*self.photParams.exptime
88 self.fitsHeader.set("EXPTIME", exptime)
89 with warnings.catch_warnings():
90 warnings.filterwarnings('ignore', 'ERFA function', ErfaWarning)
91 mjd_end = mjd_obs + astropy.time.TimeDelta(exptime, format='sec')
92 self.fitsHeader.set('DATE-END', mjd_end.isot)
94 # Add pointing information to FITS header.
95 if self.obs_metadata.pointingRA is not None:
96 self.fitsHeader.set('RATEL', obs_metadata.pointingRA)
97 if self.obs_metadata.pointingDec is not None:
98 self.fitsHeader.set('DECTEL', obs_metadata.pointingDec)
99 if self.obs_metadata.rotSkyPos is not None:
100 self.fitsHeader.set('ROTANGLE', obs_metadata.rotSkyPos)
102 # Add airmass, needed by jointcal.
103 if self.obs_metadata.OpsimMetaData is not None:
104 try:
105 airmass = self.obs_metadata.OpsimMetaData['airmass']
106 except KeyError:
107 pass
108 else:
109 self.fitsHeader.set('AIRMASS', airmass)
111 # Add boilerplate keywords requested by DM.
112 self.fitsHeader.set('TELESCOP', 'LSST')
113 self.fitsHeader.set('INSTRUME', 'CAMERA')
114 self.fitsHeader.set('SIMULATE', True)
115 self.fitsHeader.set('ORIGIN', 'IMSIM')
116 observatory = LsstObservatory()
117 self.fitsHeader.set('OBS-LONG', observatory.getLongitude().asDegrees())
118 self.fitsHeader.set('OBS-LAT', observatory.getLatitude().asDegrees())
119 self.fitsHeader.set('OBS-ELEV', observatory.getElevation())
120 obs_location = observatory.getLocation()
121 self.fitsHeader.set('OBSGEO-X', obs_location.geocentric[0].value)
122 self.fitsHeader.set('OBSGEO-Y', obs_location.geocentric[1].value)
123 self.fitsHeader.set('OBSGEO-Z', obs_location.geocentric[2].value)
125 self.crpix1 = self.fitsHeader.getScalar("CRPIX1")
126 self.crpix2 = self.fitsHeader.getScalar("CRPIX2")
128 self.afw_crpix1 = self.crpix1
129 self.afw_crpix2 = self.crpix2
131 self.crval1 = self.fitsHeader.getScalar("CRVAL1")
132 self.crval2 = self.fitsHeader.getScalar("CRVAL2")
134 self.origin = galsim.PositionD(x=self.crpix1, y=self.crpix2)
135 self._color = None
137 def _radec(self, x, y, color=None):
138 """
139 This is a method required by the GalSim WCS API
141 Convert pixel coordinates into ra, dec coordinates.
142 x and y already have crpix1 and crpix2 subtracted from them.
143 Return ra, dec in radians.
145 Note: the color arg is ignored. It is only there to
146 match the GalSim v1.5 API
147 """
149 chipNameList = [self.detectorName]
151 if type(x) is np.ndarray:
152 chipNameList = chipNameList * len(x)
154 ra, dec = self.cameraWrapper._raDecFromPixelCoords(x + self.afw_crpix1, y + self.afw_crpix2, chipNameList,
155 obs_metadata=self.obs_metadata,
156 epoch=self.epoch)
158 if type(x) is np.ndarray:
159 return (ra, dec)
160 else:
161 return (ra[0], dec[0])
163 def _xy(self, ra, dec):
164 """
165 This is a method required by the GalSim WCS API
167 Convert ra, dec in radians into x, y in pixel space with crpix subtracted.
168 """
170 chipNameList = [self.detectorName]
172 if type(ra) is np.ndarray:
173 chipNameList = chipNameList * len(ra)
175 xx, yy = self.cameraWrapper._pixelCoordsFromRaDec(ra=ra, dec=dec, chipName=chipNameList,
176 obs_metadata=self.obs_metadata,
177 epoch=self.epoch)
179 if type(ra) is np.ndarray:
180 return (xx-self.crpix1, yy-self.crpix2)
181 else:
182 return (xx[0]-self.crpix1, yy-self.crpix2)
184 def _newOrigin(self, origin):
185 """
186 This is a method required by the GalSim WCS API. It returns
187 a copy of self, but with the pixel-space origin translated to a new
188 position.
190 @param [in] origin is an instantiation of a galsim.PositionD representing
191 the a point in pixel space to which you want to move the origin of the WCS
193 @param [out] _newWcs is a WCS identical to self, but with the origin
194 in pixel space moved to the specified origin
195 """
196 _newWcs = GalSim_afw_TanSipWCS.__new__(GalSim_afw_TanSipWCS)
197 _newWcs.__dict__.update(self.__dict__)
198 _newWcs.crpix1 = origin.x
199 _newWcs.crpix2 = origin.y
200 _newWcs.fitsHeader = copy.deepcopy(self.fitsHeader)
201 _newWcs.fitsHeader.set('CRPIX1', origin.x)
202 _newWcs.fitsHeader.set('CRPIX2', origin.y)
203 return _newWcs
205 def _writeHeader(self, header, bounds):
206 for key in self.fitsHeader.getOrderedNames():
207 header[key] = self.fitsHeader.getScalar(key)
209 return header
211TreeRingInfo = namedtuple('TreeRingInfo', ['center', 'func'])
213class GalSimDetector(object):
214 """
215 This class stores information about individual detectors for use by the GalSimInterpreter
216 """
218 def __init__(self, detectorName, cameraWrapper, obs_metadata, epoch, photParams=None):
219 """
220 @param [in] detectorName is the name of the detector as stored
221 by afw
223 @param [in] cameraWrapper is an instantionat of a GalSimCameraWrapper
225 @param [in] photParams is an instantiation of the PhotometricParameters class that carries
226 details about the photometric response of the telescope.
228 This class will generate its own internal variable self.fileName which is
229 the name of the detector as it will appear in the output FITS files
230 """
232 if not isinstance(cameraWrapper, GalSimCameraWrapper):
233 raise RuntimeError("You must pass GalSimDetector an instantiation "
234 "of GalSimCameraWrapper or one of its daughter "
235 "classes")
237 if detectorName not in cameraWrapper.camera:
238 raise RuntimeError("detectorName needs to be in the camera wrapped by "
239 "cameraWrapper when instantiating a GalSimDetector\n"
240 "%s is not in your cameraWrapper.camera" % detectorName)
242 if photParams is None:
243 raise RuntimeError("You need to specify an instantiation of PhotometricParameters " +
244 "when constructing a GalSimDetector")
246 self._wcs = None # this will be created when it is actually called for
247 self._name = detectorName
248 self._cameraWrapper = cameraWrapper
249 self._obs_metadata = obs_metadata
250 self._epoch = epoch
251 self._detector_type = self._cameraWrapper.camera[self._name].getType()
253 # Default Tree Ring properties, i.e., no tree rings:
254 self._tree_rings = TreeRingInfo(galsim.PositionD(0, 0), None)
256 # We are transposing the coordinates because of the difference
257 # between how DM defines pixel coordinates and how the
258 # Camera team defines pixel coordinates
259 bbox = self._cameraWrapper.getBBox(self._name)
260 self._xMinPix = bbox.getMinX()
261 self._xMaxPix = bbox.getMaxX()
262 self._yMinPix = bbox.getMinY()
263 self._yMaxPix = bbox.getMaxY()
265 self._bbox = LsstGeom.Box2D(bbox)
267 centerPupil = self._cameraWrapper.getCenterPupil(self._name)
268 self._xCenterArcsec = arcsecFromRadians(centerPupil.getX())
269 self._yCenterArcsec = arcsecFromRadians(centerPupil.getY())
271 centerPixel = self._cameraWrapper.getCenterPixel(self._name)
272 self._xCenterPix = centerPixel.getX()
273 self._yCenterPix = centerPixel.getY()
275 self._xMinArcsec = None
276 self._yMinArcsec = None
277 self._xMaxArcsec = None
278 self._yMaxArcsec = None
280 for cameraPointPupil in self._cameraWrapper.getCornerPupilList(self._name):
282 xx = arcsecFromRadians(cameraPointPupil.getX())
283 yy = arcsecFromRadians(cameraPointPupil.getY())
284 if self._xMinArcsec is None or xx < self._xMinArcsec:
285 self._xMinArcsec = xx
286 if self._xMaxArcsec is None or xx > self._xMaxArcsec:
287 self._xMaxArcsec = xx
288 if self._yMinArcsec is None or yy < self._yMinArcsec:
289 self._yMinArcsec = yy
290 if self._yMaxArcsec is None or yy > self._yMaxArcsec:
291 self._yMaxArcsec = yy
293 self._photParams = photParams
294 self._fileName = self._getFileName()
296 def _getFileName(self):
297 """
298 Format the name of the detector to add to the name of the FITS file
299 """
300 detectorName = self.name
301 detectorName = detectorName.replace(',', '')
302 detectorName = detectorName.replace(':', '')
303 detectorName = detectorName.replace(' ', '_')
304 return detectorName
306 def pixelCoordinatesFromRaDec(self, ra, dec):
307 """
308 Convert RA, Dec into pixel coordinates on this detector
310 @param [in] ra is a numpy array or a float indicating RA in radians
312 @param [in] dec is a numpy array or a float indicating Dec in radians
314 @param [out] xPix is a numpy array indicating the x pixel coordinate
316 @param [out] yPix is a numpy array indicating the y pixel coordinate
317 """
319 nameList = [self.name]
320 if type(ra) is np.ndarray:
321 nameList = nameList*len(ra)
322 raLocal = ra
323 decLocal = dec
324 else:
325 raLocal = np.array([ra])
326 decLocal = np.array([dec])
328 xPix, yPix = self._cameraWrapper._pixelCoordsFromRaDec(raLocal, decLocal, chipName=nameList,
329 obs_metadata=self._obs_metadata,
330 epoch=self._epoch)
332 return xPix, yPix
334 def pixelCoordinatesFromPupilCoordinates(self, xPupil, yPupil):
335 """
336 Convert pupil coordinates into pixel coordinates on this detector
338 @param [in] xPupil is a numpy array or a float indicating x pupil coordinates
339 in radians
341 @param [in] yPupil a numpy array or a float indicating y pupil coordinates
342 in radians
344 @param [out] xPix is a numpy array indicating the x pixel coordinate
346 @param [out] yPix is a numpy array indicating the y pixel coordinate
347 """
349 nameList = [self._name]
350 if type(xPupil) is np.ndarray:
351 nameList = nameList*len(xPupil)
352 xp = xPupil
353 yp = yPupil
354 else:
355 xp = np.array([xPupil])
356 yp = np.array([yPupil])
358 xPix, yPix = self._cameraWrapper.pixelCoordsFromPupilCoords(xp, yp, nameList, self.obs_metadata)
360 return xPix, yPix
362 def containsRaDec(self, ra, dec):
363 """
364 Does a given RA, Dec fall on this detector?
366 @param [in] ra is a numpy array or a float indicating RA in radians
368 @param [in] dec is a numpy array or a float indicating Dec in radians
370 @param [out] answer is an array of booleans indicating whether or not
371 the corresponding RA, Dec pair falls on this detector
372 """
374 xPix, yPix = self.pixelCoordinatesFromRaDec(ra, dec)
375 points = [LsstGeom.Point2D(xx, yy) for xx, yy in zip(xPix, yPix)]
376 answer = [self._bbox.contains(pp) for pp in points]
377 return answer
379 def containsPupilCoordinates(self, xPupil, yPupil):
380 """
381 Does a given set of pupil coordinates fall on this detector?
383 @param [in] xPupil is a numpy array or a float indicating x pupil coordinates
384 in radians
386 @param [in] yPupuil is a numpy array or a float indicating y pupil coordinates
387 in radians
389 @param [out] answer is an array of booleans indicating whether or not
390 the corresponding RA, Dec pair falls on this detector
391 """
392 xPix, yPix = self.pixelCoordinatesFromPupilCoordinates(xPupil, yPupil)
393 points = [LsstGeom.Point2D(xx, yy) for xx, yy in zip(xPix, yPix)]
394 answer = [self._bbox.contains(pp) for pp in points]
395 return answer
397 @property
398 def xMinPix(self):
399 """Minimum x pixel coordinate of the detector"""
400 return self._xMinPix
402 @xMinPix.setter
403 def xMinPix(self, value):
404 raise RuntimeError("You should not be setting xMinPix on the fly; "
405 "just instantiate a new GalSimDetector")
407 @property
408 def xMaxPix(self):
409 """Maximum x pixel coordinate of the detector"""
410 return self._xMaxPix
412 @xMaxPix.setter
413 def xMaxPix(self, value):
414 raise RuntimeError("You should not be setting xMaxPix on the fly; "
415 "just instantiate a new GalSimDetector")
417 @property
418 def yMinPix(self):
419 """Minimum y pixel coordinate of the detector"""
420 return self._yMinPix
422 @yMinPix.setter
423 def yMinPix(self, value):
424 raise RuntimeError("You should not be setting yMinPix on the fly; "
425 "just instantiate a new GalSimDetector")
427 @property
428 def yMaxPix(self):
429 """Maximum y pixel coordinate of the detector"""
430 return self._yMaxPix
432 @yMaxPix.setter
433 def yMaxPix(self, value):
434 raise RuntimeError("You should not be setting yMaxPix on the fly; "
435 "just instantiate a new GalSimDetector")
437 @property
438 def xCenterPix(self):
439 """Center x pixel coordinate of the detector"""
440 return self._xCenterPix
442 @xCenterPix.setter
443 def xCenterPix(self, value):
444 raise RuntimeError("You should not be setting xCenterPix on the fly; "
445 "just instantiate a new GalSimDetector")
447 @property
448 def yCenterPix(self):
449 """Center y pixel coordinate of the detector"""
450 return self._yCenterPix
452 @yCenterPix.setter
453 def yCenterPix(self, value):
454 raise RuntimeError("You should not be setting yCenterPix on the fly; "
455 "just instantiate a new GalSimDetector")
457 @property
458 def xMaxArcsec(self):
459 """Maximum x pupil coordinate of the detector in arcseconds"""
460 return self._xMaxArcsec
462 @xMaxArcsec.setter
463 def xMaxArcsec(self, value):
464 raise RuntimeError("You should not be setting xMaxArcsec on the fly; "
465 "just instantiate a new GalSimDetector")
467 @property
468 def xMinArcsec(self):
469 """Minimum x pupil coordinate of the detector in arcseconds"""
470 return self._xMinArcsec
472 @xMinArcsec.setter
473 def xMinArcsec(self, value):
474 raise RuntimeError("You should not be setting xMinArcsec on the fly; "
475 "just instantiate a new GalSimDetector")
477 @property
478 def yMaxArcsec(self):
479 """Maximum y pupil coordinate of the detector in arcseconds"""
480 return self._yMaxArcsec
482 @yMaxArcsec.setter
483 def yMaxArcsec(self, value):
484 raise RuntimeError("You should not be setting yMaxArcsec on the fly; "
485 "just instantiate a new GalSimDetector")
487 @property
488 def yMinArcsec(self):
489 """Minimum y pupil coordinate of the detector in arcseconds"""
490 return self._yMinArcsec
492 @yMinArcsec.setter
493 def yMinArcsec(self, value):
494 raise RuntimeError("You should not be setting yMinArcsec on the fly; "
495 "just instantiate a new GalSimDetector")
497 @property
498 def xCenterArcsec(self):
499 """Center x pupil coordinate of the detector in arcseconds"""
500 return self._xCenterArcsec
502 @xCenterArcsec.setter
503 def xCenterArcsec(self, value):
504 raise RuntimeError("You should not be setting xCenterArcsec on the fly; "
505 "just instantiate a new GalSimDetector")
507 @property
508 def yCenterArcsec(self):
509 """Center y pupil coordinate of the detector in arcseconds"""
510 return self._yCenterArcsec
512 @yCenterArcsec.setter
513 def yCenterArcsec(self, value):
514 raise RuntimeError("You should not be setting yCenterArcsec on the fly; "
515 "just instantiate a new GalSimDetector")
517 @property
518 def epoch(self):
519 """Epoch of the equinox against which RA and Dec are measured in Julian years"""
520 return self._epoch
522 @epoch.setter
523 def epoch(self, value):
524 raise RuntimeError("You should not be setting epoch on the fly; "
525 "just instantiate a new GalSimDetector")
527 @property
528 def obs_metadata(self):
529 """ObservationMetaData instantiation describing the telescope pointing"""
530 return self._obs_metadata
532 @obs_metadata.setter
533 def obs_metadata(self, value):
534 raise RuntimeError("You should not be setting obs_metadata on the fly; "
535 "just instantiate a new GalSimDetector")
537 @property
538 def name(self):
539 """Name of the detector"""
540 return self._name
542 @name.setter
543 def name(self, value):
544 raise RuntimeError("You should not be setting name on the fly; "
545 "just instantiate a new GalSimDetector")
547 @property
548 def camera_wrapper(self):
549 return self._cameraWrapper
551 @camera_wrapper.setter
552 def camera_wrapper(self, value):
553 raise RuntimeError("You should not be setting the camera_wrapper on the fly; "
554 "just instantiate a new GalSimDetector")
556 @property
557 def photParams(self):
558 """PhotometricParameters instantiation characterizing the detector"""
559 return self._photParams
561 @photParams.setter
562 def photParams(self, value):
563 raise RuntimeError("You should not be setting photParams on the fly; "
564 "just instantiate a new GalSimDetector")
566 @property
567 def fileName(self):
568 """Name of the FITS file corresponding to this detector"""
569 return self._fileName
571 @fileName.setter
572 def fileName(self, value):
573 raise RuntimeError("You should not be setting fileName on the fly; "
574 "just instantiate a new GalSimDetector")
576 @property
577 def wcs(self):
578 """WCS corresponding to this detector"""
579 if self._wcs is None:
580 self._wcs = GalSim_afw_TanSipWCS(self._name, self._cameraWrapper,
581 self.obs_metadata, self.epoch,
582 photParams=self.photParams)
584 if re.match('R[0-9][0-9]_S[0-9][0-9]', self.fileName) is not None:
585 # This is an LSST camera; format the FITS header to feed through DM code
587 wcsName = self.fileName
589 self._wcs.fitsHeader.set("CHIPID", wcsName)
591 obshistid = 9999
593 if self.obs_metadata.OpsimMetaData is not None:
594 if 'obshistID' in self.obs_metadata.OpsimMetaData:
595 self._wcs.fitsHeader.set("OBSID",
596 self.obs_metadata.OpsimMetaData['obshistID'])
597 obshistid = self.obs_metadata.OpsimMetaData['obshistID']
599 bp = self.obs_metadata.bandpass
600 if not isinstance(bp, list) and not isinstance(bp, np.ndarray):
601 filt_num = {'u': 0, 'g': 1, 'r': 2, 'i': 3, 'z': 4, 'y': 5}[bp]
602 else:
603 filt_num = 2
605 out_name = 'lsst_e_%d_f%d_%s_E000' % (obshistid, filt_num, wcsName)
606 self._wcs.fitsHeader.set("OUTFILE", out_name)
608 return self._wcs
610 @wcs.setter
611 def wcs(self, value):
612 raise RuntimeError("You should not be setting wcs on the fly; "
613 "just instantiate a new GalSimDetector")
615 @property
616 def tree_rings(self):
617 return self._tree_rings
619 @tree_rings.setter
620 def tree_rings(self, center_func_tuple):
621 self._tree_rings = TreeRingInfo(*center_func_tuple)
624def make_galsim_detector(camera_wrapper, detname, phot_params,
625 obs_metadata, epoch=2000.0):
626 """
627 Create a GalSimDetector object given the desired detector name.
629 Parameters
630 ----------
631 camera_wrapper: lsst.sims.GalSimInterface.GalSimCameraWrapper
632 An object representing the camera being simulated
634 detname: str
635 The name of the detector in the LSST focal plane to create,
636 e.g., "R:2,2 S:1,1".
638 phot_params: lsst.sims.photUtils.PhotometricParameters
639 An object containing the physical parameters representing
640 the photometric properties of the system
642 obs_metadata: lsst.sims.utils.ObservationMetaData
643 Characterizing the pointing of the telescope
645 epoch: float
646 Representing the Julian epoch against which RA, Dec are
647 reckoned (default = 2000)
649 Returns
650 -------
651 GalSimDetector
652 """
653 centerPupil = camera_wrapper.getCenterPupil(detname)
654 centerPixel = camera_wrapper.getCenterPixel(detname)
656 translationPupil = camera_wrapper.pupilCoordsFromPixelCoords(centerPixel.getX()+1,
657 centerPixel.getY()+1,
658 detname,
659 obs_metadata)
661 plateScale = np.sqrt(np.power(translationPupil[0]-centerPupil.getX(), 2) +
662 np.power(translationPupil[1]-centerPupil.getY(), 2))/np.sqrt(2.0)
664 plateScale = 3600.0*np.degrees(plateScale)
666 # make a detector-custom photParams that copies all of the quantities
667 # in the catalog photParams, except the platescale, which is
668 # calculated above
669 params = PhotometricParameters(exptime=phot_params.exptime,
670 nexp=phot_params.nexp,
671 effarea=phot_params.effarea,
672 gain=phot_params.gain,
673 readnoise=phot_params.readnoise,
674 darkcurrent=phot_params.darkcurrent,
675 othernoise=phot_params.othernoise,
676 platescale=plateScale)
678 return GalSimDetector(detname, camera_wrapper,
679 obs_metadata=obs_metadata, epoch=epoch,
680 photParams=params)
683class LsstObservatory:
684 """
685 Class to encapsulate an Observatory object and compute the
686 observatory location information.
687 """
688 def __init__(self):
689 self.observatory = LsstSimMapper().MakeRawVisitInfoClass().observatory
691 def getLocation(self):
692 """
693 The LSST observatory location in geocentric coordinates.
695 Returns
696 -------
697 astropy.coordinates.earth.EarthLocation
698 """
699 return astropy.coordinates.EarthLocation.from_geodetic(
700 self.observatory.getLongitude().asDegrees(),
701 self.observatory.getLatitude().asDegrees(),
702 self.observatory.getElevation())
704 def __getattr__(self, attr):
705 if hasattr(self.observatory, attr):
706 return getattr(self.observatory, attr)