Skip to content

Commit

Permalink
Adapting tasks to work with new infrastructure. Includes new template…
Browse files Browse the repository at this point in the history
… generation and configs. Removing template size configs because the template size is dynamic (depends on instrument parameters). DonutStamp now uses the new Image class. All rotation and transposition is handled inside the DonutStamp class when creating that object. Most tests work now, although AuxTel/Latiss tests needs to be done on USDF.
  • Loading branch information
jfcrenshaw committed Dec 14, 2023
1 parent 096d2a7 commit 776820a
Show file tree
Hide file tree
Showing 48 changed files with 1,065 additions and 1,025 deletions.
41 changes: 0 additions & 41 deletions policy/cwfs/algo/exp.yaml

This file was deleted.

56 changes: 0 additions & 56 deletions policy/cwfs/algo/fft.yaml

This file was deleted.

Empty file.
6 changes: 3 additions & 3 deletions python/lsst/ts/wep/estimation/tie.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__all__ = ["TIEAlgorithm"]
__all__ = ["TieAlgorithm"]

import warnings
from typing import Iterable, Optional, Union
Expand All @@ -30,7 +30,7 @@
from lsst.ts.wep.utils import DefocalType, createZernikeBasis, createZernikeGradBasis


class TIEAlgorithm(WfAlgorithm):
class TieAlgorithm(WfAlgorithm):
"""Wavefront estimation algorithm class for the TIE solver.
Parameters
Expand Down Expand Up @@ -465,7 +465,7 @@ def _fftSolve(
# TODO: Implement the fft solver
raise NotImplementedError("The fft solver is not yet implemented.")

def estimateWf(
def estimateZk(
self,
I1: Image,
I2: Image, # type: ignore[override]
Expand Down
2 changes: 1 addition & 1 deletion python/lsst/ts/wep/estimation/wfAlgorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _validateInputs(
raise TypeError("instrument must be an Instrument.")

@abstractmethod
def estimateWf(
def estimateZk(
self,
I1: Image,
I2: Optional[Image],
Expand Down
4 changes: 2 additions & 2 deletions python/lsst/ts/wep/estimation/wfAlgorithmFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from typing import Union

from lsst.ts.wep.estimation.tie import TIEAlgorithm
from lsst.ts.wep.estimation.tie import TieAlgorithm
from lsst.ts.wep.estimation.wfAlgorithm import WfAlgorithm
from lsst.ts.wep.utils import WfAlgorithmName, configClass

Expand Down Expand Up @@ -56,6 +56,6 @@ def createWfAlgorithm(

# Return the configured algorithm
if algoName == WfAlgorithmName.TIE:
return configClass(algoConfig, TIEAlgorithm)
return configClass(algoConfig, TieAlgorithm)
else:
raise ValueError(f"{algoName} not supported.")
8 changes: 4 additions & 4 deletions python/lsst/ts/wep/estimation/wfEstimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ def units(self, value: str) -> None:
)
self._units = value

def estimateWf(
def estimateZk(
self,
I1: Image,
I2: Optional[Image] = None,
) -> np.ndarray:
"""Estimate the wavefront from the stamp or pair of stamps.
"""Estimate Zernike coefficients of the wavefront from the stamp(s).
Parameters
----------
Expand All @@ -168,8 +168,8 @@ def estimateWf(
ValueError
If I1 and I2 are on the same side of focus.
"""
# Estimated wavefront (in meters)
zk = self.algo.estimateWf(I1, I2, self.jmax, self.instrument)
# Estimate wavefront Zernike coefficients (in meters)
zk = self.algo.estimateZk(I1, I2, self.jmax, self.instrument)

