Coverage for tests/test_modelconfig.py: 100%
59 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 09:54 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 09:54 +0000
1# This file is part of multiprofit.
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# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22import numpy as np
23import pytest
25import lsst.gauss2d as g2
26import lsst.gauss2d.fit as g2f
27from lsst.multiprofit.componentconfig import (
28 CentroidConfig,
29 GaussianComponentConfig,
30 ParameterConfig,
31 SersicComponentConfig,
32 SersicIndexParameterConfig,
33)
34from lsst.multiprofit.modelconfig import ModelConfig
35from lsst.multiprofit.observationconfig import ObservationConfig
36from lsst.multiprofit.sourceconfig import ComponentGroupConfig, SourceConfig
39@pytest.fixture(scope="module")
40def channels() -> dict[str, g2f.Channel]:
41 """Return dict of generic RGB channels."""
42 return {band: g2f.Channel.get(band) for band in ("R", "G", "B")}
45@pytest.fixture(scope="module")
46def data(channels) -> g2f.DataD:
47 """Return initialized data in all bands."""
48 config = ObservationConfig(n_rows=13, n_cols=19)
49 observations = []
50 for band in channels:
51 config.band = band
52 observations.append(config.make_observation())
53 return g2f.DataD(observations)
56@pytest.fixture(scope="module")
57def psf_model():
58 """Return a triple Gaussian PSF model."""
59 rho, size_x, size_y = 0.25, 1.6, 1.2
60 drho, dsize_x, dsize_y = -0.4, 1.1, 1.9
62 n_components = 3
63 flux_total = 2.0 * (n_components + 1)
64 fluxes = [x / flux_total for x in range(1, 1 + n_components)]
66 config = SourceConfig(
67 component_groups={
68 "src": ComponentGroupConfig(
69 components_gauss={
70 str(idx): GaussianComponentConfig(
71 rho=ParameterConfig(value_initial=rho + idx * drho),
72 size_x=ParameterConfig(value_initial=size_x + idx * dsize_x),
73 size_y=ParameterConfig(value_initial=size_y + idx * dsize_y),
74 )
75 for idx in range(n_components)
76 },
77 )
78 },
79 )
80 config.validate()
81 channel = g2f.Channel.NONE
82 psf_model, priors = config.make_psf_model(
83 [
84 [{channel: flux} for flux in fluxes],
85 ],
86 )
87 return psf_model
90@pytest.fixture(scope="module")
91def psf_models(psf_model, channels) -> list[g2f.PsfModel]:
92 """Return a list of PSF models per channel."""
93 return [psf_model] * len(channels)
96@pytest.fixture(scope="module")
97def modelconfig_fluxes(channels):
98 """Return model configs and flux values."""
99 rho, size_x, size_y, sersicn, flux = 0.4, 1.5, 1.9, 0.5, 4.7
100 drho, dsize_x, dsize_y, dsersicn, dflux = -0.9, 2.5, 5.4, 2.8, 13.9
102 components_sersic = {}
103 fluxes_mix = []
104 for idx, name in enumerate(("PS", "Sersic")):
105 components_sersic[name] = SersicComponentConfig(
106 rho=ParameterConfig(value_initial=rho + idx * drho),
107 size_x=ParameterConfig(value_initial=size_x + idx * dsize_x),
108 size_y=ParameterConfig(value_initial=size_y + idx * dsize_y),
109 sersic_index=SersicIndexParameterConfig(
110 value_initial=sersicn + idx * dsersicn,
111 fixed=idx == 0,
112 prior_mean=None,
113 ),
114 )
115 fluxes_comp = {
116 channel: flux + idx_channel * dflux * idx for idx_channel, channel in enumerate(channels.values())
117 }
118 fluxes_mix.append(fluxes_comp)
120 modelconfig = ModelConfig(
121 sources={
122 "src": SourceConfig(
123 component_groups={
124 "mix": ComponentGroupConfig(
125 centroids={
126 "default": CentroidConfig(
127 x=ParameterConfig(value_initial=15.8, fixed=True),
128 y=ParameterConfig(value_initial=14.3, fixed=False),
129 ),
130 },
131 components_sersic=components_sersic,
132 ),
133 }
134 ),
135 },
136 )
137 return modelconfig, fluxes_mix
140def test_ModelConfig(modelconfig_fluxes, data, psf_models):
141 """Test ModelConfig init and funcs."""
142 modelconfig, fluxes = modelconfig_fluxes
143 model = modelconfig.make_model([[fluxes]], data=data, psf_models=psf_models)
144 assert model is not None
145 assert model.data is data
146 for observation in model.data:
147 observation.sigma_inv.fill(1.0)
148 observation.mask_inv.fill(1)
150 # Set the outputs to new images that refer to the existing data
151 # because obs.image will not return a holding pointer
152 outputs = [[g2.ImageD(obs.image.data)] for obs in model.data]
153 model.setup_evaluators(g2f.EvaluatorMode.image, outputs=outputs)
154 model.evaluate()
155 model.setup_evaluators(g2f.EvaluatorMode.loglike)
156 assert np.sum(model.evaluate()) == 0