ExoIris 0.23.1__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {exoiris-0.23.1 → exoiris-1.0.0}/CHANGELOG.md +17 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/ExoIris.egg-info/PKG-INFO +1 -1
- {exoiris-0.23.1 → exoiris-1.0.0}/ExoIris.egg-info/SOURCES.txt +1 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/PKG-INFO +1 -1
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/api/exoiris.rst +54 -4
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/api/tsdata.rst +1 -1
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/index.rst +2 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/exoiris.py +39 -5
- exoiris-1.0.0/exoiris/prtretrieval.py +164 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/spotmodel.py +2 -2
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/tslpf.py +79 -14
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/wlpf.py +1 -1
- {exoiris-0.23.1 → exoiris-1.0.0}/.github/workflows/python-package.yml +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/.gitignore +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/.readthedocs.yaml +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/CODE_OF_CONDUCT.md +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/ExoIris.egg-info/dependency_links.txt +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/ExoIris.egg-info/requires.txt +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/ExoIris.egg-info/top_level.txt +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/LICENSE +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/README.md +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/Makefile +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/make.bat +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/requirements.txt +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/_static/css/custom.css +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/api/binning.rst +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/conf.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/01a_not_so_short_intro.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/01b_short_intro.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/02_increasing_knot_resolution.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/03_increasing_data_resolution.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/04_gaussian_processes.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/05a_ldtkldm.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/A2_full_data_resolution.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/appendix_1_data_preparation.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/data/README.txt +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/data/nirHiss_order_1.h5 +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/data/nirHiss_order_2.h5 +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/example1.png +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/plot_1.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/figures.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/friendly_introduction.ipynb +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/index.rst +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/k_knot_example.svg +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/setup_multiprocessing.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/doc/source/install.rst +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/__init__.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/binning.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/ephemeris.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/ldtkld.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/loglikelihood.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/tsdata.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/tsmodel.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/exoiris/util.py +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/pyproject.toml +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/requirements.txt +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/setup.cfg +0 -0
- {exoiris-0.23.1 → exoiris-1.0.0}/tests/test_binning.py +0 -0
|
@@ -5,6 +5,23 @@ All notable changes to ExoIris will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.0] - 2026-01-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Added support for setting custom interpolation models for limb darkening via `set_limb_darkening_interpolator` method.
|
|
12
|
+
- Added support for setting custom interpolation models for radius ratios via `set_radius_ratio_interpolator` method.
|
|
13
|
+
- Added ability to set priors for offset parameters in the analysis workflow.
|
|
14
|
+
- Added interpolation type tracking in FITS header metadata (`INTERP_LD` for limb darkening interpolation).
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Improved reinitialization logic for MCMC and differential evolution populations with updated limb darkening coefficients and knots.
|
|
18
|
+
- Refactored interpolation handling in `TSLPF` to separately manage radius ratio and limb darkening interpolators.
|
|
19
|
+
- Updated documentation for API methods and workflow.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Fixed incorrect FWHM scaling in `spot_model` function. The scaling factor now correctly uses `log(4)^(1/shape)` for
|
|
23
|
+
proper full width at half maximum calculations in generalized Gaussian spot models.
|
|
24
|
+
|
|
8
25
|
## [0.23.1] - 2025-12-18
|
|
9
26
|
|
|
10
27
|
### Fixed
|
|
@@ -64,9 +64,25 @@ transmission spectrum, and even the observational data can be changed to improve
|
|
|
64
64
|
ExoIris.set_data
|
|
65
65
|
ExoIris.set_radius_ratio_knots
|
|
66
66
|
ExoIris.add_radius_ratio_knots
|
|
67
|
+
ExoIris.set_limb_darkening_knots
|
|
68
|
+
ExoIris.free_radius_ratio_knot_locations
|
|
67
69
|
ExoIris.create_dense_radius_ratio_block
|
|
68
70
|
ExoIris.plot_setup
|
|
69
71
|
|
|
72
|
+
Interpolation configuration
|
|
73
|
+
---------------------------
|
|
74
|
+
|
|
75
|
+
ExoIris uses interpolation to model the wavelength-dependent radius ratio and limb darkening
|
|
76
|
+
parameters. The interpolation method can be customized to balance smoothness against fidelity to the
|
|
77
|
+
data. Available interpolators include: ``nearest``, ``linear``, ``pchip``, ``makima``, ``bspline``,
|
|
78
|
+
``bspline-quadratic``, and ``bspline-cubic``.
|
|
79
|
+
|
|
80
|
+
.. autosummary::
|
|
81
|
+
:toctree: api/
|
|
82
|
+
|
|
83
|
+
ExoIris.set_radius_ratio_interpolator
|
|
84
|
+
ExoIris.set_limb_darkening_interpolator
|
|
85
|
+
|
|
70
86
|
Parameterization and priors
|
|
71
87
|
---------------------------
|
|
72
88
|
|
|
@@ -83,10 +99,11 @@ Noise model setup
|
|
|
83
99
|
-----------------
|
|
84
100
|
|
|
85
101
|
The noise in the spectroscopic light curves can be modeled as either white noise or time-correlated noise
|
|
86
|
-
(using a Gaussian process, GP). The noise model is chosen with the `ExoIris.
|
|
87
|
-
set to
|
|
88
|
-
using the `celerite2` package
|
|
89
|
-
|
|
102
|
+
(using a Gaussian process, GP). The noise model is chosen with the `ExoIris.set_noise_model` method, and can be
|
|
103
|
+
set to ``"white"``, ``"fixed_gp"``, or ``"free_gp"``. Selecting ``"fixed_gp"`` models the noise as a time-correlated
|
|
104
|
+
Gaussian process using the `celerite2` package with fixed hyperparameters, while ``"free_gp"`` allows the GP
|
|
105
|
+
hyperparameters to be sampled as free parameters. The corresponding `celerite2.GaussianProcess` object can be
|
|
106
|
+
accessed directly via the `ExoIris.gp` attribute.
|
|
90
107
|
|
|
91
108
|
.. autosummary::
|
|
92
109
|
:toctree: api/
|
|
@@ -98,6 +115,23 @@ via the `ExoIris.gp` attribute.
|
|
|
98
115
|
ExoIris.gp
|
|
99
116
|
ExoIris.plot_white_gp_predictions
|
|
100
117
|
|
|
118
|
+
Star spot modeling
|
|
119
|
+
------------------
|
|
120
|
+
|
|
121
|
+
ExoIris supports modeling of star spot crossings during transit and the Transit Light Source Effect (TLSE).
|
|
122
|
+
Star spots can cause both localized bumps in the light curve (when the planet occults a spot) and
|
|
123
|
+
wavelength-dependent baseline variations (TLSE) due to the inhomogeneous stellar surface.
|
|
124
|
+
|
|
125
|
+
To use spot modeling, first initialize the spot model with `ExoIris.initialize_spots`, then add spots
|
|
126
|
+
for specific epoch groups using `ExoIris.add_spot`.
|
|
127
|
+
|
|
128
|
+
.. autosummary::
|
|
129
|
+
:toctree: api/
|
|
130
|
+
|
|
131
|
+
ExoIris.initialize_spots
|
|
132
|
+
ExoIris.add_spot
|
|
133
|
+
ExoIris.nspots
|
|
134
|
+
|
|
101
135
|
|
|
102
136
|
First steps
|
|
103
137
|
-----------
|
|
@@ -155,12 +189,27 @@ Pandas `~pandas.DataFrame`.
|
|
|
155
189
|
:toctree: api/
|
|
156
190
|
|
|
157
191
|
ExoIris.transmission_spectrum
|
|
192
|
+
ExoIris.transmission_spectrum_table
|
|
158
193
|
ExoIris.posterior_samples
|
|
159
194
|
ExoIris.plot_fit
|
|
160
195
|
ExoIris.plot_transmission_spectrum
|
|
161
196
|
ExoIris.plot_residuals
|
|
162
197
|
ExoIris.plot_limb_darkening_parameters
|
|
163
198
|
|
|
199
|
+
Atmospheric retrieval
|
|
200
|
+
---------------------
|
|
201
|
+
|
|
202
|
+
ExoIris provides tools for atmospheric retrieval by creating a log-likelihood function that can be
|
|
203
|
+
used with external retrieval codes. The `ExoIris.create_loglikelihood_function` method returns a
|
|
204
|
+
callable that evaluates the log-likelihood for a given transmission spectrum model, accounting for
|
|
205
|
+
the full covariance structure of the data.
|
|
206
|
+
|
|
207
|
+
.. autosummary::
|
|
208
|
+
:toctree: api/
|
|
209
|
+
|
|
210
|
+
ExoIris.create_loglikelihood_function
|
|
211
|
+
ExoIris.transmission_spectrum_samples
|
|
212
|
+
|
|
164
213
|
Utility methods
|
|
165
214
|
---------------
|
|
166
215
|
|
|
@@ -186,6 +235,7 @@ The following properties expose key internal states and parameters of the analys
|
|
|
186
235
|
ExoIris.nk
|
|
187
236
|
ExoIris.nldp
|
|
188
237
|
ExoIris.npb
|
|
238
|
+
ExoIris.nspots
|
|
189
239
|
ExoIris.ldmodel
|
|
190
240
|
ExoIris.sampler
|
|
191
241
|
ExoIris.optimizer
|
|
@@ -43,7 +43,7 @@ from uncertainties import UFloat
|
|
|
43
43
|
|
|
44
44
|
from .ldtkld import LDTkLD
|
|
45
45
|
from .tsdata import TSData, TSDataGroup
|
|
46
|
-
from .tslpf import TSLPF
|
|
46
|
+
from .tslpf import TSLPF, interpolators
|
|
47
47
|
from .wlpf import WhiteLPF
|
|
48
48
|
from .loglikelihood import LogLikelihood
|
|
49
49
|
|
|
@@ -88,7 +88,14 @@ def load_model(fname: Path | str, name: str | None = None):
|
|
|
88
88
|
try:
|
|
89
89
|
ip = hdr['INTERP']
|
|
90
90
|
except KeyError:
|
|
91
|
-
ip = '
|
|
91
|
+
ip = 'linear'
|
|
92
|
+
|
|
93
|
+
# Read the interpolation model.
|
|
94
|
+
# =============================
|
|
95
|
+
try:
|
|
96
|
+
ip_ld = hdr['INTERP_LD']
|
|
97
|
+
except KeyError:
|
|
98
|
+
ip_ld = 'bspline-quadratic'
|
|
92
99
|
|
|
93
100
|
# Read the noise model.
|
|
94
101
|
# =====================
|
|
@@ -100,6 +107,7 @@ def load_model(fname: Path | str, name: str | None = None):
|
|
|
100
107
|
# Setup the analysis.
|
|
101
108
|
# ===================
|
|
102
109
|
a = ExoIris(name or hdr['NAME'], ldmodel=ldm, data=data, noise_model=noise_model, interpolation=ip)
|
|
110
|
+
a.set_limb_darkening_interpolator(ip_ld)
|
|
103
111
|
a.set_radius_ratio_knots(hdul['K_KNOTS'].data.astype('d'))
|
|
104
112
|
a.set_limb_darkening_knots(hdul['LD_KNOTS'].data.astype('d'))
|
|
105
113
|
|
|
@@ -263,15 +271,15 @@ class ExoIris:
|
|
|
263
271
|
data = TSDataGroup([data]) if isinstance(data, TSData) else data
|
|
264
272
|
self._tsa.set_data(data)
|
|
265
273
|
|
|
266
|
-
def set_prior(self, parameter: Literal['radius ratios', '
|
|
274
|
+
def set_prior(self, parameter: Literal['radius ratios', 'offsets', 'wn multipliers'] | str,
|
|
267
275
|
prior: str | Any, *nargs) -> None:
|
|
268
276
|
"""Set a prior on a model parameter.
|
|
269
277
|
|
|
270
278
|
Parameters
|
|
271
279
|
----------
|
|
272
280
|
parameter
|
|
273
|
-
The name of the parameter to set a prior for. Can also be 'radius ratios', '
|
|
274
|
-
to set identical priors on all the radius ratios,
|
|
281
|
+
The name of the parameter to set a prior for. Can also be 'radius ratios', 'offsets', or 'wn multipliers'
|
|
282
|
+
to set identical priors on all the radius ratios, offsets, or white noise multipliers.
|
|
275
283
|
|
|
276
284
|
prior
|
|
277
285
|
The prior distribution for the parameter. This can be "NP" for a normal prior, "UP" for a
|
|
@@ -287,6 +295,9 @@ class ExoIris:
|
|
|
287
295
|
elif parameter == 'wn multipliers':
|
|
288
296
|
for par in self.ps[self._tsa._sl_wnm]:
|
|
289
297
|
self.set_prior(par.name, prior, *nargs)
|
|
298
|
+
elif parameter == 'offsets':
|
|
299
|
+
for par in self.ps[self._tsa._sl_bias]:
|
|
300
|
+
self.set_prior(par.name, prior, *nargs)
|
|
290
301
|
else:
|
|
291
302
|
self._tsa.set_prior(parameter, prior, *nargs)
|
|
292
303
|
|
|
@@ -509,6 +520,12 @@ class ExoIris:
|
|
|
509
520
|
else:
|
|
510
521
|
return self._wa.std_errors
|
|
511
522
|
|
|
523
|
+
def set_radius_ratio_interpolator(self, interpolator: str) -> None:
|
|
524
|
+
"""Set the interpolator for the radius ratio (k) model."""
|
|
525
|
+
if interpolator not in interpolators.keys():
|
|
526
|
+
raise ValueError(f"Interpolator {interpolator} not recognized.")
|
|
527
|
+
self._tsa.set_k_interpolator(interpolator)
|
|
528
|
+
|
|
512
529
|
def add_radius_ratio_knots(self, knot_wavelengths: Sequence) -> None:
|
|
513
530
|
"""Add radius ratio (k) knots.
|
|
514
531
|
|
|
@@ -529,6 +546,16 @@ class ExoIris:
|
|
|
529
546
|
"""
|
|
530
547
|
self._tsa.set_k_knots(knot_wavelengths)
|
|
531
548
|
|
|
549
|
+
def free_radius_ratio_knot_locations(self, knot_ids: list[int] | ndarray) -> None:
|
|
550
|
+
"""Add the wavelength locations of chosen radius ratio knots to the model as free parameters.
|
|
551
|
+
|
|
552
|
+
Parameters
|
|
553
|
+
----------
|
|
554
|
+
knot_ids
|
|
555
|
+
List of radius ratio knot indices to be made free parameters.
|
|
556
|
+
"""
|
|
557
|
+
self._tsa.free_k_knot_locations(knot_ids)
|
|
558
|
+
|
|
532
559
|
def create_dense_radius_ratio_block(self, wlmin: float, wlmax: float) -> None:
|
|
533
560
|
"""Create a block of radius ratio knots using the full data resolution.
|
|
534
561
|
|
|
@@ -548,6 +575,12 @@ class ExoIris:
|
|
|
548
575
|
nk = nk[(nk >= wlmin) & (nk <= wlmax)]
|
|
549
576
|
self.set_radius_ratio_knots(r_[ck[ck < nk[0]], nk, ck[ck > nk[-1]]])
|
|
550
577
|
|
|
578
|
+
def set_limb_darkening_interpolator(self, interpolator: str) -> None:
|
|
579
|
+
"""Set the interpolator for the limb darkening model."""
|
|
580
|
+
if interpolator not in interpolators.keys():
|
|
581
|
+
raise ValueError(f"Interpolator {interpolator} not recognized.")
|
|
582
|
+
self._tsa.set_ld_interpolator(interpolator)
|
|
583
|
+
|
|
551
584
|
def add_limb_darkening_knots(self, knot_wavelengths: Sequence) -> None:
|
|
552
585
|
"""Add limb darkening knots.
|
|
553
586
|
|
|
@@ -1273,6 +1306,7 @@ class ExoIris:
|
|
|
1273
1306
|
pri.header['t14'] = self.transit_duration
|
|
1274
1307
|
pri.header['ndgroups'] = self.data.size
|
|
1275
1308
|
pri.header['interp'] = self._tsa.interpolation
|
|
1309
|
+
pri.header['interp_ld'] = self._tsa.ld_interpolation
|
|
1276
1310
|
pri.header['noise'] = self._tsa.noise_model
|
|
1277
1311
|
|
|
1278
1312
|
if self._tsa.free_k_knot_ids is None:
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# ExoIris: fast, flexible, and easy exoplanet transmission spectroscopy in Python.
|
|
2
|
+
# Copyright (C) 2025 Hannu Parviainen
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
import io
|
|
18
|
+
import warnings
|
|
19
|
+
from contextlib import redirect_stdout
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from petitRADTRANS.radtrans import Radtrans
|
|
23
|
+
from petitRADTRANS import physical_constants as cst
|
|
24
|
+
from petitRADTRANS.spectral_model import SpectralModel
|
|
25
|
+
from petitRADTRANS.retrieval.data import Data
|
|
26
|
+
|
|
27
|
+
with warnings.catch_warnings():
|
|
28
|
+
warnings.simplefilter('ignore')
|
|
29
|
+
from exoiris.exoiris import ExoIris
|
|
30
|
+
from pytransit.lpf.logposteriorfunction import LogPosteriorFunction
|
|
31
|
+
from pytransit.param import ParameterSet, GParameter, UniformPrior as UP, NormalPrior as NP
|
|
32
|
+
|
|
33
|
+
class PRTRetrieval(LogPosteriorFunction):
|
|
34
|
+
def __init__(self, name: str, line_species, ei: ExoIris, rstar: float, mplanet: float, temperature_profile='isothermal',
|
|
35
|
+
pres: int = 100, r: int | None = None, quiet: bool = False):
|
|
36
|
+
super().__init__(name)
|
|
37
|
+
self._line_names = line_species
|
|
38
|
+
if r is None:
|
|
39
|
+
self.line_species = line_species
|
|
40
|
+
else:
|
|
41
|
+
self.line_species = [ls + f'.R{r}' for ls in line_species]
|
|
42
|
+
|
|
43
|
+
self.rstar = rstar
|
|
44
|
+
self.mplanet = mplanet
|
|
45
|
+
self.temperature_profile = temperature_profile
|
|
46
|
+
self.pres = pres
|
|
47
|
+
self.ei = ei
|
|
48
|
+
self.r = r
|
|
49
|
+
|
|
50
|
+
with warnings.catch_warnings():
|
|
51
|
+
warnings.simplefilter("ignore")
|
|
52
|
+
if quiet:
|
|
53
|
+
buf = io.StringIO()
|
|
54
|
+
with redirect_stdout(buf):
|
|
55
|
+
self._create_model()
|
|
56
|
+
else:
|
|
57
|
+
self._create_model()
|
|
58
|
+
|
|
59
|
+
self._init_parameters()
|
|
60
|
+
|
|
61
|
+
self.wavelengths = self.model()[0]
|
|
62
|
+
self._loglike = ei.create_loglikelihood_function(self.wavelengths, 'radius_ratio', method='svd')
|
|
63
|
+
|
|
64
|
+
def _init_parameters(self):
|
|
65
|
+
self.ps = ParameterSet([])
|
|
66
|
+
self._init_p_planet()
|
|
67
|
+
self._init_p_temperature()
|
|
68
|
+
self._init_p_clouds()
|
|
69
|
+
self._init_p_species()
|
|
70
|
+
|
|
71
|
+
def _init_p_planet(self):
|
|
72
|
+
self.ps.thaw()
|
|
73
|
+
ps = [GParameter('radius', 'Planet radius', 'R_Jup', NP(1.3, 0.05), (0.0, 2.5)),
|
|
74
|
+
GParameter('logg', 'Planet log g', '', UP(2, 4), (2, 4))]
|
|
75
|
+
self.ps.add_global_block('planet', ps)
|
|
76
|
+
self.ps.freeze()
|
|
77
|
+
self._sl_planet = self.ps.blocks[-1].slice
|
|
78
|
+
self._st_planet = self.ps.blocks[-1].start
|
|
79
|
+
|
|
80
|
+
def _init_p_clouds(self):
|
|
81
|
+
self.ps.thaw()
|
|
82
|
+
ps = [GParameter('pc', 'Cloud top pressure', '', UP(-6, 2), (-6, 2))]
|
|
83
|
+
self.ps.add_global_block('clouds', ps)
|
|
84
|
+
self.ps.freeze()
|
|
85
|
+
self._sl_clouds = self.ps.blocks[-1].slice
|
|
86
|
+
self._st_clouds = self.ps.blocks[-1].start
|
|
87
|
+
|
|
88
|
+
def _init_p_temperature(self):
|
|
89
|
+
self.ps.thaw()
|
|
90
|
+
if self.temperature_profile == 'isothermal':
|
|
91
|
+
ps = [GParameter('tp_teq', 'equilibrium temperature', 'K', UP(800, 1200), (500, 3500))]
|
|
92
|
+
elif self.temperature_profile == 'guillot':
|
|
93
|
+
ps = [GParameter('tp_teq', 'equilibrium temperature', 'K', UP(800, 1200), (500, 3500)),
|
|
94
|
+
GParameter('tp_tin', 'intrinsinc temperature', 'K', UP(800, 1200), (500, 3500)),
|
|
95
|
+
GParameter('tp_mo', 'guillot profile mean opacity', 'K', UP(0, 1), (0, 1)),
|
|
96
|
+
GParameter('tp_g', 'guillot profile gamma', 'K', UP(0, 1), (0, 1))]
|
|
97
|
+
self.ps.add_global_block('temperature_profile', ps)
|
|
98
|
+
self._sl_tprof = self.ps.blocks[-1].slice
|
|
99
|
+
self._st_tprof = self.ps.blocks[-1].start
|
|
100
|
+
|
|
101
|
+
def _init_p_species(self):
|
|
102
|
+
self.ps.thaw()
|
|
103
|
+
ps = [GParameter(n, n, '', UP(-12, -0.2), [-12, 0]) for n in self.line_species]
|
|
104
|
+
self.ps.add_global_block('line_species', ps)
|
|
105
|
+
self.ps.freeze()
|
|
106
|
+
self._sl_species = self.ps.blocks[-1].slice
|
|
107
|
+
self._st_species = self.ps.blocks[-1].start
|
|
108
|
+
|
|
109
|
+
def _c_temperature_profile(self, pv) -> dict:
|
|
110
|
+
pv = pv[self._sl_tprof]
|
|
111
|
+
pars = {}
|
|
112
|
+
pars['temperature'] = pv[0]
|
|
113
|
+
if self.temperature_profile == 'isothermal':
|
|
114
|
+
pass
|
|
115
|
+
elif self.temperature_profile == 'guillot':
|
|
116
|
+
pars['intrinsic_temperature'] = pv[1]
|
|
117
|
+
pars['guillot_temperature_profile_infrared_mean_opacity_solar_metallicity'] = pv[2]
|
|
118
|
+
pars['guillot_temperature_profile_gamma'] = pv[3]
|
|
119
|
+
return pars
|
|
120
|
+
|
|
121
|
+
def _create_model(self):
|
|
122
|
+
self.sm = SpectralModel(
|
|
123
|
+
pressures=np.logspace(-6, 2, self.pres),
|
|
124
|
+
line_species=self.line_species,
|
|
125
|
+
rayleigh_species=['H2', 'He'],
|
|
126
|
+
gas_continuum_contributors=['H2--H2', 'H2--He'],
|
|
127
|
+
wavelength_boundaries=[self.ei.data.wlmin, self.ei.data.wlmax],
|
|
128
|
+
star_radius=self.rstar * cst.r_sun,
|
|
129
|
+
planet_radius=1.0 * cst.r_jup_mean,
|
|
130
|
+
planet_mass=self.mplanet * cst.m_jup,
|
|
131
|
+
reference_gravity=391,
|
|
132
|
+
reference_pressure=1e-2,
|
|
133
|
+
temperature_profile=self.temperature_profile,
|
|
134
|
+
temperature=1000,
|
|
135
|
+
# cloud_mode='power_law',
|
|
136
|
+
# power_law_opacity_350nm = 0.01,
|
|
137
|
+
# power_law_opacity_coefficient = -4.0,
|
|
138
|
+
# opaque_cloud_top_pressure=1e-3,
|
|
139
|
+
# cloud_fraction=1.0,
|
|
140
|
+
haze_factor=100.0,
|
|
141
|
+
# co_ratio=1,
|
|
142
|
+
use_equilibrium_chemistry=False,
|
|
143
|
+
imposed_mass_fractions={s: 1e-4 for s in self.line_species},
|
|
144
|
+
filling_species={'H2': 37, 'He': 12}
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def model(self, pv=None):
|
|
148
|
+
if pv is not None:
|
|
149
|
+
|
|
150
|
+
self.sm.model_parameters.update(self._c_temperature_profile(pv))
|
|
151
|
+
self.sm.model_parameters['planet_radius'] = pv[self._st_planet] * cst.r_jup_mean
|
|
152
|
+
self.sm.model_parameters['reference_gravity'] = 10 ** pv[self._st_planet + 1]
|
|
153
|
+
self.sm.model_parameters['opaque_cloud_top_pressure'] = 10 ** pv[self._st_clouds]
|
|
154
|
+
for i, ls in enumerate(self.line_species):
|
|
155
|
+
self.sm.model_parameters['imposed_mass_fractions'][ls] = 10 ** pv[self._st_species + i]
|
|
156
|
+
self.sm.update_spectral_calculation_parameters(**self.sm.model_parameters)
|
|
157
|
+
wavelengths, radii = self.sm.calculate_spectrum(mode='transmission')
|
|
158
|
+
return wavelengths[0] * 1e4, radii[0]
|
|
159
|
+
|
|
160
|
+
def lnlikelihood(self, pv):
|
|
161
|
+
with warnings.catch_warnings():
|
|
162
|
+
warnings.simplefilter("ignore")
|
|
163
|
+
model_spectrum = self.model(pv)[1]
|
|
164
|
+
return self._loglike(model_spectrum / (self.rstar * cst.r_sun))
|
|
@@ -41,8 +41,8 @@ from exoiris.util import bin2d
|
|
|
41
41
|
|
|
42
42
|
@njit
|
|
43
43
|
def spot_model(x, center, amplitude, fwhm, shape):
|
|
44
|
-
c =
|
|
45
|
-
return amplitude*exp(-(fabs(x-center) / c)**shape)
|
|
44
|
+
c = log(4)**(1/shape) * fwhm
|
|
45
|
+
return amplitude*exp(-(2*fabs(x-center) / c)**shape)
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
@njit
|
|
@@ -191,12 +191,13 @@ class TSLPF(LogPosteriorFunction):
|
|
|
191
191
|
self.npt: list[int] | None = None
|
|
192
192
|
self._baseline_models: list[ndarray] | None = None
|
|
193
193
|
self.interpolation: str = interpolation
|
|
194
|
+
self.ld_interpolation: str = 'bspline-quadratic'
|
|
194
195
|
|
|
195
196
|
if interpolation not in interpolator_choices:
|
|
196
197
|
raise ValueError(f'interpolation must be one of {interpolator_choices}')
|
|
197
198
|
|
|
198
199
|
self._ip = interpolators[interpolation]
|
|
199
|
-
self._ip_ld = interpolators[
|
|
200
|
+
self._ip_ld = interpolators[self.ld_interpolation]
|
|
200
201
|
|
|
201
202
|
self._gp: Optional[list[GP]] = None
|
|
202
203
|
self._gp_time: Optional[list[ndarray]] = None
|
|
@@ -468,6 +469,13 @@ class TSLPF(LogPosteriorFunction):
|
|
|
468
469
|
"""
|
|
469
470
|
self.set_k_knots(concatenate([self.k_knots, knot_wavelengths]))
|
|
470
471
|
|
|
472
|
+
def set_k_interpolator(self, interpolator: str) -> None:
|
|
473
|
+
"""Set the interpolator for the radius ratio (k) model."""
|
|
474
|
+
if interpolator not in interpolators.keys():
|
|
475
|
+
raise ValueError(f"Interpolator {interpolator} not recognized.")
|
|
476
|
+
self.interpolation = interpolator
|
|
477
|
+
self._ip = interpolators[interpolator]
|
|
478
|
+
|
|
471
479
|
def set_k_knots(self, knot_wavelengths) -> None:
|
|
472
480
|
"""Set the radius ratio (k) knot wavelengths for the model.
|
|
473
481
|
|
|
@@ -550,7 +558,14 @@ class TSLPF(LogPosteriorFunction):
|
|
|
550
558
|
self._mc_chains = fmcn.reshape([mco.shape[0], mco.shape[1], ndn])
|
|
551
559
|
self.sampler = None
|
|
552
560
|
|
|
553
|
-
def set_free_k_knots(self, ids):
|
|
561
|
+
def set_free_k_knots(self, ids: list[int] | ndarray) -> None:
|
|
562
|
+
"""Add the wavelength locations of chosen radius ratio knots as free model parameters.
|
|
563
|
+
|
|
564
|
+
Parameters
|
|
565
|
+
----------
|
|
566
|
+
ids : list of int
|
|
567
|
+
List of radius ratio knot indices to be made free parameters.
|
|
568
|
+
"""
|
|
554
569
|
self.free_k_knot_ids = ids
|
|
555
570
|
|
|
556
571
|
# Remove existing parameter block if one exists
|
|
@@ -600,6 +615,12 @@ class TSLPF(LogPosteriorFunction):
|
|
|
600
615
|
return logp
|
|
601
616
|
self._additional_log_priors.append(k_knot_order_prior)
|
|
602
617
|
|
|
618
|
+
def set_ld_interpolator(self, interpolator: str) -> None:
|
|
619
|
+
"""Set the interpolator for the limb darkening model."""
|
|
620
|
+
if interpolator not in interpolators.keys():
|
|
621
|
+
raise ValueError(f"Interpolator {interpolator} not recognized.")
|
|
622
|
+
self.ld_interpolation = interpolator
|
|
623
|
+
self._ip_ld = interpolators[interpolator]
|
|
603
624
|
|
|
604
625
|
def add_ld_knots(self, knot_wavelengths) -> None:
|
|
605
626
|
"""Add limb darkening knots to the model.
|
|
@@ -619,36 +640,80 @@ class TSLPF(LogPosteriorFunction):
|
|
|
619
640
|
knot_wavelengths : array-like
|
|
620
641
|
Array of knot wavelengths.
|
|
621
642
|
"""
|
|
643
|
+
|
|
644
|
+
# Save the old variables
|
|
645
|
+
# ----------------------
|
|
622
646
|
xo = self.ld_knots
|
|
647
|
+
pso = self.ps
|
|
648
|
+
deo = self._de_population
|
|
649
|
+
mco = self._mc_chains
|
|
650
|
+
slo = self._sl_ld
|
|
651
|
+
ndo = self.ndim
|
|
652
|
+
|
|
623
653
|
xn = self.ld_knots = sort(knot_wavelengths)
|
|
624
654
|
self.nldc = self.ld_knots.size
|
|
625
655
|
|
|
626
|
-
pvpo = self.de.population.copy() if self.de is not None else None
|
|
627
|
-
pso = self.ps
|
|
628
|
-
sldo = self._sl_ld
|
|
629
656
|
self._init_parameters()
|
|
630
657
|
psn = self.ps
|
|
631
|
-
|
|
658
|
+
sln = self._sl_ld
|
|
659
|
+
ndn = self.ndim
|
|
660
|
+
|
|
661
|
+
# Check if we have spots
|
|
662
|
+
# ----------------------
|
|
663
|
+
if self.spot_model is not None:
|
|
664
|
+
spots = self.spot_model
|
|
665
|
+
self.initialize_spots(spots.tphot, spots.wlref, spots.include_tlse)
|
|
666
|
+
for eg in spots.spot_epoch_groups:
|
|
667
|
+
self.spot_model.add_spot(eg)
|
|
668
|
+
|
|
669
|
+
# Set the priors back as they were
|
|
670
|
+
# --------------------------------
|
|
632
671
|
for po in pso:
|
|
633
672
|
if po.name in psn.names:
|
|
634
673
|
self.set_prior(po.name, po.prior)
|
|
635
674
|
|
|
636
|
-
|
|
637
|
-
|
|
675
|
+
# Resample the DE parameter population
|
|
676
|
+
# ------------------------------------
|
|
677
|
+
if self._de_population is not None:
|
|
678
|
+
den = zeros((deo.shape[0], ndn))
|
|
679
|
+
|
|
638
680
|
# Copy the old parameter values
|
|
639
681
|
# -----------------------------
|
|
640
682
|
for pid_old, p in enumerate(pso):
|
|
641
|
-
if p.name in psn:
|
|
683
|
+
if p.name in psn.names:
|
|
642
684
|
pid_new = psn.find_pid(p.name)
|
|
643
|
-
|
|
685
|
+
den[:, pid_new] = deo[:, pid_old]
|
|
686
|
+
|
|
687
|
+
# Resample the limb darkening coefficients
|
|
688
|
+
# ----------------------------------------
|
|
689
|
+
for i in range(den.shape[0]):
|
|
690
|
+
den[i, sln][0::2] = self._ip_ld(xn, xo, deo[i, slo][0::2])
|
|
691
|
+
den[i, sln][1::2] = self._ip_ld(xn, xo, deo[i, slo][1::2])
|
|
692
|
+
|
|
693
|
+
self._de_population = den
|
|
694
|
+
self.de = None
|
|
695
|
+
|
|
696
|
+
# Resample the MCMC parameter population
|
|
697
|
+
# --------------------------------------
|
|
698
|
+
if self._mc_chains is not None:
|
|
699
|
+
fmco = mco.reshape([-1, ndo])
|
|
700
|
+
fmcn = zeros((fmco.shape[0], ndn))
|
|
701
|
+
|
|
702
|
+
# Copy the old parameter values
|
|
703
|
+
# -----------------------------
|
|
704
|
+
for pid_old, p in enumerate(pso):
|
|
705
|
+
if p.name in psn.names:
|
|
706
|
+
pid_new = psn.find_pid(p.name)
|
|
707
|
+
fmcn[:, pid_new] = fmco[:, pid_old]
|
|
644
708
|
|
|
645
709
|
# Resample the radius ratios
|
|
646
710
|
# --------------------------
|
|
647
|
-
for i in range(
|
|
648
|
-
|
|
711
|
+
for i in range(fmcn.shape[0]):
|
|
712
|
+
fmcn[i, sln][0::2] = self._ip_ld(xn, xo, fmco[i, slo][0::2])
|
|
713
|
+
fmcn[i, sln][1::2] = self._ip_ld(xn, xo, fmco[i, slo][1::2])
|
|
649
714
|
|
|
650
|
-
self.
|
|
651
|
-
self.
|
|
715
|
+
self._mc_chains = fmcn.reshape([mco.shape[0], mco.shape[1], ndn])
|
|
716
|
+
self.sampler = None
|
|
652
717
|
|
|
653
718
|
def _eval_k(self, pvp) -> list[ndarray]:
|
|
654
719
|
"""Evaluate the radius ratio model.
|
|
@@ -128,7 +128,7 @@ class WhiteLPF(BaseLPF):
|
|
|
128
128
|
pv = self._local_minimization.x
|
|
129
129
|
a = as_from_rhop(pv[1], pv[0])
|
|
130
130
|
i = i_from_ba(pv[2], a)
|
|
131
|
-
t14 = d_from_pkaiews(pv[0], sqrt(pv[
|
|
131
|
+
t14 = d_from_pkaiews(pv[0], sqrt(pv[self._start_k2]), a, i, 0., 0., 1, 14)
|
|
132
132
|
return t14
|
|
133
133
|
|
|
134
134
|
def plot(self, axs=None, figsize=None, ncols=2) -> Figure:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/02_increasing_knot_resolution.ipynb
RENAMED
|
File without changes
|
{exoiris-0.23.1 → exoiris-1.0.0}/doc/source/examples/e01/03_increasing_data_resolution.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|