General configuration

User-specific paths to cross-section, CIA, and aerosols data files

First, you need to configure the paths where cross-section, CIA, and aerosols data files are located. xsec_path may indicate either cross-sections or correlated-k data.

If you want to test this tutorial notebook, you can download simple example data at https://mycore.core-cloud.net/index.php/s/w2cHuigAiwcfBVW. To download and move the data in the correct paths, you can uncomment the following cell:

[1]:
# ! wget https://forge.oasu.u-bordeaux.fr/amechineau/pytmosph3r-data/-/archive/minimal/pytmosph3r-data-minimal.zip
# ! unzip pytmosph3r-data-minimal.zip
# ! mkdir -p data
# ! mv pytmosph3r-data-minimal/* data/   # or if you already have data/, update it with: rsync -avz exo_k-public/ data/
# ! ln -sf data/gcm/K2-18b_diagfi.nc diagfi.nc    # try either this one (with aerosols) ...
# ! ln -sf data/gcm/wasp121b_Teq2100.nc diagfi.nc # ... or this one (Ultra Hot Jupiter)

Many thanks to [ParmentierLineBean+18] and [CharnayBlainBezard+21] for providing the simulations available in this example.

[2]:
from pathlib import Path

# Path where the data are stored
data_path: Path = Path('..') / 'data'

xsec_path = (data_path / "corrk").as_posix()
aerosol_path = (data_path / "aerosol").as_posix()
cia_path = (data_path / "cia").as_posix()

General Imports

Now let’s import some general libraries and set the paths that we’ve just configured for Pytmosph3R.

[3]:
import numpy as np

import astropy.units as u
import pytmosph3r as p3
import exo_k as xk

p3.criticalLogging() # disable most of the logging
xk.Settings().set_search_path(xsec_path)
xk.Settings().set_cia_search_path(cia_path)
xk.Settings().set_aerosol_search_path(aerosol_path)

#xk.Settings().set_delimiter(".")
xk.Settings().set_mks(True)
xk.Settings().set_log_interp(False)

# %matplotlib notebook

In case you encounter a bug, you can use the following command to get more information from pytmosph3r:

[4]:
p3.debug()

And return to the default behavior:

[5]:
p3.debug(False)

Simple transmission model configuration

We can now setup our Pytmosph3R model! This can be done with an input file (see library_input), or by constructing a simple atmosphere, as shown below.

Planet & star

Let’s configure a planet and a star to put in our model, we suppose a tidal locked orbit with a radius of 1 AU:

[6]:
Rp = 1.807 * 6.9911e7 * u.Unit('m')
g0 = 6.67384e-11 * 1.183 * 1.898e27 / Rp**2 * u.Unit('m3*s-2')

orbit=p3.CircularOrbit(a=1.*u.au)
planet = p3.Planet(surface_gravity=g0, radius=Rp)
star = p3.Star.fromSolar(temperature=6460.0, radius=1.458) # radius is in sun radii

Atmosphere

First, let’s choose the dimensions of our atmospheric grid. Here, we will first test a 1D model:

[7]:
grid = p3.Grid3D(n_vertical=100, n_latitudes=1, n_longitudes=1)

We can now set the temperature, pressure and gases to include in our atmosphere:

[8]:
input_atmosphere = p3.InputAtmosphere(grid=grid,

     # Feel free to add a 1D temperature profile here instead
     temperature = 2500,

     # bottom and top pressures
     max_pressure = 1e6, min_pressure = 1e-4,

     # Again, you could add more complex molecular profiles if needed
     gas_mix_ratio = {"H2O":5.01187234e-04,
                      "CO2":4.4e-4,
                      "H2":"background"
                     },
     # molecules for which we will not compute the opacity
     transparent_gases = "H2")

To choose the position of the observer and the number of rays, you can use:

[9]:
observer = p3.Observer(0, 180) # latitude, longitude in degrees
rays = p3.Rays(n_radial=100, n_angular=1) # in 1D cases, only 1 angle is required

Opacity

Let’s choose what kind of opacity we want to put in there:

[10]:
opacity = p3.Opacity(rayleigh=True, cia=['H2', 'He'])

Run the model

The model can be initialized using:

[11]:
model = p3.Model(n_vertical = 100,
                 planet=planet,
                 star=star,
                 orbit=orbit,
                 observer=observer,
                 input_atmosphere=input_atmosphere,
                 transmission=p3.Transmission(rays=rays),
                 opacity=opacity,)

