Coverage for python / lsst / images / fields / _concrete.py: 42%

32 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-25 08:35 +0000

1# This file is part of lsst-images. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12from __future__ import annotations 

13 

14__all__ = ( 

15 "Field", 

16 "FieldSerializationModel", 

17 "deserialize_field", 

18 "field_from_legacy", 

19 "field_from_legacy_background", 

20) 

21 

22from typing import TYPE_CHECKING, Annotated, Any 

23 

24import astropy.units 

25import pydantic 

26 

27from ..serialization import InputArchive 

28from ._chebyshev import ChebyshevField, ChebyshevFieldSerializationModel 

29from ._product import ProductField, ProductFieldSerializationModel 

30from ._spline import SplineField, SplineFieldSerializationModel 

31from ._sum import SumField, SumFieldSerializationModel 

32 

33if TYPE_CHECKING: 

34 try: 

35 from lsst.afw.math import BackgroundList as LegacyBackgroundList 

36 from lsst.afw.math import BackgroundMI as LegacyBackground 

37 from lsst.afw.math import BoundedField as LegacyBoundedField 

38 except ImportError: 

39 type LegacyBoundedField = Any # type: ignore[no-redef] 

40 type LegacyBackground = Any # type: ignore[no-redef] 

41 type LegacyBackgroundList = Any # type: ignore[no-redef] 

42 

43 

44# Since Sphinx can't handle doc links to type aliases, whenever we annotate 

45# a type as `Field`, we override the docs to say `BaseField`, since 

46# `BaseField` is a base class that serves as a much more useful doc link, and 

47# because the hierarchy is closed they're equivalent. But we have to use 

48# `Field` in the type annotations because there's no way to declare to MyPy 

49# et all that the hierarchy is closed. 

50 

51type Field = ChebyshevField | ProductField | SplineField | SumField 

52type FieldSerializationModel = Annotated[ 

53 ChebyshevFieldSerializationModel 

54 | ProductFieldSerializationModel 

55 | SplineFieldSerializationModel 

56 | SumFieldSerializationModel, 

57 pydantic.Field(discriminator="field_type"), 

58] 

59 

60 

61def deserialize_field(model: FieldSerializationModel, archive: InputArchive[Any]) -> Field: 

62 """Deserialize a field from a serialization model of unknown type.""" 

63 return model.finish_deserialize(archive) 

64 

65 

66ProductFieldSerializationModel.model_rebuild() 

67SumFieldSerializationModel.model_rebuild() 

68 

69 

70def field_from_legacy( 

71 legacy_bounded_field: LegacyBoundedField, unit: astropy.units.UnitBase | None = None 

72) -> Field: 

73 """Convert a legacy `lsst.afw.math.BoundedField` subclass to a `BaseField` 

74 object. 

75 """ 

76 from lsst.afw.math import ChebyshevBoundedField, ProductBoundedField 

77 

78 match legacy_bounded_field: 

79 case ChebyshevBoundedField(): 

80 return ChebyshevField.from_legacy(legacy_bounded_field, unit=unit) 

81 case ProductBoundedField(): 

82 return ProductField.from_legacy(legacy_bounded_field, unit=unit) 

83 case _: 

84 raise NotImplementedError( 

85 f"Conversion from {type(legacy_bounded_field).__name__} is not supported." 

86 ) 

87 

88 

89def field_from_legacy_background( 

90 legacy_background: LegacyBackground | LegacyBackgroundList, unit: astropy.units.UnitBase | None = None 

91) -> Field: 

92 """Convert a legacy `lsst.afw.math.Background` or 

93 `lsst.afw.math.BackgroundList` instance to a `BaseField` object. 

94 """ 

95 from lsst.afw.math import ApproximateControl, BackgroundList 

96 

97 if isinstance(legacy_background, BackgroundList): 

98 return SumField.from_legacy_background(legacy_background) 

99 

100 approx_control = legacy_background.getBackgroundControl().getApproximateControl() 

101 if approx_control.getStyle() == ApproximateControl.UNKNOWN: 

102 return SplineField.from_legacy_background(legacy_background, unit=unit) 

103 else: 

104 return ChebyshevField.from_legacy_background(legacy_background, unit=unit)