diff --git a/python/lsst/analysis/tools/actions/vector/__init__.py b/python/lsst/analysis/tools/actions/vector/__init__.py index 701cb5b25..03962d18e 100644 --- a/python/lsst/analysis/tools/actions/vector/__init__.py +++ b/python/lsst/analysis/tools/actions/vector/__init__.py @@ -1,3 +1,4 @@ +from .calcFwhmZernikes import * from .calcRhoStatistics import * from .calcShapeSize import CalcShapeSize from .ellipticity import * diff --git a/python/lsst/analysis/tools/actions/vector/calcFwhmZernikes.py b/python/lsst/analysis/tools/actions/vector/calcFwhmZernikes.py new file mode 100644 index 000000000..6d8f30700 --- /dev/null +++ b/python/lsst/analysis/tools/actions/vector/calcFwhmZernikes.py @@ -0,0 +1,97 @@ +# This file is part of analysis_tools. +# +# Developed for the LSST Data Management System. +# 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 . +from __future__ import annotations + +__all__ = ("CalcFwhmZernikesBase", "CalcFwhmZernikesLsst", "CalcFwhmZernikesLatiss") + +import numpy as np +from lsst.pex.config import Field +from ...interfaces import KeyedData, KeyedDataSchema, Vector, ScalarAction, Scalar + + +class CalcFwhmZernikesBase(ScalarAction): + + vectorKey = Field[str](doc="Vector on which to compute statistics") + # conversion_factors need to be assigned in daughter class + conversion_factors = None + + def getInputSchema(self, **kwargs) -> KeyedDataSchema: + return ((self.vectorKey, Vector),) + + def __call__(self, data: KeyedData, **kwargs) -> Scalar: + + results = np.sqrt(np.sum((data[self.vectorKey] * self.conversion_factors) ** 2.0)) + + return results + + +class CalcFwhmZernikesLsst(CalcFwhmZernikesBase): + + conversion_factors = np.array( + [ + 0.751, + 0.271, + 0.271, + 0.819, + 0.819, + 0.396, + 0.396, + 1.679, + 0.937, + 0.937, + 0.517, + 0.517, + 1.755, + 1.755, + 1.089, + 1.089, + 0.635, + 0.635, + 2.810, + ] + ) + + +class CalcFwhmZernikesLatiss(CalcFwhmZernikesBase): + + conversion_factors = np.array( + [ + 3.395, + 1.969, + 1.969, + 4.374, + 4.374, + 2.802, + 2.802, + 7.592, + 5.726, + 5.726, + 3.620, + 3.620, + 8.696, + 8.696, + 7.146, + 7.146, + 4.434, + 4.434, + 12.704, + ] + ) diff --git a/python/lsst/analysis/tools/analysisMetrics/__init__.py b/python/lsst/analysis/tools/analysisMetrics/__init__.py index bf54b6b19..c39618d77 100644 --- a/python/lsst/analysis/tools/analysisMetrics/__init__.py +++ b/python/lsst/analysis/tools/analysisMetrics/__init__.py @@ -19,6 +19,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from .analysisMetrics import * +from .aosMetrics import * from .apDiaSourceMetrics import * from .apSsoMetrics import * from .limitingMagnitudeMetric import * diff --git a/python/lsst/analysis/tools/analysisMetrics/aosMetrics.py b/python/lsst/analysis/tools/analysisMetrics/aosMetrics.py new file mode 100644 index 000000000..3bcfa5746 --- /dev/null +++ b/python/lsst/analysis/tools/analysisMetrics/aosMetrics.py @@ -0,0 +1,54 @@ +# This file is part of analysis_tools. +# +# Developed for the LSST Data Management System. +# 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 . +from __future__ import annotations + +__all__ = ("ZernikesFwhmMetricLsst", "ZernikesFwhmMetricLatiss") + +from ..actions.vector import CalcFwhmZernikesLsst, CalcFwhmZernikesLatiss +from ..interfaces import AnalysisMetric + + +class ZernikesFwhmMetricLsst(AnalysisMetric): + """Calculate FWHM from Zernikes for LsstCam.""" + + def setDefaults(self): + super().setDefaults() + + # Calculate FWHM from Zernike Coefficients + self.process.calculateActions.ZernikesFwhmMetric = CalcFwhmZernikesLsst( + vectorKey="zernikeEstimateAvg" + ) + + self.produce.units = {"ZernikesFwhmMetric": "arcsec"} + + +class ZernikesFwhmMetricLatiss(AnalysisMetric): + """Calculate FWHM from Zernikes for LAtiss.""" + + def setDefaults(self): + super().setDefaults() + + # Calculate FWHM from Zernike Coefficients + self.process.calculateActions.ZernikesFwhmMetric = CalcFwhmZernikesLatiss( + vectorKey="zernikeEstimateAvg" + ) + + self.produce.units = {"ZernikesFwhmMetric": "arcsec"} diff --git a/python/lsst/analysis/tools/tasks/__init__.py b/python/lsst/analysis/tools/tasks/__init__.py index c9e85dbd2..b4c0600ef 100644 --- a/python/lsst/analysis/tools/tasks/__init__.py +++ b/python/lsst/analysis/tools/tasks/__init__.py @@ -1,3 +1,4 @@ +from .aosAnalysis import * from .associatedSourcesTractAnalysis import * from .catalogMatch import * from .ccdVisitTableAnalysis import * diff --git a/python/lsst/analysis/tools/tasks/aosAnalysis.py b/python/lsst/analysis/tools/tasks/aosAnalysis.py new file mode 100644 index 000000000..174f434e3 --- /dev/null +++ b/python/lsst/analysis/tools/tasks/aosAnalysis.py @@ -0,0 +1,50 @@ +# This file is part of analysis_tools. +# +# Developed for the LSST Data Management System. +# 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 . +from __future__ import annotations + +__all__ = ("aosAnalysisConnections", "aosAnalysisConfig", "aosAnalysisTask") + +from lsst.pipe.base import connectionTypes as ct + +from .base import AnalysisBaseConfig, AnalysisBaseConnections, AnalysisPipelineTask + + +class aosAnalysisConnections( + AnalysisBaseConnections, + dimensions=("visit", "detector", "instrument"), +): + data = ct.Input( + doc="Zernikes from detector.", + name="zernikeEstimateAvg", + storageClass="NumpyArray", + dimensions=("visit", "detector", "instrument"), + deferLoad=True, + ) + + +class aosAnalysisConfig(AnalysisBaseConfig, pipelineConnections=aosAnalysisConnections): + pass + + +class aosAnalysisTask(AnalysisPipelineTask): + + ConfigClass = aosAnalysisConfig + _DefaultName = "aosAnalysis" diff --git a/python/lsst/analysis/tools/tasks/base.py b/python/lsst/analysis/tools/tasks/base.py index ac308693d..3b84af5a3 100644 --- a/python/lsst/analysis/tools/tasks/base.py +++ b/python/lsst/analysis/tools/tasks/base.py @@ -371,7 +371,15 @@ def loadData(self, handle: DeferredDatasetHandle, names: Iterable[str] | None = """ if names is None: names = self.collectInputNames() - return cast(KeyedData, handle.get(parameters={"columns": names})) + + # If input data is a numpy array instead of a catalog + # or dataframe, we need to create an object with keys. + if handle.ref.datasetType.storageClass_name == "NumpyArray": + dataLabel = list(names)[0] + dataDict = {dataLabel: handle.get()} + return cast(KeyedData, dataDict) + else: + return cast(KeyedData, handle.get(parameters={"columns": names})) def collectInputNames(self) -> Iterable[str]: """Get the names of the inputs.