• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cylammarco / WDPhotTools / 28834559914

07 Jul 2026 01:15AM UTC coverage: 95.688% (+0.1%) from 95.575%
28834559914

Pull #50

github

web-flow
Merge 867704e61 into f55e0425d
Pull Request #50: Tidy fitter flux space

236 of 244 new or added lines in 9 files covered. (96.72%)

2 existing lines in 1 file now uncovered.

3573 of 3734 relevant lines covered (95.69%)

1.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.02
/src/WDPhotTools/diff2_functions_least_square.py
1
import numpy as np
2✔
2

3
from .extinction import get_extinction_fraction
2✔
4

5
_MAG_DISTANCE_FACTOR = 2.17147241
2✔
6
_MAG_TO_FRAC_FLUX_VAR = 0.8483036976765438
2✔
7

8

9
def _compute_residual_terms(
2✔
10
    obs,
11
    errors,
12
    model_mag,
13
    distance,
14
    distance_err,
15
    photometry_space,
16
):
17
    """Compute chi-square terms in either magnitude or relative flux space."""
18
    if photometry_space == "magnitude":
2✔
19
        if distance_err is None:
2✔
20
            e2 = errors**2.0
2✔
21
        else:
22
            # 5 / ln(10) converts fractional distance error to magnitude error.
23
            # (ln(10) / 2.5)^2 converts magnitude variance to fractional flux variance.
24
            e2 = (errors**2.0 + (distance_err / distance * _MAG_DISTANCE_FACTOR) ** 2.0) * _MAG_TO_FRAC_FLUX_VAR
2✔
25
        d2 = ((10.0 ** ((obs - model_mag) / 2.5) - 1.0) ** 2.0) / e2
2✔
26
        return d2, e2
2✔
27

28
    if photometry_space == "flux":
2✔
29
        model_flux = 10.0 ** (-0.4 * model_mag)
2✔
30
        e2 = errors**2.0
2✔
31
        if distance_err is not None:
2✔
32
            # Relative flux follows d^-2, so sigma_f = 2 * sigma_d / d * f.
33
            e2 = e2 + (2.0 * distance_err / distance * model_flux) ** 2.0
2✔
34
        d2 = ((obs - model_flux) ** 2.0) / e2
2✔
35
        return d2, e2
2✔
36

NEW
37
    raise ValueError("Unknown photometry_space. Please choose from 'magnitude' and 'flux'.")
×
38

39

40
def diff2(
2✔
41
    _x,
42
    obs,
43
    errors,
44
    distance,
45
    distance_err,
46
    interpolator_filter,
47
    return_err,
48
    photometry_space="magnitude",
49
):
50
    """
51
    Internal method for computing the ch2-squared value (for scipy.optimize.least_squares).
52

53
    """
54

55
    mag = []
2✔
56

57
    for interp in interpolator_filter:
2✔
58
        mag.append(interp(_x[:2]))
2✔
59

60
    model_mag = np.asarray(mag).reshape(-1) + 5.0 * np.log10(distance) - 5.0
2✔
61
    d2, e2 = _compute_residual_terms(
2✔
62
        obs=obs,
63
        errors=errors,
64
        model_mag=model_mag,
65
        distance=distance,
66
        distance_err=distance_err,
67
        photometry_space=photometry_space,
68
    )
69
    # Ensure finite residuals
70
    d2 = np.where(np.isfinite(d2), d2, np.float64(1e30))
2✔
71
    if return_err:
2✔
72
        e2 = np.where(np.isfinite(e2), e2, np.float64(1e30))
2✔
73
        return d2, e2
2✔
74
    else:
75
        return d2
2✔
76

77

78
def diff2_distance(
2✔
79
    _x,
80
    obs,
81
    errors,
82
    interpolator_filter,
83
    return_err,
84
    photometry_space="magnitude",
85
):
86
    """
87
    Internal method for computing the ch2-squared value in cases when the distance is not provided (for
88
    scipy.optimize.least_squares).
89

90
    """
91

92
    if (_x[-1] <= 0.0) or (_x[-1] > 10000.0):
2✔
93
        if return_err:
2✔
94
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
95

96
        else:
97
            return np.ones_like(obs) * np.inf
×
98

99
    mag = []
2✔
100

101
    for interp in interpolator_filter:
2✔
102
        mag.append(interp(_x[:-1]))
2✔
103

104
    model_mag = np.asarray(mag).reshape(-1) + 5.0 * np.log10(_x[-1]) - 5.0