model.build()

And now you can run it with:

[12]:
model.run()
be careful: ()
            String filters not specific enough, several corresponding files have been found.
            We will use the first one:
            /builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.SI.h5
            Other files are:
            ['/builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.TauREx.h5']

Variables in the model

If you need more information about an object, you can checkout its inputs and outputs by just calling it:

[13]:
model
[13]:
 Model()
- Inputs: ['output_file', 'n_vertical', 'noise', 'interp', 'gas_dict', 'aerosols_dict', 'input_atmosphere', 'opacity', 'planet', 'star', 'observer', 'orbit', 'radiative_transfer', 'transmission', 'emission', 'lightcurve', 'phasecurve', 'parallel']
- Outputs: ['input_atmosphere', 'atmosphere', 'wns', 'wnedges', 'transmission', 'emission', 'lightcurve', 'phasecurve', 'spectrum_value', 'spectrum_noised']
- Methods: ['Rp', 'aerosol', 'available_memory', 'build', 'check_memory', 'critical', 'debug', 'default_value', 'dump_profiling', 'error', 'gas', 'info', 'inputs', 'inputs_values', 'isEnabledFor', 'outputs', 'override_data_file', 'override_file_param', 'read_data', 'run', 'search_path', 'start_profiling', 'summary', 'warning']

Plot the spectrum

The spectrum can be visualized with:

[14]:
plot = p3.Plot(model=model, label="1D", interactive=True, out_folder="outputs")
plot.plot_spectrum()
Saved to `outputs/spectrum_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_38_1.png

The spectrum can be binned down to a specific number of wavelengths:

[15]:
plot.plot_spectrum(resolution=50)
Saved to `outputs/spectrum_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_40_1.png

2D day-night transition (transmission)

A simple 2D temperature with a linear transition between the dayside and the nightside can be set up using:

[16]:
temperature = dict(T_day=3300, T_night=500, T_deep=2500, beta=10, p_iso=1e4)

Since the model is not 1D anymore, we need to add latitudes and longitudes to the atmospheric grid. Let us also add more angular points in the grid of rays:

[17]:
input_atmosphere = p3.InputAtmosphere(grid=p3.Grid3D(n_latitudes = 49, n_longitudes = 64),
     temperature = temperature,
     max_pressure = 1e6, min_pressure = 1e-4,
     gas_mix_ratio = {"H2O":5.01187234e-04,
                      "CO2":4.4e-4,
                      "H2":"background"
                     },
     transparent_gases = "H2")

model2 = p3.Model(n_vertical = 100,
     planet=planet,
     star=star,
     orbit=orbit,
     observer=observer,
     input_atmosphere=input_atmosphere,
     transmission=p3.Transmission(rays=p3.Rays(n_radial=100, n_angular=40)),
     opacity=opacity,
     )

Then the model has to be recomputed from scratch:

[18]:
model2.build()
model2.run()

Of course, you can also visualize this spectrum using:

[19]:
plot2 = p3.Plot(model=model2, label="2D", interactive=True, # to show the picture (in addition to saving it)
                out_folder="outputs")
plot2.plot_spectrum()
Saved to `outputs/spectrum_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_49_1.png

You can check the 2D varations of the temperature using:

[20]:
plot2.t_maps(latitudes=["equator"])
Saved to `outputs/t_map_latitude_24_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_51_1.png

Compare models

If you have run multiple models (here, the simple 1D and 2D cases above), you can compare them with the following:

[21]:
comparison = p3.plot.Comparison([plot, plot2], interactive=True, out_folder="outputs")
# comparison.diff_spectra(mode='transmission')
comparison.latitudes=["equator"]
comparison.plot_tp()
Saved to `outputs/tp_comp_output.pdf`
_images/tutorial_pytmosph3r_54_1.png

GCM Transmission

To provide an input file to Pytmosph3R (here “doc/example.nc”), the DiagfiModel and HDF5Model can be used, for netCDF diagfi and HDF5 files, respectively. The input atmosphere can be modified (for example here we set the molecular abundances of \(H_2O\), \(CO_2\) and \(H_2\)):

[22]:
diagfi_file = (data_path / 'gcm' / 'K2-18b_diagfi.nc').as_posix()

