|
11 | 11 |
|
12 | 12 | from __future__ import annotations |
13 | 13 |
|
14 | | -from typing import Literal |
| 14 | +from typing import Literal, Self |
15 | 15 |
|
16 | | -from pydantic import Field |
| 16 | +from pydantic import Field, model_validator |
17 | 17 | from pydantic_settings import BaseSettings, SettingsConfigDict |
18 | 18 |
|
19 | 19 | #: Backend selector accepted by every method (concrete names are method-specific). |
20 | 20 | BackendName = str |
21 | 21 |
|
22 | 22 |
|
| 23 | +def _require_lt(lo: float | None, hi: float | None, lo_name: str, hi_name: str) -> None: |
| 24 | + """Raise ``ValueError`` if both bounds are set and ``lo`` is not below ``hi``.""" |
| 25 | + if lo is not None and hi is not None and lo >= hi: |
| 26 | + raise ValueError(f"{lo_name} ({lo}) must be < {hi_name} ({hi})") |
| 27 | + |
| 28 | + |
23 | 29 | class GLSSettings(BaseSettings): |
24 | 30 | """Settings for the generalized Lomb-Scargle (GLS) periodogram.""" |
25 | 31 |
|
26 | 32 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_GLS_", extra="forbid") |
27 | 33 |
|
| 34 | + @model_validator(mode="after") |
| 35 | + def _check_bounds(self) -> Self: |
| 36 | + _require_lt( |
| 37 | + self.minimum_frequency, self.maximum_frequency, |
| 38 | + "minimum_frequency", "maximum_frequency", |
| 39 | + ) |
| 40 | + return self |
| 41 | + |
28 | 42 | minimum_frequency: float | None = Field( |
29 | 43 | default=None, |
30 | 44 | description="Lowest trial frequency (cycles/day); None -> 1/baseline.", |
@@ -68,6 +82,18 @@ class BLSSettings(BaseSettings): |
68 | 82 |
|
69 | 83 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_BLS_", extra="forbid") |
70 | 84 |
|
| 85 | + @model_validator(mode="after") |
| 86 | + def _check_bounds(self) -> Self: |
| 87 | + _require_lt( |
| 88 | + self.min_period_days, self.max_period_days, |
| 89 | + "min_period_days", "max_period_days", |
| 90 | + ) |
| 91 | + _require_lt( |
| 92 | + self.duration_min_frac, self.duration_max_frac, |
| 93 | + "duration_min_frac", "duration_max_frac", |
| 94 | + ) |
| 95 | + return self |
| 96 | + |
71 | 97 | min_period_days: float = Field(default=0.2, gt=0.0, description="Minimum period.") |
72 | 98 | max_period_days: float = Field(default=100.0, gt=0.0, description="Maximum period.") |
73 | 99 | min_transits: int = Field( |
@@ -129,6 +155,14 @@ class PDMSettings(BaseSettings): |
129 | 155 |
|
130 | 156 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_PDM_", extra="forbid") |
131 | 157 |
|
| 158 | + @model_validator(mode="after") |
| 159 | + def _check_bounds(self) -> Self: |
| 160 | + _require_lt( |
| 161 | + self.minimum_frequency, self.maximum_frequency, |
| 162 | + "minimum_frequency", "maximum_frequency", |
| 163 | + ) |
| 164 | + return self |
| 165 | + |
132 | 166 | minimum_frequency: float | None = Field( |
133 | 167 | default=None, |
134 | 168 | description="Lowest trial frequency (cycles/day); None -> 1/baseline.", |
@@ -170,6 +204,14 @@ class MHAOVSettings(BaseSettings): |
170 | 204 |
|
171 | 205 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_MHAOV_", extra="forbid") |
172 | 206 |
|
| 207 | + @model_validator(mode="after") |
| 208 | + def _check_bounds(self) -> Self: |
| 209 | + _require_lt( |
| 210 | + self.minimum_frequency, self.maximum_frequency, |
| 211 | + "minimum_frequency", "maximum_frequency", |
| 212 | + ) |
| 213 | + return self |
| 214 | + |
173 | 215 | minimum_frequency: float | None = Field( |
174 | 216 | default=None, |
175 | 217 | description="Lowest trial frequency (cycles/day); None -> 1/baseline.", |
@@ -210,6 +252,14 @@ class CESettings(BaseSettings): |
210 | 252 |
|
211 | 253 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_CE_", extra="forbid") |
212 | 254 |
|
| 255 | + @model_validator(mode="after") |
| 256 | + def _check_bounds(self) -> Self: |
| 257 | + _require_lt( |
| 258 | + self.minimum_frequency, self.maximum_frequency, |
| 259 | + "minimum_frequency", "maximum_frequency", |
| 260 | + ) |
| 261 | + return self |
| 262 | + |
213 | 263 | minimum_frequency: float | None = Field( |
214 | 264 | default=None, |
215 | 265 | description="Lowest trial frequency (cycles/day); None -> 1/baseline.", |
@@ -249,6 +299,14 @@ class StringLengthSettings(BaseSettings): |
249 | 299 |
|
250 | 300 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_SL_", extra="forbid") |
251 | 301 |
|
| 302 | + @model_validator(mode="after") |
| 303 | + def _check_bounds(self) -> Self: |
| 304 | + _require_lt( |
| 305 | + self.minimum_frequency, self.maximum_frequency, |
| 306 | + "minimum_frequency", "maximum_frequency", |
| 307 | + ) |
| 308 | + return self |
| 309 | + |
252 | 310 | minimum_frequency: float | None = Field( |
253 | 311 | default=None, |
254 | 312 | description="Lowest trial frequency (cycles/day); None -> 1/baseline.", |
@@ -286,6 +344,18 @@ class TLSSettings(BaseSettings): |
286 | 344 |
|
287 | 345 | model_config = SettingsConfigDict(env_prefix="CUPERIOD_TLS_", extra="forbid") |
288 | 346 |
|
| 347 | + @model_validator(mode="after") |
| 348 | + def _check_bounds(self) -> Self: |
| 349 | + _require_lt( |
| 350 | + self.min_period_days, self.max_period_days, |
| 351 | + "min_period_days", "max_period_days", |
| 352 | + ) |
| 353 | + _require_lt( |
| 354 | + self.duration_min_frac, self.duration_max_frac, |
| 355 | + "duration_min_frac", "duration_max_frac", |
| 356 | + ) |
| 357 | + return self |
| 358 | + |
289 | 359 | min_period_days: float = Field(default=0.5, gt=0.0, description="Minimum period.") |
290 | 360 | max_period_days: float = Field(default=100.0, gt=0.0, description="Maximum period.") |
291 | 361 | min_transits: int = Field( |
|
0 commit comments