Skip to content

Commit

Permalink
Merge pull request #232 from LSSTDESC/issue_231/write_stack_versions_…
Browse files Browse the repository at this point in the history
…and_tags

write selected LSST Stack package versions and tags to FITS primary header
  • Loading branch information
jchiang87 authored Oct 22, 2019
2 parents d1eac95 + 31d7c2f commit be926d2
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 6 deletions.
5 changes: 5 additions & 0 deletions data/default_imsim_configs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ sort_magnorm = True
# AtmosphericPSF + OptWF PSF to account for additional instrumental
# effects.
gaussianFWHM = 0.4

[stack_packages]
lsst_sims = metapackage
throughputs
sims_skybrightness_data
9 changes: 8 additions & 1 deletion python/desc/imsim/ImageSimulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
import sqlite3
import numpy as np
from astropy._erfa import ErfaWarning
import galsim
from lsst.afw.cameraGeom import WAVEFRONT, GUIDER
from lsst.sims.photUtils import BandpassDict
from lsst.sims.GalSimInterface import make_galsim_detector
from lsst.sims.GalSimInterface import make_gs_interpreter
from lsst.sims.GalSimInterface import LSSTCameraWrapper
from .imSim import read_config, parsePhoSimInstanceFile, add_cosmic_rays,\
add_treering_info, get_logger, TracebackDecorator
add_treering_info, get_logger, TracebackDecorator, get_version_keywords
from .bleed_trails import apply_channel_bleeding
from .skyModel import make_sky_model
from .process_monitor import process_monitor
Expand Down Expand Up @@ -502,6 +503,12 @@ def write_eimage_files(self, gs_interpreter):
----------
gs_interpreter: GalSimInterpreter object
"""
# Add version keywords to eimage headers
version_keywords = get_version_keywords()
for image in gs_interpreter.detectorImages.values():
image.header = galsim.FitsHeader(header=version_keywords)

# Write the eimage files using filenames containing the visit number.
prefix = IMAGE_SIMULATOR.config['persistence']['eimage_prefix']
obsHistID = str(IMAGE_SIMULATOR.obs_md.OpsimMetaData['obshistID'])
nameRoot = os.path.join(IMAGE_SIMULATOR.outdir, prefix) + obsHistID
Expand Down
8 changes: 5 additions & 3 deletions python/desc/imsim/camera_readout.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
getRotSkyPos, ObservationMetaData, altAzPaFromRaDec
from lsst.sims.GalSimInterface import LsstObservatory
from .camera_info import CameraInfo, getHourAngle
from .imSim import get_logger, get_config, airmass
from .imSim import get_logger, get_config, airmass, get_version_keywords
from .cosmic_rays import CosmicRays
from .version import __version__ as imsim_version

__all__ = ['ImageSource', 'set_itl_bboxes', 'set_e2v_bboxes',
'set_phosim_bboxes', 'set_noao_keywords', 'cte_matrix']
Expand Down Expand Up @@ -476,7 +475,6 @@ def write_fits_file(self, outfile, overwrite=True, run_number=None,
output[0].header.insert(5, ('WCSAXES', wcsaxes, ''))
if run_number is None:
run_number = self.visit
output[0].header['IMSIMVER'] = imsim_version
output[0].header['RUNNUM'] = str(run_number)
output[0].header['DARKTIME'] = output[0].header['EXPTIME']
output[0].header['TIMESYS'] = 'TAI'
Expand Down Expand Up @@ -531,6 +529,10 @@ def write_fits_file(self, outfile, overwrite=True, run_number=None,
amp_name = '_C'.join((self.sensor_id, seg_id))
output.append(self.get_amplifier_hdu(amp_name, compress=compress))
output[-1].header['EXTNAME'] = 'Segment%s' % seg_id

# Set the imSim version and LSST Stack product versions and
# tags in the primary HDU.
output[0].header.update(get_version_keywords())
self.fits_atomic_write(output, outfile, overwrite=overwrite)

@staticmethod
Expand Down
51 changes: 49 additions & 2 deletions python/desc/imsim/imSim.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import copy
import psutil
import galsim
import eups

# python_future no longer handles configparser as of 0.16.
# This is needed for PY2/3 compatibility.
Expand Down Expand Up @@ -46,6 +47,7 @@
from .trim import InstCatTrimmer
from .sed_wrapper import SedWrapper
from .atmPSF import AtmosphericPSF
from .version import __version__ as imsim_version

_POINT_SOURCE = 1
_SERSIC_2D = 2
Expand All @@ -61,7 +63,8 @@
'_POINT_SOURCE', '_SERSIC_2D', '_RANDOM_WALK', '_FITS_IMAGE',
'parsePhoSimInstanceFile',
'add_treering_info', 'airmass', 'FWHMeff', 'FWHMgeom', 'make_psf',
'save_psf', 'load_psf', 'TracebackDecorator', 'GsObjectList']
'save_psf', 'load_psf', 'TracebackDecorator', 'GsObjectList',
'get_stack_products', 'get_version_keywords']


class PhosimInstanceCatalogParseError(RuntimeError):
Expand Down Expand Up @@ -611,7 +614,7 @@ def read_config(config_file=None):
config_file.
"""
my_config = ImSimConfiguration()
cp = configparser.ConfigParser()
cp = configparser.ConfigParser(allow_no_value=True)
cp.optionxform = str
if config_file is None:
config_file = os.path.join(lsstUtils.getPackageDir('imsim'),
Expand Down Expand Up @@ -904,3 +907,47 @@ def __call__(self, *args, **kwds):
except Exception as eobj:
traceback.print_exc()
raise eobj

def get_stack_products(product_names=None):
"""Get the LSST Stack products corresponding to a set of product
names.
Parameters
----------
product_names: dict [None]
A dict with LSST Stack package names as keys and either
'metapackage' or None as values. The setup eups.Product
corresponding to each of these packages will be returned in a
dictionary. If None, then use the products listed in the
config file.
Returns
-------
dict of eups.Products keyed by package name.
"""
config = get_config()
stack_packages = config['stack_packages'] if product_names is None \
else product_names
eupsenv = eups.Eups()
products = dict()
for product_name, product_type in stack_packages.items():
products[product_name] = eupsenv.getSetupProducts(product_name)[0]
products[product_name].type = product_type
return products

def get_version_keywords():
"""
Return a dictionary of header keywords containing eups tagging and
version information.
"""
keywords = {'IMSIMVER': imsim_version}
products = get_stack_products()
for iprod, (product_name, product) in enumerate(products.items()):
keywords[f'PKG{iprod:05d}'] = product_name
if product.type == 'metapackage':
tag = [_ for _ in product.tags if _ != 'current'][0]
keywords[f'TAG{iprod:05d}'] = tag
else:
keywords[f'VER{iprod:05d}'] = product.version
return keywords
69 changes: 69 additions & 0 deletions tests/test_get_stack_products.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
Unit test for get_stack_products function.
"""
import unittest
import subprocess
from collections import defaultdict
import desc.imsim


class GetStackProductsTestCase(unittest.TestCase):
"""
TestCase subclass for testing get_stacks_products.
"""
def setUp(self):
pass

def tearDown(self):
pass

def test_get_stack_products(self):
"""Test the get_stack_products function."""
target_dicts = [None, {_: None for _ in
'afw sims_photUtils sims_utils'.split()}]

for targets in target_dicts:
products = desc.imsim.get_stack_products(targets)
if targets is not None:
self.assertEqual(products.keys(), targets.keys())
for target in products:
# Test result against eups command line result.
command = f'eups list {target} -s'
line = subprocess.check_output(command, shell=True)
tokens = line.decode('utf-8').strip().split()
version = tokens[0]
tags = set(tokens[1:])
tags.remove('setup')

self.assertEqual(products[target].version, version)
self.assertEqual(set(products[target].tags), tags)

def test_get_version_keywords(self):
"""Test the get_version_keywords function."""
class KeywordInfo:
"""Class to hold FITS keyword name and type values for
versioning keywords."""
pass
version_keywords = desc.imsim.get_version_keywords()
keyword_info = defaultdict(KeywordInfo)
for key, value in version_keywords.items():
if key.startswith('PKG'):
iprod = int(key[len('PKG'):])
keyword_info[iprod].name = value
if key.startswith('TAG'):
iprod = int(key[len('TAG'):])
keyword_info[iprod].type = 'metapackage'
if key.startswith('VER'):
iprod = int(key[len('VER'):])
keyword_info[iprod].type = ''
# Repackage the keyword_info dict to use package names instead
# of the iprod index so that we can compare directly to the
# 'stack_packages' config.
keyword_info = {_.name: _.type for _ in keyword_info.values()}

config = desc.imsim.get_config()
self.assertEqual(keyword_info, config['stack_packages'])


if __name__ == '__main__':
unittest.main()

0 comments on commit be926d2

Please sign in to comment.