input_atmosphere = p3.InputAtmosphere(gas_mix_ratio = {'H2O':5e-4, 'CO2':4.4e-4, "H2":"background"},
                                      transparent_gases = "H2", min_pressure=1e-4)

model = p3.DiagfiModel(n_vertical = 100,
     filename=diagfi_file,
     planet=planet,
     star=star,
     orbit=orbit,
     observer=observer,
     input_atmosphere=input_atmosphere,
     transmission=p3.Transmission(rays=p3.Rays(n_radial=100, n_angular=40)),
     opacity=p3.Opacity(rayleigh=True, cia=['H2', 'He'], wn_range=[1000, 20000]),
     )

model.build()
model.run()
be careful: ()
            String filters not specific enough, several corresponding files have been found.
            We will use the first one:
            /builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.SI.h5
            Other files are:
            ['/builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.TauREx.h5']

It is recommended to add a transparent background gas (using the parameter transparent_gases to InputAtmosphere), as has been done for \(H_2\) here.

As before, we can plot the spectrum:

[23]:
plot = p3.Plot(model=model, label="example", interactive=True, out_folder="outputs")
plot.plot_spectrum()
Saved to `outputs/spectrum_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_60_1.png

GCM Emission

We give here a full example of an emission spectrum and phase curve generated for a GCM simulation (netCDF format).

[24]:
Rp = 1.807 * 6.9911e7
g0 = 6.67384e-11 * 1.183 * 1.898e27 / Rp**2

input_atmosphere = p3.InputAtmosphere(gas_mix_ratio = {'H2O':5e-4, 'CO2':4.4e-4, "H2":"background"},
                                      transparent_gases = "H2", min_pressure=1e-4)

model = p3.DiagfiModel(n_vertical = 100, input_atmosphere=input_atmosphere,
       filename=diagfi_file,

       planet = p3.Planet(surface_gravity=g0, radius=Rp),
       star = p3.star.Star.fromSolar(temperature=6460.0, radius=1.458), # radius is in sun radii
       orbit=orbit,
       observer=p3.Observer(0, 0),

       # emission=p3.Emission(
       #     planet_to_star_flux_ratio=True, # if you want to normalize to stellar flux
       #     n_phases=20, # activates phase curve
       #     surface_resolution=500, # High resolution increases accuracy (and slowness!)
       # ),
       phasecurve=p3.Phasecurve(n_phases=20),
       opacity=p3.Opacity(rayleigh=True, cia=['H2', 'He'], wn_range=[1000, 20000]),
       )

model.build()
model.run()
be careful: ()
            String filters not specific enough, several corresponding files have been found.
            We will use the first one:
            /builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.SI.h5
            Other files are:
            ['/builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.TauREx.h5']

Plots include the phase curves shown for a set of wavelengths, for all wavelengths, and you can also choose to plot a spectrum at a specific phase:

[25]:
plot = p3.Plot(model=model, interactive=True,
               substellar_longitude=0, # longitude of the substellar point (to plot in orbital phases)
               out_folder="outputs")

plot.plot_spectrum(mode='phasecurve', phase=[0,90,180])

plot.plot_phasecurve(wl=[1,8,15])
plot.plot_2d_phasecurve()
Saved to `outputs/spectrum_phasecurve_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_65_1.png
Saved to `outputs/phasecurve_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_65_3.png
Saved to `outputs/phasecurves_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_65_5.png
[25]:
<matplotlib.collections.QuadMesh at 0x7fca55415390>

Gas & aerosols

In our previous examples, the molecular abundances have been set to a constant value. To use more sophisticated profiles, you can use a chemistry module:

[26]:
chemistry = p3.Parmentier2018Dissociation(gases=["H2O"])

Be sure to check the documentation on each chemistry module before using it.

Note

You can also write your own module. For that, try to make a copy of one of the existing chemistries, and change compute_vmr() to set your rules for setting a volume mixing ratio.

You can also use abundances from your input file. Since your variables may have names that do not correspond to what Pytmosph3R expects, Model has two dictionary parameters gas_dict and aerosol_dict that may be used to specify which name is associated with what gas. For example,

[27]:
gas_dict = {"H2O": "h2o_var","CO2":"co2_var"}

leads to the variable called h2o_var in the input file (typically a GCM diagfi netCDF file) to be associated with the volume mixing ratio of \(H_2O\), while co2_var will be attributed to \(CO_2\).

Note

