ExoIris 0.23.2__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.2 → exoiris-1.0.0}/CHANGELOG.md +17 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/ExoIris.egg-info/PKG-INFO +1 -1
- {exoiris-0.23.2 → exoiris-1.0.0}/ExoIris.egg-info/SOURCES.txt +1 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/PKG-INFO +1 -1
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/api/exoiris.rst +54 -4
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/api/tsdata.rst +1 -1
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/index.rst +2 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/exoiris.py +29 -5
- exoiris-1.0.0/exoiris/prtretrieval.py +164 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/spotmodel.py +2 -2
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/tslpf.py +71 -13
- {exoiris-0.23.2 → exoiris-1.0.0}/.github/workflows/python-package.yml +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/.gitignore +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/.readthedocs.yaml +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/CODE_OF_CONDUCT.md +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/ExoIris.egg-info/dependency_links.txt +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/ExoIris.egg-info/requires.txt +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/ExoIris.egg-info/top_level.txt +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/LICENSE +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/README.md +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/Makefile +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/make.bat +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/requirements.txt +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/_static/css/custom.css +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/api/binning.rst +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/conf.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/01a_not_so_short_intro.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/01b_short_intro.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/02_increasing_knot_resolution.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/03_increasing_data_resolution.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/04_gaussian_processes.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/05a_ldtkldm.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/A2_full_data_resolution.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/appendix_1_data_preparation.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/data/README.txt +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/data/nirHiss_order_1.h5 +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/data/nirHiss_order_2.h5 +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/example1.png +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/e01/plot_1.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/figures.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/friendly_introduction.ipynb +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/index.rst +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/k_knot_example.svg +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/examples/setup_multiprocessing.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/doc/source/install.rst +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/__init__.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/binning.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/ephemeris.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/ldtkld.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/loglikelihood.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/tsdata.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/tsmodel.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/util.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/exoiris/wlpf.py +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/pyproject.toml +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/requirements.txt +0 -0
- {exoiris-0.23.2 → exoiris-1.0.0}/setup.cfg +0 -0
- {exoiris-0.23.2 → 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
|
|
|
@@ -558,6 +575,12 @@ class ExoIris:
|
|
|
558
575
|
nk = nk[(nk >= wlmin) & (nk <= wlmax)]
|
|
559
576
|
self.set_radius_ratio_knots(r_[ck[ck < nk[0]], nk, ck[ck > nk[-1]]])
|
|
560
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
|
+
|
|
561
584
|
def add_limb_darkening_knots(self, knot_wavelengths: Sequence) -> None:
|
|
562
585
|
"""Add limb darkening knots.
|
|
563
586
|
|
|
@@ -1283,6 +1306,7 @@ class ExoIris:
|
|
|
1283
1306
|
pri.header['t14'] = self.transit_duration
|
|
1284
1307
|
pri.header['ndgroups'] = self.data.size
|
|
1285
1308
|
pri.header['interp'] = self._tsa.interpolation
|
|
1309
|
+
pri.header['interp_ld'] = self._tsa.ld_interpolation
|
|
1286
1310
|
pri.header['noise'] = self._tsa.noise_model
|
|
1287
1311
|
|
|
1288
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
|
|
|
@@ -607,6 +615,12 @@ class TSLPF(LogPosteriorFunction):
|
|
|
607
615
|
return logp
|
|
608
616
|
self._additional_log_priors.append(k_knot_order_prior)
|
|
609
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]
|
|
610
624
|
|
|
611
625
|
def add_ld_knots(self, knot_wavelengths) -> None:
|
|
612
626
|
"""Add limb darkening knots to the model.
|
|
@@ -626,36 +640,80 @@ class TSLPF(LogPosteriorFunction):
|
|
|
626
640
|
knot_wavelengths : array-like
|
|
627
641
|
Array of knot wavelengths.
|
|
628
642
|
"""
|
|
643
|
+
|
|
644
|
+
# Save the old variables
|
|
645
|
+
# ----------------------
|
|
629
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
|
+
|
|
630
653
|
xn = self.ld_knots = sort(knot_wavelengths)
|
|
631
654
|
self.nldc = self.ld_knots.size
|
|
632
655
|
|
|
633
|
-
pvpo = self.de.population.copy() if self.de is not None else None
|
|
634
|
-
pso = self.ps
|
|
635
|
-
sldo = self._sl_ld
|
|
636
656
|
self._init_parameters()
|
|
637
657
|
psn = self.ps
|
|
638
|
-
|
|
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
|
+
# --------------------------------
|
|
639
671
|
for po in pso:
|
|
640
672
|
if po.name in psn.names:
|
|
641
673
|
self.set_prior(po.name, po.prior)
|
|
642
674
|
|
|
643
|
-
|
|
644
|
-
|
|
675
|
+
# Resample the DE parameter population
|
|
676
|
+
# ------------------------------------
|
|
677
|
+
if self._de_population is not None:
|
|
678
|
+
den = zeros((deo.shape[0], ndn))
|
|
679
|
+
|
|
645
680
|
# Copy the old parameter values
|
|
646
681
|
# -----------------------------
|
|
647
682
|
for pid_old, p in enumerate(pso):
|
|
648
|
-
if p.name in psn:
|
|
683
|
+
if p.name in psn.names:
|
|
649
684
|
pid_new = psn.find_pid(p.name)
|
|
650
|
-
|
|
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]
|
|
651
708
|
|
|
652
709
|
# Resample the radius ratios
|
|
653
710
|
# --------------------------
|
|
654
|
-
for i in range(
|
|
655
|
-
|
|
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])
|
|
656
714
|
|
|
657
|
-
self.
|
|
658
|
-
self.
|
|
715
|
+
self._mc_chains = fmcn.reshape([mco.shape[0], mco.shape[1], ndn])
|
|
716
|
+
self.sampler = None
|
|
659
717
|
|
|
660
718
|
def _eval_k(self, pvp) -> list[ndarray]:
|
|
661
719
|
"""Evaluate the radius ratio model.
|
|
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.2 → exoiris-1.0.0}/doc/source/examples/e01/02_increasing_knot_resolution.ipynb
RENAMED
|
File without changes
|
{exoiris-0.23.2 → 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
|
|
File without changes
|