Source code for lsst.sims.movingObjects.directObs

import logging
import numpy as np
import datetime

from .baseObs import BaseObs

__all__ = ['DirectObs']


[docs]class DirectObs(BaseObs): """ Generate observations of a set of moving objects: exact ephemeris at the times of each observation. First generates observations on a rough grid and looks for observations within a specified tolerance of the actual observations; for the observations which pass this cut, generates a precise ephemeris and checks if the object is within the FOV. Parameters ---------- footPrint: str, opt Specify the footprint for the FOV. Options include "camera", "circle", "rectangle". 'Camera' means use the actual LSST camera footprint (following a rough cut with a circular FOV). Default is circular FOV. rFov : float, opt If footprint is "circular", this is the radius of the fov (in degrees). Default 1.75 degrees. xTol : float, opt If footprint is "rectangular", this is half of the width of the (on-sky) fov in the RA direction (in degrees). Default 5 degrees. (so size of footprint in degrees will be 10 degrees in the RA direction). yTol : float, opt If footprint is "rectangular", this is half of the width of the fov in Declination (in degrees). Default is 3 degrees (so size of footprint in degrees will be 6 degrees in the Dec direction). ephMode: str, opt Mode for ephemeris generation - nbody or 2body. Default is nbody. prelimEphMode: str, opt Mode for preliminary ephemeris generation, if any is done. Default is 2body. ephType: str, opt Type of ephemerides to generate - full or basic. Full includes all values calculated by openorb; Basic includes a more basic set. Default is Basic. (this includes enough information for most standard MAF metrics). ephFile: str or None, opt The name of the planetary ephemerides file to use for ephemeris generation. Default (None) will use the default for PyOrbEphemerides. obsCode: str, opt Observatory code for ephemeris generation. Default is "I11" - Cerro Pachon. obsTimeCol: str, opt Name of the time column in the obsData. Default 'observationStartMJD'. obsTimeScale: str, opt Type of timescale for MJD (TAI or UTC currently). Default TAI. seeingCol: str, opt Name of the seeing column in the obsData. Default 'seeingFwhmGeom'. This should be the geometric/physical seeing as it is used for the trailing loss calculation. visitExpTimeCol: str, opt Name of the visit exposure time column in the obsData. Default 'visitExposureTime'. obsRA: str, opt Name of the RA column in the obsData. Default 'fieldRA'. obsDec: str, opt Name of the Dec column in the obsData. Default 'fieldDec'. obsRotSkyPos: str, opt Name of the Rotator column in the obsData. Default 'rotSkyPos'. obsDegrees: bool, opt Whether the observational data is in degrees or radians. Default True (degrees). outfileName : str, opt The output file name. Default is 'lsst_obs.dat'. obsMetadata : str, opt A string that captures provenance information about the observations. For example: 'kraken_2026, MJD 59853-61677' or 'baseline2018a minus NES' Default ''. tstep: float, opt The time between initial (rough) ephemeris generation points, in days. Default 1 day. roughTol: float, opt The initial rough tolerance value for positions, used as a first cut to identify potential observations (in degrees). Default 10 degrees. """ def __init__(self, footprint='circular', rFov=1.75, xTol=5, yTol=3, ephMode='nbody', prelimEphMode='2body', ephType='basic', obsCode='I11', ephFile=None, obsTimeCol='observationStartMJD', obsTimeScale='TAI', seeingCol='seeingFwhmGeom', visitExpTimeCol='visitExposureTime', obsRA='fieldRA', obsDec='fieldDec', obsRotSkyPos='rotSkyPos', obsDegrees=True, outfileName='lsst_obs.dat', obsMetadata='', tstep=1.0, roughTol=10.0): super().__init__(footprint=footprint, rFov=rFov, xTol=xTol, yTol=yTol, ephMode=ephMode, ephType=ephType, obsCode=obsCode, ephFile=ephFile, obsTimeCol=obsTimeCol, obsTimeScale=obsTimeScale, seeingCol=seeingCol, visitExpTimeCol=visitExpTimeCol, obsRA=obsRA, obsDec=obsDec, obsRotSkyPos=obsRotSkyPos, obsDegrees=obsDegrees, outfileName=outfileName, obsMetadata=obsMetadata) self.tstep = tstep self.roughTol = roughTol if prelimEphMode not in ('2body', 'nbody'): raise ValueError('Ephemeris generation must be 2body or nbody.') self.prelimEphMode = prelimEphMode def _headerMeta(self): # Specific header information for direct obs. self.outfile.write('# direct obs header metadata\n') self.outfile.write('# observation generation via %s\n' % self.__class__.__name__) self.outfile.write('# ephMode %s prelimEphMode %s\n' % (self.ephMode, self.prelimEphMode)) self.outfile.write('# rough tolerance for preliminary match %f\n' % self.roughTol) self.outfile.write('# time step for preliminary match %f\n' % self.tstep)
[docs] def run(self, orbits, obsData): """Find and write the observations of each object to disk. For each object, generate a very rough grid of ephemeris points (typically using 2body integration). Then identify pointings in obsData which are within Parameters ---------- orbits: lsst.sims.movingObjects.Orbits The orbits to generate ephemerides for. obsData : np.recarray The simulated pointing history data. """ # Set the times for the rough ephemeris grid. timeStep = float(self.tstep) timeStart = obsData[self.obsTimeCol].min() - timeStep timeEnd = obsData[self.obsTimeCol].max() + timeStep rough_times = np.arange(timeStart, timeEnd + timeStep / 2.0, timeStep) logging.info('Generating preliminary ephemerides on a grid of %f day timesteps.' % (timeStep)) # For each object, identify observations where the object is within the FOV (or camera footprint). for i, sso in enumerate(orbits): objid = sso.orbits['objId'].iloc[0] sedname = sso.orbits['sed_filename'].iloc[0] # Generate ephemerides on the rough grid. logging.debug(("%d/%d id=%s : " % (i, len(orbits), objid)) + datetime.datetime.now().strftime("Prelim start: %Y-%m-%d %H:%M:%S") + " nRoughTimes: %s" % len(rough_times)) ephs = self.generateEphemerides(sso, rough_times, ephMode=self.prelimEphMode, ephType=self.ephType)[0] mu = ephs['velocity'] logging.debug(("%d/%d id=%s : " % (i, len(orbits), objid)) + datetime.datetime.now().strftime("Prelim end: %Y-%m-%d %H:%M:%S") + " π(median, max), min(geo_dist): %.2f, %.2f deg/day %.2f AU" % (np.median(mu), np.max(mu), np.min(ephs['geo_dist']))) # Find observations which come within roughTol of the fov. ephsIdxs = np.searchsorted(ephs['time'], obsData[self.obsTimeCol]) roughIdxObs = self._ssoInCircleFov(ephs[ephsIdxs], obsData, self.roughTol) if len(roughIdxObs) > 0: # Generate exact ephemerides for these times. times = obsData[self.obsTimeCol][roughIdxObs] logging.debug(("%d/%d id=%s : " % (i, len(orbits), objid)) + datetime.datetime.now().strftime("Exact start: %Y-%m-%d %H:%M:%S") + " nExactTimes: %s" % len(times)) ephs = self.generateEphemerides(sso, times, ephMode=self.ephMode, ephType=self.ephType)[0] logging.debug(("%d/%d id=%s : " % (i, len(orbits), objid)) + datetime.datetime.now().strftime("Exact end: %Y-%m-%d %H:%M:%S")) # Identify the objects which fell within the specific footprint. idxObs = self.ssoInFov(ephs, obsData[roughIdxObs]) logging.info(("%d/%d id=%s : " % (i, len(orbits), objid)) + "Object in %d out of %d potential fields (%.2f%% success rate)" % (len(idxObs), len(times), 100.*float(len(idxObs))/len(times))) # Write these observations to disk. self.writeObs(objid, ephs[idxObs], obsData[roughIdxObs][idxObs], sedname=sedname)