The gas_mix_ratio defined in input_atmosphere override the values read from the input file in gas_dict. If a gas was not present in the input file, its value in gas_mix_ratio simply adds it to the composition of the atmosphere.

You can use the gas_units parameter to set the unit of gases (‘mmr’, ‘vmr’, ‘log_vmr’, …).

Note

If your data is MMR and if the input file does not have a ‘controle’ variable in which the molar mass of the gases is stored, you will need to set yourself, to convert the data from MMR to VMR. This is done by setting input_mu to the desired value.

Aerosols are handled via a dictionary similar to the gas one, except that aerosols have also other parameters, such as the effective radius, denoted by reff (see aerosols). See below an example:

[28]:
aerosols_dict = {"H2O":"h2o_ice","H2O_reff":"H2Oice_reff"}

Here h2o_ice is associated with the mass mixing ratio of \(H_2O\) (once again!), but H2Oice_reff to the effective radius of \(H_2O\). The rest of the parameters should be filled in too, and existing ones can be overridden in the InputAtmosphere:

[29]:
aerosols = {'H2O':{'reff':1e-5,'condensate_density':940}}

To create a layer of aerosols below a specific pressure, you could set the mass mixing ratio of the aerosol to a high value:

[30]:
aerosols = {'H2O':{"mmr":1e-1, # in kg/kg
                   "reff":1e-5, # (in m)
                   "condensate_density":940, # in kg/m^3
                   "p_min":1000, # in Pa
                  }}

We give below a full example to create a layer of aerosols:

[31]:
input_atmosphere = p3.InputAtmosphere(gas_mix_ratio={"H2": "background"},
                                      chemistry=p3.InterpolationChemistry(gases=["H2O", "CO2"],
                                                                          filename=str(
                                                                              data_path / "chemistry" / "Parmentier_Abundances-TiO.dat")),
                                      aerosols={'H2O': {"mmr": 1e-1,  # in kg/kg
                                                        "reff": 1e-5,  # (in m)
                                                        "condensate_density": 940,  # in kg/m^3
                                                        "p_min": 1000,  # in Pa
                                                        'optical_properties': 'h2o_ice',
                                                        }},
                                      transparent_gases="H2",
                                      min_pressure=1e-4)

model = p3.DiagfiModel(n_vertical = 100,
    filename=diagfi_file,
    gas_dict = {"H2O": "h2o_var","CO2":"co2_var"},
    aerosols_dict = {"H2O":"h2o_ice","H2O_reff":"H2Oice_reff"},
    planet=planet,
    star=star,
    orbit=orbit,
    observer=observer,
    input_atmosphere=input_atmosphere,
    transmission=p3.Transmission(store_transmittance = True, # to plot transmittance
        rays=p3.Rays(n_radial=100, n_angular=40)),
    opacity=p3.Opacity(rayleigh=True, cia=['H2', 'He'], wn_range=[1000, 20000]))

model.build()
model.run()
/builds/jleconte/pytmosph3r-public/pytmosph3r/util/util.py:218: UserWarning: Unable to determine the kind of log. `units` should be set to `log` or `ln`, provided: `None`.
  warnings.warn(f'Unable to determine the kind of log. `units` should be set to `log` or `ln`, provided: `{units}`.')
be careful: ()
            String filters not specific enough, several corresponding files have been found.
            We will use the first one:
            /builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.SI.h5
            Other files are:
            ['/builds/jleconte/pytmosph3r-public/data/corrk/H2O_R300_0.3-50mu.ktable.TauREx.h5']

You can check that this layer of aerosols is completely opaque by comparing the map of the aerosol to the transmittance:

[32]:
plot = p3.Plot(model=model, r_factor=0.1, # Rp = Rp/10 to enlarge the atmosphere
               interactive=True, out_folder="outputs")
plot.transmittance_map()
plot.a_maps(["H2O"], longitudes=["terminator"])
/builds/jleconte/pytmosph3r-public/pytmosph3r/plot/modelplot.py:789: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
  ax.set_xticklabels(["0°","45°","90°","135","180°","225°","270°","315°"], fontsize=9)
Saved to `outputs/transmittances/transmittance_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_82_2.png
Saved to `outputs/H2O/a_map_longitude_16.pdf`
_images/tutorial_pytmosph3r_82_4.png

Other possible plots

Other types of data can be plotted. For example the PT-profile at three points on the equator may be plotted with this command:

[33]:
plot.latitudes=["equator"]
plot.longitudes=["day", "terminator", "night"]
plot.plot_tps()
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
Saved to `outputs/tp_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_86_2.png