# Convert to desired units
if self.units == "m":
Expand Down
55 changes: 34 additions & 21 deletions python/lsst/ts/wep/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ class Image(object):
Whether the image is intra- or extra-focal.
Can be specified using a DefocalType Enum or the corresponding string.
bandLabel : BandLabel or str, optional
Photometric band for the exposure.
Can be specified using a BandLabel Enum or the corresponding string.
Photometric band for the exposure. Can be specified using a
BandLabel Enum or the corresponding string. If None, BandLabel.REF
is used. The empty string "" also maps to BandLabel.REF.
(the default is BandLabel.REF)
planeType : PlaneType or str, optional
Whether the image is on the image plane or the pupil plane.
Can be specified using a PlaneType Enum, or the corresponding string.
(the default is PlaneType.Image)
blendOffsets : np.ndarray or tuple or list, optional
Positions of blended donuts relative to location of center donut,
in pixels. Must be provided in the format [dxList, dyList].
The lengths of dxList and dyList must be the same.
Positions of blended donuts relative to central donut, in pixels.
Must be provided in the format [[dx1, dy1], [dx2, dy2], ...].
(the default is an empty array, i.e. no blends)
mask : np.ndarray, optional
The mask for the image. Mask creation is meant to be handled by the
Expand All @@ -67,7 +67,7 @@ def __init__(
defocalType: Union[DefocalType, str],
bandLabel: Union[BandLabel, str] = BandLabel.REF,
planeType: Union[PlaneType, str] = PlaneType.Image,
blendOffsets: Union[np.ndarray, tuple, list] = np.zeros((2, 0)),
blendOffsets: Union[np.ndarray, tuple, list, None] = None,
mask: Optional[np.ndarray] = None,
) -> None:
self.image = image
Expand Down Expand Up @@ -103,7 +103,7 @@ def image(self, value: np.ndarray) -> None:
raise TypeError("image must be a numpy array.")
if len(value.shape) != 2 or value.shape[0] != value.shape[1]:
raise ValueError("The image array must be square.")
self._image = value
self._image = value.copy()

@property
def fieldAngle(self) -> np.ndarray:
Expand Down Expand Up @@ -164,21 +164,24 @@ def bandLabel(self) -> BandLabel:
return self._bandLabel

@bandLabel.setter
def bandLabel(self, value: Union[BandLabel, str]) -> None:
def bandLabel(self, value: Union[BandLabel, str, None]) -> None:
"""Set the band label.
Parameters
----------
value : BandLabel or str
Photometric band for the exposure.
Can be specified using a BandLabel Enum or the corresponding string.
value : BandLabel or str or None
Photometric band for the exposure. Can be specified using a
BandLabel Enum or the corresponding string. If None, BandLabel.REF
is used. The empty string "" also maps to BandLabel.REF.
Raises
------
TypeError
The provided value is not a BandLabel Enum or string.
"""
if isinstance(value, str) or isinstance(value, BandLabel):
if value is None or value == "":
self._bandLabel = BandLabel.REF
elif isinstance(value, str) or isinstance(value, BandLabel):
self._bandLabel = BandLabel(value)
else:
raise TypeError(
Expand Down Expand Up @@ -219,27 +222,35 @@ def blendOffsets(self) -> Union[np.ndarray, None]:
return self._blendOffsets

@blendOffsets.setter
def blendOffsets(self, value: Union[np.ndarray, tuple, list]) -> None:
def blendOffsets(self, value: Union[np.ndarray, tuple, list, None]) -> None:
"""Set the blend offsets array for the image.
Parameters
----------
value : np.ndarray or tuple or list
Positions of blended donuts relative to location of center donut,
in pixels. Must be provided in the format [dxList, dyList].
The lengths of dxList and dyList must be the same.
value : np.ndarray or tuple or list or None
Positions of blended donuts relative to central donut, in pixels.
Must be provided in the format [[dx1, dy1], [dx2, dy2], ...].
If None, an empty array is populated for you.
Raises
------
ValueError
If the provided value does not have the correct shape
"""
value = np.array(value, dtype=float)
if value.shape[0] != 2 or len(value.shape) != 2:
# If None, populate an empty array with the correct shape
if value is None:
value = np.zeros((0, 2))

# Convert to float array
value = np.atleast_2d(value).astype(float)

# Check shape
if value.shape[1] != 2 or len(value.shape) != 2:
raise ValueError(
"blendOffsets must have shape (2, N), "
"blendOffsets must have shape (N, 2), "
"where N is the number of blends you wish to mask."
)

self._blendOffsets = value

@property
Expand All @@ -266,9 +277,11 @@ def mask(self, value: Optional[np.ndarray]) -> None:
If the mask is an array and does not match the shape of the image
"""
if value is not None and not isinstance(value, np.ndarray):
raise TypeError("mask must be an array, or None.")
raise TypeError("mask must be an array or None.")
elif isinstance(value, np.ndarray) and value.shape != self.image.shape:
raise ValueError("mask must have the same shape as self.image.")
elif isinstance(value, np.ndarray):
value = value.copy()
self._mask = value

def copy(self) -> Self:
Expand Down
Loading

0 comments on commit 776820a

Please sign in to comment.