2✔
105
    d2, e2 = _compute_residual_terms(
2✔
106
        obs=obs,
107
        errors=errors,
108
        model_mag=model_mag,
109
        distance=_x[-1],
110
        distance_err=None,
111
        photometry_space=photometry_space,
112
    )
113

114
    if np.isfinite(d2).all():
2✔
115
        if return_err:
2✔
116
            return d2, e2
2✔
117

118
        else:
119
            return d2
2✔
120

121
    else:
122
        if return_err:
2✔
123
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
124

125
        else:
126
            return np.ones_like(obs) * np.inf
2✔
127

128

129
def diff2_distance_red_interpolated(
2✔
130
    _x,
131
    obs,
132
    errors,
133
    interpolator_filter,
134
    rv,
135
    extinction_mode,
136
    reddening_vector,
137
    ebv,
138
    ra,
139
    dec,
140
    zmin,
141
    zmax,
142
    return_err,
143
    photometry_space="magnitude",
144
):
145
    """
146
    Internal method for computing the ch2-squared value in cases when the distance is not provided.
147

148
    """
149

150
    if (_x[-1] <= 0.0) or (_x[-1] > 10000.0):
2✔
UNCOV
151
        if return_err:
×
UNCOV
152
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
×
153

154
        else:
155
            return np.ones_like(obs) * np.inf
×
156

157
    mag = []
2✔
158

159
    for interp in interpolator_filter:
2✔
160
        mag.append(interp(_x[:2]))
2✔
161

162
    if extinction_mode == "total":
2✔
163
        extinction_fraction = 1.0
2✔
164

165
    else:
166
        extinction_fraction = get_extinction_fraction(_x[-1], ra, dec, zmin, zmax)
×
167