The temperature can be represented as a 2D colormap if a specific altitude is chosen (or latitude, or longitude).

[34]:
plot.t_maps(altitudes=["middle"]) #other possibilities: "top", "surface" or the layer index
plot.t_maps(latitudes=["equator"])
plot.t_maps(longitudes=["day", 3])
Saved to `outputs/t_map_altitude_50_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_88_1.png
Saved to `outputs/t_map_latitude_24_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_88_3.png
Saved to `outputs/t_map_longitude_32_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_88_5.png
Saved to `outputs/t_map_longitude_3_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_88_7.png

2D maps of the molecular distribution are also possible:

[35]:
plot.x_maps(["H2O", "CO2"], altitudes=["middle"])
plot.x_maps(["H2O", "CO2"], latitudes=["equator"])
plot.x_maps(["H2O", "CO2"], longitudes=["terminator"])
Saved to `outputs/H2O/H2O_map_altitude_50_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_90_1.png
Saved to `outputs/CO2/CO2_map_altitude_50_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_90_3.png
Saved to `outputs/H2O/H2O_map_latitude_24_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_90_5.png
Saved to `outputs/CO2/CO2_map_latitude_24_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_90_7.png
Saved to `outputs/H2O/H2O_map_longitude_16_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_90_9.png
Saved to `outputs/CO2/CO2_map_longitude_16_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_90_11.png

Note

For more information on all available plots, see Plot. If you encounter some problems, you can debug the library by running in debug mode (use p3.debug()).

Output files

The model can be saved using the HDF5 format to have maximum information. But to be able to visualize the data with softwares such as ncview or ParaView, you also have the option of writing a netCDF file.

netCDF

To visualize the atmopshere using paraview or ncview, you can save the model in a netCDF format using:

[36]:
p3.interface.write_netcdf("outputs/output_tutorial.nc", model)

You can check the header of the netCDF with ncdump:

[37]:
!ncdump -h "outputs/output_tutorial.nc"
/usr/bin/sh: 1: ncdump: not found

In paraview, you will be able to see something like this:

pytmosph3r_2d.png

HDF5

Writing an output HDF5 file

To save the model using the HDF5 format, we recommend to split the output in two parts. The first part is the model with its “input” parameters so that one can re-run Pytmosph3R using this file. This can be done by running the following piece of code preferably before running the model (but after building it with build()).

[38]:
p3.write_hdf5("outputs/output_tutorial.h5", model, "Model")

In case running the model fails, you will at least have the input model saved somewhere to investigate where the error might come from.

Once you have run the model (with run()), you can save its outputs in the “Output” group:

[39]:
p3.write_hdf5("outputs/output_tutorial.h5", model, "Output")

Following the division “Model” and “Output” is advised to be able to re-use the file for a new run or for plots.

Alternatively, you can also write the inputs and outputs using:

[40]:
p3.write_hdf5("outputs/output_tutorial.h5", model, "All")

Reading a HDF5 (pytmosph3r) file

The HDF5 output file can be read with Plot() to make various plots.

You can also reload a model, change its parameters and re-run it:

[41]:
# Plotting the hdf5 output
plot = p3.Plot("outputs/output_tutorial.h5", interactive=True, out_folder='outputs')
plot.plot_tp() # initial temperature profile in the simulation

# reloading the hdf5 output
f = p3.HDF5Input("outputs/output_tutorial.h5")
model = f.read('Model')
model.build()
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
Saved to `outputs/tp_north_day_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_112_2.png
/builds/jleconte/pytmosph3r-public/pytmosph3r/util/util.py:218: UserWarning: Unable to determine the kind of log. `units` should be set to `log` or `ln`, provided: `None`.
  warnings.warn(f'Unable to determine the kind of log. `units` should be set to `log` or `ln`, provided: `{units}`.')
[42]:
model.input_atmosphere.temperature = 2500 # overwrite temperature

model.build()
[43]:
# model.run()
plot = p3.Plot(model=model, interactive=True, out_folder='outputs')
plot.plot_tp() # new temperature profile
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
Saved to `outputs/tp_north_day_pytmosph3r.pdf`
_images/tutorial_pytmosph3r_114_2.png