def make_refcat(ra, dec): """ Make a reference catalog for forced photometry Parameters: ----------- ra : sequence or array Right Ascension in decimal degrees dec : sequence or array Declination in decimal degrees Returns: -------- src_cat : lsst.afw.table.tableLib.SourceCatalog Source catalog for the forced photometry task """ schema = out_butler.get('src_schema', immediate=True).schema mapper = afwTable.SchemaMapper(schema) mapper.addMinimalSchema(schema) newSchema = mapper.getOutputSchema() src_cat = afwTable.SourceCatalog(newSchema) for row in zip(ra, dec): record = src_cat.addNew() record.set('coord_ra', Angle(row[0] * degrees)) record.set('coord_dec', Angle(row[1] * degrees)) return (src_cat)
def create_source_catalog_from_text_and_butler(repo_dir, info, dataset='src'): butler = dafPersistence.Butler(repo_dir) schema = butler.get(dataset + "_schema", immediate=True).schema mapper = SchemaMapper(schema) mapper.addMinimalSchema(schema) newSchema = mapper.getOutputSchema() src_cat = SourceCatalog(newSchema) for row in info: record = src_cat.addNew() record.set('coord_ra', Angle(row['RA']*degrees)) record.set('coord_dec', Angle(row['Dec']*degrees)) print(src_cat['coord_ra'], src_cat['coord_dec']) return(src_cat)
def _horizonRotAngle(self): """!Compute rotation angle of camera with respect to horizontal coordinates from self.visitInfo. @returns horizon rotation angle. """ observatory = self.visitInfo.getObservatory() lat = observatory.getLatitude() lon = observatory.getLongitude() radec = self.visitInfo.getBoresightRaDec() ra = radec.getRa() dec = radec.getDec() era = self.visitInfo.getEra() ha = (era + lon - ra).wrap() alt = self.visitInfo.getBoresightAzAlt().getLatitude() # parallactic angle sinParAng = (np.cos(lat.asRadians()) * np.sin(ha.asRadians()) / np.cos(alt.asRadians())) cosParAng = np.sqrt(1 - sinParAng * sinParAng) if dec > lat: cosParAng = -cosParAng parAng = Angle(np.arctan2(sinParAng, cosParAng)) bra = self.visitInfo.getBoresightRotAngle() return (bra - parAng).wrap()
def testZenithZero(self): """There should be no refraction exactly at zenith.""" elevation = Angle(np.pi / 2.) wl = 505. # in nm refractZen = refraction(wl, elevation, self.observatory, weather=self.weather) self.assertAlmostEqual(refractZen.asDegrees(), 0.)
def testRefractWeatherNan(self): """Test the values of refraction when no weather is supplied.""" elevation = Angle(np.random.random() * np.pi / 2.) elevation = Angle(np.pi / 6.) # Airmass 2.0 wls = [370., 480., 620., 860., 960., 1025.] # in nm refVals = [ 76.7339313496466, 75.36869048516252, 74.60378630142982, 74.06932963258161, 73.95668242959853, 73.9006681751504, ] for wl, refVal in zip(wls, refVals): refract = refraction(wl, elevation, self.observatory) self.assertFloatsAlmostEqual(refract.asArcseconds(), refVal, rtol=1e-3)
def testNoDifferential(self): """There should be no differential refraction if the wavelength is the same as the reference.""" wl = 470. # in nm wl_ref = wl elevation = Angle(np.random.random() * np.pi / 2.) diffRefraction = differentialRefraction(wl, wl_ref, elevation, self.observatory, weather=self.weather) self.assertFloatsAlmostEqual(diffRefraction.asDegrees(), 0.)
def refraction(wavelength, elevation, observatory, weather=None): """Calculate overall refraction under atmospheric and observing conditions. The calculation is taken from Stone 1996 "An Accurate Method for Computing Atmospheric Refraction" Parameters ---------- wavelength : `float` wavelength is in nm (valid for 230.2 < wavelength < 2058.6) elevation : `lsst.afw.geom.Angle` Elevation of the observation, as an Angle. observatory : `lsst.afw.coord.Observatory` Class containing the longitude, latitude, and altitude of the observatory. weather : `lsst.afw.coord.Weather`, optional Class containing the measured temperature, pressure, and humidity at the observatory during an observation If omitted, typical conditions for the observatory's elevation will be calculated. Returns ------- `lsst.afw.geom.Angle` The angular refraction for light of the given wavelength, under the given observing conditions. """ if wavelength < 230.2: raise ValueError( "Refraction calculation is valid for wavelengths between 230.2 and 2058.6 nm." ) if wavelength > 2058.6: raise ValueError( "Refraction calculation is valid for wavelengths between 230.2 and 2058.6 nm." ) latitude = observatory.getLatitude() altitude = observatory.getElevation() if weather is None: weather = defaultWeather(altitude * units.meter) reducedN = deltaN(wavelength, weather) / deltaRefractScale temperature = extractTemperature(weather, useKelvin=True) atmosScaleheightRatio = 4.5908E-6 * temperature / units.Kelvin # Account for oblate Earth # This replicates equation 10 of Stone 1996 relativeGravity = (1. + 0.005302 * np.sin(latitude.asRadians())**2. - 0.00000583 * np.sin(2. * latitude.asRadians())**2. - 0.000000315 * altitude) # Calculate the tangent of the zenith angle. tanZ = np.tan(np.pi / 2. - elevation.asRadians()) atmosTerm1 = reducedN * relativeGravity * (1. - atmosScaleheightRatio) atmosTerm2 = reducedN * relativeGravity * (atmosScaleheightRatio - reducedN / 2.) result = Angle(atmosTerm1 * tanZ + atmosTerm2 * tanZ**3.) return result
def testParallacticAngleSouthMeridian(self): """An observation on the Meridian that is South of zenith has a parallactic angle of zero.""" meridianBoresightRA = self.data1.era + self.data1.observatory.getLongitude( ) southBoresightDec = self.data1.observatory.getLatitude( ) - 10. * degrees visitInfo = afwImage.VisitInfo( era=self.data1.era, boresightRaDec=SpherePoint(meridianBoresightRA, southBoresightDec), observatory=self.data1.observatory, ) self.assertAnglesAlmostEqual(visitInfo.getBoresightParAngle(), Angle(0.))
def create_source_catalog_from_external_catalog(self, dataRef, coord_file, dataset='src', debug=False): butler = dataRef.getButler() schema = butler.get(dataset + "_schema", immediate=True).schema mapper = afwTable.SchemaMapper(schema) mapper.addMinimalSchema(schema) newSchema = mapper.getOutputSchema() info = load_external_catalog_info(coord_file) src_cat = afwTable.SourceCatalog(newSchema) for row in info: record = src_cat.addNew() record.set('coord_ra', Angle(row['RA'] * degrees)) record.set('coord_dec', Angle(row['Dec'] * degrees)) if debug: print(src_cat['coord_ra'], src_cat['coord_dec']) return (src_cat)
def testWavelengthRangeError(self): """Refraction should raise an error if the wavelength is out of range.""" elevation = Angle(np.random.random() * np.pi / 2.) wl_low = 230. wl_high = 2059. self.assertRaises(ValueError, refraction, wl_low, elevation, self.observatory, weather=self.weather) self.assertRaises(ValueError, refraction, wl_high, elevation, self.observatory, weather=self.weather)
def testRefractHighAirmass(self): """Compare the refraction calculation to precomputed values.""" elevation = Angle(np.pi / 6.) # Airmass 2.0 wls = [370., 480., 620., 860., 960., 1025.] # in nm refVals = [ 73.04868430514726, 71.74884360909664, 71.02058121935002, 70.51172189207065, 70.40446894800584, 70.35113687114644, ] for wl, refVal in zip(wls, refVals): refract = refraction(wl, elevation, self.observatory, weather=self.weather) self.assertFloatsAlmostEqual(refract.asArcseconds(), refVal, rtol=1e-3)
def setUp(self): """Define parameters used by every test.""" filter_name = 'g' n_step = 3 pixel_scale = Angle(afwGeom.arcsecToRad(0.25)) size = 20 self.latitude = lsst_observatory.getLatitude() # NOTE that this array is randomly generated random_seed = 3 rand_gen = np.random rand_gen.seed(random_seed) self.array = np.float32(rand_gen.random(size=(size, size))) self.dcrTemplate = BasicGenerateTemplate(size=size, filter_name=filter_name, n_step=n_step, pixel_scale=pixel_scale) self.dcrTemplate.create_skyMap(doWrite=False) self.dec = self.dcrTemplate.wcs.getSkyOrigin().getLatitude() self.ra = self.dcrTemplate.wcs.getSkyOrigin().getLongitude() self.azimuth = Angle(np.radians(140.0)) self.elevation = Angle(np.radians(50.0)) ha_term1 = np.sin(self.elevation.asRadians()) ha_term2 = np.sin(self.dec.asRadians())*np.sin(self.latitude.asRadians()) ha_term3 = np.cos(self.dec.asRadians())*np.cos(self.latitude.asRadians()) self.hour_angle = Angle(np.arccos((ha_term1 - ha_term2) / ha_term3)) p_angle = parallactic_angle(self.hour_angle, self.dec, self.latitude) self.rotation_angle = Angle(p_angle) self.dcrTemplate.weights = np.zeros_like(self.array) nonzero_inds = self.array > 0 self.dcrTemplate.weights[nonzero_inds] = 1./np.abs(self.array[nonzero_inds]) self.dcr_gen = self.dcrTemplate._dcr_generator(self.dcrTemplate.bandpass, pixel_scale=self.dcrTemplate.pixel_scale, elevation=self.elevation, rotation_angle=self.rotation_angle, use_midpoint=False) self.exposure = self.dcrTemplate.create_exposure(self.array, self.elevation, self.azimuth, variance=None, boresightRotAngle=self.rotation_angle, dec=self.dec, ra=self.ra)
class DcrCoaddTestBase(object): """Base class many unit tests can inherit from to simplify setup. Attributes ---------- array : np.ndarray Random array of input data values. azimuth : lsst.afw.geom Angle Azimuth angle of the observation dcr_gen : GenerateTemplate.dcr_generator Generator of Differential Chromatic Refraction (DCR) values per sub-band. dcrTemplate : `GenerateTemplate` Basic instance of the `GenerateTemplate` class for testing. elevation : lsst.afw.geom Angle Elevation angle of the observation exposure : lsst.afw.image.ExposureD object The exposure containing the data from `array` and associated metadata. rotation_angle : lsst.afw.geom Angle, optional The rotation angle of the field around the boresight. """ def setUp(self): """Define parameters used by every test.""" filter_name = 'g' n_step = 3 pixel_scale = Angle(afwGeom.arcsecToRad(0.25)) size = 20 self.latitude = lsst_observatory.getLatitude() # NOTE that this array is randomly generated random_seed = 3 rand_gen = np.random rand_gen.seed(random_seed) self.array = np.float32(rand_gen.random(size=(size, size))) self.dcrTemplate = BasicGenerateTemplate(size=size, filter_name=filter_name, n_step=n_step, pixel_scale=pixel_scale) self.dcrTemplate.create_skyMap(doWrite=False) self.dec = self.dcrTemplate.wcs.getSkyOrigin().getLatitude() self.ra = self.dcrTemplate.wcs.getSkyOrigin().getLongitude() self.azimuth = Angle(np.radians(140.0)) self.elevation = Angle(np.radians(50.0)) ha_term1 = np.sin(self.elevation.asRadians()) ha_term2 = np.sin(self.dec.asRadians())*np.sin(self.latitude.asRadians()) ha_term3 = np.cos(self.dec.asRadians())*np.cos(self.latitude.asRadians()) self.hour_angle = Angle(np.arccos((ha_term1 - ha_term2) / ha_term3)) p_angle = parallactic_angle(self.hour_angle, self.dec, self.latitude) self.rotation_angle = Angle(p_angle) self.dcrTemplate.weights = np.zeros_like(self.array) nonzero_inds = self.array > 0 self.dcrTemplate.weights[nonzero_inds] = 1./np.abs(self.array[nonzero_inds]) self.dcr_gen = self.dcrTemplate._dcr_generator(self.dcrTemplate.bandpass, pixel_scale=self.dcrTemplate.pixel_scale, elevation=self.elevation, rotation_angle=self.rotation_angle, use_midpoint=False) self.exposure = self.dcrTemplate.create_exposure(self.array, self.elevation, self.azimuth, variance=None, boresightRotAngle=self.rotation_angle, dec=self.dec, ra=self.ra) def tearDown(self): """Free memory.""" del self.dcrTemplate del self.exposure del self.dcr_gen
# the GNU General Public License along with this program. If not, # see <http://www.lsstcorp.org/LegalNotices/>. # from __future__ import absolute_import from __future__ import division from __future__ import print_function from lsst.daf.base import DateTime from lsst.afw.coord import Coord, IcrsCoord, Observatory, Weather from lsst.afw.geom import Angle from .imageLib import VisitInfo, RotType_UNKNOWN __all__ = ["makeVisitInfo"] nanFloat = float("nan") nanAngle = Angle(nanFloat) def makeVisitInfo( exposureId=0, exposureTime=nanFloat, darkTime=nanFloat, date=DateTime(), ut1=nanFloat, era=nanAngle, boresightRaDec=IcrsCoord(nanAngle, nanAngle), boresightAzAlt=Coord(nanAngle, nanAngle), boresightAirmass=nanFloat, boresightRotAngle=nanAngle, rotType=RotType_UNKNOWN, observatory=Observatory(nanAngle, nanAngle, nanFloat),
def setUp(self): """Create a catalog to run through the ingestion process.""" # First, connect to the database. self.host = os.environ.get("TEST_MYSQL_HOST", "lsst-db.ncsa.illinois.edu") self.db = os.environ.get("TEST_MYSQL_DB", "test") self.port = int(os.environ.get("TEST_MYSQL_PORT", "3306")) self.conn = None try: self.conn = IngestCatalogTask.connect( host=self.host, port=self.port, db=self.db) except: pass # Create a schema containing one of every kind of afw field schema = afw_table.Schema() keys = ( # Scalar fields schema.addField("scalar.u", type="U"), schema.addField("scalar.i", type="I"), schema.addField("scalar.l", type="L"), schema.addField("scalar.f", type="F"), schema.addField("scalar.d", type="D"), schema.addField("scalar.flag", type="Flag"), schema.addField("scalar.angle", type="Angle"), # Fixed-length array types schema.addField("fix.string", type="String", size=4), schema.addField("fix.array.u", type="ArrayU", size=2), schema.addField("fix.array.i", type="ArrayI", size=2), schema.addField("fix.array.f", type="ArrayF", size=2), schema.addField("fix.array.d", type="ArrayD", size=2), # Variable-length array types schema.addField("var.array.u", type="ArrayU", size=0), schema.addField("var.array.i", type="ArrayI", size=0), schema.addField("var.array.f", type="ArrayF", size=0), schema.addField("var.array.d", type="ArrayD", size=0), ) # Setup schema aliases aliases = dict( S="scalar", F="fix", af="fix.array", V="var", av="var.array", vla="av", ) for source, target in aliases.iteritems(): schema.getAliasMap().set(source, target) # Create two rows of fake data... self.rows = ( ( 0, -2147483648, -9223372036854775808, 1.0, math.pi, False, Angle(1.0), "ab ", np.array([0, 65535], dtype=np.uint16), np.array([-2147483648, 2147483647], dtype=np.int32), np.array([1.0, 2.0], dtype=np.float32), np.array([math.pi, math.e], dtype=np.float64), np.array(range(0), dtype=np.uint16), np.array(range(1), dtype=np.int32), np.array(range(3), dtype=np.float32), np.array(range(4), dtype=np.float64), ), ( 65535, 2147483647, 9223372036854775807, 2.0, math.e, True, Angle(2.0), "", np.array(range(2), dtype=np.uint16), np.array(range(2), dtype=np.int32), np.array(range(2), dtype=np.float32), np.array(range(2), dtype=np.float64), np.array([], dtype=np.uint16), np.array([], dtype=np.int32), np.array([], dtype=np.float32), np.array([], dtype=np.float64), ) ) # and a corresponding catalog self.catalog = afw_table.BaseCatalog(schema) for row in self.rows: record = self.catalog.addNew() for i, k in enumerate(keys): record.set(k, row[i]) # Finally, choose table/view names that are unique with very high # probability. suffix = uuid.uuid4().hex self.table_name = "catalog_" + suffix self.view_name = "view_" + suffix