168
    av = np.array([i(rv) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
169
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(_x[-1]) - 5.0
2✔
170
    d2, e2 = _compute_residual_terms(
2✔
171
        obs=obs,
172
        errors=errors,
173
        model_mag=model_mag,
174
        distance=_x[-1],
175
        distance_err=None,
176
        photometry_space=photometry_space,
177
    )
178

179
    if np.isfinite(d2).all():
2✔
180
        if return_err:
2✔
181
            return d2, e2
2✔
182

183
        else:
184
            return d2
2✔
185

186
    else:
187
        if return_err:
2✔
188
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
189

190
        else:
191
            return np.ones_like(obs) * np.inf
2✔
192

193

194
def diff2_distance_red_interpolated_fixed_logg(
2✔
195
    _x,
196
    obs,
197
    errors,
198
    interpolator_filter,
199
    rv,
200
    extinction_mode,
201
    reddening_vector,
202
    ebv,
203
    ra,
204
    dec,
205
    zmin,
206
    zmax,
207
    return_err,
208
    photometry_space="magnitude",
209
):
210
    """
211
    Internal method for computing the ch2-squared value in cases when the distance is not provided.
212

213
    """
214

215
    if (_x[-1] <= 0.0) or (_x[-1] > 10000.0):
2✔
216
        if return_err:
×
217
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
×
218

219
        else:
220
            return np.ones_like(obs) * np.inf
×
221

222
    mag = []
2✔
223

224
    for interp in interpolator_filter:
2✔
225
        mag.append(interp(_x[:-1]))
2✔
226

227
    if extinction_mode == "total":
2✔
228
        extinction_fraction = 1.0
2✔
229

230
    else:
231
        extinction_fraction = get_extinction_fraction(_x[-1], ra, dec, zmin, zmax)
×
232

233
    av = np.array([i(rv) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
234
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(_x[-1]) - 5.0
2✔
235
    d2, e2 = _compute_residual_terms(
2✔
236
        obs=obs,
237
        errors=errors,
238
        model_mag=model_mag,
239
        distance=_x[-1],
240
        distance_err=None,
241
        photometry_space=photometry_space,
242
    )
243

244
    if np.isfinite(d2).all():
2✔
245
        if return_err:
2✔
246
            return d2, e2
2✔
247

248
        else:
249
            return d2
2✔
250

251
    else:
252
        if return_err:
2✔
253
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
254

255
        else:
256
            return np.ones_like(obs) * np.inf
2✔
257

258

259
def diff2_distance_red_filter(
2✔
260
    _x,
261
    obs,
262
    errors,
263
    interpolator_filter,
264
    interpolator_teff,
265
    logg_pos,
266
    rv,
267
    extinction_mode,
268
    reddening_vector,
269
    ebv,
270
    ra,
271
    dec,
272
    zmin,
273
    zmax,
274
    return_err,
275
    photometry_space="magnitude",
276
):
277
    """
278
    Internal method for computing the ch2-squared value in cases when
279
    the distance is not provided.
280

281
    """
282

283
    if (_x[-1] <= 0.0) or (_x[-1] > 10000.0):
2✔
284
        if return_err:
2✔
285
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
286

287
        else:
288
            return np.ones_like(obs) * np.inf
×
289

290
    mag = []
2✔
291

292
    for interp in interpolator_filter:
2✔
293
        mag.append(interp(_x[:2]))
2✔
294

295
    if extinction_mode == "total":
2✔
296
        extinction_fraction = 1.0
2✔
297

298
    else:
299
        extinction_fraction = get_extinction_fraction(_x[-1], ra, dec, zmin, zmax)
×
300

301
    teff = float(np.asarray(interpolator_teff(_x[:2])).reshape(-1)[0])
2✔
302
    logg = _x[logg_pos]
2✔
303
    av = np.array([i([logg, teff, rv]) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
304
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(_x[-1]) - 5.0
2✔
305
    d2, e2 = _compute_residual_terms(
2✔
306
        obs=obs,
307
        errors=errors,
308
        model_mag=model_mag,
309
        distance=_x[-1],
310
        distance_err=None,
311
        photometry_space=photometry_space,
312
    )
313

314
    if np.isfinite(d2).all():
2✔
315
        if return_err:
2✔
316
            return d2, e2
2✔
317

318
        else:
319
            return d2
2✔
320

321
    else:
322
        if return_err:
2✔
323
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
324

325
        else:
326
            return np.ones_like(obs) * np.inf
2✔
327

328

329
def diff2_distance_red_filter_fixed_logg(
2✔
330
    _x,
331
    obs,
332
    errors,
333
    interpolator_filter,
334
    interpolator_teff,
335
    logg,
336
    rv,
337
    extinction_mode,
338
    reddening_vector,
339
    ebv,
340
    ra,
341
    dec,
342
    zmin,
343
    zmax,
344
    return_err,
345
    photometry_space="magnitude",
346
):
347
    """
348
    Internal method for computing the ch2-squared value in cases when the distance is not provided.
349

350
    """
351

352
    if (_x[-1] <= 0.0) or (_x[-1] > 10000.0):
2✔
353
        if return_err:
2✔
354
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
355

356
        else:
357
            return np.ones_like(obs) * np.inf
×
358

359
    mag = []
2✔
360

361
    for interp in interpolator_filter:
2✔
362
        mag.append(interp(_x[:-1]))
2✔
363

364
    if extinction_mode == "total":
2✔
365
        extinction_fraction = 1.0
2✔
366

367
    else:
368
        extinction_fraction = get_extinction_fraction(_x[-1], ra, dec, zmin, zmax)
×
369

370
    teff = float(np.asarray(interpolator_teff(_x[:-1])).reshape(-1)[0])
2✔
371
    av = np.array([i([logg, teff, rv]) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
372
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(_x[-1]) - 5.0
2✔
373
    d2, e2 = _compute_residual_terms(
2✔
374
        obs=obs,
375
        errors=errors,
376
        model_mag=model_mag,
377
        distance=_x[-1],
378
        distance_err=None,
379
        photometry_space=photometry_space,
380
    )
381

382
    if np.isfinite(d2).all():
2✔
383
        if return_err:
2✔
384
            return d2, e2
2✔
385

386
        else:
387
            return d2
2✔
388

389
    else:
390
        if return_err:
2✔
391
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
392

393
        else:
394
            return np.ones_like(obs) * np.inf
2✔
395

396

397
def diff2_red_interpolated(
2✔
398
    _x,
399
    obs,
400
    errors,
401
    distance,
402
    distance_err,
403
    interpolator_filter,
404
    rv,
405
    extinction_mode,
406
    reddening_vector,
407
    ebv,
408
    ra,
409
    dec,
410
    zmin,
411
    zmax,
412
    return_err,
413
    photometry_space="magnitude",
414
):
415
    """
416
    Internal method for computing the ch2-squared value.
417

418
    """
419

420
    mag = []
2✔
421

422
    for interp in interpolator_filter:
2✔
423
        mag.append(interp(_x))
2✔
424

425
    if extinction_mode == "total":
2✔
426
        extinction_fraction = 1.0
2✔
427

428
    else:
429
        extinction_fraction = get_extinction_fraction(distance, ra, dec, zmin, zmax)
×
430

431
    av = np.array([i(rv) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
432
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(distance) - 5.0
2✔
433
    d2, e2 = _compute_residual_terms(
2✔
434
        obs=obs,
435
        errors=errors,
436
        model_mag=model_mag,
437
        distance=distance,
438
        distance_err=distance_err,
439
        photometry_space=photometry_space,
440
    )
441

442
    if np.isfinite(d2).all():
2✔
443
        if return_err:
2✔
444
            return d2, e2
2✔
445

446
        else:
447
            return d2
2✔
448

449
    else:
450
        if return_err:
2✔
451
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
452

453
        else:
454
            return np.ones_like(obs) * np.inf
×
455

456

457
def diff2_red_filter(
2✔
458
    _x,
459
    obs,
460
    errors,
461
    distance,
462
    distance_err,
463
    interpolator_filter,
464
    interpolator_teff,
465
    logg_pos,
466
    rv,
467
    extinction_mode,
468
    reddening_vector,
469
    ebv,
470
    ra,
471
    dec,
472
    zmin,
473
    zmax,
474
    return_err,
475
    photometry_space="magnitude",
476
):
477
    """
478
    Internal method for computing the ch2-squared value (for scipy.optimize.least_square).
479

480
    """
481

482
    mag = []
2✔
483

484
    for interp in interpolator_filter:
2✔
485
        mag.append(interp(_x))
2✔
486

487
    teff = float(np.asarray(interpolator_teff(_x)).reshape(-1)[0])
2✔
488

489
    if not np.isfinite(teff):
2✔
490
        if return_err:
2✔
491
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
492

493
        else:
494
            return np.ones_like(obs) * np.inf
×
495

496
    if extinction_mode == "total":
2✔
497
        extinction_fraction = 1.0
2✔
498

499
    else:
500
        extinction_fraction = get_extinction_fraction(distance, ra, dec, zmin, zmax)
×
501

502
    logg = _x[logg_pos]
2✔
503
    av = np.array([i([logg, teff, rv]) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
504
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(distance) - 5.0
2✔
505
    d2, e2 = _compute_residual_terms(
2✔
506
        obs=obs,
507
        errors=errors,
508
        model_mag=model_mag,
509
        distance=distance,
510
        distance_err=distance_err,
511
        photometry_space=photometry_space,
512
    )
513

514
    if np.isfinite(d2).all():
2✔
515
        if return_err:
2✔
516
            return d2, e2
2✔
517

518
        else:
519
            return d2
2✔
520

521
    else:
522
        if return_err:
×
523
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
×
524

525
        else:
526
            return np.ones_like(obs) * np.inf
×
527

528

529
def diff2_red_filter_fixed_logg(
2✔
530
    _x,
531
    obs,
532
    errors,
533
    distance,
534
    distance_err,
535
    interpolator_filter,
536
    interpolator_teff,
537
    logg,
538
    rv,
539
    extinction_mode,
540
    reddening_vector,
541
    ebv,
542
    ra,
543
    dec,
544
    zmin,
545
    zmax,
546
    return_err,
547
    photometry_space="magnitude",
548
):
549
    """
550
    Internal method for computing the ch2-squared value (for scipy.optimize.least_square).
551

552
    """
553

554
    mag = []
2✔
555

556
    for interp in interpolator_filter:
2✔
557
        mag.append(interp(_x))
2✔
558

559
    if extinction_mode == "total":
2✔
560
        extinction_fraction = 1.0
2✔
561

562
    else:
563
        extinction_fraction = get_extinction_fraction(distance, ra, dec, zmin, zmax)
×
564

565
    teff = float(np.asarray(interpolator_teff(_x)).reshape(-1)[0])
2✔
566
    av = np.array([i([logg, teff, rv]) for i in reddening_vector]).reshape(-1) * ebv * extinction_fraction
2✔
567
    model_mag = np.asarray(mag).reshape(-1) + av + 5.0 * np.log10(distance) - 5.0
2✔
568
    d2, e2 = _compute_residual_terms(
2✔
569
        obs=obs,
570
        errors=errors,
571
        model_mag=model_mag,
572
        distance=distance,
573
        distance_err=distance_err,
574
        photometry_space=photometry_space,
575
    )
576

577
    if np.isfinite(d2).all():
2✔
578
        if return_err:
2✔
579
            return d2, e2
2✔
580

581
        else:
582
            return d2
2✔
583

584
    else:
585
        if return_err:
2✔
586
            return np.ones_like(obs) * np.inf, np.ones_like(obs) * np.inf
2✔
587

588
        else:
589
            return np.ones_like(obs)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc