diff --git a/doc/developer-guide/developer-guide.rst b/doc/developer-guide/developer-guide.rst index 5e01b1bf..cbc47e0c 100644 --- a/doc/developer-guide/developer-guide.rst +++ b/doc/developer-guide/developer-guide.rst @@ -51,8 +51,6 @@ This module is a high-level module to use other modules. :caption: Class diagram of wep * **WfEstimator**: Calculate the wavefront error in annular Zernike polynomials up to 22 terms based on the defocal donut images. -* **Utility**: Utility functions used in WEP. -* **PlotUtil**: Plot utility functions used in WEP. * **ParamReader**: Parameter reader class to read the yaml configuration files used in the calculation. * **DonutImageCheck**: Donut image check class to judge the donut image is effective or not. * **DonutDetector**: Detect donuts directly from an out of focus image by convolution with a template image. @@ -71,7 +69,6 @@ This module calculates the wavefront error by solving the TIE. * **CompensableImage**: Compensable image class to project the donut image from the image plane to the pupil plane. * **Image**: Image class to have the function to get the donut center. * **Instrument**: Instrument class to have the instrument information used in the Algorithm class to solve the TIE. -* **Tool**: Annular Zernike polynomials related functions. * **CentroidFindFactory**: Factory for creating the centroid find object to calculate the centroid of donut. * **CentroidDefault**: Default centroid class. * **CentroidRandomWalk**: CentroidDefault child class to get the centroid of donut by the random walk model. @@ -144,6 +141,20 @@ This module has the tasks to run WEP as a pipeline with Gen 3 LSST DM middleware * **GenerateDonutFromRefitWcsTaskConfig**: Configuration setup for GenerateDonutFromRefitWcsTask. * **GenerateDonutCatalogUtils**: Common utility functions for the GenerateDonutCatalog...Tasks. +.. _WEP_modules_wep_utils: + +wep.utils +------------- + +This module contains utility functions that are used elsewhere in WEP. + +* **enumUtils**: Enum definitions and related functions. +* **ioUtils**: Functions for reading and writing files. +* **taskUtils**: Functions for running command line tasks from a python script. +* **zernikeUtils**: Functions for evaluating and fitting Zernike polynomials. +* **plotUtils**: Functions for plotting results. +* **miscUtils**: Miscellaneous utility functions. + .. _WEP_API: Python API reference @@ -163,6 +174,9 @@ This section is autogenerated from docstrings. .. automodapi:: lsst.ts.wep.task :no-inheritance-diagram: +.. automodapi:: lsst.ts.wep.utils + :no-inheritance-diagram: + .. _WEP_contributing: Contributing diff --git a/doc/versionHistory.rst b/doc/versionHistory.rst index eee112cb..d6d3650b 100644 --- a/doc/versionHistory.rst +++ b/doc/versionHistory.rst @@ -5,6 +5,15 @@ ################## Version History ################## + +.. _lsst.ts.wep-7.0.0: + +------------- +7.0.0 +------------- + +* Organize all utility functions inside the ``utils`` module. + .. _lsst.ts.wep-6.4.12: ------------- diff --git a/python/lsst/ts/wep/__init__.py b/python/lsst/ts/wep/__init__.py index 9ccac8a2..3246b1fc 100644 --- a/python/lsst/ts/wep/__init__.py +++ b/python/lsst/ts/wep/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from .utility import * # This class needs the scons to build the cython code. In the Jenkins test, # this will be a problem to import. diff --git a/python/lsst/ts/wep/cwfs/__init__.py b/python/lsst/ts/wep/cwfs/__init__.py index 49f51077..2db09b56 100644 --- a/python/lsst/ts/wep/cwfs/__init__.py +++ b/python/lsst/ts/wep/cwfs/__init__.py @@ -13,4 +13,3 @@ from .donutTemplatePhosim import * from .image import * from .instrument import * -from .tool import * diff --git a/python/lsst/ts/wep/cwfs/algorithm.py b/python/lsst/ts/wep/cwfs/algorithm.py index fdac5c22..1a122f4b 100644 --- a/python/lsst/ts/wep/cwfs/algorithm.py +++ b/python/lsst/ts/wep/cwfs/algorithm.py @@ -27,15 +27,15 @@ import galsim import numpy as np from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.cwfs.tool import ( +from lsst.ts.wep.paramReader import ParamReader +from lsst.ts.wep.utils import ( + DefocalType, ZernikeAnnularEval, ZernikeMaskedFit, extractArray, padArray, + plotZernike, ) -from lsst.ts.wep.paramReader import ParamReader -from lsst.ts.wep.plotUtil import plotZernike -from lsst.ts.wep.utility import DefocalType from scipy.ndimage import ( binary_dilation, binary_erosion, diff --git a/python/lsst/ts/wep/cwfs/baseCwfsTestCase.py b/python/lsst/ts/wep/cwfs/baseCwfsTestCase.py index 08fc4414..e653676b 100644 --- a/python/lsst/ts/wep/cwfs/baseCwfsTestCase.py +++ b/python/lsst/ts/wep/cwfs/baseCwfsTestCase.py @@ -27,7 +27,7 @@ from lsst.ts.wep.cwfs.algorithm import Algorithm from lsst.ts.wep.cwfs.compensableImage import CompensableImage from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.utility import DefocalType, getConfigDir +from lsst.ts.wep.utils import DefocalType, getConfigDir class BaseCwfsTestCase(object): diff --git a/python/lsst/ts/wep/cwfs/centroidFindFactory.py b/python/lsst/ts/wep/cwfs/centroidFindFactory.py index bccf0f0c..ca1e012f 100644 --- a/python/lsst/ts/wep/cwfs/centroidFindFactory.py +++ b/python/lsst/ts/wep/cwfs/centroidFindFactory.py @@ -24,7 +24,7 @@ from lsst.ts.wep.cwfs.centroidConvolveTemplate import CentroidConvolveTemplate from lsst.ts.wep.cwfs.centroidOtsu import CentroidOtsu from lsst.ts.wep.cwfs.centroidRandomWalk import CentroidRandomWalk -from lsst.ts.wep.utility import CentroidFindType +from lsst.ts.wep.utils import CentroidFindType class CentroidFindFactory(object): diff --git a/python/lsst/ts/wep/cwfs/compensableImage.py b/python/lsst/ts/wep/cwfs/compensableImage.py index 34380795..3bfbc494 100644 --- a/python/lsst/ts/wep/cwfs/compensableImage.py +++ b/python/lsst/ts/wep/cwfs/compensableImage.py @@ -28,14 +28,17 @@ import numpy as np from galsim.utilities import horner2d from lsst.ts.wep.cwfs.image import Image -from lsst.ts.wep.cwfs.tool import ( +from lsst.ts.wep.paramReader import ParamReader +from lsst.ts.wep.utils import ( + CentroidFindType, + DefocalType, + FilterType, ZernikeAnnularGrad, ZernikeAnnularJacobian, extractArray, padArray, + rotMatrix, ) -from lsst.ts.wep.paramReader import ParamReader -from lsst.ts.wep.utility import CentroidFindType, DefocalType, FilterType, rotMatrix from scipy.interpolate import RectBivariateSpline from scipy.ndimage import ( binary_dilation, diff --git a/python/lsst/ts/wep/cwfs/donutTemplateFactory.py b/python/lsst/ts/wep/cwfs/donutTemplateFactory.py index eeca981c..47ccbcc4 100644 --- a/python/lsst/ts/wep/cwfs/donutTemplateFactory.py +++ b/python/lsst/ts/wep/cwfs/donutTemplateFactory.py @@ -23,7 +23,7 @@ from lsst.ts.wep.cwfs.donutTemplateModel import DonutTemplateModel from lsst.ts.wep.cwfs.donutTemplatePhosim import DonutTemplatePhosim -from lsst.ts.wep.utility import DonutTemplateType +from lsst.ts.wep.utils import DonutTemplateType class DonutTemplateFactory(object): diff --git a/python/lsst/ts/wep/cwfs/donutTemplateModel.py b/python/lsst/ts/wep/cwfs/donutTemplateModel.py index 71ea8020..9f5198f6 100644 --- a/python/lsst/ts/wep/cwfs/donutTemplateModel.py +++ b/python/lsst/ts/wep/cwfs/donutTemplateModel.py @@ -27,7 +27,7 @@ from lsst.ts.wep.cwfs.compensableImage import CompensableImage from lsst.ts.wep.cwfs.donutTemplateDefault import DonutTemplateDefault from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.utility import CamType, FilterType, getConfigDir, readPhoSimSettingData +from lsst.ts.wep.utils import CamType, FilterType, getConfigDir, readPhoSimSettingData class DonutTemplateModel(DonutTemplateDefault): diff --git a/python/lsst/ts/wep/cwfs/donutTemplatePhosim.py b/python/lsst/ts/wep/cwfs/donutTemplatePhosim.py index b25ec653..1770a06a 100644 --- a/python/lsst/ts/wep/cwfs/donutTemplatePhosim.py +++ b/python/lsst/ts/wep/cwfs/donutTemplatePhosim.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.cwfs.donutTemplateDefault import DonutTemplateDefault -from lsst.ts.wep.utility import DefocalType, getConfigDir +from lsst.ts.wep.utils import DefocalType, getConfigDir class DonutTemplatePhosim(DonutTemplateDefault): diff --git a/python/lsst/ts/wep/cwfs/image.py b/python/lsst/ts/wep/cwfs/image.py index 588207fc..9fb153a7 100644 --- a/python/lsst/ts/wep/cwfs/image.py +++ b/python/lsst/ts/wep/cwfs/image.py @@ -27,7 +27,7 @@ import numpy as np from astropy.io import fits from lsst.ts.wep.cwfs.centroidFindFactory import CentroidFindFactory -from lsst.ts.wep.utility import CentroidFindType +from lsst.ts.wep.utils import CentroidFindType class Image(object): diff --git a/python/lsst/ts/wep/cwfs/instrument.py b/python/lsst/ts/wep/cwfs/instrument.py index 19f20df5..4f118d4b 100644 --- a/python/lsst/ts/wep/cwfs/instrument.py +++ b/python/lsst/ts/wep/cwfs/instrument.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.paramReader import ParamReader -from lsst.ts.wep.utility import CamType, getCamNameFromCamType, getConfigDir +from lsst.ts.wep.utils import CamType, getCamNameFromCamType, getConfigDir class Instrument(object): diff --git a/python/lsst/ts/wep/deblend/deblendAdapt.py b/python/lsst/ts/wep/deblend/deblendAdapt.py index 687a3b93..5f385571 100644 --- a/python/lsst/ts/wep/deblend/deblendAdapt.py +++ b/python/lsst/ts/wep/deblend/deblendAdapt.py @@ -25,7 +25,7 @@ from lsst.ts.wep.cwfs.centroidFindFactory import CentroidFindFactory from lsst.ts.wep.deblend.deblendDefault import DeblendDefault from lsst.ts.wep.deblend.nelderMeadModify import nelderMeadModify -from lsst.ts.wep.utility import CentroidFindType +from lsst.ts.wep.utils import CentroidFindType from scipy.ndimage import ( binary_closing, binary_erosion, diff --git a/python/lsst/ts/wep/deblend/deblendDefault.py b/python/lsst/ts/wep/deblend/deblendDefault.py index 9ae05d82..027279b0 100644 --- a/python/lsst/ts/wep/deblend/deblendDefault.py +++ b/python/lsst/ts/wep/deblend/deblendDefault.py @@ -23,7 +23,7 @@ import numpy as np from lsst.ts.wep.cwfs.centroidFindFactory import CentroidFindFactory -from lsst.ts.wep.utility import CentroidFindType +from lsst.ts.wep.utils import CentroidFindType class DeblendDefault(object): diff --git a/python/lsst/ts/wep/deblend/deblendDonutFactory.py b/python/lsst/ts/wep/deblend/deblendDonutFactory.py index e5c8376b..5f9550c3 100644 --- a/python/lsst/ts/wep/deblend/deblendDonutFactory.py +++ b/python/lsst/ts/wep/deblend/deblendDonutFactory.py @@ -22,7 +22,7 @@ __all__ = ["DeblendDonutFactory"] from lsst.ts.wep.deblend.deblendAdapt import DeblendAdapt -from lsst.ts.wep.utility import DeblendDonutType +from lsst.ts.wep.utils import DeblendDonutType class DeblendDonutFactory(object): diff --git a/python/lsst/ts/wep/donutDetector.py b/python/lsst/ts/wep/donutDetector.py index 8e5b0c57..0fa15a0a 100644 --- a/python/lsst/ts/wep/donutDetector.py +++ b/python/lsst/ts/wep/donutDetector.py @@ -27,7 +27,7 @@ import pandas as pd from lsst.ts.wep.cwfs.centroidFindFactory import CentroidFindFactory from lsst.ts.wep.deblend.deblendAdapt import DeblendAdapt -from lsst.ts.wep.utility import CentroidFindType +from lsst.ts.wep.utils import CentroidFindType from scipy.spatial.distance import cdist diff --git a/python/lsst/ts/wep/task/calcZernikesTask.py b/python/lsst/ts/wep/task/calcZernikesTask.py index a60a0cb3..ddefcfe0 100644 --- a/python/lsst/ts/wep/task/calcZernikesTask.py +++ b/python/lsst/ts/wep/task/calcZernikesTask.py @@ -29,7 +29,7 @@ from lsst.pipe.base import connectionTypes from lsst.ts.wep.task.combineZernikesSigmaClipTask import CombineZernikesSigmaClipTask from lsst.ts.wep.task.donutStamps import DonutStamps -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DefocalType, createInstDictFromConfig, getCamTypeFromButlerName, diff --git a/python/lsst/ts/wep/task/cutOutDonutsBase.py b/python/lsst/ts/wep/task/cutOutDonutsBase.py index 24d1261c..d041ca89 100644 --- a/python/lsst/ts/wep/task/cutOutDonutsBase.py +++ b/python/lsst/ts/wep/task/cutOutDonutsBase.py @@ -36,7 +36,7 @@ from lsst.ts.wep.cwfs.instrument import Instrument from lsst.ts.wep.task.donutStamp import DonutStamp from lsst.ts.wep.task.donutStamps import DonutStamps -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DonutTemplateType, createInstDictFromConfig, getCamTypeFromButlerName, diff --git a/python/lsst/ts/wep/task/cutOutDonutsCwfsTask.py b/python/lsst/ts/wep/task/cutOutDonutsCwfsTask.py index be5462bd..48e04c7b 100644 --- a/python/lsst/ts/wep/task/cutOutDonutsCwfsTask.py +++ b/python/lsst/ts/wep/task/cutOutDonutsCwfsTask.py @@ -35,7 +35,7 @@ CutOutDonutsBaseTaskConnections, ) from lsst.ts.wep.task.donutStamps import DonutStamps -from lsst.ts.wep.utility import DefocalType +from lsst.ts.wep.utils import DefocalType from lsst.utils.timer import timeMethod diff --git a/python/lsst/ts/wep/task/cutOutDonutsScienceSensorTask.py b/python/lsst/ts/wep/task/cutOutDonutsScienceSensorTask.py index 2289ba1e..b426a47e 100644 --- a/python/lsst/ts/wep/task/cutOutDonutsScienceSensorTask.py +++ b/python/lsst/ts/wep/task/cutOutDonutsScienceSensorTask.py @@ -39,7 +39,7 @@ CutOutDonutsBaseTaskConnections, ) from lsst.ts.wep.task.donutStamps import DonutStamps -from lsst.ts.wep.utility import DefocalType +from lsst.ts.wep.utils import DefocalType from lsst.utils.timer import timeMethod diff --git a/python/lsst/ts/wep/task/donutSourceSelectorTask.py b/python/lsst/ts/wep/task/donutSourceSelectorTask.py index 03b13896..d85af5d2 100644 --- a/python/lsst/ts/wep/task/donutSourceSelectorTask.py +++ b/python/lsst/ts/wep/task/donutSourceSelectorTask.py @@ -32,7 +32,7 @@ from lsst.afw.cameraGeom import FIELD_ANGLE, PIXELS from lsst.meas.algorithms.sourceSelector import _getFieldFromCatalog from lsst.ts.wep.paramReader import ParamReader -from lsst.ts.wep.utility import getConfigDir +from lsst.ts.wep.utils import getConfigDir from lsst.utils.timer import timeMethod from sklearn.neighbors import NearestNeighbors diff --git a/python/lsst/ts/wep/task/donutStamp.py b/python/lsst/ts/wep/task/donutStamp.py index 17c0fa9f..4ef8bd6a 100644 --- a/python/lsst/ts/wep/task/donutStamp.py +++ b/python/lsst/ts/wep/task/donutStamp.py @@ -32,7 +32,7 @@ from lsst.afw.cameraGeom import FIELD_ANGLE, PIXELS from lsst.meas.algorithms.stamps import AbstractStamp from lsst.ts.wep.cwfs.compensableImage import CompensableImage -from lsst.ts.wep.utility import DefocalType, FilterType, getFilterTypeFromBandLabel +from lsst.ts.wep.utils import DefocalType, FilterType, getFilterTypeFromBandLabel @dataclass diff --git a/python/lsst/ts/wep/task/generateDonutDirectDetectTask.py b/python/lsst/ts/wep/task/generateDonutDirectDetectTask.py index b7bfac2d..2b1395d5 100644 --- a/python/lsst/ts/wep/task/generateDonutDirectDetectTask.py +++ b/python/lsst/ts/wep/task/generateDonutDirectDetectTask.py @@ -34,7 +34,7 @@ from lsst.ts.wep.cwfs.donutTemplateFactory import DonutTemplateFactory from lsst.ts.wep.task.donutQuickMeasurementTask import DonutQuickMeasurementTask from lsst.ts.wep.task.donutSourceSelectorTask import DonutSourceSelectorTask -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DefocalType, DonutTemplateType, createInstDictFromConfig, diff --git a/python/lsst/ts/wep/utility.py b/python/lsst/ts/wep/utility.py deleted file mode 100644 index a10e3137..00000000 --- a/python/lsst/ts/wep/utility.py +++ /dev/null @@ -1,767 +0,0 @@ -# This file is part of ts_wep. -# -# Developed for the LSST Telescope and Site Systems. -# This product includes software developed by the LSST Project -# (https://www.lsst.org). -# See the COPYRIGHT file at the top-level directory of this distribution -# for details of code ownership. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -__all__ = [ - "FilterType", - "CamType", - "BscDbType", - "DefocalType", - "ImageType", - "CentroidFindType", - "DonutTemplateType", - "DeblendDonutType", - "getModulePath", - "getConfigDir", - "getObsLsstCmdTaskConfigDir", - "runProgram", - "searchDonutPos", - "writeFile", - "readPhoSimSettingData", - "mapFilterRefToG", - "getBscDbType", - "getImageType", - "getCentroidFindType", - "getDeblendDonutType", - "getDonutTemplateType", - "getAmpImagesFromDir", - "writePipetaskCmd", - "writeCleanUpRepoCmd", - "getCamType", - "getCamTypeFromButlerName", - "getDefocalDisInMm", -] - -import os -import re -import subprocess -from enum import IntEnum, auto - -import numpy as np -from lsst.afw.cameraGeom import DetectorType -from lsst.utils import getPackageDir -from scipy.ndimage import center_of_mass - - -class FilterType(IntEnum): - LSST_U = 1 - LSST_G = auto() - LSST_R = auto() - LSST_I = auto() - LSST_Z = auto() - LSST_Y = auto() - REF = auto() - - -class CamType(IntEnum): - LsstCam = 1 - LsstFamCam = auto() - ComCam = auto() - AuxTel = auto() - AuxTelZWO = auto() - - -class BscDbType(IntEnum): - LocalDb = 1 - LocalDbForStarFile = auto() - - -class DefocalType(IntEnum): - Intra = 1 - Extra = auto() - - -class ImageType(IntEnum): - Amp = 1 - Eimg = auto() - - -class CentroidFindType(IntEnum): - RandomWalk = 1 - Otsu = auto() - ConvolveTemplate = auto() - - -class DonutTemplateType(IntEnum): - Model = 1 - Phosim = auto() - - -class DeblendDonutType(IntEnum): - Adapt = 1 - - -def getModulePath(): - """Get the path of module. - - Returns - ------- - str - Directory path of module. - """ - - return getPackageDir("ts_wep") - - -def getConfigDir(): - """Get the directory of configuration files. - - Returns - ------- - str - Directory of configuration files. - """ - - return os.path.join(getModulePath(), "policy") - - -def getObsLsstCmdTaskConfigDir(): - """Get the obs_lsst command line task configuration directory. - - Returns - ------- - str - obs_lsst command line task configuration directory. - """ - - return os.path.join(getPackageDir("obs_lsst"), "config") - - -def runProgram(command, binDir=None, argstring=None): - """Run the program w/o arguments. - - Parameters - ---------- - command : str - Command of application. - binDir : str, optional - Directory of binary application. (the default is None.) - argstring : str, optional - Arguments of program. (the default is None.) - - Raises - ------ - RuntimeError - Error running of command. - """ - - # Directory of binary application - if binDir is not None: - command = os.path.join(binDir, command) - - # Arguments for the program - if argstring is not None: - command += " " + argstring - - # Call the program w/o arguments - if subprocess.call(command, shell=True) != 0: - raise RuntimeError("Error running: %s" % command) - - -def searchDonutPos(img): - """Search the position of donut on image. - - Parameters - ---------- - img : numpy.ndarray - Donut image. - - Returns - ------- - float - X position of donut center in pixel. - float - Y position of donut center in pixel. - """ - - # Search the donut position by the center of mass - # Need to update this method to the more robust one such as the convolution - realcy, realcx = center_of_mass(img) - - return realcx, realcy - - -def writeFile(filePath, content): - """Write the content to file. - - Parameters - ---------- - filePath : str - File path. - content : str - File content. - """ - - with open(filePath, "w") as file: - file.write(content) - - -def readPhoSimSettingData(folderPath, fileName, atype): - """Read the PhoSim setting data (segmentation or focal plane layout). - - Parameters - ---------- - folderPath : str - Path to folder. - fileName : str - File name ("segmentation.txt", "focalplanelayout.txt"). - atype : str - Type of data to read ("readOutDim", "darkCurrent", "fieldCenter", - "eulerRot"). - - Returns - ------- - dict - Needed CCD data. - - Raises - ------ - ValueError - File can not be read. - ValueError - Type is not correct. - """ - - # Check the file name - if fileName not in ("segmentation.txt", "focalplanelayout.txt"): - raise ValueError("'%s' can not be read." % fileName) - - # Check the type - if atype not in ("readOutDim", "darkCurrent", "fieldCenter", "eulerRot"): - raise ValueError("'%s' can not be read." % atype) - - # Get the file path - pathToFile = os.path.join(folderPath, fileName) - - # Amplifier list (only list the scientific ccd here) - ampList = [ - "C00", - "C01", - "C02", - "C03", - "C04", - "C05", - "C06", - "C07", - "C10", - "C11", - "C12", - "C13", - "C14", - "C15", - "C16", - "C17", - ] - - # Open the file to read - ccdData = {} - fid = open(pathToFile) - for line in fid: - line = line.strip() - - # Get each element - lineElement = line.split() - - data = [] - # Analyze the sensor name to find the amplifier - if fileName == "segmentation.txt": - sensorNameStr = lineElement[0].split("_") - if len(sensorNameStr) == 3: - if sensorNameStr[2] in ampList: - # Get the segmentation in txt file - if atype == "readOutDim": - # parallel prescan, serial overscan, serial prescan, - # parallel overscan (pixel) - data = lineElement[15:19] - elif atype == "darkCurrent": - data = lineElement[13:15] - - elif fileName == "focalplanelayout.txt": - # Analyze the sensor name to make sure this line of data is - # needed - sensorNameStr = lineElement[0].split("_") - if len(sensorNameStr) == 2 or len(sensorNameStr) == 3: - if atype == "fieldCenter": - # Collect the field center: - # x position (microns), y position (microns), pixel - # size (microns) number of x pixels, number of y pixels - data = lineElement[1:6] - elif atype == "eulerRot": - # Collect the euler Rotation (degrees) - data = lineElement[12:15] - - # Collect the data - if data: - ccdData.update({lineElement[0]: data}) - - # Close the file - fid.close() - - return ccdData - - -def mapFilterRefToG(filterType): - """Map the reference filter to the G filter. - - Parameters - ---------- - filterType : enum 'FilterType' - Filter type. - - Returns - ------- - enum 'FilterType' - Mapped filter type. - """ - - if filterType == FilterType.REF: - return FilterType.LSST_G - else: - return filterType - - -def getBscDbType(bscDbType): - """Get the bright star catalog (BSC) database type. - - Parameters - ---------- - bscDbType : str - BSC database type to use (localDb or file). - - Returns - ------- - enum 'BscDbType' - BSC database type. - - Raises - ------ - ValueError - The bscDb is not supported. - """ - - if bscDbType == "localDb": - return BscDbType.LocalDb - elif bscDbType == "file": - return BscDbType.LocalDbForStarFile - else: - raise ValueError("The bscDb (%s) is not supported." % bscDbType) - - -def getImageType(imageType): - """Get the image type. - - Parameters - ---------- - imageType : str - Image type to use (amp or eimage). - - Returns - ------- - enum 'ImageType' - ImageType enum. - - Raises - ------ - ValueError - The image type is not supported. - """ - - if imageType == "amp": - return ImageType.Amp - elif imageType == "eimage": - return ImageType.Eimg - else: - raise ValueError("The %s is not supported." % imageType) - - -def getCentroidFindType(centroidFindType): - """Get the centroid find type. - - Parameters - ---------- - centroidFindType : str - Centroid find algorithm to use (randomWalk, otsu, or convolveTemplate). - - Returns - ------- - enum 'CentroidFindType' - Centroid find type algorithm. - - Raises - ------ - ValueError - The centroid find type is not supported. - """ - - if centroidFindType == "randomWalk": - return CentroidFindType.RandomWalk - elif centroidFindType == "otsu": - return CentroidFindType.Otsu - elif centroidFindType == "convolveTemplate": - return CentroidFindType.ConvolveTemplate - else: - raise ValueError("The %s is not supported." % centroidFindType) - - -def getDeblendDonutType(deblendDonutType): - """Get the deblend donut type. - - Parameters - ---------- - deblendDonutType : str - Deblend donut algorithm to use (adapt). - - Returns - ------- - enum 'DeblendDonutType' - Deblend donut type algorithm. - - Raises - ------ - ValueError - The deblend donut type is not supported. - """ - - if deblendDonutType == "adapt": - return DeblendDonutType.Adapt - else: - raise ValueError("The %s is not supported." % deblendDonutType) - - -def getDonutTemplateType(donutTemplateType): - """Get the donut template type. - - Parameters - ---------- - donutTemplateType : str - Donut template type to use (model or phosim). - - Returns - ------- - enum 'DonutTemplateType' - Donut template type algorithm. - - Raises - ------ - ValueError - The donut template type is not supported. - """ - - if donutTemplateType == "model": - return DonutTemplateType.Model - elif donutTemplateType == "phosim": - return DonutTemplateType.Phosim - else: - raise ValueError(f"The {donutTemplateType} is not supported.") - - -def getAmpImagesFromDir(rawExpDir): - """Apply regular expression to find - repackaged amplifier image files. - Use the negative lookahead to find those - fits files that do not contain '_e' in their name. - - Parameters - ---------- - rawExpDir : str - path to the input directory with raw repackaged files - - Returns - ------- - list [str] - raw amplifier image files - """ - return list(filter(re.compile(r"^((?!_e).)*fits$").match, os.listdir(rawExpDir))) - - -def writePipetaskCmd( - repoDir, runName, instrument, collections, taskName=None, pipelineYaml=None -): - """ - Format a command line call to run a Gen 3 pipeline task. - Can also be a set of tasks if specified in a pipeline yaml file. - - Parameters - ---------- - repoDir: str - Location of Gen 3 repository. - runName: str - Name of collection for data produced by the task. - instrument: str - The instrument to use for the task. - collections: str - The data collections needed for the task. - taskName: str, optional - Full task function name in lsst namespace. One of taskName - or pipelineYaml must be specified to run. (The default is None). - pipelineYaml: str, optional - Yaml file that specifies a pipeline configuration to run - instead of a single task. (The default is None.) - - Returns - ------- - str - Pipetask run command. - - Raises - ------ - ValueError - Need to at least specify name of task or name of pipeline file. - """ - if (taskName is None) and (pipelineYaml is None): - raise ValueError("At least one of taskName or pipelineYaml must not be None") - - pipetaskCmd = "pipetask run " - pipetaskCmd += f"-b {repoDir} " # Specify repo - pipetaskCmd += f"-i {collections} " # Specify collections with data to use - pipetaskCmd += f"--instrument {instrument} " - pipetaskCmd += f"--register-dataset-types --output-run {runName}" - if taskName is not None: - pipetaskCmd += f" -t {taskName}" - if pipelineYaml is not None: - pipetaskCmd += f" -p {pipelineYaml}" - - return pipetaskCmd - - -def writeCleanUpRepoCmd(repoDir, runName): - """ - Format a command line call to clean up the data created by a pipeline. - - Parameters - ---------- - repoDir: str - Location of Gen 3 repository. - runName: str - Name of collection for data produced by the task. - - Returns - ------- - str - Butler prune-collection command. - """ - - cleanUpCmd = "butler remove-runs " - cleanUpCmd += f"{repoDir} {runName} --no-confirm" - - return cleanUpCmd - - -def getCamType(instName): - """Get the camera type from instrument name. - - Parameters - ---------- - instName : str - Instrument name. - - Returns - ------- - camType : enum 'CamType' - Camera type. - - Raises - ------ - ValueError - Instrument name is not supported. - """ - if instName == "lsst": - return CamType.LsstCam - elif instName == "lsstfam": - return CamType.LsstFamCam - elif instName == "comcam": - return CamType.ComCam - elif instName == "auxTel": - return CamType.AuxTel - else: - raise ValueError(f"Instrument name ({instName}) is not supported.") - - -def getCamNameFromCamType(camType): - """Get the camera name for policy files from CamType. - - Parameters - ---------- - camType : enum 'CamType' - Camera Type. - - Returns - ------- - str - Instrument Name. - - Raises - ------ - ValueError - Camera Type is not supported. - """ - - if camType == CamType.LsstCam: - return "lsst" - elif camType == CamType.LsstFamCam: - return "lsstfam" - elif camType == CamType.ComCam: - return "comcam" - elif camType == CamType.AuxTel: - return "auxTel" - elif camType == CamType.AuxTelZWO: - return "auxTelZWO" - else: - raise ValueError(f"CamType ({camType}) is not supported.") - - -def getCamTypeFromButlerName(instName, detectorType): - """Get the camera type from instrument name used by the LSST DM - middleware for each instrument. - - Parameters - ---------- - instName : str - Instrument name. - detectorType : lsst.afw.cameraGeom.DetectorType - Type of CCD. "SCIENCE" or "WAVEFRONT". - - Returns - ------- - camType : enum 'CamType' - Camera type. - - Raises - ------ - ValueError - Combination of instrument name and detector type is not supported. - ValueError - Detector type is not supported. - """ - if detectorType == DetectorType.WAVEFRONT: - if instName == "LSSTCam": - return CamType.LsstCam - else: - raise ValueError( - f"Wavefront sensors for instrument name ({instName}) are not supported." - ) - elif detectorType == DetectorType.SCIENCE: - if instName == "LSSTCam": - return CamType.LsstFamCam - elif instName == "LSSTComCam": - return CamType.ComCam - elif instName == "LATISS": - return CamType.AuxTel - else: - raise ValueError( - f"Science sensors for instrument name ({instName}) are not supported." - ) - else: - raise ValueError(f"Detector Type ({detectorType.name}) is not supported.") - - -def getFilterTypeFromBandLabel(bandLabel): - """Get the FilterType associated with the name of the bandpass - accessed in an exposure using `exposure.filter.bandLabel`. - - Parameters - ---------- - bandLabel : str - Bandpass label of the exposure. - - Returns - ------- - filterType : enum `FilterType` - Filter type. - """ - filterLabelDict = {} - filterLabelDict["u"] = FilterType.LSST_U - filterLabelDict["g"] = FilterType.LSST_G - filterLabelDict["r"] = FilterType.LSST_R - filterLabelDict["i"] = FilterType.LSST_I - filterLabelDict["z"] = FilterType.LSST_Z - filterLabelDict["y"] = FilterType.LSST_Y - - return filterLabelDict.get(bandLabel, FilterType.REF) - - -def getDefocalDisInMm(instName): - """ - Get the defocal distance for the instrument - - Parameters - ---------- - instName : str - Instrument name, one of - 'lsst', 'lsstfam', 'comcam', - 'auxTel' - - Returns - ------- - defocalDisInMm : float - Defocal distance in mm. - - Raises - ------ - ValueError - Instrument name is not supported. - """ - if instName in ["lsst", "lsstfam", "comcam"]: - return 1.5 - elif instName == "auxTel": - return 0.8 - else: - raise ValueError(f"Instrument name ({instName}) is not supported.") - - -def createInstDictFromConfig(config): - """Create configuration dictionary for the instrument. - - Parameters - ---------- - config : lsst.pipe.base.PipelineTaskConfig - Task configuration. - - Returns - ------- - dict - Instrument configuration parameters - """ - - return { - "obscuration": config.instObscuration, - "focalLength": config.instFocalLength, - "apertureDiameter": config.instApertureDiameter, - "offset": config.instDefocalOffset, - "pixelSize": config.instPixelSize, - } - - -def rotMatrix(thetaDegrees): - """Create a 2-d rotation matrix for given angle. - - Parameters - ---------- - thetaDegrees : float - Rotation angle in degrees. - - Returns - ------- - np.ndarray - Rotation matrix for theta. - """ - - theta = np.radians(thetaDegrees) - return np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) diff --git a/python/lsst/ts/wep/utils/__init__.py b/python/lsst/ts/wep/utils/__init__.py new file mode 100644 index 00000000..1498f37c --- /dev/null +++ b/python/lsst/ts/wep/utils/__init__.py @@ -0,0 +1,6 @@ +from .enumUtils import * +from .ioUtils import * +from .miscUtils import * +from .plotUtils import * +from .taskUtils import * +from .zernikeUtils import * diff --git a/python/lsst/ts/wep/utils/enumUtils.py b/python/lsst/ts/wep/utils/enumUtils.py new file mode 100644 index 00000000..30fab0a4 --- /dev/null +++ b/python/lsst/ts/wep/utils/enumUtils.py @@ -0,0 +1,381 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +__all__ = [ + "FilterType", + "CamType", + "BscDbType", + "DefocalType", + "ImageType", + "CentroidFindType", + "DonutTemplateType", + "DeblendDonutType", + "getFilterTypeFromBandLabel", + "mapFilterRefToG", + "getCamType", + "getCamNameFromCamType", + "getCamTypeFromButlerName", + "getBscDbType", + "getImageType", + "getCentroidFindType", + "getDonutTemplateType", + "getDeblendDonutType", +] + +from enum import IntEnum, auto + +from lsst.afw.cameraGeom import DetectorType + + +class FilterType(IntEnum): + LSST_U = 1 + LSST_G = auto() + LSST_R = auto() + LSST_I = auto() + LSST_Z = auto() + LSST_Y = auto() + REF = auto() + + +class CamType(IntEnum): + LsstCam = 1 + LsstFamCam = auto() + ComCam = auto() + AuxTel = auto() + AuxTelZWO = auto() + + +class BscDbType(IntEnum): + LocalDb = 1 + LocalDbForStarFile = auto() + + +class DefocalType(IntEnum): + Intra = 1 + Extra = auto() + + +class ImageType(IntEnum): + Amp = 1 + Eimg = auto() + + +class CentroidFindType(IntEnum): + RandomWalk = 1 + Otsu = auto() + ConvolveTemplate = auto() + + +class DonutTemplateType(IntEnum): + Model = 1 + Phosim = auto() + + +class DeblendDonutType(IntEnum): + Adapt = 1 + + +def getFilterTypeFromBandLabel(bandLabel): + """Get the FilterType associated with the name of the bandpass + accessed in an exposure using `exposure.filter.bandLabel`. + + Parameters + ---------- + bandLabel : str + Bandpass label of the exposure. + + Returns + ------- + filterType : enum `FilterType` + Filter type. + """ + filterLabelDict = {} + filterLabelDict["u"] = FilterType.LSST_U + filterLabelDict["g"] = FilterType.LSST_G + filterLabelDict["r"] = FilterType.LSST_R + filterLabelDict["i"] = FilterType.LSST_I + filterLabelDict["z"] = FilterType.LSST_Z + filterLabelDict["y"] = FilterType.LSST_Y + + return filterLabelDict.get(bandLabel, FilterType.REF) + + +def mapFilterRefToG(filterType): + """Map the reference filter to the G filter. + + Parameters + ---------- + filterType : enum 'FilterType' + Filter type. + + Returns + ------- + enum 'FilterType' + Mapped filter type. + """ + + if filterType == FilterType.REF: + return FilterType.LSST_G + else: + return filterType + + +def getCamType(instName): + """Get the camera type from instrument name. + + Parameters + ---------- + instName : str + Instrument name. + + Returns + ------- + camType : enum 'CamType' + Camera type. + + Raises + ------ + ValueError + Instrument name is not supported. + """ + if instName == "lsst": + return CamType.LsstCam + elif instName == "lsstfam": + return CamType.LsstFamCam + elif instName == "comcam": + return CamType.ComCam + elif instName == "auxTel": + return CamType.AuxTel + else: + raise ValueError(f"Instrument name ({instName}) is not supported.") + + +def getCamNameFromCamType(camType): + """Get the camera name for policy files from CamType. + + Parameters + ---------- + camType : enum 'CamType' + Camera Type. + + Returns + ------- + str + Instrument Name. + + Raises + ------ + ValueError + Camera Type is not supported. + """ + + if camType == CamType.LsstCam: + return "lsst" + elif camType == CamType.LsstFamCam: + return "lsstfam" + elif camType == CamType.ComCam: + return "comcam" + elif camType == CamType.AuxTel: + return "auxTel" + elif camType == CamType.AuxTelZWO: + return "auxTelZWO" + else: + raise ValueError(f"CamType ({camType}) is not supported.") + + +def getCamTypeFromButlerName(instName, detectorType): + """Get the camera type from instrument name used by the LSST DM + middleware for each instrument. + + Parameters + ---------- + instName : str + Instrument name. + detectorType : lsst.afw.cameraGeom.DetectorType + Type of CCD. "SCIENCE" or "WAVEFRONT". + + Returns + ------- + camType : enum 'CamType' + Camera type. + + Raises + ------ + ValueError + Combination of instrument name and detector type is not supported. + ValueError + Detector type is not supported. + """ + if detectorType == DetectorType.WAVEFRONT: + if instName == "LSSTCam": + return CamType.LsstCam + else: + raise ValueError( + f"Wavefront sensors for instrument name ({instName}) are not supported." + ) + elif detectorType == DetectorType.SCIENCE: + if instName == "LSSTCam": + return CamType.LsstFamCam + elif instName == "LSSTComCam": + return CamType.ComCam + elif instName == "LATISS": + return CamType.AuxTel + else: + raise ValueError( + f"Science sensors for instrument name ({instName}) are not supported." + ) + else: + raise ValueError(f"Detector Type ({detectorType.name}) is not supported.") + + +def getBscDbType(bscDbType): + """Get the bright star catalog (BSC) database type. + + Parameters + ---------- + bscDbType : str + BSC database type to use (localDb or file). + + Returns + ------- + enum 'BscDbType' + BSC database type. + + Raises + ------ + ValueError + The bscDb is not supported. + """ + + if bscDbType == "localDb": + return BscDbType.LocalDb + elif bscDbType == "file": + return BscDbType.LocalDbForStarFile + else: + raise ValueError("The bscDb (%s) is not supported." % bscDbType) + + +def getImageType(imageType): + """Get the image type. + + Parameters + ---------- + imageType : str + Image type to use (amp or eimage). + + Returns + ------- + enum 'ImageType' + ImageType enum. + + Raises + ------ + ValueError + The image type is not supported. + """ + + if imageType == "amp": + return ImageType.Amp + elif imageType == "eimage": + return ImageType.Eimg + else: + raise ValueError("The %s is not supported." % imageType) + + +def getCentroidFindType(centroidFindType): + """Get the centroid find type. + + Parameters + ---------- + centroidFindType : str + Centroid find algorithm to use (randomWalk, otsu, or convolveTemplate). + + Returns + ------- + enum 'CentroidFindType' + Centroid find type algorithm. + + Raises + ------ + ValueError + The centroid find type is not supported. + """ + + if centroidFindType == "randomWalk": + return CentroidFindType.RandomWalk + elif centroidFindType == "otsu": + return CentroidFindType.Otsu + elif centroidFindType == "convolveTemplate": + return CentroidFindType.ConvolveTemplate + else: + raise ValueError("The %s is not supported." % centroidFindType) + + +def getDonutTemplateType(donutTemplateType): + """Get the donut template type. + + Parameters + ---------- + donutTemplateType : str + Donut template type to use (model or phosim). + + Returns + ------- + enum 'DonutTemplateType' + Donut template type algorithm. + + Raises + ------ + ValueError + The donut template type is not supported. + """ + + if donutTemplateType == "model": + return DonutTemplateType.Model + elif donutTemplateType == "phosim": + return DonutTemplateType.Phosim + else: + raise ValueError(f"The {donutTemplateType} is not supported.") + + +def getDeblendDonutType(deblendDonutType): + """Get the deblend donut type. + + Parameters + ---------- + deblendDonutType : str + Deblend donut algorithm to use (adapt). + + Returns + ------- + enum 'DeblendDonutType' + Deblend donut type algorithm. + + Raises + ------ + ValueError + The deblend donut type is not supported. + """ + + if deblendDonutType == "adapt": + return DeblendDonutType.Adapt + else: + raise ValueError("The %s is not supported." % deblendDonutType) diff --git a/python/lsst/ts/wep/utils/ioUtils.py b/python/lsst/ts/wep/utils/ioUtils.py new file mode 100644 index 00000000..0b620f9a --- /dev/null +++ b/python/lsst/ts/wep/utils/ioUtils.py @@ -0,0 +1,208 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +__all__ = [ + "getModulePath", + "getConfigDir", + "getObsLsstCmdTaskConfigDir", + "writeFile", + "readPhoSimSettingData", + "getAmpImagesFromDir", +] + +import os +import re + +from lsst.utils import getPackageDir + + +def getModulePath(): + """Get the path of module. + + Returns + ------- + str + Directory path of module. + """ + + return getPackageDir("ts_wep") + + +def getConfigDir(): + """Get the directory of configuration files. + + Returns + ------- + str + Directory of configuration files. + """ + + return os.path.join(getModulePath(), "policy") + + +def getObsLsstCmdTaskConfigDir(): + """Get the obs_lsst command line task configuration directory. + + Returns + ------- + str + obs_lsst command line task configuration directory. + """ + + return os.path.join(getPackageDir("obs_lsst"), "config") + + +def writeFile(filePath, content): + """Write the content to file. + + Parameters + ---------- + filePath : str + File path. + content : str + File content. + """ + + with open(filePath, "w") as file: + file.write(content) + + +def readPhoSimSettingData(folderPath, fileName, atype): + """Read the PhoSim setting data (segmentation or focal plane layout). + + Parameters + ---------- + folderPath : str + Path to folder. + fileName : str + File name ("segmentation.txt", "focalplanelayout.txt"). + atype : str + Type of data to read ("readOutDim", "darkCurrent", "fieldCenter", + "eulerRot"). + + Returns + ------- + dict + Needed CCD data. + + Raises + ------ + ValueError + File can not be read. + ValueError + Type is not correct. + """ + + # Check the file name + if fileName not in ("segmentation.txt", "focalplanelayout.txt"): + raise ValueError("'%s' can not be read." % fileName) + + # Check the type + if atype not in ("readOutDim", "darkCurrent", "fieldCenter", "eulerRot"): + raise ValueError("'%s' can not be read." % atype) + + # Get the file path + pathToFile = os.path.join(folderPath, fileName) + + # Amplifier list (only list the scientific ccd here) + ampList = [ + "C00", + "C01", + "C02", + "C03", + "C04", + "C05", + "C06", + "C07", + "C10", + "C11", + "C12", + "C13", + "C14", + "C15", + "C16", + "C17", + ] + + # Open the file to read + ccdData = {} + fid = open(pathToFile) + for line in fid: + line = line.strip() + + # Get each element + lineElement = line.split() + + data = [] + # Analyze the sensor name to find the amplifier + if fileName == "segmentation.txt": + sensorNameStr = lineElement[0].split("_") + if len(sensorNameStr) == 3: + if sensorNameStr[2] in ampList: + # Get the segmentation in txt file + if atype == "readOutDim": + # parallel prescan, serial overscan, serial prescan, + # parallel overscan (pixel) + data = lineElement[15:19] + elif atype == "darkCurrent": + data = lineElement[13:15] + + elif fileName == "focalplanelayout.txt": + # Analyze the sensor name to make sure this line of data is + # needed + sensorNameStr = lineElement[0].split("_") + if len(sensorNameStr) == 2 or len(sensorNameStr) == 3: + if atype == "fieldCenter": + # Collect the field center: + # x position (microns), y position (microns), pixel + # size (microns) number of x pixels, number of y pixels + data = lineElement[1:6] + elif atype == "eulerRot": + # Collect the euler Rotation (degrees) + data = lineElement[12:15] + + # Collect the data + if data: + ccdData.update({lineElement[0]: data}) + + # Close the file + fid.close() + + return ccdData + + +def getAmpImagesFromDir(rawExpDir): + """Apply regular expression to find + repackaged amplifier image files. + Use the negative lookahead to find those + fits files that do not contain '_e' in their name. + + Parameters + ---------- + rawExpDir : str + path to the input directory with raw repackaged files + + Returns + ------- + list [str] + raw amplifier image files + """ + return list(filter(re.compile(r"^((?!_e).)*fits$").match, os.listdir(rawExpDir))) diff --git a/python/lsst/ts/wep/utils/miscUtils.py b/python/lsst/ts/wep/utils/miscUtils.py new file mode 100644 index 00000000..228b1cff --- /dev/null +++ b/python/lsst/ts/wep/utils/miscUtils.py @@ -0,0 +1,216 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +__all__ = [ + "searchDonutPos", + "getDefocalDisInMm", + "createInstDictFromConfig", + "rotMatrix", + "padArray", + "extractArray", +] + +import numpy as np +from scipy.ndimage import center_of_mass + + +def searchDonutPos(img): + """Search the position of donut on image. + + Parameters + ---------- + img : numpy.ndarray + Donut image. + + Returns + ------- + float + X position of donut center in pixel. + float + Y position of donut center in pixel. + """ + + # Search the donut position by the center of mass + # Need to update this method to the more robust one such as the convolution + realcy, realcx = center_of_mass(img) + + return realcx, realcy + + +def getDefocalDisInMm(instName): + """ + Get the defocal distance for the instrument + + Parameters + ---------- + instName : str + Instrument name, one of + 'lsst', 'lsstfam', 'comcam', + 'auxTel' + + Returns + ------- + defocalDisInMm : float + Defocal distance in mm. + + Raises + ------ + ValueError + Instrument name is not supported. + """ + if instName in ["lsst", "lsstfam", "comcam"]: + return 1.5 + elif instName == "auxTel": + return 0.8 + else: + raise ValueError(f"Instrument name ({instName}) is not supported.") + + +def createInstDictFromConfig(config): + """Create configuration dictionary for the instrument. + + Parameters + ---------- + config : lsst.pipe.base.PipelineTaskConfig + Task configuration. + + Returns + ------- + dict + Instrument configuration parameters + """ + + return { + "obscuration": config.instObscuration, + "focalLength": config.instFocalLength, + "apertureDiameter": config.instApertureDiameter, + "offset": config.instDefocalOffset, + "pixelSize": config.instPixelSize, + } + + +def rotMatrix(thetaDegrees): + """Create a 2-d rotation matrix for given angle. + + Parameters + ---------- + thetaDegrees : float + Rotation angle in degrees. + + Returns + ------- + np.ndarray + Rotation matrix for theta. + """ + + theta = np.radians(thetaDegrees) + return np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) + + +def padArray(inArray, dim): + """Extend the boundary of image. + + For example, the input image is 120x120 matrix. This function will create + an image such as 140x140 (dim x dim) matrix and put the input image in the + center of new image. + + Parameters + ---------- + inArray : numpy.ndarray + Input central image. + dim : int + Dimension of new extended image. + + Returns + ------- + numpy.ndarray + Extended image from the dimension of inArray to dim x dim. + + Raises + ------ + Exception + Check the dimension of inArray is n by n or not. + Exception + Check the extending dimension is bigger than the dimension of inArray + or not. + """ + + # Check the conditions + m, n = inArray.shape + if m != n: + raise Exception("padArray: array is not square.") + + if m > dim: + raise Exception("padArray: array is larger than dimension.") + + # Extend the boundary of image by creating a bigger matrix and putting the + # input image in the center + out = np.zeros([dim, dim]) + ii = int(np.floor((dim - m) / 2)) + + # Put the original image in the center of extended image + out[ii : ii + m, ii : ii + m] = inArray + + return out + + +def extractArray(inArray, dim): + """Extract the central image. + + For example, the input image is a 140x140 matrix. This function will + extract the central matrix with the dimension of 120x120 (dim x dim). + + Parameters + ---------- + inArray : numpy.ndarray + Input image. + dim : int + Dimension of extracted image. + + Returns + ------- + numpy.ndarray + Extracted central image from the dimension of inArray to dim x dim. + + Raises + ------ + Exception + Check the dimension of inArray is n by n or not. + Exception + Check the extracted dimension is smaller than the dimension of inArray + or not. + """ + + # Check the conditions + m, n = inArray.shape + if m != n: + raise Exception("extractArray: array is not square.") + + if m < dim: + raise Exception("extractArray: array is smaller than dimension") + + # Calculate the begining index to extract the central image + ii = int(np.floor((m - dim) / 2)) + + # Extract the central image + out = inArray[ii : ii + dim, ii : ii + dim] + + return out diff --git a/python/lsst/ts/wep/plotUtil.py b/python/lsst/ts/wep/utils/plotUtils.py similarity index 100% rename from python/lsst/ts/wep/plotUtil.py rename to python/lsst/ts/wep/utils/plotUtils.py diff --git a/python/lsst/ts/wep/utils/taskUtils.py b/python/lsst/ts/wep/utils/taskUtils.py new file mode 100644 index 00000000..03394781 --- /dev/null +++ b/python/lsst/ts/wep/utils/taskUtils.py @@ -0,0 +1,133 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +__all__ = [ + "runProgram", + "writePipetaskCmd", + "writeCleanUpRepoCmd", +] + +import os +import subprocess + + +def runProgram(command, binDir=None, argstring=None): + """Run the program w/o arguments. + + Parameters + ---------- + command : str + Command of application. + binDir : str, optional + Directory of binary application. (the default is None.) + argstring : str, optional + Arguments of program. (the default is None.) + + Raises + ------ + RuntimeError + Error running of command. + """ + + # Directory of binary application + if binDir is not None: + command = os.path.join(binDir, command) + + # Arguments for the program + if argstring is not None: + command += " " + argstring + + # Call the program w/o arguments + if subprocess.call(command, shell=True) != 0: + raise RuntimeError("Error running: %s" % command) + + +def writePipetaskCmd( + repoDir, runName, instrument, collections, taskName=None, pipelineYaml=None +): + """ + Format a command line call to run a Gen 3 pipeline task. + Can also be a set of tasks if specified in a pipeline yaml file. + + Parameters + ---------- + repoDir: str + Location of Gen 3 repository. + runName: str + Name of collection for data produced by the task. + instrument: str + The instrument to use for the task. + collections: str + The data collections needed for the task. + taskName: str, optional + Full task function name in lsst namespace. One of taskName + or pipelineYaml must be specified to run. (The default is None). + pipelineYaml: str, optional + Yaml file that specifies a pipeline configuration to run + instead of a single task. (The default is None.) + + Returns + ------- + str + Pipetask run command. + + Raises + ------ + ValueError + Need to at least specify name of task or name of pipeline file. + """ + if (taskName is None) and (pipelineYaml is None): + raise ValueError("At least one of taskName or pipelineYaml must not be None") + + pipetaskCmd = "pipetask run " + pipetaskCmd += f"-b {repoDir} " # Specify repo + pipetaskCmd += f"-i {collections} " # Specify collections with data to use + pipetaskCmd += f"--instrument {instrument} " + pipetaskCmd += f"--register-dataset-types --output-run {runName}" + if taskName is not None: + pipetaskCmd += f" -t {taskName}" + if pipelineYaml is not None: + pipetaskCmd += f" -p {pipelineYaml}" + + return pipetaskCmd + + +def writeCleanUpRepoCmd(repoDir, runName): + """ + Format a command line call to clean up the data created by a pipeline. + + Parameters + ---------- + repoDir: str + Location of Gen 3 repository. + runName: str + Name of collection for data produced by the task. + + Returns + ------- + str + Butler prune-collection command. + """ + + cleanUpCmd = "butler remove-runs " + cleanUpCmd += f"{repoDir} {runName} --no-confirm" + + return cleanUpCmd diff --git a/python/lsst/ts/wep/cwfs/tool.py b/python/lsst/ts/wep/utils/zernikeUtils.py similarity index 79% rename from python/lsst/ts/wep/cwfs/tool.py rename to python/lsst/ts/wep/utils/zernikeUtils.py index ef3edc9c..c25938eb 100644 --- a/python/lsst/ts/wep/cwfs/tool.py +++ b/python/lsst/ts/wep/utils/zernikeUtils.py @@ -29,8 +29,6 @@ "ZernikeEval", "ZernikeFit", "ZernikeMaskedFit", - "padArray", - "extractArray", ] import numpy as np @@ -320,94 +318,3 @@ def ZernikeMaskedFit(s, x, y, numTerms, mask, e): # Calculate coefficients of normal/ spherical Zernike polynomials return ZernikeAnnularFit(s, x, y, numTerms, e) - - -def padArray(inArray, dim): - """Extend the boundary of image. - - For example, the input image is 120x120 matrix. This function will create - an image such as 140x140 (dim x dim) matrix and put the input image in the - center of new image. - - Parameters - ---------- - inArray : numpy.ndarray - Input central image. - dim : int - Dimension of new extended image. - - Returns - ------- - numpy.ndarray - Extended image from the dimension of inArray to dim x dim. - - Raises - ------ - Exception - Check the dimension of inArray is n by n or not. - Exception - Check the extending dimension is bigger than the dimension of inArray - or not. - """ - - # Check the conditions - m, n = inArray.shape - if m != n: - raise Exception("padArray: array is not square.") - - if m > dim: - raise Exception("padArray: array is larger than dimension.") - - # Extend the boundary of image by creating a bigger matrix and putting the - # input image in the center - out = np.zeros([dim, dim]) - ii = int(np.floor((dim - m) / 2)) - - # Put the original image in the center of extended image - out[ii : ii + m, ii : ii + m] = inArray - - return out - - -def extractArray(inArray, dim): - """Extract the central image. - - For example, the input image is a 140x140 matrix. This function will - extract the central matrix with the dimension of 120x120 (dim x dim). - - Parameters - ---------- - inArray : numpy.ndarray - Input image. - dim : int - Dimension of extracted image. - - Returns - ------- - numpy.ndarray - Extracted central image from the dimension of inArray to dim x dim. - - Raises - ------ - Exception - Check the dimension of inArray is n by n or not. - Exception - Check the extracted dimension is smaller than the dimension of inArray - or not. - """ - - # Check the conditions - m, n = inArray.shape - if m != n: - raise Exception("extractArray: array is not square.") - - if m < dim: - raise Exception("extractArray: array is smaller than dimension") - - # Calculate the begining index to extract the central image - ii = int(np.floor((m - dim) / 2)) - - # Extract the central image - out = inArray[ii : ii + dim, ii : ii + dim] - - return out diff --git a/python/lsst/ts/wep/wfEstimator.py b/python/lsst/ts/wep/wfEstimator.py index c2db057a..ae17190a 100644 --- a/python/lsst/ts/wep/wfEstimator.py +++ b/python/lsst/ts/wep/wfEstimator.py @@ -24,7 +24,7 @@ from lsst.ts.wep.cwfs.algorithm import Algorithm from lsst.ts.wep.cwfs.compensableImage import CompensableImage from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.utility import CamType, CentroidFindType, DefocalType, FilterType +from lsst.ts.wep.utils import CamType, CentroidFindType, DefocalType, FilterType class WfEstimator(object): diff --git a/tests/cwfs/test_algorithm.py b/tests/cwfs/test_algorithm.py index 5d5b7e85..eb65ba0c 100644 --- a/tests/cwfs/test_algorithm.py +++ b/tests/cwfs/test_algorithm.py @@ -26,7 +26,7 @@ from lsst.ts.wep.cwfs.algorithm import Algorithm from lsst.ts.wep.cwfs.compensableImage import CompensableImage from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.utility import CamType, DefocalType, getConfigDir, getModulePath +from lsst.ts.wep.utils import CamType, DefocalType, getConfigDir, getModulePath class TestAlgorithm(unittest.TestCase): diff --git a/tests/cwfs/test_centroidFindFactory.py b/tests/cwfs/test_centroidFindFactory.py index 0110471d..12f225a8 100644 --- a/tests/cwfs/test_centroidFindFactory.py +++ b/tests/cwfs/test_centroidFindFactory.py @@ -25,7 +25,7 @@ from lsst.ts.wep.cwfs.centroidFindFactory import CentroidFindFactory from lsst.ts.wep.cwfs.centroidOtsu import CentroidOtsu from lsst.ts.wep.cwfs.centroidRandomWalk import CentroidRandomWalk -from lsst.ts.wep.utility import CentroidFindType +from lsst.ts.wep.utils import CentroidFindType class TestCentroidFindFactory(unittest.TestCase): diff --git a/tests/cwfs/test_centroidOtsu.py b/tests/cwfs/test_centroidOtsu.py index 12a0a300..59570165 100644 --- a/tests/cwfs/test_centroidOtsu.py +++ b/tests/cwfs/test_centroidOtsu.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.cwfs.centroidOtsu import CentroidOtsu -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestCentroidOtsu(unittest.TestCase): diff --git a/tests/cwfs/test_centroidRandomWalk.py b/tests/cwfs/test_centroidRandomWalk.py index 79c0265f..84921086 100644 --- a/tests/cwfs/test_centroidRandomWalk.py +++ b/tests/cwfs/test_centroidRandomWalk.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.cwfs.centroidRandomWalk import CentroidRandomWalk -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestCentroidRandomWalk(unittest.TestCase): diff --git a/tests/cwfs/test_compensableImage.py b/tests/cwfs/test_compensableImage.py index d25044ca..869d4d92 100644 --- a/tests/cwfs/test_compensableImage.py +++ b/tests/cwfs/test_compensableImage.py @@ -27,7 +27,7 @@ from lsst.ts.wep.cwfs.compensableImage import CompensableImage from lsst.ts.wep.cwfs.image import Image from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( CamType, DefocalType, FilterType, diff --git a/tests/cwfs/test_donutTemplateFactory.py b/tests/cwfs/test_donutTemplateFactory.py index a03528f1..491939a7 100644 --- a/tests/cwfs/test_donutTemplateFactory.py +++ b/tests/cwfs/test_donutTemplateFactory.py @@ -24,7 +24,7 @@ from lsst.ts.wep.cwfs.donutTemplateFactory import DonutTemplateFactory from lsst.ts.wep.cwfs.donutTemplateModel import DonutTemplateModel from lsst.ts.wep.cwfs.donutTemplatePhosim import DonutTemplatePhosim -from lsst.ts.wep.utility import DonutTemplateType +from lsst.ts.wep.utils import DonutTemplateType class TestTemplateMakerFactory(unittest.TestCase): diff --git a/tests/cwfs/test_donutTemplateModel.py b/tests/cwfs/test_donutTemplateModel.py index 12daaf85..d9af9174 100644 --- a/tests/cwfs/test_donutTemplateModel.py +++ b/tests/cwfs/test_donutTemplateModel.py @@ -23,7 +23,7 @@ import numpy as np from lsst.ts.wep.cwfs.donutTemplateModel import DonutTemplateModel -from lsst.ts.wep.utility import CamType, DefocalType +from lsst.ts.wep.utils import CamType, DefocalType class TestTemplateModel(unittest.TestCase): diff --git a/tests/cwfs/test_donutTemplatePhosim.py b/tests/cwfs/test_donutTemplatePhosim.py index 5605dc18..a12c79b0 100644 --- a/tests/cwfs/test_donutTemplatePhosim.py +++ b/tests/cwfs/test_donutTemplatePhosim.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.cwfs.donutTemplatePhosim import DonutTemplatePhosim -from lsst.ts.wep.utility import DefocalType, getModulePath +from lsst.ts.wep.utils import DefocalType, getModulePath class TestTemplatePhosim(unittest.TestCase): diff --git a/tests/cwfs/test_image.py b/tests/cwfs/test_image.py index ed0ed0ec..ee20e033 100644 --- a/tests/cwfs/test_image.py +++ b/tests/cwfs/test_image.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.cwfs.centroidRandomWalk import CentroidRandomWalk from lsst.ts.wep.cwfs.image import Image -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestImage(unittest.TestCase): diff --git a/tests/cwfs/test_imgsAuxTelZWO.py b/tests/cwfs/test_imgsAuxTelZWO.py index a92250d9..dd86dac1 100644 --- a/tests/cwfs/test_imgsAuxTelZWO.py +++ b/tests/cwfs/test_imgsAuxTelZWO.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.cwfs.baseCwfsTestCase import BaseCwfsTestCase from lsst.ts.wep.cwfs.image import Image -from lsst.ts.wep.utility import CamType, CentroidFindType, getModulePath +from lsst.ts.wep.utils import CamType, CentroidFindType, getModulePath class TestImgsAuxTelZWO(BaseCwfsTestCase, unittest.TestCase): diff --git a/tests/cwfs/test_imgsLsstFam.py b/tests/cwfs/test_imgsLsstFam.py index 56dfc16b..670fb68c 100644 --- a/tests/cwfs/test_imgsLsstFam.py +++ b/tests/cwfs/test_imgsLsstFam.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.cwfs.baseCwfsTestCase import BaseCwfsTestCase -from lsst.ts.wep.utility import CamType, CentroidFindType, getModulePath +from lsst.ts.wep.utils import CamType, CentroidFindType, getModulePath class TestImgsLsstFam(BaseCwfsTestCase, unittest.TestCase): diff --git a/tests/cwfs/test_instrument.py b/tests/cwfs/test_instrument.py index a860c774..14eb7829 100644 --- a/tests/cwfs/test_instrument.py +++ b/tests/cwfs/test_instrument.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.cwfs.instrument import Instrument -from lsst.ts.wep.utility import CamType, getConfigDir, getModulePath +from lsst.ts.wep.utils import CamType, getConfigDir, getModulePath class TestInstrument(unittest.TestCase): diff --git a/tests/cwfs/test_multiImgs.py b/tests/cwfs/test_multiImgs.py index 1c89cba0..0dcb634c 100644 --- a/tests/cwfs/test_multiImgs.py +++ b/tests/cwfs/test_multiImgs.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.cwfs.baseCwfsTestCase import BaseCwfsTestCase -from lsst.ts.wep.utility import CamType, CentroidFindType, getModulePath +from lsst.ts.wep.utils import CamType, CentroidFindType, getModulePath class TestWepWithMultiImgs(BaseCwfsTestCase, unittest.TestCase): diff --git a/tests/deblend/test_deblendAdapt.py b/tests/deblend/test_deblendAdapt.py index 5464f61b..d696de4b 100644 --- a/tests/deblend/test_deblendAdapt.py +++ b/tests/deblend/test_deblendAdapt.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.deblend.deblendAdapt import DeblendAdapt -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestDeblendAdapt(unittest.TestCase): diff --git a/tests/deblend/test_deblendDefault.py b/tests/deblend/test_deblendDefault.py index 1ed335e7..a1e97d4d 100644 --- a/tests/deblend/test_deblendDefault.py +++ b/tests/deblend/test_deblendDefault.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.deblend.deblendDefault import DeblendDefault -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestDeblendDefault(unittest.TestCase): diff --git a/tests/deblend/test_deblendDonutFactory.py b/tests/deblend/test_deblendDonutFactory.py index 0304bd10..8af36e4d 100644 --- a/tests/deblend/test_deblendDonutFactory.py +++ b/tests/deblend/test_deblendDonutFactory.py @@ -23,7 +23,7 @@ from lsst.ts.wep.deblend.deblendAdapt import DeblendAdapt from lsst.ts.wep.deblend.deblendDonutFactory import DeblendDonutFactory -from lsst.ts.wep.utility import DeblendDonutType +from lsst.ts.wep.utils import DeblendDonutType class TestDeblendDonutFactory(unittest.TestCase): diff --git a/tests/task/test_calcZernikesTaskCwfs.py b/tests/task/test_calcZernikesTaskCwfs.py index efd468c2..2708579c 100644 --- a/tests/task/test_calcZernikesTaskCwfs.py +++ b/tests/task/test_calcZernikesTaskCwfs.py @@ -28,7 +28,7 @@ from lsst.ts.wep.task.combineZernikesMeanTask import CombineZernikesMeanTask from lsst.ts.wep.task.combineZernikesSigmaClipTask import CombineZernikesSigmaClipTask from lsst.ts.wep.task.donutStamps import DonutStamps -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( getModulePath, runProgram, writeCleanUpRepoCmd, diff --git a/tests/task/test_calcZernikesTaskLatiss.py b/tests/task/test_calcZernikesTaskLatiss.py index a3164a55..234ef442 100644 --- a/tests/task/test_calcZernikesTaskLatiss.py +++ b/tests/task/test_calcZernikesTaskLatiss.py @@ -29,7 +29,7 @@ from lsst.ts.wep.task.calcZernikesTask import CalcZernikesTask, CalcZernikesTaskConfig from lsst.ts.wep.task.combineZernikesMeanTask import CombineZernikesMeanTask from lsst.ts.wep.task.combineZernikesSigmaClipTask import CombineZernikesSigmaClipTask -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( getModulePath, runProgram, writeCleanUpRepoCmd, diff --git a/tests/task/test_calcZernikesTaskScienceSensor.py b/tests/task/test_calcZernikesTaskScienceSensor.py index 1aee5357..8cc4bcaa 100644 --- a/tests/task/test_calcZernikesTaskScienceSensor.py +++ b/tests/task/test_calcZernikesTaskScienceSensor.py @@ -27,7 +27,7 @@ from lsst.ts.wep.task.calcZernikesTask import CalcZernikesTask, CalcZernikesTaskConfig from lsst.ts.wep.task.combineZernikesMeanTask import CombineZernikesMeanTask from lsst.ts.wep.task.combineZernikesSigmaClipTask import CombineZernikesSigmaClipTask -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( getModulePath, runProgram, writeCleanUpRepoCmd, diff --git a/tests/task/test_cutOutDonutsBase.py b/tests/task/test_cutOutDonutsBase.py index f6d96df9..22f618a4 100644 --- a/tests/task/test_cutOutDonutsBase.py +++ b/tests/task/test_cutOutDonutsBase.py @@ -29,7 +29,7 @@ CutOutDonutsBaseTask, CutOutDonutsBaseTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( CamType, DefocalType, getModulePath, diff --git a/tests/task/test_cutOutDonutsCwfsTask.py b/tests/task/test_cutOutDonutsCwfsTask.py index 389fc65e..d913df45 100644 --- a/tests/task/test_cutOutDonutsCwfsTask.py +++ b/tests/task/test_cutOutDonutsCwfsTask.py @@ -27,7 +27,7 @@ CutOutDonutsCwfsTask, CutOutDonutsCwfsTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DefocalType, getModulePath, runProgram, diff --git a/tests/task/test_cutOutDonutsLatissTask.py b/tests/task/test_cutOutDonutsLatissTask.py index 8662a97e..c2a284dc 100644 --- a/tests/task/test_cutOutDonutsLatissTask.py +++ b/tests/task/test_cutOutDonutsLatissTask.py @@ -30,7 +30,7 @@ CutOutDonutsScienceSensorTask, CutOutDonutsScienceSensorTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DefocalType, getModulePath, runProgram, diff --git a/tests/task/test_cutOutDonutsScienceSensorTask.py b/tests/task/test_cutOutDonutsScienceSensorTask.py index b78f4e59..3f8048a5 100644 --- a/tests/task/test_cutOutDonutsScienceSensorTask.py +++ b/tests/task/test_cutOutDonutsScienceSensorTask.py @@ -28,7 +28,7 @@ CutOutDonutsScienceSensorTask, CutOutDonutsScienceSensorTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DefocalType, getModulePath, runProgram, diff --git a/tests/task/test_donutQuickMeasurementTask.py b/tests/task/test_donutQuickMeasurementTask.py index 995ce28d..f56a1738 100644 --- a/tests/task/test_donutQuickMeasurementTask.py +++ b/tests/task/test_donutQuickMeasurementTask.py @@ -30,7 +30,7 @@ DonutQuickMeasurementTask, DonutQuickMeasurementTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( DefocalType, DonutTemplateType, getModulePath, diff --git a/tests/task/test_donutSourceSelectorTask.py b/tests/task/test_donutSourceSelectorTask.py index 43e84500..fb530a61 100644 --- a/tests/task/test_donutSourceSelectorTask.py +++ b/tests/task/test_donutSourceSelectorTask.py @@ -32,7 +32,7 @@ DonutSourceSelectorTask, DonutSourceSelectorTaskConfig, ) -from lsst.ts.wep.utility import getConfigDir, getModulePath +from lsst.ts.wep.utils import getConfigDir, getModulePath class TestDonutSourceSelectorTask(unittest.TestCase): diff --git a/tests/task/test_donutStamp.py b/tests/task/test_donutStamp.py index dea9f79a..956f4858 100644 --- a/tests/task/test_donutStamp.py +++ b/tests/task/test_donutStamp.py @@ -31,7 +31,7 @@ from lsst.ts.wep.cwfs.compensableImage import CompensableImage from lsst.ts.wep.cwfs.instrument import Instrument from lsst.ts.wep.task.donutStamp import DonutStamp -from lsst.ts.wep.utility import CamType, DefocalType, getConfigDir +from lsst.ts.wep.utils import CamType, DefocalType, getConfigDir class TestDonutStamp(unittest.TestCase): diff --git a/tests/task/test_donutStamps.py b/tests/task/test_donutStamps.py index 653e7def..58d6ac48 100644 --- a/tests/task/test_donutStamps.py +++ b/tests/task/test_donutStamps.py @@ -29,7 +29,7 @@ from lsst.daf.base import PropertyList from lsst.ts.wep.task.donutStamp import DonutStamp from lsst.ts.wep.task.donutStamps import DonutStamps -from lsst.ts.wep.utility import DefocalType +from lsst.ts.wep.utils import DefocalType class TestDonutStamps(lsst.utils.tests.TestCase): diff --git a/tests/task/test_generateDonutCatalogOnlineTask.py b/tests/task/test_generateDonutCatalogOnlineTask.py index 75304e5f..2d090e71 100644 --- a/tests/task/test_generateDonutCatalogOnlineTask.py +++ b/tests/task/test_generateDonutCatalogOnlineTask.py @@ -31,7 +31,7 @@ GenerateDonutCatalogOnlineTaskConfig, ) from lsst.ts.wep.task.refCatalogInterface import RefCatalogInterface -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestGenerateDonutCatalogOnlineTask(unittest.TestCase): diff --git a/tests/task/test_generateDonutCatalogUtils.py b/tests/task/test_generateDonutCatalogUtils.py index 6ef01b94..9ac4153e 100644 --- a/tests/task/test_generateDonutCatalogUtils.py +++ b/tests/task/test_generateDonutCatalogUtils.py @@ -32,7 +32,7 @@ donutCatalogToDataFrame, runSelection, ) -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestGenerateDonutCatalogUtils(unittest.TestCase): diff --git a/tests/task/test_generateDonutCatalogWcsTask.py b/tests/task/test_generateDonutCatalogWcsTask.py index 2340e814..1681ac0f 100644 --- a/tests/task/test_generateDonutCatalogWcsTask.py +++ b/tests/task/test_generateDonutCatalogWcsTask.py @@ -30,7 +30,7 @@ GenerateDonutCatalogWcsTask, GenerateDonutCatalogWcsTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( getModulePath, runProgram, writeCleanUpRepoCmd, diff --git a/tests/task/test_generateDonutDirectDetectTask.py b/tests/task/test_generateDonutDirectDetectTask.py index b05cddf2..3ba9ce6a 100644 --- a/tests/task/test_generateDonutDirectDetectTask.py +++ b/tests/task/test_generateDonutDirectDetectTask.py @@ -29,7 +29,7 @@ GenerateDonutDirectDetectTask, GenerateDonutDirectDetectTaskConfig, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( getModulePath, runProgram, writeCleanUpRepoCmd, diff --git a/tests/task/test_generateDonutFromRefitWcsTask.py b/tests/task/test_generateDonutFromRefitWcsTask.py index 7dba252f..18443a9d 100644 --- a/tests/task/test_generateDonutFromRefitWcsTask.py +++ b/tests/task/test_generateDonutFromRefitWcsTask.py @@ -34,7 +34,7 @@ GenerateDonutFromRefitWcsTaskConfig, RefCatalogInterface, ) -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( getModulePath, runProgram, writeCleanUpRepoCmd, diff --git a/tests/task/test_refCatalogInterface.py b/tests/task/test_refCatalogInterface.py index 799b6a51..1f83f8cd 100644 --- a/tests/task/test_refCatalogInterface.py +++ b/tests/task/test_refCatalogInterface.py @@ -24,7 +24,7 @@ from lsst.daf import butler as dafButler from lsst.ts.wep.task.refCatalogInterface import RefCatalogInterface -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestRefCatalogInterface(unittest.TestCase): diff --git a/tests/test_donutDetector.py b/tests/test_donutDetector.py index e488ac15..7b3a15f5 100644 --- a/tests/test_donutDetector.py +++ b/tests/test_donutDetector.py @@ -25,7 +25,7 @@ import pandas as pd from lsst.ts.wep.cwfs.donutTemplateFactory import DonutTemplateFactory from lsst.ts.wep.donutDetector import DonutDetector -from lsst.ts.wep.utility import DefocalType, DonutTemplateType +from lsst.ts.wep.utils import DefocalType, DonutTemplateType class TestDonutDetector(unittest.TestCase): diff --git a/tests/test_donutImageCheck.py b/tests/test_donutImageCheck.py index d0c88d46..9e4e6a48 100644 --- a/tests/test_donutImageCheck.py +++ b/tests/test_donutImageCheck.py @@ -24,7 +24,7 @@ import numpy as np from lsst.ts.wep.donutImageCheck import DonutImageCheck -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestDonutImageCheck(unittest.TestCase): diff --git a/tests/test_paramReader.py b/tests/test_paramReader.py index 95c8aa5f..3913ba2e 100644 --- a/tests/test_paramReader.py +++ b/tests/test_paramReader.py @@ -25,7 +25,7 @@ import numpy as np from lsst.ts.wep.paramReader import ParamReader -from lsst.ts.wep.utility import getModulePath +from lsst.ts.wep.utils import getModulePath class TestParamReader(unittest.TestCase): diff --git a/tests/test_wfEstimator.py b/tests/test_wfEstimator.py index 57bc2b3f..9bd0e830 100644 --- a/tests/test_wfEstimator.py +++ b/tests/test_wfEstimator.py @@ -23,7 +23,7 @@ import unittest import numpy as np -from lsst.ts.wep.utility import CamType, DefocalType, getConfigDir, getModulePath +from lsst.ts.wep.utils import CamType, DefocalType, getConfigDir, getModulePath from lsst.ts.wep.wfEstimator import WfEstimator diff --git a/tests/test_utility.py b/tests/utils/test_enumUtils.py similarity index 53% rename from tests/test_utility.py rename to tests/utils/test_enumUtils.py index da32e144..e2196707 100644 --- a/tests/test_utility.py +++ b/tests/utils/test_enumUtils.py @@ -19,14 +19,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os import unittest from enum import IntEnum -import numpy as np from lsst.afw.cameraGeom import DetectorType -from lsst.ts.wep.task.calcZernikesTask import CalcZernikesTaskConfig -from lsst.ts.wep.utility import ( +from lsst.ts.wep.utils import ( BscDbType, CamType, CentroidFindType, @@ -34,60 +31,31 @@ DonutTemplateType, FilterType, ImageType, - createInstDictFromConfig, - getAmpImagesFromDir, getBscDbType, getCamNameFromCamType, getCamType, getCamTypeFromButlerName, getCentroidFindType, - getConfigDir, getDeblendDonutType, - getDefocalDisInMm, getDonutTemplateType, getFilterTypeFromBandLabel, getImageType, - getModulePath, - getObsLsstCmdTaskConfigDir, mapFilterRefToG, - rotMatrix, - writeCleanUpRepoCmd, - writePipetaskCmd, ) -class TestUtility(unittest.TestCase): - """Test the Utility functions.""" +class TestEnumUtils(unittest.TestCase): + """Test the Enum utils.""" - def _writePipetaskCmd( - self, - repoName, - instrument, - collections, - runName, - taskName=None, - pipelineName=None, - ): - # Write the part of the command that is always included - testCmd = f"pipetask run -b {repoName} -i {collections} " - testCmd += f"--instrument {instrument} " - testCmd += f"--register-dataset-types --output-run {runName}" - - # Write with taskName - if taskName is not None: - testCmd += f" -t {taskName}" - - # Write with pipeline filename - if pipelineName is not None: - testCmd += f" -p {pipelineName}" - - return testCmd - - def _writeCleanUpCmd(self, repoName, runName): - testCmd = f"butler remove-runs {repoName} {runName}" - testCmd += " --no-confirm" - - return testCmd + def testGetFilterTypeFromBandLabel(self): + # Test allowable filter band labels + self.assertEqual(getFilterTypeFromBandLabel("u"), FilterType.LSST_U) + self.assertEqual(getFilterTypeFromBandLabel("g"), FilterType.LSST_G) + self.assertEqual(getFilterTypeFromBandLabel("r"), FilterType.LSST_R) + self.assertEqual(getFilterTypeFromBandLabel("i"), FilterType.LSST_I) + self.assertEqual(getFilterTypeFromBandLabel("z"), FilterType.LSST_Z) + self.assertEqual(getFilterTypeFromBandLabel("y"), FilterType.LSST_Y) + self.assertEqual(getFilterTypeFromBandLabel("SomeFilter"), FilterType.REF) def testMapFilterRefToG(self): mappedFilterType = mapFilterRefToG(FilterType.REF) @@ -97,138 +65,6 @@ def testMapFilterRefToGForFilterU(self): mappedFilterType = mapFilterRefToG(FilterType.LSST_U) self.assertEqual(mappedFilterType, FilterType.LSST_U) - def testGetConfigDir(self): - ansConfigDir = os.path.join(getModulePath(), "policy") - self.assertEqual(getConfigDir(), ansConfigDir) - - def testGetObsLsstCmdTaskConfigDir(self): - obsLsstCmdTaskConfirDir = getObsLsstCmdTaskConfigDir() - configNormPath = os.path.normpath(obsLsstCmdTaskConfirDir) - configNormPathList = configNormPath.split(os.sep) - - self.assertEqual(configNormPathList[-1], "config") - self.assertTrue(("obs_lsst" in configNormPathList)) - - def testGetBscDbType(self): - self.assertEqual(getBscDbType("localDb"), BscDbType.LocalDb) - self.assertEqual(getBscDbType("file"), BscDbType.LocalDbForStarFile) - - def testGetBscDbTypeWithWrongInput(self): - self.assertRaises(ValueError, getBscDbType, "wrongType") - - def testGetImageType(self): - self.assertEqual(getImageType("amp"), ImageType.Amp) - self.assertEqual(getImageType("eimage"), ImageType.Eimg) - - def testGetImageTypeWithWrongInput(self): - self.assertRaises(ValueError, getImageType, "wrongType") - - def testGetCentroidFindType(self): - self.assertEqual(getCentroidFindType("randomWalk"), CentroidFindType.RandomWalk) - self.assertEqual(getCentroidFindType("otsu"), CentroidFindType.Otsu) - self.assertEqual( - getCentroidFindType("convolveTemplate"), CentroidFindType.ConvolveTemplate - ) - - def testGetCentroidFindTypeWithWrongInput(self): - self.assertRaises(ValueError, getCentroidFindType, "wrongType") - - def testGetDeblendDonutType(self): - self.assertEqual(getDeblendDonutType("adapt"), DeblendDonutType.Adapt) - - def testGetDeblendDonutTypeWithWrongInput(self): - self.assertRaises(ValueError, getDeblendDonutType, "wrongType") - - def testGetDonutTemplateType(self): - self.assertEqual(getDonutTemplateType("model"), DonutTemplateType.Model) - self.assertEqual(getDonutTemplateType("phosim"), DonutTemplateType.Phosim) - - def testGetDonutTemplateTypeWithWrongInput(self): - self.assertRaises(ValueError, getDonutTemplateType, "wrongType") - - def testGetAmpImagesFromDir(self): - # path to repackaged phosim files - # with amplifier images and e-images - defocalImgDir = os.path.join( - getModulePath(), - "tests", - "testData", - "phosimOutput", - "realComCam", - "repackagedFiles", - "extra", - ) - # test that there are e-images in that dir - filesInDir = os.listdir(defocalImgDir) - self.assertTrue("MC_H_20211231_006001_R22_S11_e.fits.gz" in filesInDir) - self.assertTrue("MC_H_20211231_006001_R22_S10_e.fits.gz" in filesInDir) - - # get names of amp files - ampFiles = getAmpImagesFromDir(defocalImgDir) - - # assert the returned content - self.assertIsInstance(ampFiles, list) - - # assert that amp images are on the returned list - self.assertTrue("MC_H_20211231_006001_R22_S10.fits" in ampFiles) - self.assertTrue("MC_H_20211231_006001_R22_S11.fits" in ampFiles) - - # assert that no other files are there - # by checking that the length of list corresponds to - # two files tested above - self.assertEqual(len(ampFiles), 2) - - def testWritePipetaskCmd(self): - repoName = "testRepo" - instrument = "lsst.obs.lsst.LsstCam" - collections = "refcats" - runName = "run2" - - # Test writing with task name - taskName = "lsst.ts.wep.testTask" - testCmdTask = self._writePipetaskCmd( - repoName, instrument, collections, runName, taskName=taskName - ) - - pipeOutTask = writePipetaskCmd( - repoName, runName, instrument, collections, taskName=taskName - ) - self.assertEqual(testCmdTask, pipeOutTask) - - # Test writing with pipeline - pipelineYamlFile = "testPipeOut.yaml" - testCmdYaml = self._writePipetaskCmd( - repoName, instrument, collections, runName, pipelineName=pipelineYamlFile - ) - - pipeOutYaml = writePipetaskCmd( - repoName, runName, instrument, collections, pipelineYaml=pipelineYamlFile - ) - self.assertEqual(testCmdYaml, pipeOutYaml) - - assertMsg = "At least one of taskName or pipelineYaml must not be None" - with self.assertRaises(ValueError) as context: - writePipetaskCmd(repoName, runName, instrument, collections) - self.assertTrue(assertMsg in str(context.exception)) - - def testWriteCleanUpRepoCmd(self): - repoName = "testRepo" - runName = "run2" - - testCmd = self._writeCleanUpCmd(repoName, runName) - self.assertEqual(testCmd, writeCleanUpRepoCmd(repoName, runName)) - - def testGetDefocalDisInMm(self): - self.assertEqual(getDefocalDisInMm("lsst"), 1.5) - self.assertEqual(getDefocalDisInMm("lsstfam"), 1.5) - self.assertEqual(getDefocalDisInMm("comcam"), 1.5) - self.assertEqual(getDefocalDisInMm("auxTel"), 0.8) - instName = "telescope" - assertMsg = f"Instrument name ({instName}) is not supported." - with self.assertRaises(ValueError) as context: - getDefocalDisInMm(instName) - self.assertTrue(assertMsg in str(context.exception)) - def testGetCamType(self): self.assertEqual(getCamType("lsst"), CamType.LsstCam) self.assertEqual(getCamType("lsstfam"), CamType.LsstFamCam) @@ -240,6 +76,25 @@ def testGetCamType(self): getCamType(instName) self.assertTrue(assertMsg in str(context.exception)) + def testGetCamNameFromCamType(self): + # Test allowable CamType values + self.assertEqual(getCamNameFromCamType(CamType.LsstCam), "lsst") + self.assertEqual(getCamNameFromCamType(CamType.LsstFamCam), "lsstfam") + self.assertEqual(getCamNameFromCamType(CamType.ComCam), "comcam") + self.assertEqual(getCamNameFromCamType(CamType.AuxTel), "auxTel") + self.assertEqual(getCamNameFromCamType(CamType.AuxTelZWO), "auxTelZWO") + + # Create a TestCamType that has a value greater than the last CamType + class TestCamType(IntEnum): + BadInst = len(CamType) + 1 + + # Test the error is raised correctly with an incorrect CamType. + badCamType = TestCamType.BadInst + errMsg = f"CamType ({badCamType}) is not supported." + with self.assertRaises(ValueError) as context: + getCamNameFromCamType(badCamType) + self.assertEqual(str(context.exception), errMsg) + def testGetCamTypeFromButlerName(self): self.assertEqual( getCamTypeFromButlerName("LSSTCam", DetectorType.WAVEFRONT), CamType.LsstCam @@ -276,66 +131,44 @@ def testGetCamTypeFromButlerName(self): getCamTypeFromButlerName(instName, detType) self.assertTrue(detAssertMsg in str(context.exception)) - def testGetFilterTypeFromBandLabel(self): - # Test allowable filter band labels - self.assertEqual(getFilterTypeFromBandLabel("u"), FilterType.LSST_U) - self.assertEqual(getFilterTypeFromBandLabel("g"), FilterType.LSST_G) - self.assertEqual(getFilterTypeFromBandLabel("r"), FilterType.LSST_R) - self.assertEqual(getFilterTypeFromBandLabel("i"), FilterType.LSST_I) - self.assertEqual(getFilterTypeFromBandLabel("z"), FilterType.LSST_Z) - self.assertEqual(getFilterTypeFromBandLabel("y"), FilterType.LSST_Y) - self.assertEqual(getFilterTypeFromBandLabel("SomeFilter"), FilterType.REF) + def testGetBscDbType(self): + self.assertEqual(getBscDbType("localDb"), BscDbType.LocalDb) + self.assertEqual(getBscDbType("file"), BscDbType.LocalDbForStarFile) - def testGetCamNameFromCamType(self): - # Test allowable CamType values - self.assertEqual(getCamNameFromCamType(CamType.LsstCam), "lsst") - self.assertEqual(getCamNameFromCamType(CamType.LsstFamCam), "lsstfam") - self.assertEqual(getCamNameFromCamType(CamType.ComCam), "comcam") - self.assertEqual(getCamNameFromCamType(CamType.AuxTel), "auxTel") - self.assertEqual(getCamNameFromCamType(CamType.AuxTelZWO), "auxTelZWO") + def testGetBscDbTypeWithWrongInput(self): + self.assertRaises(ValueError, getBscDbType, "wrongType") - # Create a TestCamType that has a value greater than the last CamType - class TestCamType(IntEnum): - BadInst = len(CamType) + 1 + def testGetImageType(self): + self.assertEqual(getImageType("amp"), ImageType.Amp) + self.assertEqual(getImageType("eimage"), ImageType.Eimg) - # Test the error is raised correctly with an incorrect CamType. - badCamType = TestCamType.BadInst - errMsg = f"CamType ({badCamType}) is not supported." - with self.assertRaises(ValueError) as context: - getCamNameFromCamType(badCamType) - self.assertEqual(str(context.exception), errMsg) + def testGetImageTypeWithWrongInput(self): + self.assertRaises(ValueError, getImageType, "wrongType") - def testCreateInstDictFromConfig(self): - # Test instDict creation in tasks - testConfig = CalcZernikesTaskConfig() - testInstDict = createInstDictFromConfig(testConfig) - truthInstDict = { - "obscuration": 0.61, - "focalLength": 10.312, - "apertureDiameter": 8.36, - "offset": None, - "pixelSize": 10.0e-6, - } + def testGetCentroidFindType(self): + self.assertEqual(getCentroidFindType("randomWalk"), CentroidFindType.RandomWalk) + self.assertEqual(getCentroidFindType("otsu"), CentroidFindType.Otsu) + self.assertEqual( + getCentroidFindType("convolveTemplate"), CentroidFindType.ConvolveTemplate + ) + + def testGetCentroidFindTypeWithWrongInput(self): + self.assertRaises(ValueError, getCentroidFindType, "wrongType") - self.assertDictEqual(truthInstDict, testInstDict) + def testGetDonutTemplateType(self): + self.assertEqual(getDonutTemplateType("model"), DonutTemplateType.Model) + self.assertEqual(getDonutTemplateType("phosim"), DonutTemplateType.Phosim) - def testRotMatrix(self): - # Test rotation with 0 degrees - testTheta1 = 0 - rotMatrix1 = np.array([[1, 0], [0, 1]]) - np.testing.assert_array_almost_equal(rotMatrix1, rotMatrix(testTheta1)) + def testGetDonutTemplateTypeWithWrongInput(self): + self.assertRaises(ValueError, getDonutTemplateType, "wrongType") - # Test rotation with 90 degrees - testTheta2 = 90 - rotMatrix2 = np.array([[0, -1], [1, 0]]) - np.testing.assert_array_almost_equal(rotMatrix2, rotMatrix(testTheta2)) + def testGetDeblendDonutType(self): + self.assertEqual(getDeblendDonutType("adapt"), DeblendDonutType.Adapt) - # Test rotation with 45 degrees - testTheta3 = 45 - rotMatrix3 = np.array([[0.707107, -0.707107], [0.707107, 0.707107]]) - np.testing.assert_array_almost_equal(rotMatrix3, rotMatrix(testTheta3)) + def testGetDeblendDonutTypeWithWrongInput(self): + self.assertRaises(ValueError, getDeblendDonutType, "wrongType") if __name__ == "__main__": - # Do the unit test + # Run the unit test unittest.main() diff --git a/tests/utils/test_ioUtils.py b/tests/utils/test_ioUtils.py new file mode 100644 index 00000000..cca96b09 --- /dev/null +++ b/tests/utils/test_ioUtils.py @@ -0,0 +1,78 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import unittest + +from lsst.ts.wep.utils import ( + getAmpImagesFromDir, + getConfigDir, + getModulePath, + getObsLsstCmdTaskConfigDir, +) + + +class TestIoUtils(unittest.TestCase): + """Test the IO utility functions.""" + + def testGetConfigDir(self): + ansConfigDir = os.path.join(getModulePath(), "policy") + self.assertEqual(getConfigDir(), ansConfigDir) + + def testGetObsLsstCmdTaskConfigDir(self): + obsLsstCmdTaskConfirDir = getObsLsstCmdTaskConfigDir() + configNormPath = os.path.normpath(obsLsstCmdTaskConfirDir) + configNormPathList = configNormPath.split(os.sep) + + self.assertEqual(configNormPathList[-1], "config") + self.assertTrue(("obs_lsst" in configNormPathList)) + + def testGetAmpImagesFromDir(self): + # path to repackaged phosim files + # with amplifier images and e-images + defocalImgDir = os.path.join( + getModulePath(), + "tests", + "testData", + "phosimOutput", + "realComCam", + "repackagedFiles", + "extra", + ) + # test that there are e-images in that dir + filesInDir = os.listdir(defocalImgDir) + self.assertTrue("MC_H_20211231_006001_R22_S11_e.fits.gz" in filesInDir) + self.assertTrue("MC_H_20211231_006001_R22_S10_e.fits.gz" in filesInDir) + + # get names of amp files + ampFiles = getAmpImagesFromDir(defocalImgDir) + + # assert the returned content + self.assertIsInstance(ampFiles, list) + + # assert that amp images are on the returned list + self.assertTrue("MC_H_20211231_006001_R22_S10.fits" in ampFiles) + self.assertTrue("MC_H_20211231_006001_R22_S11.fits" in ampFiles) + + # assert that no other files are there + # by checking that the length of list corresponds to + # two files tested above + self.assertEqual(len(ampFiles), 2) diff --git a/tests/utils/test_miscUtils.py b/tests/utils/test_miscUtils.py new file mode 100644 index 00000000..3777e7ad --- /dev/null +++ b/tests/utils/test_miscUtils.py @@ -0,0 +1,105 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import unittest + +import numpy as np +from lsst.ts.wep.task.calcZernikesTask import CalcZernikesTaskConfig +from lsst.ts.wep.utils import ( + createInstDictFromConfig, + extractArray, + getDefocalDisInMm, + padArray, + rotMatrix, +) + + +class TestMiscUtils(unittest.TestCase): + """Test the miscellaneous utility functions.""" + + def testGetDefocalDisInMm(self): + self.assertEqual(getDefocalDisInMm("lsst"), 1.5) + self.assertEqual(getDefocalDisInMm("lsstfam"), 1.5) + self.assertEqual(getDefocalDisInMm("comcam"), 1.5) + self.assertEqual(getDefocalDisInMm("auxTel"), 0.8) + instName = "telescope" + assertMsg = f"Instrument name ({instName}) is not supported." + with self.assertRaises(ValueError) as context: + getDefocalDisInMm(instName) + self.assertTrue(assertMsg in str(context.exception)) + + def testCreateInstDictFromConfig(self): + # Test instDict creation in tasks + testConfig = CalcZernikesTaskConfig() + testInstDict = createInstDictFromConfig(testConfig) + truthInstDict = { + "obscuration": 0.61, + "focalLength": 10.312, + "apertureDiameter": 8.36, + "offset": None, + "pixelSize": 10.0e-6, + } + + self.assertDictEqual(truthInstDict, testInstDict) + + def testRotMatrix(self): + # Test rotation with 0 degrees + testTheta1 = 0 + rotMatrix1 = np.array([[1, 0], [0, 1]]) + np.testing.assert_array_almost_equal(rotMatrix1, rotMatrix(testTheta1)) + + # Test rotation with 90 degrees + testTheta2 = 90 + rotMatrix2 = np.array([[0, -1], [1, 0]]) + np.testing.assert_array_almost_equal(rotMatrix2, rotMatrix(testTheta2)) + + # Test rotation with 45 degrees + testTheta3 = 45 + rotMatrix3 = np.array([[0.707107, -0.707107], [0.707107, 0.707107]]) + np.testing.assert_array_almost_equal(rotMatrix3, rotMatrix(testTheta3)) + + def testPadArray(self): + imgDim = 10 + padPixelSize = 20 + + img, imgPadded = self._padRandomImg(imgDim, padPixelSize) + + self.assertEqual(imgPadded.shape[0], imgDim + padPixelSize) + + def _padRandomImg(self, imgDim, padPixelSize): + img = np.random.rand(imgDim, imgDim) + imgPadded = padArray(img, imgDim + padPixelSize) + + return img, imgPadded + + def testExtractArray(self): + imgDim = 10 + padPixelSize = 20 + img, imgPadded = self._padRandomImg(imgDim, padPixelSize) + + imgExtracted = extractArray(imgPadded, imgDim) + + self.assertEqual(imgExtracted.shape[0], imgDim) + + +if __name__ == "__main__": + # Do the unit test + unittest.main() diff --git a/tests/utils/test_taskUtils.py b/tests/utils/test_taskUtils.py new file mode 100644 index 00000000..355afb52 --- /dev/null +++ b/tests/utils/test_taskUtils.py @@ -0,0 +1,98 @@ +# This file is part of ts_wep. +# +# Developed for the LSST Telescope and Site Systems. +# This product includes software developed by the LSST Project +# (https://www.lsst.org). +# See the COPYRIGHT file at the top-level directory of this distribution +# for details of code ownership. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import unittest + +from lsst.ts.wep.utils import writeCleanUpRepoCmd, writePipetaskCmd + + +class TestTaskUtils(unittest.TestCase): + """Test the task utility functions.""" + + def _writePipetaskCmd( + self, + repoName, + instrument, + collections, + runName, + taskName=None, + pipelineName=None, + ): + # Write the part of the command that is always included + testCmd = f"pipetask run -b {repoName} -i {collections} " + testCmd += f"--instrument {instrument} " + testCmd += f"--register-dataset-types --output-run {runName}" + + # Write with taskName + if taskName is not None: + testCmd += f" -t {taskName}" + + # Write with pipeline filename + if pipelineName is not None: + testCmd += f" -p {pipelineName}" + + return testCmd + + def _writeCleanUpCmd(self, repoName, runName): + testCmd = f"butler remove-runs {repoName} {runName}" + testCmd += " --no-confirm" + + return testCmd + + def testWritePipetaskCmd(self): + repoName = "testRepo" + instrument = "lsst.obs.lsst.LsstCam" + collections = "refcats" + runName = "run2" + + # Test writing with task name + taskName = "lsst.ts.wep.testTask" + testCmdTask = self._writePipetaskCmd( + repoName, instrument, collections, runName, taskName=taskName + ) + + pipeOutTask = writePipetaskCmd( + repoName, runName, instrument, collections, taskName=taskName + ) + self.assertEqual(testCmdTask, pipeOutTask) + + # Test writing with pipeline + pipelineYamlFile = "testPipeOut.yaml" + testCmdYaml = self._writePipetaskCmd( + repoName, instrument, collections, runName, pipelineName=pipelineYamlFile + ) + + pipeOutYaml = writePipetaskCmd( + repoName, runName, instrument, collections, pipelineYaml=pipelineYamlFile + ) + self.assertEqual(testCmdYaml, pipeOutYaml) + + assertMsg = "At least one of taskName or pipelineYaml must not be None" + with self.assertRaises(ValueError) as context: + writePipetaskCmd(repoName, runName, instrument, collections) + self.assertTrue(assertMsg in str(context.exception)) + + def testWriteCleanUpRepoCmd(self): + repoName = "testRepo" + runName = "run2" + + testCmd = self._writeCleanUpCmd(repoName, runName) + self.assertEqual(testCmd, writeCleanUpRepoCmd(repoName, runName)) diff --git a/tests/cwfs/test_tool.py b/tests/utils/test_zernikeUtils.py similarity index 89% rename from tests/cwfs/test_tool.py rename to tests/utils/test_zernikeUtils.py index 9423f92a..b3c55a93 100644 --- a/tests/cwfs/test_tool.py +++ b/tests/utils/test_zernikeUtils.py @@ -24,22 +24,20 @@ import numpy as np from astropy.io import fits -from lsst.ts.wep.cwfs.tool import ( +from lsst.ts.wep.utils import ( ZernikeAnnularEval, ZernikeAnnularFit, ZernikeAnnularGrad, ZernikeAnnularJacobian, ZernikeMaskedFit, - extractArray, - padArray, + getModulePath, ) -from lsst.ts.wep.utility import getModulePath from lsst.utils.tests import TestCase from scipy.integrate import nquad -class TestTool(TestCase): - """Test the functions in Tool.""" +class TestZernikeUtils(TestCase): + """Test the Zernike utility functions.""" def setUp(self): self.testDataDir = os.path.join( @@ -222,29 +220,6 @@ def testZernikeMaskFit(self): self.assertLess(np.sum(np.abs(zr - self.zerCoef[0:nc]) ** 2), 1e-10) - def testPadArray(self): - imgDim = 10 - padPixelSize = 20 - - img, imgPadded = self._padRandomImg(imgDim, padPixelSize) - - self.assertEqual(imgPadded.shape[0], imgDim + padPixelSize) - - def _padRandomImg(self, imgDim, padPixelSize): - img = np.random.rand(imgDim, imgDim) - imgPadded = padArray(img, imgDim + padPixelSize) - - return img, imgPadded - - def testExtractArray(self): - imgDim = 10 - padPixelSize = 20 - img, imgPadded = self._padRandomImg(imgDim, padPixelSize) - - imgExtracted = extractArray(imgPadded, imgDim) - - self.assertEqual(imgExtracted.shape[0], imgDim) - if __name__ == "__main__": # Do the unit test