├── .flake8
├── .gitignore
├── README.md
├── environment.yml
├── exercises
├── 01-molecule_spectra
│ ├── absorption.ipynb
│ └── absorption.py
├── 02-line_shape
│ ├── lineshape.ipynb
│ └── lineshape.py
├── 03-rtcalc
│ ├── rtcalc.ipynb
│ └── rtcalc.py
├── 04-jacobian
│ ├── jacobian.ipynb
│ └── jacobian.py
├── 05-inversion
│ ├── H2Orad.jpg
│ ├── input
│ │ ├── S_xa.xml
│ │ ├── measurement.xml
│ │ ├── x_apriori.xml
│ │ └── x_true.xml
│ ├── oem.ipynb
│ ├── oem.py
│ ├── oem_solution.ipynb
│ └── simulate_measurement.py
├── 06-non_lin_inversion
│ ├── Halo.jpg
│ ├── Temperature_retrieval.ipynb
│ ├── atmosphere
│ │ ├── atmospheres_true.xml
│ │ ├── aux1d_true.xml
│ │ └── aux2d_true.xml
│ ├── nonlin_oem.py
│ ├── observation
│ │ ├── SensorCharacteristics_118GHz.xml
│ │ ├── SensorCharacteristics_22GHz.xml
│ │ ├── SensorCharacteristics_50GHz.xml
│ │ ├── dropsonde.xml
│ │ ├── lat.xml
│ │ ├── y_obs_118GHz.xml
│ │ ├── y_obs_22GHz.xml
│ │ └── y_obs_50GHz.xml
│ ├── prepare_airborne_data.py
│ ├── simulate_airborne_measurement.py
│ └── temperature_retrieval.py
├── 07-olr
│ ├── input
│ │ ├── midlatitude-summer.xml
│ │ ├── midlatitude-winter.xml
│ │ ├── subarctic-summer.xml
│ │ ├── subarctic-winter.xml
│ │ └── tropical.xml
│ ├── olr.ipynb
│ ├── olr.py
│ └── olr_module.py
├── 08_heating_rates
│ ├── heating_rates.ipynb
│ ├── heating_rates_module.py
│ └── input
│ │ ├── midlatitude-summer.xml
│ │ ├── midlatitude-winter.xml
│ │ ├── subarctic-summer.xml
│ │ ├── subarctic-winter.xml
│ │ └── tropical.xml
└── 09-scattering
│ ├── input
│ ├── chevallierl91_all_extract.xml
│ └── pndfield_input.xml
│ ├── scattering.ipynb
│ └── scattering_module.py
├── script
├── AdvRaRe_script.bib
├── AdvRaRe_script.tex
├── AdvRaRe_script_print.tex
├── AdvRaRe_script_screen.tex
├── Makefile
├── figures
│ ├── Buehler_et_al_OLR_spectra.png
│ ├── Energy_levels.png
│ ├── Fig_moments_inertia.png
│ ├── Reduced_mass.png
│ ├── Transitions_vib_rot.png
│ ├── Vibration_parabol.png
│ ├── Vibration_parabol_2.png
│ ├── buehler_portrait_sketch.png
│ ├── interaction_types.png
│ ├── perturbedLayer_EmissionTransmission.png
│ ├── plotTypes_Jacobian.png
│ ├── rotating_mass.png
│ ├── schematic_energy_states.png
│ └── transition_types.png
└── j_abbr.bib
└── test
├── Makefile
└── README.md
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 88
3 | extend-ignore = E203
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/plots
2 | **/results
3 | .DS_Store
4 | .ipynb_checkpoints
5 | .vscode/*
6 | __pycache__
7 | script/AdvRaRe_script_print.aux
8 | script/AdvRaRe_script_print.fdb_latexmk
9 | script/AdvRaRe_script_print.fls
10 | script/AdvRaRe_script_print.log
11 | script/AdvRaRe_script_print.out
12 | script/AdvRaRe_script_print.pdf
13 | script/AdvRaRe_script_print.synctex.gz
14 | script/AdvRaRe_script_print.toc
15 | script/AdvRaRe_script_screen.aux
16 | script/AdvRaRe_script_screen.fdb_latexmk
17 | script/AdvRaRe_script_screen.fls
18 | script/AdvRaRe_script_screen.log
19 | script/AdvRaRe_script_screen.out
20 | script/AdvRaRe_script_screen.pdf
21 | script/AdvRaRe_script_screen.toc
22 | test/*.pdf
23 | *.png
24 | *.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Advanced Radiation and Remote Sensing
2 |
3 | A practical introduction to remote sensing using the
4 | _Atmospheric Radiative Transfer Simulator_ ([ARTS][arts]).
5 |
6 | The exercises make use of the ARTS Python API.
7 |
8 | Required ARTS version is release 2.6.14.
9 |
10 | ARTS requires the Miniforge3 Python environment.
11 | Installers can be downloaded from the [Miniforge Github project][conda] page.
12 | If you are not familiar with the installation procedure of Miniforge, [this page provides good instructions][robotology].
13 |
14 | After you have installed Miniforge, ARTS can be installed with the following command:
15 | ```
16 | mamba install -c rttools pyarts
17 | ```
18 |
19 | If you use Spyder, Visual Studio Code or any other IDE to run the Python scripts, make sure to select the correct interpreter path (`~/miniforge3/bin/python`) in your IDE.
20 |
21 | Note that ARTS is only available for Linux and macOS. If you are on Windows or have trouble with the setup, students attending the course at Universität Hamburg can use the computers in the pool rooms or the [VDI system of CEN][vdi-cen] as a fallback solution:
22 |
23 | After logging in to a Linux virtual machine, running this command will provide a Miniforge environment with pyarts preinstalled:
24 |
25 | ```
26 | source /data/share/lehre/unix/rtcourse/activate.sh
27 | ```
28 |
29 | [arts]: http://radiativetransfer.org/
30 | [vdi-cen]: https://www.cen.uni-hamburg.de/facilities/cen-it/vdi.html
31 | [conda]: https://github.com/conda-forge/miniforge#miniforge
32 | [robotology]: https://github.com/robotology/robotology-superbuild/blob/master/doc/install-miniforge.md
33 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: arts-lectures
2 | channels:
3 | - rttools
4 | - conda-forge
5 | - nodefaults
6 | dependencies:
7 | - ipympl
8 | - jupyterlab
9 | - pyarts=2.6.8
10 |
11 |
--------------------------------------------------------------------------------
/exercises/01-molecule_spectra/absorption.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "Manfred Brath, Oliver Lemke\n",
10 | "\n",
11 | "## Exercise 1: Molecule absorption spectra"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "%matplotlib widget\n",
21 | "\n",
22 | "import os\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "from absorption import tag2tex, calculate_absxsec, pyarts\n",
26 | "\n",
27 | "# make plot folder, if it is not existing\n",
28 | "os.makedirs(\"plots\", exist_ok=True)\n",
29 | "\n",
30 | "#speed of light \n",
31 | "c = pyarts.arts.constants.c # m/s"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "\n",
39 | "### 1. Absorption spectra in the microwave spectral range (rotational spectra). \n",
40 | "\n",
41 | "Calculate the molecule absorption spectra of \n",
42 | "\n",
43 | "* $\\mathrm{HCl}$\n",
44 | "* $\\mathrm{H_2O}$\n",
45 | "* $\\mathrm{O_3}$ \n",
46 | "\n",
47 | "for a temperature of 200 K and 300 K.\n",
48 | "\n",
49 | "* How does the rotational spectra change?\n",
50 | "* Can you explain the changes?\n"
51 | ]
52 | },
53 | {
54 | "cell_type": "code",
55 | "execution_count": null,
56 | "metadata": {},
57 | "outputs": [],
58 | "source": [
59 | "# Define parameters\n",
60 | "species = \"HCl\"\n",
61 | "temperature = 300 # K \n",
62 | "pressure = 101325 # Pa\n",
63 | "\n",
64 | "# Call ARTS to calculate absorption cross sections\n",
65 | "freq, abs_xsec = calculate_absxsec(species, pressure, temperature)"
66 | ]
67 | },
68 | {
69 | "cell_type": "code",
70 | "execution_count": null,
71 | "metadata": {},
72 | "outputs": [],
73 | "source": [
74 | "# Plot the results.\n",
75 | "\n",
76 | "fig, ax = plt.subplots()\n",
77 | "ax.plot(freq / 1e9, abs_xsec)\n",
78 | "ax.set_xlim(freq.min() / 1e9, freq.max() / 1e9)\n",
79 | "ax.set_ylim(bottom=0)\n",
80 | "ax.set_xlabel(\"Frequency [GHz]\")\n",
81 | "ax.set_ylabel(r\"Abs. cross section [$\\sf m^2$]\")\n",
82 | "ax.set_title(f\"{tag2tex(species)} p:{pressure/100} hPa T:{temperature:0.0f} K\")\n",
83 | "\n",
84 | "fig.savefig( # Save figure.\n",
85 | " f\"plots/plot_xsec_{species}_{pressure:.0f}Pa_{temperature:.0f}K.pdf\"\n",
86 | ")"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": [
93 | "\n",
94 | "### 2. Absorption spectra in the infrared spectral range (vibrational spectra). \n",
95 | "\n",
96 | "Calculate the molecule absorption spectra of \n",
97 | "\n",
98 | "* $\\mathrm{CO_2}$\n",
99 | "* $\\mathrm{H_2O}$\n",
100 | "* $\\mathrm{O_3}$\n",
101 | "* $\\mathrm{O_2}$\n",
102 | "* $\\mathrm{N_2}$\n",
103 | "\n",
104 | "for a temperature of 300 K.\n",
105 | "\n",
106 | "Adjust the frequency limits using the keywordargument *fmin* and *fmax* of `calculate_absxsec`. For plotting in the infrared range, it is common to use wavenumber in $\\left[\\text{cm}^{-1}\\right]$ instead of frequency. Copy the python cells from above and adjust them.\n",
107 | "\n",
108 | "* Can you explain the differences between $\\mathrm{CO_2}$, $\\mathrm{H_2O}$ and $\\mathrm{O_3}$ on one side and $\\mathrm{O_2}$ and $\\mathrm{N_2}$ on the other side?"
109 | ]
110 | },
111 | {
112 | "cell_type": "markdown",
113 | "metadata": {},
114 | "source": []
115 | }
116 | ],
117 | "metadata": {
118 | "kernelspec": {
119 | "display_name": "Python 3 (ipykernel)",
120 | "language": "python",
121 | "name": "python3"
122 | },
123 | "language_info": {
124 | "codemirror_mode": {
125 | "name": "ipython",
126 | "version": 3
127 | },
128 | "file_extension": ".py",
129 | "mimetype": "text/x-python",
130 | "name": "python",
131 | "nbconvert_exporter": "python",
132 | "pygments_lexer": "ipython3",
133 | "version": "3.12.5"
134 | },
135 | "varInspector": {
136 | "cols": {
137 | "lenName": 16,
138 | "lenType": 16,
139 | "lenVar": 40
140 | },
141 | "kernels_config": {
142 | "python": {
143 | "delete_cmd_postfix": "",
144 | "delete_cmd_prefix": "del ",
145 | "library": "var_list.py",
146 | "varRefreshCmd": "print(var_dic_list())"
147 | },
148 | "r": {
149 | "delete_cmd_postfix": ") ",
150 | "delete_cmd_prefix": "rm(",
151 | "library": "var_list.r",
152 | "varRefreshCmd": "cat(var_dic_list()) "
153 | }
154 | },
155 | "types_to_exclude": [
156 | "module",
157 | "function",
158 | "builtin_function_or_method",
159 | "instance",
160 | "_Feature"
161 | ],
162 | "window_display": false
163 | },
164 | "vscode": {
165 | "interpreter": {
166 | "hash": "6bd45fef6a38d15b43f43de43ba5066924911f80576952f97fb08adaede44831"
167 | }
168 | }
169 | },
170 | "nbformat": 4,
171 | "nbformat_minor": 4
172 | }
173 |
--------------------------------------------------------------------------------
/exercises/01-molecule_spectra/absorption.py:
--------------------------------------------------------------------------------
1 | # %% Import modules and define functions
2 | """Calculate and plot absorption cross sections."""
3 | import re
4 |
5 | import numpy as np
6 | import pyarts
7 |
8 |
9 | def tag2tex(tag):
10 | """Replace all numbers in a species tag with LaTeX subscripts."""
11 | return re.sub("([a-zA-Z]+)([0-9]+)", r"\1$_{\2}$", tag)
12 |
13 |
14 | def calculate_absxsec(
15 | species="N2O",
16 | pressure=800e2,
17 | temperature=300.0,
18 | fmin=10e9,
19 | fmax=2000e9,
20 | fnum=10_000,
21 | lineshape="VP",
22 | normalization="VVH",
23 | verbosity=0,
24 | vmr=0.01,
25 | lines_off=0
26 | ):
27 | """Calculate absorption cross sections.
28 |
29 | Parameters:
30 | species (str): Absorption species name.
31 | pressure (float): Atmospheric pressure [Pa].
32 | temperature (float): Atmospheric temperature [K].
33 | fmin (float): Minimum frequency [Hz].
34 | fmax (float): Maximum frequency [Hz].
35 | fnum (int): Number of frequency grid points.
36 | lineshape (str): Line shape model.
37 | Available options:
38 | DP - Doppler profile,
39 | LP - Lorentz profile,
40 | VP - Voigt profile,
41 | SDVP - Speed-dependent Voigt profile,
42 | HTP - Hartman-Tran profile.
43 | normalization (str): Line shape normalization factor.
44 | Available options:
45 | VVH - Van Vleck and Huber,
46 | VVW - Van Vleck and Weisskopf,
47 | RQ - Rosenkranz quadratic,
48 | None - No extra normalization.
49 | verbosity (int): Set ARTS verbosity (``0`` prevents all output).
50 | vmr (float): Volume mixing ratio. This is mainly important for the
51 | water vapor continua.
52 | lines_off (int): Switch off lines, if no contnua is selected,
53 | absorption will be zero.
54 |
55 | Returns:
56 | ndarray, ndarray: Frequency grid [Hz], Abs. cross sections [m^2]
57 | """
58 |
59 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
60 |
61 | # Create ARTS workspace and load default settings
62 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
63 | ws.water_p_eq_agendaSet()
64 | ws.PlanetSet(option="Earth")
65 |
66 | ws.verbositySetScreen(ws.verbosity, verbosity)
67 |
68 | # We do not want to calculate the Jacobian Matrix
69 | ws.jacobianOff()
70 |
71 | # Define absorption species
72 | ws.abs_speciesSet(species=[species])
73 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
74 | ws.abs_lines_per_speciesLineShapeType(option=lineshape)
75 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
76 | ws.abs_lines_per_speciesNormalization(option=normalization)
77 | ws.abs_lines_per_speciesTurnOffLineMixing()
78 | if lines_off:
79 | ws.abs_lines_per_speciesSetEmpty()
80 |
81 | # Create a frequency grid
82 | ws.VectorNLinSpace(ws.f_grid, fnum, fmin, fmax)
83 |
84 | # Throw away lines outside f_grid
85 | ws.abs_lines_per_speciesCompact()
86 |
87 | # Atmospheric settings
88 | ws.AtmosphereSet1D()
89 | ws.stokes_dim = 1
90 |
91 | # Setting the pressure, temperature and vmr
92 | ws.rtp_pressure = float(pressure) # [Pa]
93 | ws.rtp_temperature = float(temperature) # [K]
94 | ws.rtp_vmr = np.array([vmr]) # [VMR]
95 | ws.Touch(ws.rtp_nlte)
96 |
97 | # isotop
98 | ws.isotopologue_ratiosInitFromBuiltin()
99 |
100 | # Calculate absorption cross sections
101 | ws.lbl_checkedCalc()
102 | ws.propmat_clearsky_agenda_checked = 1
103 | ws.propmat_clearskyInit()
104 | ws.propmat_clearskyAddLines()
105 | ws.propmat_clearskyAddPredefined()
106 |
107 | # Convert abs coeff to cross sections on return
108 | number_density = pressure * vmr / (pyarts.arts.constants.k * temperature)
109 |
110 | return (
111 | ws.f_grid.value.value.copy(),
112 | ws.propmat_clearsky.value.data.value[0, 0, :, 0].copy() / number_density,
113 | )
114 |
115 |
116 | # %% Run module as script
117 | if __name__ == "__main__":
118 | import matplotlib.pyplot as plt
119 |
120 | pyarts.cat.download.retrieve(verbose=True, version='2.6.8')
121 |
122 | # Define parameters
123 | species = "N2O"
124 | pressure = 1000e2
125 | temperature = 300.0
126 | fmin = 1e12
127 | fmax = 1e14
128 | fnum = 10_000
129 |
130 | # Calculate absorption cross sections
131 | f_grid, absxsec = calculate_absxsec(
132 | species, pressure, temperature, fmin, fmax, fnum
133 | )
134 |
135 | # Plot absorption cross sections
136 | fig, ax = plt.subplots()
137 | ax.semilogy(f_grid, absxsec)
138 | ax.set_xlabel("Frequency [Hz]")
139 | ax.set_ylabel(r"Abs. cross section [$\sf m^2$]")
140 | plt.show()
141 |
--------------------------------------------------------------------------------
/exercises/02-line_shape/lineshape.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "\n",
10 | "Manfred Brath, Oliver Lemke\n",
11 | "\n",
12 | "## Exercise 2: line shape"
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": 1,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "%matplotlib widget\n",
22 | "\n",
23 | "import os\n",
24 | "import matplotlib.pyplot as plt\n",
25 | "import numpy as np\n",
26 | "from lineshape import tag2tex, calculate_absxsec, linewidth\n",
27 | "\n",
28 | "# make plot folder, if it is not existing\n",
29 | "os.makedirs(\"plots\", exist_ok=True)\n",
30 | "\n",
31 | "cache = None "
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "The `calculate_absxsec` function calculates absorption cross sections and uses several keyword arguments as inputs, among other lineshape and normalization, see also the function definition in `lineshape.py` or the contextual help. \n",
39 | "\n",
40 | "### 1) Absorption cross section and coefficient\n",
41 | "Choose the $183\\,\\text{GHz}$-line of water vapor and perform calculations over a restricted frequency range for a number of different pressures between $10^5\\,\\text{Pa}$ and $10^1\\,\\text{Pa}$. Use the keyword arguments *fmin* and *fmax* to adjust the frequency range. Keep the temperature constant.\n",
42 | "\n",
43 | "* How does the shape of the spectral lines change?\n",
44 | "\n",
45 | "By now we investigated absorption in terms of the absorption cross-section\n",
46 | "$\\sigma$. Another widely used unit is the absorption coeffiction\n",
47 | "$\\alpha$. It takes the number concentration $n$ of the absorber\n",
48 | "into account:\n",
49 | "$$ \\alpha=n\\cdot\\sigma $$\n",
50 | "\n",
51 | "* Use the ideal gas law to calculate the number concentration.\n",
52 | "* How does the absorption coefficient in the line centre change, if pressure is changed?"
53 | ]
54 | },
55 | {
56 | "cell_type": "code",
57 | "execution_count": 2,
58 | "metadata": {},
59 | "outputs": [],
60 | "source": [
61 | "# Define parameters\n",
62 | "species = \"H2O\"\n",
63 | "temperature = 300\n",
64 | "pressure = 101325\n",
65 | "\n",
66 | "# Call ARTS to calculate absorption cross sections\n",
67 | "freq, abs_xsec, cache = calculate_absxsec(species, pressure, temperature, ws=cache)"
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "metadata": {
74 | "pycharm": {
75 | "name": "#%%\n"
76 | }
77 | },
78 | "outputs": [],
79 | "source": [
80 | "# example plotting code\n",
81 | "\n",
82 | "fig, ax = plt.subplots()\n",
83 | "ax.plot(freq / 1e9, abs_xsec)\n",
84 | "ax.set_xlim(freq.min() / 1e9, freq.max() / 1e9)\n",
85 | "ax.set_ylim(bottom=0)\n",
86 | "ax.set_xlabel(\"Frequency [GHz]\")\n",
87 | "ax.set_ylabel(r\"Abs. cross section [$\\sf m^2$]\")\n",
88 | "ax.set_title(f\"{tag2tex(species)} p:{pressure/100} hPa T:{temperature:0.0f} K\")\n",
89 | "\n",
90 | "#uncomment to save the figure\n",
91 | "# fig.savefig( # Save figure.\n",
92 | "# f\"plots/plot_xsec_{species}_{pressure:.0f}Pa_{temperature:.0f}K.pdf\"\n",
93 | "# )"
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "# Define parameters\n",
103 | "species = \"H2O\"\n",
104 | "temperature = 300\n",
105 | "pressure = 10132.5\n",
106 | "\n",
107 | "# Call ARTS to calculate absorption cross sections\n",
108 | "freq, abs_xsec, cache = calculate_absxsec(species, pressure, temperature, ws=cache)"
109 | ]
110 | },
111 | {
112 | "cell_type": "code",
113 | "execution_count": null,
114 | "metadata": {},
115 | "outputs": [],
116 | "source": [
117 | "# example plotting code\n",
118 | "\n",
119 | "fig, ax = plt.subplots()\n",
120 | "ax.plot(freq / 1e9, abs_xsec*pressure)\n",
121 | "ax.set_xlim(freq.min() / 1e9, freq.max() / 1e9)\n",
122 | "ax.set_ylim(bottom=0)\n",
123 | "ax.set_xlabel(\"Frequency [GHz]\")\n",
124 | "ax.set_ylabel(r\"Abs. cross section [$\\sf m^2$]\")\n",
125 | "ax.set_title(f\"{tag2tex(species)} p:{pressure/100} hPa T:{temperature:0.0f} K\")\n",
126 | "\n",
127 | "#uncomment to save the figure\n",
128 | "# fig.savefig( # Save figure.\n",
129 | "# f\"plots/plot_xsec_{species}_{pressure:.0f}Pa_{temperature:.0f}K.pdf\"\n",
130 | "# )"
131 | ]
132 | },
133 | {
134 | "cell_type": "markdown",
135 | "metadata": {},
136 | "source": [
137 | "### 2) Linewidth\n",
138 | "The full-width at half maximum (FWHM) is a measure of the line width. Use the function `linewidth()`, which is imported from lineshape module, to calculate the FWHM for a given absorption spectrum.\n",
139 | "\n",
140 | "* Make a plot of this as a function of altitude in pressure coordinates for a microwave line and an infrared absorption line.\n",
141 | "* Hint: Use a low pressure when selecting a line."
142 | ]
143 | },
144 | {
145 | "cell_type": "markdown",
146 | "metadata": {},
147 | "source": []
148 | }
149 | ],
150 | "metadata": {
151 | "kernelspec": {
152 | "display_name": "Python 3",
153 | "language": "python",
154 | "name": "python3"
155 | },
156 | "language_info": {
157 | "codemirror_mode": {
158 | "name": "ipython",
159 | "version": 3
160 | },
161 | "file_extension": ".py",
162 | "mimetype": "text/x-python",
163 | "name": "python",
164 | "nbconvert_exporter": "python",
165 | "pygments_lexer": "ipython3",
166 | "version": "3.12.7"
167 | },
168 | "varInspector": {
169 | "cols": {
170 | "lenName": 16,
171 | "lenType": 16,
172 | "lenVar": 40
173 | },
174 | "kernels_config": {
175 | "python": {
176 | "delete_cmd_postfix": "",
177 | "delete_cmd_prefix": "del ",
178 | "library": "var_list.py",
179 | "varRefreshCmd": "print(var_dic_list())"
180 | },
181 | "r": {
182 | "delete_cmd_postfix": ") ",
183 | "delete_cmd_prefix": "rm(",
184 | "library": "var_list.r",
185 | "varRefreshCmd": "cat(var_dic_list()) "
186 | }
187 | },
188 | "types_to_exclude": [
189 | "module",
190 | "function",
191 | "builtin_function_or_method",
192 | "instance",
193 | "_Feature"
194 | ],
195 | "window_display": false
196 | }
197 | },
198 | "nbformat": 4,
199 | "nbformat_minor": 4
200 | }
201 |
--------------------------------------------------------------------------------
/exercises/02-line_shape/lineshape.py:
--------------------------------------------------------------------------------
1 | # %% Import modules and define functions
2 | """Calculate and plot absorption cross sections."""
3 | import re
4 |
5 | import numpy as np
6 | import pyarts
7 | import scipy as sp
8 |
9 |
10 | def tag2tex(tag):
11 | """Replace all numbers in a species tag with LaTeX subscripts."""
12 | return re.sub("([a-zA-Z]+)([0-9]+)", r"\1$_{\2}$", tag)
13 |
14 |
15 | def linewidth(f, a):
16 | """Calculate the full-width at half maximum (FWHM) of an absorption line.
17 |
18 | Parameters:
19 | f (ndarray): Frequency grid.
20 | a (ndarray): Line properties
21 | (e.g. absorption coefficients or cross-sections).
22 |
23 | Returns:
24 | float: Linewidth.
25 |
26 | Examples:
27 | >>> f = np.linspace(0, np.pi, 100)
28 | >>> a = np.sin(f)**2
29 | >>> linewidth(f, a)
30 | 1.571048056449009
31 | """
32 |
33 | idx = np.argmax(a)
34 |
35 | if idx < 3 or idx > len(a) - 3:
36 | raise RuntimeError(
37 | "Maximum is located too near at the edge.\n"
38 | + "Could not found any peak. \n"
39 | + "Please adjust the frequency range."
40 | )
41 |
42 | s = sp.interpolate.UnivariateSpline(f, a - np.max(a) / 2, s=0)
43 |
44 | zeros = s.roots()
45 | sidx = np.argsort((zeros - f[idx]) ** 2)
46 |
47 | if zeros.size == 2:
48 | logic = zeros[sidx] > f[idx]
49 |
50 | if np.sum(logic) == 1:
51 | fwhm = abs(np.diff(zeros[sidx])[0])
52 |
53 | else:
54 | print(
55 | "I only found one half maxima.\n"
56 | + "You should adjust the frequency range to have more reliable results.\n"
57 | )
58 |
59 | fwhm = abs(zeros[sidx[0]] - f[idx]) * 2
60 |
61 | elif zeros.size == 1:
62 | fwhm = abs(zeros[0] - f[idx]) * 2
63 |
64 | print(
65 | "I only found one half maxima.\n"
66 | + "You should adjust the frequency range to have more reliable results.\n"
67 | )
68 |
69 | elif zeros.size > 2:
70 | sidx = sidx[0:2]
71 |
72 | logic = zeros[sidx] > f[idx]
73 |
74 | print(
75 | "It seems, that there are more than one peak"
76 | + " within the frequency range.\n"
77 | + "I stick to the maximum peak.\n"
78 | + "But I would suggest to adjust the frequevncy range. \n"
79 | )
80 |
81 | if np.sum(logic) == 1:
82 | fwhm = abs(np.diff(zeros[sidx])[0])
83 |
84 | else:
85 | print(
86 | "I only found one half maxima.\n"
87 | + "You should adjust the frequency range to have more reliable results.\n"
88 | )
89 |
90 | fwhm = abs(zeros[sidx[0]] - f[idx]) * 2
91 |
92 | elif zeros.size == 0:
93 | raise RuntimeError(
94 | "Could not found any peak. :( \n"
95 | + "Probably, frequency range is too small.\n"
96 | )
97 |
98 | return fwhm
99 |
100 |
101 | def calculate_absxsec(
102 | species="N2O",
103 | pressure=800e2,
104 | temperature=300.0,
105 | fmin=10e9,
106 | fmax=2000e9,
107 | fnum=10_000,
108 | lineshape="VP",
109 | normalization="VVH",
110 | verbosity=0,
111 | ws=None,
112 | vmr=0.05,
113 | lines_off=0
114 | ):
115 | """Calculate absorption cross sections.
116 |
117 | Parameters:
118 | species (str): Absorption species name.
119 | pressure (float): Atmospheric pressure [Pa].
120 | temperature (float): Atmospheric temperature [K].
121 | fmin (float): Minimum frequency [Hz].
122 | fmax (float): Maximum frequency [Hz].
123 | fnum (int): Number of frequency grid points.
124 | lineshape (str): Line shape model.
125 | Available options:
126 | DP - Doppler profile,
127 | LP - Lorentz profile,
128 | VP - Voigt profile,
129 | SDVP - Speed-dependent Voigt profile,
130 | HTP - Hartman-Tran profile.
131 | normalization (str): Line shape normalization factor.
132 | Available options:
133 | VVH - Van Vleck and Huber,
134 | VVW - Van Vleck and Weisskopf,
135 | RQ - Rosenkranz quadratic,
136 | None - No extra normalization.
137 | verbosity (int): Set ARTS verbosity (``0`` prevents all output).
138 | vmr (float): Volume mixing ratio. This is mainly important for the
139 | water vapor continua.
140 | lines_off (int): Switch off lines, if no contnua is selected,
141 | absorption will be zero.
142 |
143 | Returns:
144 | ndarray, ndarray: Frequency grid [Hz], Abs. cross sections [m^2]
145 | """
146 | # Create ARTS workspace and load default settings
147 | reload = False
148 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
149 |
150 | if ws is not None:
151 | # check if species fits to cached species
152 | temp = str(ws.abs_species.value[0][0])
153 | species_cache = temp.split("-")[0]
154 |
155 | if species != species_cache:
156 | print(
157 | f"Cached species:{species_cache} \n"
158 | f"Desired species:{species} \n"
159 | "As the chached and the desired species are different,\n"
160 | "I have to read in the catalog..."
161 | )
162 | reload = True
163 |
164 | if ws is None or reload:
165 | ws = pyarts.workspace.Workspace(verbosity=0)
166 | ws.water_p_eq_agendaSet()
167 | ws.PlanetSet(option="Earth")
168 |
169 | ws.verbositySetScreen(ws.verbosity, verbosity)
170 |
171 | # We do not want to calculate the Jacobian Matrix
172 | ws.jacobianOff()
173 |
174 | # Define absorption species
175 | ws.abs_speciesSet(species=[species])
176 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
177 |
178 | ws.abs_lines_per_speciesLineShapeType(option=lineshape)
179 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
180 | ws.abs_lines_per_speciesNormalization(option=normalization)
181 | ws.abs_lines_per_speciesTurnOffLineMixing()
182 | if lines_off:
183 | ws.abs_lines_per_speciesSetEmpty()
184 |
185 | # Create a frequency grid
186 | ws.VectorNLinSpace(ws.f_grid, fnum, fmin, fmax)
187 |
188 | # Throw away lines outside f_grid
189 | ws.abs_lines_per_speciesCompact()
190 |
191 | # Atmospheric settings
192 | ws.AtmosphereSet1D()
193 | ws.stokes_dim = 1
194 |
195 | # Setting the pressure, temperature and vmr
196 | ws.rtp_pressure = float(pressure) # [Pa]
197 | ws.rtp_temperature = float(temperature) # [K]
198 | ws.rtp_vmr = np.array([vmr]) # [VMR]
199 | ws.Touch(ws.rtp_nlte)
200 |
201 | # isotop
202 | ws.isotopologue_ratiosInitFromBuiltin()
203 |
204 | # Calculate absorption cross sections
205 | ws.lbl_checkedCalc()
206 | ws.propmat_clearsky_agenda_checked = 1
207 | ws.propmat_clearskyInit()
208 | ws.propmat_clearskyAddLines()
209 | ws.propmat_clearskyAddPredefined()
210 |
211 | # Convert abs coeff to cross sections on return
212 | number_density = pressure * vmr / (pyarts.arts.constants.k * temperature)
213 |
214 | return (
215 | ws.f_grid.value.value.copy(),
216 | ws.propmat_clearsky.value.data.value[0, 0, :, 0].copy() / number_density,
217 | ws,
218 | )
219 |
220 |
221 | # %% Run module as script
222 | if __name__ == "__main__":
223 | import matplotlib.pyplot as plt
224 |
225 | species = "H2O"
226 | temperature = 300 # [K]
227 | pressure = 101325 # [Pa]
228 | cache = None # cache for ARTS workspace
229 |
230 | # Call ARTS to calculate absorption cross sections
231 | freq, abs_xsec, cache = calculate_absxsec(species, pressure, temperature, fnum=1000, ws=cache)
232 |
233 | fig, ax = plt.subplots()
234 | ax.plot(freq, abs_xsec)
235 | ax.set_ylim(bottom=0)
236 | ax.set_xlabel("Frequency [GHz]")
237 | ax.set_ylabel(r"Abs. cross section [$\sf m^2$]")
238 | plt.show()
239 |
240 | # recall ARTS to calculate absorption cross sections at another pressure
241 | species = "H2O"
242 | temperature = 300 # [K]
243 | pressure = 10132.5 # [Pa]
244 |
245 | freq, abs_xsec, cache = calculate_absxsec(species, pressure, temperature, fnum=1000, ws=cache)
246 |
247 | fig, ax = plt.subplots()
248 | ax.plot(freq, abs_xsec)
249 | ax.set_ylim(bottom=0)
250 | ax.set_xlabel("Frequency [GHz]")
251 | ax.set_ylabel(r"Abs. cross section [$\sf m^2$]")
252 | plt.show()
253 |
254 | # recall ARTS to calculate absorption cross sections for another species
255 | species = "O3"
256 | temperature = 300 # [K]
257 | pressure = 10132.5 # [Pa]
258 |
259 | freq, abs_xsec, cache = calculate_absxsec(species, pressure, temperature, fnum=1000, ws=cache)
260 |
261 | fig, ax = plt.subplots()
262 | ax.plot(freq, abs_xsec)
263 | ax.set_ylim(bottom=0)
264 | ax.set_xlabel("Frequency [GHz]")
265 | ax.set_ylabel(r"Abs. cross section [$\sf m^2$]")
266 | plt.show()
267 |
268 |
269 |
--------------------------------------------------------------------------------
/exercises/03-rtcalc/rtcalc.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "Manfred Brath, Oliver Lemke\n",
10 | "\n",
11 | "## Exercise 3: Atmospheric Brightness Temperature Spectra"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "%matplotlib widget\n",
21 | "\n",
22 | "import os\n",
23 | "import matplotlib.pyplot as plt\n",
24 | "import numpy as np\n",
25 | "from rtcalc import run_arts, tags2tex\n",
26 | "\n",
27 | "# make plot folder, if it is not existing\n",
28 | "os.makedirs(\"plots\", exist_ok=True)"
29 | ]
30 | },
31 | {
32 | "cell_type": "markdown",
33 | "metadata": {},
34 | "source": [
35 | "### 1)\n",
36 | "The function `run_arts` simulates a brightness temperature and a atmospheric \n",
37 | "zenith opacity spectrum in the microwave spectral range for a midlatitude-summer \n",
38 | "atmosphere over a smooth and wet land surface for a desired height and looking \n",
39 | "direction.\n",
40 | "\n",
41 | "\n",
42 | "Run the function `run_arts` with the given values for height and direction. \n",
43 | "Ignore the brightness temperature for now and consider the zenith opacity spectrum \n",
44 | "to answer the following questions:\n",
45 | "\n",
46 | "* The spectrum includes four spectral lines. To which species do these lines \n",
47 | "belong? Play around with different absorption species.\n",
48 | "* We speak of window regions where the zenith opacity is below 1. Where are they?"
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": null,
54 | "metadata": {},
55 | "outputs": [],
56 | "source": [
57 | "# Parameters\n",
58 | "species = [\"N2\", \"O2\", \"H2O\"]\n",
59 | "height = 0.0\n",
60 | "zenith_angle = 0.0\n",
61 | "\n",
62 | "freq, bt, od = run_arts(species, zenith_angle, height)"
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": null,
68 | "metadata": {},
69 | "outputs": [],
70 | "source": [
71 | "HIGHLIGHT_FREQS = (22.3, 60.0, 118.8, 183.0)\n",
72 | "\n",
73 | "# Plot the zenith opacity with logarithmic scale on y axis\n",
74 | "fig, ax = plt.subplots()\n",
75 | "ax.semilogy(freq / 1e9, od)\n",
76 | "ax.axhline(1, linewidth=0.8, color=\"#b0b0b0\", zorder=0)\n",
77 | "ax.grid(True, axis=\"x\")\n",
78 | "ax.set_xticks(HIGHLIGHT_FREQS)\n",
79 | "ax.set_xlim(freq.min() / 1e9, freq.max() / 1e9)\n",
80 | "ax.set_xlabel(\"Frequency [GHz]\")\n",
81 | "ax.set_ylabel(\"Zenith opacity\")\n",
82 | "ax.set_title(f\"{', '.join(tags2tex(species))}\")\n",
83 | "fig.savefig(f\"plots/opacity_{'+'.join(species)}.pdf\")"
84 | ]
85 | },
86 | {
87 | "cell_type": "markdown",
88 | "metadata": {},
89 | "source": [
90 | "### 2)\n",
91 | "Brightness temperature is a unit for intensity. It is the temperature of a \n",
92 | "blackbody that emits the same amount of intensity. Mathematically, the \n",
93 | "transformation between intensity in SI units and intensity in brightness \n",
94 | "temperature is done with the Planck formula. ARTS is capable to perform \n",
95 | "simulation in units of brightness temperature. Uncomment the code part for \n",
96 | "the second task. Investigate the brightness temperature spectra for \n",
97 | "different hypothetical sensors:\n",
98 | "\n",
99 | "* A ground-based sensor looking in the zenith direction.\n",
100 | "* A sensor on an airplane ($z=10\\,\\text{km}$) looking in the zenith direction.\n",
101 | "\n",
102 | "Consider both opacity and brightness temperatures to answer the following \n",
103 | "questions:\n",
104 | "\n",
105 | "* In plot (a), why do the lines near $60\\,\\text{GHz}$ and near $180\\,\\text{GHz}$ \n",
106 | "appear flat on top? \n",
107 | "* In plot (b), why is the line at $180\\,\\text{GHz}$ smaller than before? \n",
108 | "* Describe the difference between plots (a) and (b). What happens to the \n",
109 | "lines, what happens to the background? Can you explain what you\n",
110 | "see? "
111 | ]
112 | },
113 | {
114 | "cell_type": "code",
115 | "execution_count": null,
116 | "metadata": {},
117 | "outputs": [],
118 | "source": [
119 | "# Parameters\n",
120 | "species = [\"N2\", \"O2\", \"H2O\"]\n",
121 | "height = 0.0\n",
122 | "zenith_angle = 0.0\n",
123 | "\n",
124 | "freq, bt, od = run_arts(species, zenith_angle, height)"
125 | ]
126 | },
127 | {
128 | "cell_type": "code",
129 | "execution_count": null,
130 | "metadata": {},
131 | "outputs": [],
132 | "source": [
133 | "HIGHLIGHT_FREQS = (22.3, 60.0, 118.8, 183.0)\n",
134 | "\n",
135 | "# Plot the zenith opacity with logarithmic scale on y axis\n",
136 | "fig, ax = plt.subplots()\n",
137 | "ax.semilogy(freq / 1e9, od)\n",
138 | "ax.axhline(1, linewidth=0.8, color=\"#b0b0b0\", zorder=0)\n",
139 | "ax.grid(True, axis=\"x\")\n",
140 | "ax.set_xticks(HIGHLIGHT_FREQS)\n",
141 | "ax.set_xlim(freq.min() / 1e9, freq.max() / 1e9)\n",
142 | "ax.set_xlabel(\"Frequency [GHz]\")\n",
143 | "ax.set_ylabel(\"Zenith opacity\")\n",
144 | "ax.set_title(f\"{', '.join(tags2tex(species))}\")\n",
145 | "fig.savefig(f\"plots/opacity_{'+'.join(species)}.pdf\")\n",
146 | "\n",
147 | "# Plot the brightness temperature\n",
148 | "fig, ax = plt.subplots()\n",
149 | "ax.plot(freq / 1e9, bt)\n",
150 | "ax.grid(True)\n",
151 | "ax.set_xticks(HIGHLIGHT_FREQS)\n",
152 | "ax.set_xlim(freq.min() / 1e9, freq.max() / 1e9)\n",
153 | "ax.set_xlabel(\"Frequency [GHz]\")\n",
154 | "ax.set_ylabel(\"Brightness temperature [K]\")\n",
155 | "ax.set_title(f\"{', '.join(tags2tex(species))}, {height / 1e3} km, {zenith_angle}°\")\n",
156 | "fig.savefig(\n",
157 | " f\"plots/brightness_temperature_{'+'.join(species)}_{height / 1e3:.0f}km_{zenith_angle:.0f}deg.pdf\"\n",
158 | ")"
159 | ]
160 | },
161 | {
162 | "cell_type": "markdown",
163 | "metadata": {},
164 | "source": [
165 | "### 3)\n",
166 | "Make the same calculation as in task 2 for a satellite sensor ($z=800\\,\\text{km}$) looking \n",
167 | "nadir (straight down).\n",
168 | "\n",
169 | "Answer following questions:\n",
170 | "\n",
171 | "* Explain the brightness temperature simulated in the window regions.\n",
172 | "* Why does the line at $22\\,\\text{GHz}$ look different from the others?\n",
173 | "* Investigate the the $\\text{O}_{2}$ line at $120\\,\\text{GHz}$. Perform an ARTS simulation\n",
174 | "focused around that frequency. Why does the shape close to the center of the $\\text{O}_{2}$ \n",
175 | "line at $120\\,\\text{GHz}$ looks so differently compared to the $183\\,\\text{GHz}$. "
176 | ]
177 | },
178 | {
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": []
182 | }
183 | ],
184 | "metadata": {
185 | "kernelspec": {
186 | "display_name": "Python 3 (ipykernel)",
187 | "language": "python",
188 | "name": "python3"
189 | },
190 | "language_info": {
191 | "codemirror_mode": {
192 | "name": "ipython",
193 | "version": 3
194 | },
195 | "file_extension": ".py",
196 | "mimetype": "text/x-python",
197 | "name": "python",
198 | "nbconvert_exporter": "python",
199 | "pygments_lexer": "ipython3",
200 | "version": "3.12.5"
201 | },
202 | "vscode": {
203 | "interpreter": {
204 | "hash": "6bd45fef6a38d15b43f43de43ba5066924911f80576952f97fb08adaede44831"
205 | }
206 | }
207 | },
208 | "nbformat": 4,
209 | "nbformat_minor": 4
210 | }
211 |
--------------------------------------------------------------------------------
/exercises/03-rtcalc/rtcalc.py:
--------------------------------------------------------------------------------
1 | # %% Import modules and define functions
2 | """Calculate and plot zenith opacity and brightness temperatures. """
3 | import re
4 | import numpy as np
5 | import pyarts.workspace
6 |
7 |
8 | def tags2tex(tags):
9 | """Replace all numbers in every species tag with LaTeX subscripts."""
10 | return [re.sub("([a-zA-Z]+)([0-9]+)", r"\1$_{\2}$", tag) for tag in tags]
11 |
12 |
13 | def run_arts(
14 | species,
15 | zenith_angle=0.0,
16 | height=0.0,
17 | fmin=10e9,
18 | fmax=250e9,
19 | fnum=1_000,
20 | verbosity=0
21 | ):
22 | """Perform a radiative transfer simulation.
23 |
24 | Parameters:
25 | species (list[str]): List of species tags.
26 | zenith_angle (float): Viewing angle [deg].
27 | height (float): Sensor height [m].
28 | fmin (float): Minimum frequency [Hz].
29 | fmax (float): Maximum frequency [Hz].
30 | fnum (int): Number of frequency grid points.
31 |
32 | Returns:
33 | ndarray, ndarray, ndarray:
34 | Frequency grid [Hz], Brightness temperature [K], Optical depth [1]
35 | """
36 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
37 |
38 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
39 |
40 |
41 | ws.water_p_eq_agendaSet()
42 | ws.PlanetSet(option="Earth")
43 | ws.iy_main_agendaSet(option="Emission")
44 | ws.ppath_agendaSet(option="FollowSensorLosPath")
45 | ws.ppath_step_agendaSet(option="GeometricPath")
46 | ws.iy_space_agendaSet(option="CosmicBackground")
47 | ws.iy_surface_agendaSet(option="UseSurfaceRtprop")
48 |
49 | ws.verbositySetScreen(ws.verbosity, verbosity)
50 |
51 | # Number of Stokes components to be computed
52 | ws.IndexSet(ws.stokes_dim, 1)
53 |
54 | # No jacobian calculation
55 | ws.jacobianOff()
56 |
57 | # Clearsky = No scattering
58 | ws.cloudboxOff()
59 |
60 | # A pressure grid rougly matching 0 to 80 km, in steps of 2 km.
61 | ws.VectorNLogSpace(ws.p_grid, 100, 1013e2, 10.0)
62 |
63 | ws.abs_speciesSet(species=species)
64 |
65 | # Read a line file and a matching small frequency grid
66 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
67 |
68 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
69 | ws.abs_lines_per_speciesTurnOffLineMixing()
70 |
71 | # Create a frequency grid
72 | ws.VectorNLinSpace(ws.f_grid, int(fnum), float(fmin), float(fmax))
73 |
74 | # Throw away lines outside f_grid
75 | ws.abs_lines_per_speciesCompact()
76 |
77 | # Atmospheric scenario
78 | ws.AtmRawRead(basename="planets/Earth/Fascod/midlatitude-summer/midlatitude-summer")
79 |
80 | # Non reflecting surface
81 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.1)
82 | ws.surface_rtprop_agendaSet(option="Specular_NoPol_ReflFix_SurfTFromt_surface")
83 |
84 | # No sensor properties
85 | ws.sensorOff()
86 |
87 | # We select here to use Planck brightness temperatures
88 | ws.StringSet(ws.iy_unit, "PlanckBT")
89 |
90 | # Extract optical depth as auxiliary variables
91 | ws.ArrayOfStringSet(ws.iy_aux_vars, ["Optical depth"])
92 |
93 | # Atmosphere and surface
94 | ws.AtmosphereSet1D()
95 | ws.AtmFieldsCalc()
96 | ws.Extract(ws.z_surface, ws.z_field, 0)
97 | ws.Extract(ws.t_surface, ws.t_field, 0)
98 |
99 | # Definition of sensor position and line of sight (LOS)
100 | ws.MatrixSet(ws.sensor_pos, np.array([[height]]))
101 | ws.MatrixSet(ws.sensor_los, np.array([[zenith_angle]]))
102 |
103 | # Perform RT calculations
104 | ws.propmat_clearsky_agendaAuto()
105 | ws.lbl_checkedCalc()
106 | ws.propmat_clearsky_agenda_checkedCalc()
107 | ws.atmfields_checkedCalc()
108 | ws.atmgeom_checkedCalc()
109 | ws.cloudbox_checkedCalc()
110 | ws.sensor_checkedCalc()
111 | ws.yCalc()
112 |
113 | return (
114 | ws.f_grid.value[:].copy(),
115 | ws.y.value[:].copy(),
116 | ws.y_aux.value[0][:].copy(),
117 | )
118 |
119 |
120 | # %% Run module as script
121 | if __name__ == "__main__":
122 | import matplotlib.pyplot as plt
123 |
124 | # pyarts.cat.download.retrieve(verbose=True)
125 |
126 | species = ["N2", "O2", "H2O"]
127 | height = 0.0 # m
128 | zenith_angle = 0.0 # deg
129 |
130 | # Run the radiative transfer simulation to get the frequency grid,
131 | # brightness temperature and optical depth
132 | freq, bt, od = run_arts(species, zenith_angle, height)
133 |
134 | # Plot the zenith opacity with logarithmic scale on y axis
135 | fig, ax = plt.subplots()
136 | ax.semilogy(freq, od)
137 | ax.set_xlabel("Frequency [Hz]")
138 | ax.set_ylabel("Zenith opacity")
139 |
140 | # Plot the brightness temperature
141 | fig, ax = plt.subplots()
142 | ax.plot(freq, bt)
143 | ax.set_xlabel("Frequency [GHz]")
144 | ax.set_ylabel("Brightness temperature [K]")
145 |
146 | plt.show()
147 |
--------------------------------------------------------------------------------
/exercises/04-jacobian/jacobian.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "\n",
10 | "Manfred Brath, Oliver Lemke\n",
11 | "\n",
12 | "## Exercise 4: Jacobian and opacity rule"
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": null,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "%matplotlib widget\n",
22 | "\n",
23 | "import os\n",
24 | "import matplotlib.pyplot as plt\n",
25 | "import numpy as np\n",
26 | "from pyarts import xml\n",
27 | "from jacobian import (calc_jacobians, plot_brightness_temperature,\n",
28 | " plot_jacobian, plot_opacity, plot_opacity_profile,\n",
29 | " argclosest)\n",
30 | "\n",
31 | "os.makedirs(\"plots\", exist_ok=True)"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "### 1)\n",
39 | "Run the next cells to calculate the brightness temperature spectrum in \n",
40 | "nadir direction and the zenith opacity around the $183\\,\\text{GHz}$ line \n",
41 | "of water vapor for a midlatitude summer atmosphere. Answer following \n",
42 | "question:\n",
43 | "\n",
44 | "* Are there window regions?\n",
45 | "\n",
46 | "The atmospheric temperature profile for the calculation was:\n",
47 | "\n",
48 | "|**Pressure** $\\left[\\text{hPa}\\right]$|**Temperature** $\\left[\\text{K}\\right]$ | **Altitude** $\\left[\\text{km}\\right]$\n",
49 | "|--- |--- |---\n",
50 | "|1013.0 | 294.2 | 0\n",
51 | "|902.0 | 289.7 | 1\n",
52 | "|802.0 | 285.2 | 2\n",
53 | "|710.0 | 279.2 | 3\n",
54 | "|628.0 | 273.2 | 4\n",
55 | "|554.0 | 267.2 | 5\n",
56 | "|487.0 | 261.2 | 6\n",
57 | "|426.0 | 254.7 | 7\n",
58 | "|372.0 | 248.2 | 8\n",
59 | "|324.0 | 241.7 | 9\n",
60 | "|281.0 | 235.3 | 10\n",
61 | "|243.0 | 228.8 | 11\n",
62 | "|209.0 | 222.3 | 12\n",
63 | "|179.0 | 215.8 | 13\n",
64 | "|153.0 | 215.7 | 14\n",
65 | "|130.0 | 215.7 | 15\n",
66 | "|111.0 | 215.7 | 16\n",
67 | "|95.0 | 215.7 | 17\n",
68 | "|81.2 | 216.8 | 18\n",
69 | "|69.5 | 217.9 | 19\n",
70 | "|59.5 | 219.2 | 20\n",
71 | "\n",
72 | "Consider the table and answer following questions:\n",
73 | "\n",
74 | "* From which altitude does the radiation at the peak of the line ($\\approx183\\,\\text{GHz}$) \n",
75 | "originate? \n",
76 | "\n",
77 | "* From which altitude does the radiation at the wing ($150\\,\\text{GHz}$) originate? \n"
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": null,
83 | "metadata": {},
84 | "outputs": [],
85 | "source": [
86 | "# Calculate Jacobians (ARTS)\n",
87 | "jacobian_quantity = \"H2O\"\n",
88 | "calc_jacobians(jacobian_quantity=jacobian_quantity)\n",
89 | "\n",
90 | "# read in everything\n",
91 | "freq = np.array(xml.load(\"results/f_grid.xml\"))\n",
92 | "tau = np.array(xml.load(\"results/optical_thickness.xml\"))\n",
93 | "bt = np.array(xml.load(\"results/y.xml\"))\n",
94 | "jac = np.array(xml.load(\"results/jacobian.xml\"))\n",
95 | "alt = np.array(xml.load(\"results/z_field.xml\")).ravel()\n",
96 | "jac /= np.gradient(alt / 1000) # normalize by layer thickness in km"
97 | ]
98 | },
99 | {
100 | "cell_type": "code",
101 | "execution_count": null,
102 | "metadata": {},
103 | "outputs": [],
104 | "source": [
105 | "# select frequency\n",
106 | "highlight_frequency = None # Hz\n",
107 | "\n",
108 | "if highlight_frequency is None:\n",
109 | " fig, (ax0, ax1) = plt.subplots(ncols=2)\n",
110 | " plot_brightness_temperature(freq, bt, ax=ax0)\n",
111 | " plot_opacity(freq, tau, ax=ax1)\n",
112 | " freq_ind = None\n",
113 | "else:\n",
114 | " fig, ((ax0, ax1), (ax2, ax3)) = plt.subplots(2, 2)\n",
115 | " plot_brightness_temperature(freq, bt, where=highlight_frequency, ax=ax0)\n",
116 | " plot_opacity(freq, tau, where=highlight_frequency, ax=ax1)\n",
117 | " freq_ind = argclosest(freq, highlight_frequency)\n",
118 | " plot_jacobian(\n",
119 | " alt, jac[freq_ind, :], jacobian_quantity=jacobian_quantity, ax=ax2\n",
120 | " )\n",
121 | " plot_opacity_profile(alt, tau[:, freq_ind], ax=ax3)\n",
122 | "\n",
123 | "fig.tight_layout()\n",
124 | "fig.savefig(f\"plots/jacobians-{freq_ind}.pdf\")"
125 | ]
126 | },
127 | {
128 | "cell_type": "markdown",
129 | "metadata": {},
130 | "source": [
131 | "### 2)\n",
132 | "Change the variable `highlight_frequency` from `None` to any desired \n",
133 | "frequency in $[\\text{Hz}]$ within the range of the brightness temperature spectrum \n",
134 | "of task 1 and rerun previous cell. This will calculate the water vapor Jacobian and \n",
135 | "the opacity $\\tau$ between the top of the atmosphere $z_{TOA}$ and altitude $z$ for the \n",
136 | "selected frequency. Additionally, a circle marks the selected frequency in the plot \n",
137 | "of the brightness temperature spectrum and in the plot of the zenith opacity. \n",
138 | "\n",
139 | "Write down the altitude of the Jacobian peak and the altitude where the \n",
140 | "opacity reaches 1 for some different frequencies and answer following \n",
141 | "questions:\n",
142 | "\n",
143 | "* Why are the altitude where the opacity reaches 1 and the altitude of \n",
144 | "the Jacobian peak not exactly the same?\n",
145 | "* Why are the Jacobians sometimes positive and sometimes negative?"
146 | ]
147 | },
148 | {
149 | "cell_type": "markdown",
150 | "metadata": {},
151 | "source": []
152 | }
153 | ],
154 | "metadata": {
155 | "kernelspec": {
156 | "display_name": "Python 3 (ipykernel)",
157 | "language": "python",
158 | "name": "python3"
159 | },
160 | "language_info": {
161 | "codemirror_mode": {
162 | "name": "ipython",
163 | "version": 3
164 | },
165 | "file_extension": ".py",
166 | "mimetype": "text/x-python",
167 | "name": "python",
168 | "nbconvert_exporter": "python",
169 | "pygments_lexer": "ipython3",
170 | "version": "3.12.5"
171 | },
172 | "vscode": {
173 | "interpreter": {
174 | "hash": "6bd45fef6a38d15b43f43de43ba5066924911f80576952f97fb08adaede44831"
175 | }
176 | }
177 | },
178 | "nbformat": 4,
179 | "nbformat_minor": 4
180 | }
181 |
--------------------------------------------------------------------------------
/exercises/04-jacobian/jacobian.py:
--------------------------------------------------------------------------------
1 | # %% Import modules and define functions
2 | """Calculate and plot clear-sky Jacobians."""
3 | import re
4 | from os import makedirs
5 |
6 | import matplotlib.pyplot as plt
7 | import numpy as np
8 | import pyarts
9 | from matplotlib.transforms import blended_transform_factory
10 |
11 |
12 | def argclosest(array, value):
13 | """Returns the index in ``array`` which is closest to ``value``."""
14 | return np.abs(array - value).argmin()
15 |
16 |
17 | def tag2tex(tag):
18 | """Replace all numbers in a species tag with LaTeX subscripts."""
19 | return re.sub("([a-zA-Z]+)([0-9]+)", r"\1$_{\2}$", tag)
20 |
21 |
22 | def plot_brightness_temperature(frequency, y, where=None, ax=None):
23 | if ax is None:
24 | ax = plt.gca()
25 |
26 | ax.plot(frequency / 1e9, y)
27 | ax.set_xlim(frequency.min() / 1e9, frequency.max() / 1e9)
28 | ax.set_xlabel("Frequency [GHz]")
29 | ax.set_ylabel(r"$T\mathrm{_B}$ [K]")
30 |
31 | if where is not None:
32 | freq_ind = argclosest(frequency, where)
33 | (l,) = ax.plot(
34 | frequency[freq_ind] / 1e9, y[freq_ind], marker="o", color="tab:red"
35 | )
36 | ax.text(
37 | 0.05,
38 | 0.9,
39 | f"{frequency[freq_ind]/1e9:.2f} GHz",
40 | size="small",
41 | color=l.get_color(),
42 | transform=ax.transAxes,
43 | )
44 |
45 |
46 | def plot_opacity(frequency, opacity, where=None, ax=None):
47 | if ax is None:
48 | ax = plt.gca()
49 |
50 | ax.semilogy(frequency / 1e9, opacity[-1, :])
51 | ax.set_xlim(frequency.min() / 1e9, frequency.max() / 1e9)
52 | ax.axhline(1, color="darkgrey", linewidth=0.8, zorder=-1)
53 | ax.set_xlabel("Frequency [GHz]")
54 | ax.set_ylabel("Zenith Opacity")
55 |
56 | if where is not None:
57 | freq_ind = argclosest(frequency, where)
58 | ax.plot(
59 | frequency[freq_ind] / 1e9,
60 | opacity[-1, freq_ind],
61 | marker="o",
62 | color="tab:red",
63 | )
64 |
65 |
66 | def plot_jacobian(height, jacobian, jacobian_quantity, ax=None):
67 | if ax is None:
68 | ax = plt.gca()
69 |
70 | ax.plot(jacobian, height / 1000.0)
71 | ax.set_ylim(0.4, 20)
72 | unit = "K/K/km" if jacobian_quantity == "T" else "K/1/km"
73 | ax.set_xlabel(f"{tag2tex(jacobian_quantity)} Jacobian [{unit}]")
74 | ax.set_ylabel("$z$ [km]")
75 | jac_peak = height[np.abs(jacobian).argmax()] / 1000.0
76 | trans = blended_transform_factory(ax.transAxes, ax.transData)
77 | lh = ax.axhline(jac_peak, color="black", zorder=3)
78 | ax.text(
79 | 1,
80 | jac_peak,
81 | f"{jac_peak:.2f} km",
82 | size="small",
83 | ha="right",
84 | va="bottom",
85 | color=lh.get_color(),
86 | bbox={"color": "white", "alpha": 0.5},
87 | zorder=2,
88 | transform=trans,
89 | )
90 |
91 |
92 | def plot_opacity_profile(height, opacity, ax=None):
93 | if ax is None:
94 | ax = plt.gca()
95 |
96 | ax.semilogx(opacity, height[::-1] / 1000.0)
97 | ax.set_xlim(1e-8, 1e2)
98 | ax.set_xticks(10.0 ** np.arange(-8, 4, 2))
99 | ax.set_xlabel(r"Opacity $\tau(z, z_\mathrm{TOA})$")
100 | ax.set_ylim(0.4, 20)
101 | ax.set_ylabel("$z$ [km]")
102 |
103 | try:
104 | tau1 = height[::-1][np.where(opacity >= 1)[0][0]]
105 | except IndexError:
106 | pass
107 | else:
108 | tau1 /= 1000
109 | trans = blended_transform_factory(ax.transAxes, ax.transData)
110 | lh = ax.axhline(tau1, color="black", zorder=3)
111 | ax.text(
112 | 0.05,
113 | tau1,
114 | f"{tau1:.2f} km",
115 | va="bottom",
116 | size="small",
117 | color=lh.get_color(),
118 | bbox={"color": "white", "alpha": 0.5},
119 | zorder=2,
120 | transform=trans,
121 | )
122 | ax.axvline(1, color="darkgrey", linewidth=0.8, zorder=-1)
123 |
124 |
125 | def calc_jacobians(
126 | jacobian_quantity="H2O", fmin=150e9, fmax=200e9, fnum=200, verbosity=0,
127 | ):
128 | """Calculate jacobians for a given species and frequency range and
129 | save the result as arts-xml-files.
130 |
131 | Parameters:
132 | jacobian_quantity (str): Species tag for which to calculate the
133 | jacobian.
134 | fmin (float): Minimum frequency [Hz].
135 | fmax (float): Maximum frequency [Hz].
136 | fnum (int): Number of frequency grid points.
137 | verbosity (int): ARTS verbosity level.
138 |
139 | """
140 |
141 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
142 | makedirs("results", exist_ok=True)
143 |
144 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
145 | ws.water_p_eq_agendaSet()
146 | ws.PlanetSet(option="Earth")
147 | ws.verbositySetScreen(ws.verbosity, verbosity)
148 |
149 | # Modified emission agenda to store internally calculated optical thickness.
150 | @pyarts.workspace.arts_agenda
151 | def iy_main_agenda__EmissionOpacity(ws):
152 | ws.ppathCalc()
153 | ws.iyEmissionStandard()
154 | ws.ppvar_optical_depthFromPpvar_trans_cumulat()
155 | ws.Touch(ws.geo_pos)
156 | ws.WriteXML("ascii", ws.ppvar_optical_depth, "results/optical_thickness.xml")
157 | ws.WriteXML("ascii", ws.ppvar_p, "results/ppvar_p.xml")
158 |
159 | ws.iy_main_agenda = iy_main_agenda__EmissionOpacity
160 |
161 | # cosmic background radiation
162 | ws.iy_space_agendaSet(option="CosmicBackground")
163 |
164 | # standard surface agenda (i.e., make use of surface_rtprop_agenda)
165 | ws.iy_surface_agendaSet(option="UseSurfaceRtprop")
166 |
167 | # sensor-only path
168 | ws.ppath_agendaSet(option="FollowSensorLosPath")
169 |
170 | # no refraction
171 | ws.ppath_step_agendaSet(option="GeometricPath")
172 |
173 | # Number of Stokes components to be computed
174 | ws.IndexSet(ws.stokes_dim, 1)
175 |
176 | #########################################################################
177 |
178 | # A pressure grid rougly matching 0 to 80 km, in steps of 2 km.
179 | ws.VectorNLogSpace(ws.p_grid, 200, 1013e2, 10.0)
180 |
181 | # Definition of species:
182 | # you can take out and add again one of the species to see what effect it has
183 | # on radiative transfer in the ws.atmosphere.
184 | abs_species = {"N2", "O2", "H2O"}
185 | if jacobian_quantity != "T":
186 | abs_species.add(jacobian_quantity)
187 |
188 | ws.abs_speciesSet(species=list(abs_species))
189 |
190 | # Create a frequency grid
191 | ws.VectorNLinSpace(ws.f_grid, int(fnum), float(fmin), float(fmax))
192 |
193 | # Read a line file and a matching small frequency grid
194 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
195 |
196 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
197 | ws.abs_lines_per_speciesTurnOffLineMixing()
198 |
199 | # Create a frequency grid
200 | ws.VectorNLinSpace(ws.f_grid, int(fnum), float(fmin), float(fmax))
201 |
202 | # Throw away lines outside f_grid
203 | ws.abs_lines_per_speciesCompact()
204 |
205 | # Atmospheric scenario
206 | ws.AtmRawRead(basename="planets/Earth/Fascod/midlatitude-summer/midlatitude-summer")
207 |
208 | # Non reflecting surface
209 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.4)
210 | ws.surface_rtprop_agendaSet(option="Specular_NoPol_ReflFix_SurfTFromt_surface")
211 |
212 | # We select here to use Planck brightness temperatures
213 | ws.StringSet(ws.iy_unit, "PlanckBT")
214 |
215 | #########################################################################
216 |
217 | # Atmosphere and surface
218 | ws.AtmosphereSet1D()
219 | ws.AtmFieldsCalc(interp_order=3)
220 | ws.Extract(ws.z_surface, ws.z_field, 0)
221 | ws.Extract(ws.t_surface, ws.t_field, 0)
222 |
223 | # Definition of sensor position and line of sight (LOS)
224 | ws.VectorSet(ws.rte_pos, np.array([800e3]))
225 | ws.MatrixSet(ws.sensor_pos, np.array([[800e3]]))
226 | ws.MatrixSet(ws.sensor_los, np.array([[180]]))
227 | ws.VectorSet(ws.rte_los, np.array([180]))
228 | ws.sensorOff()
229 |
230 | # Jacobian calculation
231 | ws.jacobianInit()
232 | if jacobian_quantity == "T":
233 | ws.jacobianAddTemperature(g1=ws.p_grid, g2=ws.lat_grid, g3=ws.lon_grid)
234 | else:
235 | ws.jacobianAddAbsSpecies(
236 | g1=ws.p_grid,
237 | g2=ws.lat_grid,
238 | g3=ws.lon_grid,
239 | species=jacobian_quantity,
240 | unit="rel",
241 | )
242 | ws.jacobianClose()
243 |
244 | # Clearsky = No scattering
245 | ws.cloudboxOff()
246 |
247 | # Perform RT calculations
248 | ws.propmat_clearsky_agendaAuto()
249 | ws.lbl_checkedCalc()
250 | ws.propmat_clearsky_agenda_checkedCalc()
251 | ws.atmfields_checkedCalc()
252 | ws.atmgeom_checkedCalc()
253 | ws.cloudbox_checkedCalc()
254 | ws.sensor_checkedCalc()
255 |
256 | ws.yCalc()
257 |
258 | # Write output
259 | ws.WriteXML("ascii", ws.f_grid, "results/f_grid.xml")
260 | ws.WriteXML("ascii", ws.jacobian, "results/jacobian.xml")
261 | ws.WriteXML("ascii", ws.z_field, "results/z_field.xml")
262 | ws.WriteXML("ascii", ws.y, "results/y.xml")
263 |
264 |
265 | # %% Calculate and plot Jacobians
266 | if __name__ == "__main__":
267 | # Calculate Jacobians (ARTS)
268 | jacobian_quantity = "H2O"
269 | calc_jacobians(jacobian_quantity=jacobian_quantity)
270 |
271 | # read in data
272 | freq = np.array(pyarts.xml.load("results/f_grid.xml"))
273 | jac = np.array(pyarts.xml.load("results/jacobian.xml"))
274 | alt = np.array(pyarts.xml.load("results/z_field.xml")).ravel()
275 | jac /= np.gradient(alt / 1000) # normalize by layer thickness in km
276 |
277 | # plot jacobian
278 | highlight_frequency = 180e9 # Hz
279 | fig, ax = plt.subplots()
280 | freq_ind = argclosest(freq, highlight_frequency)
281 | plot_jacobian(alt, jac[freq_ind, :], jacobian_quantity=jacobian_quantity, ax=ax)
282 |
283 | plt.show()
284 |
--------------------------------------------------------------------------------
/exercises/05-inversion/H2Orad.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/exercises/05-inversion/H2Orad.jpg
--------------------------------------------------------------------------------
/exercises/05-inversion/input/measurement.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 50000000000
6 | 50100000000
7 | 50200000000
8 | 50300000000
9 | 50400000000
10 | 50500000000
11 | 50600000000
12 | 50700000000
13 | 50800000000
14 | 50900000000
15 | 51000000000
16 | 51100000000
17 | 51200000000
18 | 51300000000
19 | 51400000000
20 | 51500000000
21 | 51600000000
22 | 51700000000
23 | 51800000000
24 | 51900000000
25 | 52000000000
26 | 52100000000
27 | 52200000000
28 | 52300000000
29 | 52400000000
30 | 52500000000
31 | 52600000000
32 | 52700000000
33 | 52800000000
34 | 52900000000
35 | 53000000000
36 | 53100000000
37 | 53200000000
38 | 53300000000
39 | 53400000000
40 | 53500000000
41 | 53600000000
42 | 53700000000
43 | 53800000000
44 | 53900000000
45 | 54000000000
46 | 54100000000
47 | 54200000000
48 | 54300000000
49 | 54400000000
50 | 54500000000
51 | 54600000000
52 | 54700000000
53 | 54800000000
54 | 54900000000
55 | 55000000000
56 | 55100000000
57 | 55200000000
58 | 55300000000
59 | 55400000000
60 | 55500000000
61 | 55600000000
62 | 55700000000
63 | 55800000000
64 | 55900000000
65 | 56000000000
66 | 56100000000
67 | 56200000000
68 | 56300000000
69 | 56400000000
70 | 56500000000
71 | 56600000000
72 | 56700000000
73 | 56800000000
74 | 56900000000
75 | 57000000000
76 | 57100000000
77 | 57200000000
78 | 57300000000
79 | 57400000000
80 | 57500000000
81 | 57600000000
82 | 57700000000
83 | 57800000000
84 | 57900000000
85 | 58000000000
86 | 58100000000
87 | 58200000000
88 | 58300000000
89 | 58400000000
90 | 58500000000
91 | 58600000000
92 | 58700000000
93 | 58800000000
94 | 58900000000
95 | 59000000000
96 | 59100000000
97 | 59200000000
98 | 59300000000
99 | 59400000000
100 | 59500000000
101 | 59600000000
102 | 59700000000
103 | 59800000000
104 | 59900000000
105 | 60000000000
106 |
107 |
108 | 8.38988182159685
109 | 9.20986299420447
110 | 8.83299755684208
111 | 9.15297450686034
112 | 9.39077345288096
113 | 9.45244134240556
114 | 9.42684406789398
115 | 10.1193202769042
116 | 10.2683747350239
117 | 9.97362593555777
118 | 11.5944140796949
119 | 11.1952720449821
120 | 10.9894158696077
121 | 11.7196323414726
122 | 11.8463587195616
123 | 14.3248547778704
124 | 12.9662767779138
125 | 12.6758611958006
126 | 13.6070222025976
127 | 14.6531389242255
128 | 17.1362117230122
129 | 15.9581040664949
130 | 16.2269108495007
131 | 16.2956064199011
132 | 18.1965381382876
133 | 22.46106789282
134 | 22.5812767488472
135 | 21.5579476481772
136 | 22.8653201964649
137 | 25.7560216103673
138 | 32.097634785864
139 | 39.7852325366991
140 | 32.396370812101
141 | 32.6245509006426
142 | 36.6457038321752
143 | 47.5476669807945
144 | 100.279869428124
145 | 54.6169621963311
146 | 51.5324425456793
147 | 56.6937974838673
148 | 70.6516437125308
149 | 109.794699725964
150 | 97.400659492921
151 | 83.9248120942002
152 | 86.3928526519532
153 | 101.072511150644
154 | 135.417823054258
155 | 167.886124563677
156 | 132.209151928673
157 | 126.659445194322
158 | 137.398614192878
159 | 163.615179374814
160 | 209.906159947615
161 | 189.791417726047
162 | 174.190381139581
163 | 175.541507155752
164 | 190.715390807859
165 | 212.559892767552
166 | 226.810523096463
167 | 216.165764880204
168 | 214.323205408745
169 | 219.692042322321
170 | 226.237191599281
171 | 228.515007900191
172 | 228.178844814579
173 | 225.920186684296
174 | 223.324224636851
175 | 222.734932090952
176 | 226.161927574621
177 | 228.316537893037
178 | 228.785635202119
179 | 227.715491533668
180 | 226.348396072693
181 | 226.287563863247
182 | 227.37256267871
183 | 228.96019693159
184 | 229.718288386687
185 | 229.185625349683
186 | 228.58201193542
187 | 228.45574845385
188 | 228.579604222609
189 | 229.060408565345
190 | 229.110024519198
191 | 229.858662213182
192 | 229.530701629829
193 | 229.637600650818
194 | 229.262343860025
195 | 229.62576947705
196 | 229.336267812089
197 | 229.528742367022
198 | 229.48839580211
199 | 229.737223826999
200 | 229.867374801096
201 | 229.884088344261
202 | 229.687032592504
203 | 229.623851500732
204 | 229.55799568187
205 | 229.436975508594
206 | 229.296476995049
207 | 229.558714255526
208 | 229.478949715291
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/exercises/05-inversion/input/x_apriori.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | "z"
6 | "T"
7 | "abs_species-H2O"
8 |
9 |
10 | 2.7384196e+04
11 | 2.0535250e+04
12 | 1.5399265e+04
13 | 1.1547820e+04
14 | 8.6596432e+03
15 | 6.4938163e+03
16 | 4.8696753e+03
17 | 3.6517413e+03
18 | 2.7384196e+03
19 | 2.0535250e+03
20 | 1.5399265e+03
21 | 1.1547820e+03
22 | 8.6596432e+02
23 | 6.4938163e+02
24 | 4.8696753e+02
25 | 3.6517413e+02
26 | 2.7384196e+02
27 | 2.0535250e+02
28 | 1.5399265e+02
29 | 1.1547820e+02
30 | 8.6596432e+01
31 | 6.4938163e+01
32 | 4.8696753e+01
33 | 3.6517413e+01
34 | 2.7384196e+01
35 | 2.0535250e+01
36 | 1.5399265e+01
37 | 1.1547820e+01
38 | 8.6596432e+00
39 | 6.4938163e+00
40 | 4.8696753e+00
41 | 3.6517413e+00
42 | 2.7384196e+00
43 | 2.0535250e+00
44 | 1.5399265e+00
45 | 1.1547820e+00
46 | 8.6596432e-01
47 | 6.4938163e-01
48 | 4.8696753e-01
49 | 3.6517413e-01
50 | 2.7384196e-01
51 | 2.0535250e-01
52 | 1.5399265e-01
53 | 1.1547820e-01
54 | 8.6596432e-02
55 | 6.4938163e-02
56 | 4.8696753e-02
57 | 3.6517413e-02
58 |
59 |
60 |
61 |
62 |
63 |
64 | 9.5371233e+03
65 | 1.1382265e+04
66 | 1.3226469e+04
67 | 1.5074930e+04
68 | 1.6925013e+04
69 | 1.8773849e+04
70 | 2.0620446e+04
71 | 2.2464313e+04
72 | 2.4306930e+04
73 | 2.6151285e+04
74 | 2.8003650e+04
75 | 2.9871923e+04
76 | 3.1764466e+04
77 | 3.3691008e+04
78 | 3.5657752e+04
79 | 3.7668282e+04
80 | 3.9724272e+04
81 | 4.1825906e+04
82 | 4.3969391e+04
83 | 4.6144987e+04
84 | 4.8339003e+04
85 | 5.0533932e+04
86 | 5.2712183e+04
87 | 5.4860289e+04
88 | 5.6972066e+04
89 | 5.9048289e+04
90 | 6.1093413e+04
91 | 6.3114087e+04
92 | 6.5116671e+04
93 | 6.7104834e+04
94 | 6.9081294e+04
95 | 7.1047847e+04
96 | 7.3004650e+04
97 | 7.4950177e+04
98 | 7.6881981e+04
99 | 7.8796439e+04
100 | 8.0689283e+04
101 | 8.2555200e+04
102 | 8.4385926e+04
103 | 8.6172850e+04
104 | 8.7912972e+04
105 | 8.9606672e+04
106 | 9.1257429e+04
107 | 9.2873933e+04
108 | 9.4471140e+04
109 | 9.6062199e+04
110 | 9.7659081e+04
111 | 9.9272672e+04
112 | 2.2580964e+02
113 | 2.1665000e+02
114 | 2.1665000e+02
115 | 2.1665000e+02
116 | 2.1665000e+02
117 | 2.1665000e+02
118 | 2.1741329e+02
119 | 2.1928865e+02
120 | 2.2116401e+02
121 | 2.2303937e+02
122 | 2.2491473e+02
123 | 2.2679009e+02
124 | 2.2869840e+02
125 | 2.3457378e+02
126 | 2.4044916e+02
127 | 2.4632454e+02
128 | 2.5219993e+02
129 | 2.5807531e+02
130 | 2.6395069e+02
131 | 2.6982607e+02
132 | 2.7065000e+02
133 | 2.7004918e+02
134 | 2.6435061e+02
135 | 2.5865204e+02
136 | 2.5295348e+02
137 | 2.4725491e+02
138 | 2.4155634e+02
139 | 2.3585778e+02
140 | 2.3015921e+02
141 | 2.2446064e+02
142 | 2.1876208e+02
143 | 2.1370695e+02
144 | 2.1031956e+02
145 | 2.0693217e+02
146 | 2.0354478e+02
147 | 2.0015739e+02
148 | 1.9677000e+02
149 | 1.9338261e+02
150 | 1.8999522e+02
151 | 1.8660783e+02
152 | 1.8322045e+02
153 | 1.7983306e+02
154 | 1.7644567e+02
155 | 1.7305828e+02
156 | 1.6967089e+02
157 | 1.6628350e+02
158 | 1.6289611e+02
159 | 1.5950872e+02
160 | 4.0980665e-05
161 | 1.4322150e-05
162 | 9.5894901e-06
163 | 6.3009805e-06
164 | 5.5101813e-06
165 | 5.5914470e-06
166 | 5.7563719e-06
167 | 5.9523387e-06
168 | 6.4025975e-06
169 | 6.9307512e-06
170 | 7.4159843e-06
171 | 7.8954985e-06
172 | 8.1504573e-06
173 | 8.1960547e-06
174 | 8.0711026e-06
175 | 7.8957773e-06
176 | 7.6763297e-06
177 | 7.4802112e-06
178 | 7.4187052e-06
179 | 7.4086676e-06
180 | 7.3904626e-06
181 | 7.3450192e-06
182 | 7.2716644e-06
183 | 7.1431916e-06
184 | 6.9678756e-06
185 | 6.7631159e-06
186 | 6.2770529e-06
187 | 5.7700694e-06
188 | 5.2963156e-06
189 | 4.7210808e-06
190 | 4.1069694e-06
191 | 3.6673236e-06
192 | 3.2157895e-06
193 | 2.7313444e-06
194 | 2.2445186e-06
195 | 1.7555167e-06
196 | 1.3383695e-06
197 | 9.4866917e-07
198 | 7.3774328e-07
199 | 5.5896598e-07
200 | 4.5608904e-07
201 | 3.7997375e-07
202 | 3.2590461e-07
203 | 2.8505600e-07
204 | 2.4875208e-07
205 | 2.3080539e-07
206 | 2.1406542e-07
207 | 1.9970193e-07
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/exercises/05-inversion/input/x_true.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | "z"
6 | "T"
7 | "abs_species-H2O"
8 |
9 |
10 | 27384.196
11 | 20535.25
12 | 15399.265
13 | 11547.82
14 | 8659.6432
15 | 6493.8163
16 | 4869.6753
17 | 3651.7413
18 | 2738.4196
19 | 2053.525
20 | 1539.9265
21 | 1154.782
22 | 865.96432
23 | 649.38163
24 | 486.96753
25 | 365.17413
26 | 273.84196
27 | 205.3525
28 | 153.99265
29 | 115.4782
30 | 86.596432
31 | 64.938163
32 | 48.696753
33 | 36.517413
34 | 27.384196
35 | 20.53525
36 | 15.399265
37 | 11.54782
38 | 8.6596432
39 | 6.4938163
40 | 4.8696753
41 | 3.6517413
42 | 2.7384196
43 | 2.053525
44 | 1.5399265
45 | 1.154782
46 | 0.86596432
47 | 0.64938163
48 | 0.48696753
49 | 0.36517413
50 | 0.27384196
51 | 0.2053525
52 | 0.15399265
53 | 0.1154782
54 | 0.086596432
55 | 0.064938163
56 | 0.048696753
57 | 0.036517413
58 |
59 |
60 |
61 |
62 |
63 |
64 | 9537.1233
65 | 11382.265
66 | 13226.469
67 | 15074.93
68 | 16925.013
69 | 18773.849
70 | 20620.446
71 | 22464.313
72 | 24306.93
73 | 26151.285
74 | 28003.65
75 | 29871.923
76 | 31764.466
77 | 33691.008
78 | 35657.752
79 | 37668.282
80 | 39724.272
81 | 41825.906
82 | 43969.391
83 | 46144.987
84 | 48339.003
85 | 50533.932
86 | 52712.183
87 | 54860.289
88 | 56972.066
89 | 59048.289
90 | 61093.413
91 | 63114.087
92 | 65116.671
93 | 67104.834
94 | 69081.294
95 | 71047.847
96 | 73004.65
97 | 74950.177
98 | 76881.981
99 | 78796.439
100 | 80689.283
101 | 82555.2
102 | 84385.926
103 | 86172.85
104 | 87912.972
105 | 89606.672
106 | 91257.429
107 | 92873.933
108 | 94471.14
109 | 96062.199
110 | 97659.081
111 | 99272.672
112 | 235.80964
113 | 226.65
114 | 226.65
115 | 226.65
116 | 226.65
117 | 226.65
118 | 227.41329
119 | 229.28865
120 | 231.16401
121 | 233.03937
122 | 234.91473
123 | 236.79009
124 | 238.6984
125 | 244.57378
126 | 250.44916
127 | 256.32454
128 | 262.19993
129 | 268.07531
130 | 273.95069
131 | 279.82607
132 | 280.65
133 | 280.04918
134 | 274.35061
135 | 268.65204
136 | 262.95348
137 | 257.25491
138 | 251.55634
139 | 245.85778
140 | 240.15921
141 | 234.46064
142 | 228.76208
143 | 223.70695
144 | 220.31956
145 | 216.93217
146 | 213.54478
147 | 210.15739
148 | 206.77
149 | 203.38261
150 | 199.99522
151 | 196.60783
152 | 193.22045
153 | 189.83306
154 | 186.44567
155 | 183.05828
156 | 179.67089
157 | 176.2835
158 | 172.89611
159 | 169.50872
160 | 4.0980665e-05
161 | 1.432215e-05
162 | 9.5894901e-06
163 | 6.3009805e-06
164 | 5.5101813e-06
165 | 5.591447e-06
166 | 5.7563719e-06
167 | 5.9523387e-06
168 | 6.4025975e-06
169 | 6.9307512e-06
170 | 7.4159843e-06
171 | 7.8954985e-06
172 | 8.1504573e-06
173 | 8.1960547e-06
174 | 8.0711026e-06
175 | 7.8957773e-06
176 | 7.6763297e-06
177 | 7.4802112e-06
178 | 7.4187052e-06
179 | 7.4086676e-06
180 | 7.3904626e-06
181 | 7.3450192e-06
182 | 7.2716644e-06
183 | 7.1431916e-06
184 | 6.9678756e-06
185 | 6.7631159e-06
186 | 6.2770529e-06
187 | 5.7700694e-06
188 | 5.2963156e-06
189 | 4.7210808e-06
190 | 4.1069694e-06
191 | 3.6673236e-06
192 | 3.2157895e-06
193 | 2.7313444e-06
194 | 2.2445186e-06
195 | 1.7555167e-06
196 | 1.3383695e-06
197 | 9.4866917e-07
198 | 7.3774328e-07
199 | 5.5896598e-07
200 | 4.5608904e-07
201 | 3.7997375e-07
202 | 3.2590461e-07
203 | 2.85056e-07
204 | 2.4875208e-07
205 | 2.3080539e-07
206 | 2.1406542e-07
207 | 1.9970193e-07
208 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/exercises/05-inversion/oem.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "\n",
10 | "Manfred Brath, Oliver Lemke\n",
11 | "\n",
12 | "## Exercise 5: Inversion theory: Optimal Estimation Method (OEM)"
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": null,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "%matplotlib widget\n",
22 | "\n",
23 | "import os\n",
24 | "import matplotlib.pyplot as plt\n",
25 | "import numpy as np\n",
26 | "from scipy.linalg import inv\n",
27 | "\n",
28 | "from pyarts import xml\n",
29 | "from oem import forward_model\n",
30 | "\n",
31 | "os.makedirs(\"plots\", exist_ok=True)"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "In this exercise you will work with \"realistic\" data measured by a water \n",
39 | "vapor radiometer. The data is not real but has been simulated for a well- \n",
40 | "known atmospheric state using ARTS. Simulated measurements allow to \n",
41 | "compare retrieval results to the true atmospheric state. The radiometer \n",
42 | "(image below) measures thermal radiation in a frequency range around the \n",
43 | "$22\\,\\text{GHz}$ water vapor absorption line. \n",
44 | "As the pressure broadening of absorption lines varies with height the \n",
45 | "measurement contains information about the vertical water vapor profile. \n",
46 | "This information can be retrieved using the \"Optimal Estimation Method\" (OEM). \n",
47 | "The radiometer is placed in $10\\,\\text{km}$ height, which resembles an upward \n",
48 | "looking airborne measurement. The scarce concentration of water vapor in the \n",
49 | "stratosphere allows to perform a linear retrieval approach. Retrievals that \n",
50 | "cover the whole atmosphere, including the highly absorbent lower troposphere, \n",
51 | "need more advanced retrieval approaches like an iterative OEM. \n",
52 | "\n",
53 | "\n",
54 | "\n",
55 | "* Run the next cell."
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "# Load the (simulated) measurement.\n",
65 | "measurement = xml.load(\"input/measurement.xml\")\n",
66 | "f_grid = measurement.grids[0]\n",
67 | "y_measurement = measurement.data\n",
68 | "\n",
69 | "# Load the a priori atmospheric state.\n",
70 | "atm_fields = xml.load(\"input/x_apriori.xml\")\n",
71 | "z = atm_fields.get(\"z\", keep_dims=False)\n",
72 | "x_apriori = atm_fields.get(\"abs_species-H2O\", keep_dims=False)\n",
73 | "\n",
74 | "# Load the covariance matrices.\n",
75 | "S_xa = xml.load(\"input/S_xa.xml\")\n",
76 | "S_y = 2.5e-3 * np.eye(f_grid.size) # in [K^2]"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "metadata": {},
82 | "source": [
83 | "* Plot the observed brightness\n",
84 | "temperature spectrum `y_measurement` as function of frequency\n",
85 | "`f_grid`."
86 | ]
87 | },
88 | {
89 | "cell_type": "code",
90 | "execution_count": null,
91 | "metadata": {},
92 | "outputs": [],
93 | "source": [
94 | "# Plot the y measurement.\n"
95 | ]
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {},
100 | "source": [
101 | "* Run the next cell to simulate the brightness temperature spectrum \n",
102 | "`y` and the water vapor Jacobian `K` for \n",
103 | "the *a priori* state."
104 | ]
105 | },
106 | {
107 | "cell_type": "code",
108 | "execution_count": null,
109 | "metadata": {},
110 | "outputs": [],
111 | "source": [
112 | "# Run the forward model (ARTS).\n",
113 | "y, K = forward_model(f_grid, atm_fields)"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {},
119 | "source": [
120 | "* Plot the simulated brightness temperature spectrum alongside with\n",
121 | "the observed brightness temperature spectrum."
122 | ]
123 | },
124 | {
125 | "cell_type": "code",
126 | "execution_count": null,
127 | "metadata": {},
128 | "outputs": [],
129 | "source": [
130 | "# Plot the y measurement alongside the simulated y for the a priori.\n"
131 | ]
132 | },
133 | {
134 | "cell_type": "markdown",
135 | "metadata": {},
136 | "source": [
137 | "* Plot the Jacobians `K` in a suitable way. Explain the plot."
138 | ]
139 | },
140 | {
141 | "cell_type": "code",
142 | "execution_count": null,
143 | "metadata": {},
144 | "outputs": [],
145 | "source": [
146 | "# Plot the Jacobians.\n"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {},
152 | "source": [
153 | "* Plot the measurement covariance matrix `S_y` and the *apriori* covariance matrix `S_xa` in a suitable way. \n",
154 | "What do the covariance matrices mean?"
155 | ]
156 | },
157 | {
158 | "cell_type": "code",
159 | "execution_count": null,
160 | "metadata": {},
161 | "outputs": [],
162 | "source": [
163 | "# Plot the covariance matrices.\n"
164 | ]
165 | },
166 | {
167 | "cell_type": "markdown",
168 | "metadata": {},
169 | "source": [
170 | "* Implement the function `retrieve()` according to the OEM solution: \n",
171 | "$$\\hat{\\mathbf{x}}=\\mathbf{x}_{a}+\\left(\\mathbf{K}^{T}\\mathbf{S}_{y}^{-1}\\mathbf{K}+\\mathbf{S}_{xa}^{-1}\\right)^{-1}\\mathbf{K}^{T}\\mathbf{S}_{y}^{-1}\\left(\\mathbf{y}_{measure}-\\mathbf{y}_{a}\\right)$$\n",
172 | "\n",
173 | " with $\\mathbf{x}_{a}$ the a priori profile, $\\mathbf{K}$ the Jacobian,\n",
174 | "$\\mathbf{S}_{y}$ the measurement covariance matrix, $\\mathbf{S}_{xa}$\n",
175 | "the *a priori* covariance matrix, $\\mathbf{y}_{measure}$ the\n",
176 | "observed brightness temperature spectrum and $\\mathbf{y}_{a}$ the\n",
177 | "simulated brightness temperature spectrum of profile $\\mathbf{x}_{a}$. \n",
178 | "In Python, a matrix `M` can be transposed using `M.T`\n",
179 | "and inversed using `inv(M)` We are using the inverse function \n",
180 | "`scipy.linalg.inv()` provided by the SciPy package. \n",
181 | "Two matrices `M1` and `M2` can be multiplied using\n",
182 | "`M1 @ M2.`\n",
183 | "\n",
184 | "* Use the function `retrieve()` to retrieve the water vapor profile."
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": null,
190 | "metadata": {},
191 | "outputs": [],
192 | "source": [
193 | "# Implement the retrieve function.\n",
194 | "def retrieve(y, K, xa, ya, Sa, Sy):\n",
195 | " \"\"\"Perform an OEM retrieval.\n",
196 | "\n",
197 | " Parameters:\n",
198 | " y (np.ndarray): Measuremed brightness temperature [K].\n",
199 | " K (np.ndarray): Jacobians [K/1].\n",
200 | " xa (np.ndarray): A priori state [VMR].\n",
201 | " ya (np.ndarray): Forward simulation of a priori state ``F(xa)`` [K].\n",
202 | " Sa (np.ndarray): A priori error covariance matrix.\n",
203 | " Sy (np.ndarray): Measurement covariance matrix\n",
204 | "\n",
205 | " Returns:\n",
206 | " np.ndarray: Retrieved atmospheric state.\n",
207 | "\n",
208 | " \"\"\"\n",
209 | " print(\"Function needs to be implemented by you\")"
210 | ]
211 | },
212 | {
213 | "cell_type": "code",
214 | "execution_count": null,
215 | "metadata": {},
216 | "outputs": [],
217 | "source": [
218 | "# retrieve\n",
219 | "x_oem = retrieve(y_measurement, K, x_apriori, y, S_xa, S_y)\n"
220 | ]
221 | },
222 | {
223 | "cell_type": "markdown",
224 | "metadata": {},
225 | "source": [
226 | "* Plot the retrieved water vapor `x_oem` and the *a priori* \n",
227 | "water vapor profile as function of height `z`.\n",
228 | "\n",
229 | "* Load the true water vapor retrieval (`input/x_true.xml`) and\n",
230 | "add it to the previous plot. Dicuss the results."
231 | ]
232 | },
233 | {
234 | "cell_type": "code",
235 | "execution_count": null,
236 | "metadata": {},
237 | "outputs": [],
238 | "source": [
239 | "# Plot the OEM result next to the true atmospheric state and the a priori."
240 | ]
241 | },
242 | {
243 | "cell_type": "markdown",
244 | "metadata": {},
245 | "source": [
246 | "* Implement the function `gain_matrix()` to calculate\n",
247 | "the same-named matrix: \n",
248 | "$$\\mathbf{G}=\\left(\\mathbf{K}^{T}\\mathbf{S}_{y}^{-1}\\mathbf{K}+\\mathbf{S}_{xa}^{-1}\\right)^{-1}\\mathbf{K}^{T}\\mathbf{S}_{y}^{-1}$$"
249 | ]
250 | },
251 | {
252 | "cell_type": "code",
253 | "execution_count": null,
254 | "metadata": {},
255 | "outputs": [],
256 | "source": [
257 | "# Implement the averaging_kernel_matrix function.\n",
258 | "def gain_matrix(K, Sa, Sy):\n",
259 | " \"\"\"Calculate the gain matrix.\n",
260 | "\n",
261 | " Parameters:\n",
262 | " K (np.ndarray): Simulated Jacobians.\n",
263 | " Sa (np.ndarray): A priori error covariance matrix.\n",
264 | " Sy (np.ndarray): Measurement covariance matrix.\n",
265 | "\n",
266 | " Returns:\n",
267 | " np.ndarray: gain matrix.\n",
268 | " \"\"\"\n",
269 | " print(\"Function needs to be implemented by you\")"
270 | ]
271 | },
272 | {
273 | "cell_type": "markdown",
274 | "metadata": {},
275 | "source": [
276 | "* Plot the gain matrix `G` in a suitable way. \n",
277 | "* Explain where which part of the measurement vector contributes to the retrieval.\n"
278 | ]
279 | },
280 | {
281 | "cell_type": "code",
282 | "execution_count": null,
283 | "metadata": {},
284 | "outputs": [],
285 | "source": [
286 | "# Calculate gain matrix \n",
287 | "G = gain_matrix(K, S_xa, S_y)"
288 | ]
289 | },
290 | {
291 | "cell_type": "code",
292 | "execution_count": null,
293 | "metadata": {},
294 | "outputs": [],
295 | "source": [
296 | "# Plot gain matrix"
297 | ]
298 | },
299 | {
300 | "cell_type": "markdown",
301 | "metadata": {},
302 | "source": [
303 | "* Implement the function `averaging_kernel_matrix()` to calculate\n",
304 | "the same-named matrix: \n",
305 | "$$\\mathbf{A}=\\left(\\mathbf{K}^{T}\\mathbf{S}_{y}^{-1}\\mathbf{K}+\\mathbf{S}_{xa}^{-1}\\right)^{-1}\\mathbf{K}^{T}\\mathbf{S}_{y}^{-1}\\mathbf{K}$$"
306 | ]
307 | },
308 | {
309 | "cell_type": "code",
310 | "execution_count": null,
311 | "metadata": {},
312 | "outputs": [],
313 | "source": [
314 | "# Implement the averaging_kernel_matrix function.\n",
315 | "def averaging_kernel_matrix(K, Sa, Sy):\n",
316 | " \"\"\"Calculate the averaging kernel matrix.\n",
317 | "\n",
318 | " Parameters:\n",
319 | " K (np.ndarray): Simulated Jacobians.\n",
320 | " Sa (np.ndarray): A priori error covariance matrix.\n",
321 | " Sy (np.ndarray): Measurement covariance matrix.\n",
322 | "\n",
323 | " Returns:\n",
324 | " np.ndarray: Averaging kernel matrix.\n",
325 | " \"\"\"\n",
326 | " print(\"Function needs to be implemented by you\")"
327 | ]
328 | },
329 | {
330 | "cell_type": "markdown",
331 | "metadata": {},
332 | "source": [
333 | "* Plot the kernels (columns) of $\\mathbf{A}$ as function of height\n",
334 | "`z` and interpret the results. \n",
335 | "The measurement response is defined as the sum over all averaging\n",
336 | "kernels in a given height (row). The measurement response indicates\n",
337 | "in which heights the measurement actually adds information to the\n",
338 | "retrieval result.\n",
339 | "* Calculate the measurement response and plot it together with the averaging\n",
340 | "kernels.\n",
341 | "* In which heights does the measurement provide useful information?\n",
342 | "* Is it possible to estimate the vertical resolution?"
343 | ]
344 | },
345 | {
346 | "cell_type": "code",
347 | "execution_count": null,
348 | "metadata": {},
349 | "outputs": [],
350 | "source": [
351 | "# Calculate averaging kernel \n",
352 | "A = averaging_kernel_matrix(K, S_xa, S_y)"
353 | ]
354 | },
355 | {
356 | "cell_type": "code",
357 | "execution_count": null,
358 | "metadata": {},
359 | "outputs": [],
360 | "source": [
361 | "# Plot averaging kernels"
362 | ]
363 | }
364 | ],
365 | "metadata": {
366 | "kernelspec": {
367 | "display_name": "Python 3",
368 | "language": "python",
369 | "name": "python3"
370 | },
371 | "language_info": {
372 | "codemirror_mode": {
373 | "name": "ipython",
374 | "version": 3
375 | },
376 | "file_extension": ".py",
377 | "mimetype": "text/x-python",
378 | "name": "python",
379 | "nbconvert_exporter": "python",
380 | "pygments_lexer": "ipython3",
381 | "version": "3.12.7"
382 | }
383 | },
384 | "nbformat": 4,
385 | "nbformat_minor": 4
386 | }
387 |
--------------------------------------------------------------------------------
/exercises/05-inversion/oem.py:
--------------------------------------------------------------------------------
1 | """Perform an OEM retrieval and plot the results."""
2 | import numpy as np
3 | import pyarts.workspace
4 | from pyarts import xml
5 |
6 |
7 | def forward_model(f_grid, atm_fields_compact, retrieval_quantity='H2O',verbosity=0):
8 | """Perform a radiative transfer simulation.
9 |
10 | Parameters:
11 | f_grid (ndarray): Frequency grid [Hz].
12 | atm_fields_compact (GriddedField4): Atmosphere field.
13 | verbosity (int): Reporting levels between 0 (only error messages)
14 | and 3 (everything).
15 |
16 | Returns:
17 | ndarray, ndarray: Frequency grid [Hz], Jacobian [K/1]
18 | """
19 |
20 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
21 |
22 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
23 | ws.water_p_eq_agendaSet()
24 | ws.PlanetSet(option="Earth")
25 | ws.verbositySetScreen(ws.verbosity, verbosity)
26 |
27 | # standard emission agenda
28 | ws.iy_main_agendaSet(option="Emission")
29 |
30 | # cosmic background radiation
31 | ws.iy_space_agendaSet(option="CosmicBackground")
32 |
33 | # standard surface agenda (i.e., make use of surface_rtprop_agenda)
34 | ws.iy_surface_agendaSet(option="UseSurfaceRtprop")
35 |
36 | # sensor-only path
37 | ws.ppath_agendaSet(option="FollowSensorLosPath")
38 |
39 | # no refraction
40 | ws.ppath_step_agendaSet(option="GeometricPath")
41 |
42 | # Non reflecting surface
43 | ws.surface_rtprop_agendaSet(option="Specular_NoPol_ReflFix_SurfTFromt_surface")
44 |
45 | # Number of Stokes components to be computed
46 | ws.IndexSet(ws.stokes_dim, 1)
47 |
48 | #########################################################################
49 |
50 | # Definition of absorption species
51 | ws.abs_speciesSet(
52 | species=[
53 | "H2O, H2O-SelfContCKDMT400, H2O-ForeignContCKDMT400",
54 | "O2-TRE05",
55 | "N2, N2-CIAfunCKDMT252, N2-CIArotCKDMT252",
56 | ]
57 | )
58 |
59 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
60 |
61 | # Load CKDMT400 model data
62 | ws.ReadXML(ws.predefined_model_data, "model/mt_ckd_4.0/H2O.xml")
63 |
64 | # ws.abs_lines_per_speciesLineShapeType(option=lineshape)
65 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
66 | # ws.abs_lines_per_speciesNormalization(option=normalization)
67 |
68 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.4)
69 |
70 | # Set the frequency grid
71 | ws.f_grid = f_grid
72 |
73 | # Throw away lines outside f_grid
74 | ws.abs_lines_per_speciesCompact()
75 |
76 | # No sensor properties
77 | ws.sensorOff()
78 |
79 | # We select here to use Planck brightness temperatures
80 | ws.StringSet(ws.iy_unit, "PlanckBT")
81 |
82 | #########################################################################
83 |
84 | # Atmosphere and surface
85 | ws.AtmosphereSet1D()
86 | ws.atm_fields_compact = atm_fields_compact
87 | ws.atm_fields_compactAddConstant(
88 | ws.atm_fields_compact, "abs_species-N2", 0.78, 0, ["abs_species-H2O"]
89 | )
90 | ws.atm_fields_compactAddConstant(
91 | ws.atm_fields_compact, "abs_species-O2", 0.21, 0, ["abs_species-H2O"]
92 | )
93 | ws.AtmFieldsAndParticleBulkPropFieldFromCompact()
94 |
95 | ws.Extract(ws.z_surface, ws.z_field, 0)
96 | ws.Extract(ws.t_surface, ws.t_field, 0)
97 |
98 | # Definition of sensor position and line of sight (LOS)
99 | ws.MatrixSet(ws.sensor_pos, np.array([[10e3]]))
100 | ws.MatrixSet(ws.sensor_los, np.array([[0]]))
101 | ws.sensorOff()
102 |
103 | # Jacobian calculation
104 | ws.jacobianInit()
105 | if retrieval_quantity=='H2O':
106 | ws.jacobianAddAbsSpecies(
107 | g1=ws.p_grid,
108 | g2=ws.lat_grid,
109 | g3=ws.lon_grid,
110 | species="H2O, H2O-SelfContCKDMT400, H2O-ForeignContCKDMT400",
111 | unit="vmr",
112 | )
113 | elif retrieval_quantity=='Temperature':
114 | ws.jacobianAddTemperature(
115 | g1=ws.p_grid,
116 | g2=ws.lat_grid,
117 | g3=ws.lon_grid
118 | )
119 | else:
120 | raise ValueError('only H2O or Temperature are allowed as retrieval quantity')
121 | ws.jacobianClose()
122 |
123 | # Clearsky = No scattering
124 | ws.cloudboxOff()
125 |
126 | # on-the-fly absorption
127 | ws.propmat_clearsky_agendaAuto()
128 |
129 | # Perform RT calculations
130 | ws.lbl_checkedCalc()
131 | ws.atmfields_checkedCalc()
132 | ws.atmgeom_checkedCalc()
133 | ws.cloudbox_checkedCalc()
134 | ws.sensor_checkedCalc()
135 |
136 | ws.yCalc()
137 |
138 | return ws.y.value[:].copy(), ws.jacobian.value[:].copy()
139 |
140 | # %%
141 | if __name__ == "__main__":
142 |
143 | import matplotlib.pyplot as plt
144 |
145 |
146 | # Load the (simulated) measurement.
147 | measurement = xml.load("input/measurement.xml")
148 | f_grid = measurement.grids[0]
149 | y_measurement = measurement.data
150 |
151 | # Load the a priori atmospheric state.
152 | atm_fields = xml.load("input/x_apriori.xml")
153 | z = atm_fields.get("z", keep_dims=False)
154 | x_apriori = atm_fields.get("abs_species-H2O", keep_dims=False)
155 |
156 | # Load the covariance matrices.
157 | S_xa = xml.load("input/S_xa.xml")
158 | S_y = 2.5e-3 * np.eye(f_grid.size) # in [K^2]
159 |
160 |
161 | y, K = forward_model(f_grid, atm_fields)
162 |
163 |
164 | # Plot absorption cross sections
165 | fig, ax = plt.subplots()
166 | ax.plot(f_grid, y)
167 | ax.set_xlabel("Frequency [Hz]")
168 | ax.set_ylabel(r"Brightness Temperature [$\sf K$]")
169 | plt.show()
170 | fig.savefig('forward_spectra.pdf')
--------------------------------------------------------------------------------
/exercises/05-inversion/simulate_measurement.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Fri Dec 13 14:11:05 2024
5 |
6 | @author: Manfred Brath
7 | """
8 |
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | from copy import deepcopy
12 |
13 | import pyarts as pa
14 | import oem
15 |
16 |
17 |
18 | # %% paths/constants
19 |
20 | # Load the a priori atmospheric state.
21 | atm_fields = pa.xml.load("input/x_apriori.xml")
22 | z = atm_fields.get("z", keep_dims=False)
23 | x_apriori = atm_fields.get("abs_species-H2O", keep_dims=False)
24 |
25 | nedt=0.05 #K
26 | #%% prepare measurement simulation
27 |
28 | #define channels
29 | f_min=22.15e9
30 | f_max=22.30e9
31 | f_num=2000
32 | f_grid=np.linspace(f_min,f_max,f_num)
33 |
34 |
35 | #define "true" atmosphere
36 | atm_true=deepcopy(atm_fields)
37 |
38 | #add pertuberation to the water vapor profile
39 | vmr_h2o=atm_true.get("abs_species-H2O", keep_dims=False)
40 | vmr_h2o_perp=vmr_h2o+3e-5*np.exp(-z/50000)*0.5*np.sin(2*np.pi*0.0001*z)+0.5e-5
41 | vmr_h2o_perp[vmr_h2o_perp<0]=vmr_h2o[-1]
42 |
43 | atm_true.set("abs_species-H2O", np.array(vmr_h2o_perp[np.newaxis,:,np.newaxis,np.newaxis]))
44 |
45 | plt.style.use('seaborn-v0_8')
46 |
47 | fig, ax =plt.subplots(1,2)
48 |
49 | ax[0].plot(atm_fields.get("abs_species-H2O", keep_dims=False), z/1000, label='a priori')
50 | ax[0].plot(atm_true.get("abs_species-H2O", keep_dims=False), z/1000, label='perturbed')
51 | ax[0].set_ylabel('altitude / km')
52 | ax[0].set_xlabel(r'$vmr_{\text{H}_2 \text{O} }$')
53 | ax[0].legend()
54 |
55 | ax[1].plot(atm_true.get("T", keep_dims=False), z/1000)
56 | ax[1].set_xlabel('temperature / K')
57 |
58 | fig.tight_layout()
59 |
60 |
61 | # %% simulate measurement
62 |
63 | y_apr, K_apr = oem.forward_model(f_grid, atm_fields)
64 | y, K = oem.forward_model(f_grid, atm_true)
65 |
66 | # %%add noise
67 | #we use a seed to be deterministic
68 | rng = np.random.default_rng(12345)
69 | y_obs = y+rng.normal(scale=nedt,size=f_num)
70 |
71 | fig, ax =plt.subplots(1,1)
72 | ax.plot(f_grid,y_apr,label='a priori')
73 | ax.plot(f_grid,y,label='simulation')
74 | ax.plot(f_grid,y_obs,label='measurement')
75 |
76 | #save measurement
77 | measurement=pa.arts.GriddedField1()
78 | measurement.grids=[f_grid]
79 | measurement.gridnames[0]='Frequency'
80 | measurement.data=y_obs
81 | measurement.savexml("input/measurement.xml")
82 |
83 | #save "true profile"
84 | atm_true.savexml("input/x_true.xml")
85 |
86 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/Halo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/exercises/06-non_lin_inversion/Halo.jpg
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/Temperature_retrieval.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "\n",
8 | "## Exercise 8: Temperature retrieval from airborne observations"
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "metadata": {},
15 | "outputs": [],
16 | "source": [
17 | "%matplotlib widget\n",
18 | "\n",
19 | "import os\n",
20 | "import matplotlib.pyplot as plt\n",
21 | "import numpy as np\n",
22 | "from pyarts import xml\n",
23 | "from nonlin_oem import Forward_model, set_correlation_length, create_apriori_covariance_matrix, temperature_retrieval\n",
24 | "\n",
25 | "os.makedirs(\"plots\", exist_ok=True)"
26 | ]
27 | },
28 | {
29 | "cell_type": "markdown",
30 | "metadata": {},
31 | "source": [
32 | "In this exercise we want to retrieve temperature profiles from (simulated) \n",
33 | "airborne microwave radiometer observation using the optimal estimation method.\n",
34 | "The radiometer is the HAMP radiometer on board the HALO aircraft.\n",
35 | "The radiometer measures the brightness temperature at several set of channels.\n",
36 | "In this exercise we will use the sets of channels around 22GHz, 50GHz and 118GHz.\n",
37 | "The radiometer is mounted in the belly pod of the HALO aircraft and measures the \n",
38 | "brightness temperature at nadir.\n",
39 | "We will use a simplified version of the HAMP radiometer but with the correct\n",
40 | "channels and NeDT.\n",
41 | "\n",
42 | "\n",
43 | "*source https://halo-research.de/ressources/image-galery/*\n",
44 | "\n",
45 | "The NeDT (Noise equivalent delta temperature) are the following:\n",
46 | "\n",
47 | "* 22GHz channels: 0.1K\n",
48 | "* 50GHz channels: 0.2K\n",
49 | "* 118GHz channels: 0.6K\n",
50 | "\n",
51 | "The measurement data consists of a short ($\\approx$ 100km) flight segment of the HALO aircraft at clear sky conditions over the tropical pacific.\n",
52 | "The flight segment is at 15km altitude. The measurement data consists of brightness temperature observations for the three sets of channels.\n",
53 | "Each data set consists of a file that contains the measurement data (*y_obs_xxxGHz.xml*), a file with the frequencies (*f_grid_xxxGHz.xml*) \n",
54 | "and a file with the latitudeof the measurements (*lat.xml*).\n",
55 | "Furthermore there also exists dropsonde measurement data (*dropsonde.xml*) from one dropsonde that was released during the flight segment. The dropsonde data contains the temperature, altitude and H2O vmr profiles as function of pressure. \n",
56 | "The measurement data is stored in the directory *observation*.\n",
57 | "The surface temperature during that flight segment was 300K. The surface reflectivity is 0.4 for all frequencies for that flight segment."
58 | ]
59 | },
60 | {
61 | "cell_type": "markdown",
62 | "metadata": {},
63 | "source": [
64 | "### Part I - Preparations"
65 | ]
66 | },
67 | {
68 | "cell_type": "markdown",
69 | "metadata": {},
70 | "source": [
71 | "#### 1) \n",
72 | "Read in the measurement data and sensor characteristics. Plot the brightness temperature observations for the three sets of channels as function of latitude. Furthermore plot the dropsonde temperature profile.\n",
73 | "Depending on the channel set, the actual channel consists of a single center frequency or of at least two sidebands channels around the center frequency."
74 | ]
75 | },
76 | {
77 | "cell_type": "code",
78 | "execution_count": null,
79 | "metadata": {},
80 | "outputs": [],
81 | "source": [
82 | "# Load the observation data\n",
83 | "sensor_characteristics_22GHz = xml.load(\"observation/SensorCharacteristics_22GHz.xml\") # sensor characteristics\n",
84 | "\n",
85 | "# The sensor characteristics is a matrix with the following columns:\n",
86 | "# 0: Frequency in GHz\n",
87 | "# 1: sideband offset1 to center frequency in GHz\n",
88 | "# 2: sideband offset2 to center frequency in GHz\n",
89 | "# 3: bandwidth in GHz\n",
90 | "# 4: relative mandatory frequency grid spacing for the passbands\n",
91 | "#\n",
92 | "# The rows of the matrix are the different channels\n",
93 | "\n",
94 | "\n",
95 | "y_obs_22GHz = xml.load(\"observation/y_obs_22GHz.xml.xml\")[:] # [:] converts the data to a numpy array\n",
96 | "#...\n",
97 | "\n",
98 | "\n",
99 | "#...and the dropsonde data\n",
100 | "dropsonde = xml.load(\"observation/dropsonde.xml\")\n",
101 | "\n",
102 | "# dropsonde.grids[0] gives you the name of the variables in the dropsonde file\n",
103 | "# Use dropsonde.get(\"VARIABLENAME\", keep_dims=False) to get the data of the variable VARIABLENAME\n",
104 | "# dropsonde.grids[1][:] gives the pressure grid\n",
105 | "\n",
106 | "# Plot the observation data"
107 | ]
108 | },
109 | {
110 | "cell_type": "markdown",
111 | "metadata": {},
112 | "source": [
113 | "#### 2)\n",
114 | "Decide and explain which set of channels you want to use for the temperature retrieval.\n",
115 | "If you want you can use the dropsonde data and the function forward model to simulate the brightness temperatures and jacobians for the dropsonde temperature profile, but **you don't have to**."
116 | ]
117 | },
118 | {
119 | "cell_type": "code",
120 | "execution_count": null,
121 | "metadata": {},
122 | "outputs": [],
123 | "source": [
124 | "# y_obs, jacobians = Forward_model([], dropsonde_data,..., sensor_description=sensor_characteristics)\n"
125 | ]
126 | },
127 | {
128 | "cell_type": "markdown",
129 | "metadata": {},
130 | "source": []
131 | },
132 | {
133 | "cell_type": "markdown",
134 | "metadata": {},
135 | "source": [
136 | "#### 3)\n",
137 | "Prepare the covariance matrices for the temperature retrieval.\n",
138 | "Use the function *create_apriori_covariance_matrix* to create the a priori covariance matrix. The function assumes that an exponentially decaying correlation function is used. \n",
139 | "\n",
140 | "You can use the function *set_correlation_length(z, len_sfc, len_toa=None)* to set the correlation length for the a priori covariance matrix. You can use a constant or a linearly increasing correlation length with height.\n",
141 | "Make an educated guess for the correlation length. Remember that the flight segment is over the tropical pacific.\n",
142 | "\n",
143 | " *Set the a priori covaraince matrix for the temperature retrieval.\n",
144 | "* Set the measurement error covariance matrix using the NeDT values. Assume a diagonal matrix.\n",
145 | "* Plot the covariance matrices in suitable way."
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": null,
151 | "metadata": {},
152 | "outputs": [],
153 | "source": [
154 | "# correlation_length = set_correlation_length(z, len_sfc, len_toa)\n",
155 | "# S_a = create_apriori_covariance_matrix(x, z, delta_x, correlation_length)\n",
156 | "\n",
157 | "# S_y = ..."
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "metadata": {},
163 | "source": [
164 | "### Part II - Retrieval\n"
165 | ]
166 | },
167 | {
168 | "cell_type": "markdown",
169 | "metadata": {},
170 | "source": [
171 | "#### 4)\n",
172 | "Use the function *temperature_retrieval* to retrieve the temperature profile from the first brightness temperature observation of the flight segment. The function has the following signature:\n",
173 | "```python \n",
174 | " T_ret, DeltaT, y_fit = temperature_retrieval(y_obs, f_grid, sensor_pos, sensor_los, background_atmosphere, surface_temperature, surface_reflectivity, S_y, S_a, senssor_description=[], Diagnostics=False) \n",
175 | "``` \n",
176 | "*sensor_pos*, *sensor_los*, *background_atmosphere*, *surface_temperature*, *surface_reflectivity* describe the background state of the atmosphere and the sensor position and line of sight. \n",
177 | "*background_atmosphere* has a double function. It includes the background atmospheric state (e. g. water vapor profile) for the non retrieved atmospheric variables and the a priori temperature profile. \n",
178 | "If *sensor_description* is set to *[]* then the function uses f_grid. If *sensor_description* is set then the sensor description is used.\n",
179 | "\n",
180 | "The function returns the retrieved temperature profile, the total error of the retrieved temperature profile and the fitted brightness temperature measurement. \n",
181 | "\n",
182 | "Use the prepared covariance matrices for the retrieval and the dropsonde data as background state and a priori temperature profile.\n",
183 | "\n",
184 | "Check the results:\n",
185 | "* Plot the a priori temperature profile, retrieved temperature profile in one plot and the difference between the retrieved and a priori temperature profile in a second plot. \n",
186 | "* Plot the difference between the fitted and measured brightness temperature.\n",
187 | "* If you want you can also plot the averaging kernels and the gain matrix. To do that, set the keyword *Diagnostics=True* in the function *temperature_retrieval* and add *A* and *G* to the output of the function.\n"
188 | ]
189 | },
190 | {
191 | "cell_type": "code",
192 | "execution_count": null,
193 | "metadata": {},
194 | "outputs": [],
195 | "source": [
196 | "# T_ret, DeltaT, y_fit = temperature_retrieval(\n",
197 | " # y_obs,\n",
198 | " # [],\n",
199 | " # sensor_pos,\n",
200 | " # sensor_los,\n",
201 | " # dropsonde,\n",
202 | " # surface_temperature,\n",
203 | " # surface_reflectivity,\n",
204 | " # S_y,\n",
205 | " # S_a,\n",
206 | " # sensor_description=sensor_characteristics)"
207 | ]
208 | },
209 | {
210 | "cell_type": "markdown",
211 | "metadata": {},
212 | "source": [
213 | "#### 5)\n",
214 | "Repeat the retrieval for the rest of the flight segment. \n",
215 | "\n",
216 | "* Plot the retrieved temperature profiles and the difference to the a priori as function of altitude and latitude. \n",
217 | "* Plot the total error of the retrieved temperature profiles as function of altitude and latitude. \n",
218 | "* Plot the difference between the fitted and measured brightness temperature (residuals) as function of latitude.\n",
219 | "\n",
220 | "\n",
221 | "\n"
222 | ]
223 | }
224 | ],
225 | "metadata": {
226 | "language_info": {
227 | "name": "python"
228 | }
229 | },
230 | "nbformat": 4,
231 | "nbformat_minor": 2
232 | }
233 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/observation/SensorCharacteristics_118GHz.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 118750000000 1300000000 0 400000000 400000000
5 | 118750000000 2300000000 0 400000000 400000000
6 | 118750000000 4200000000 0 400000000 400000000
7 | 118750000000 8500000000 0 400000000 400000000
8 |
9 |
10 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/observation/SensorCharacteristics_22GHz.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 22240000000 0 0 230000000 230000000
5 | 23040000000 0 0 230000000 230000000
6 | 23840000000 0 0 230000000 230000000
7 | 25440000000 0 0 230000000 230000000
8 | 26240000000 0 0 230000000 230000000
9 | 27840000000 0 0 230000000 230000000
10 | 31400000000 0 0 230000000 230000000
11 |
12 |
13 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/observation/SensorCharacteristics_50GHz.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 50300000000 0 0 230000000 230000000
5 | 51760000000 0 0 230000000 230000000
6 | 52800000000 0 0 230000000 230000000
7 | 53750000000 0 0 230000000 230000000
8 | 54940000000 0 0 230000000 230000000
9 | 56660000000 0 0 230000000 230000000
10 | 58000000000 0 0 230000000 230000000
11 |
12 |
13 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/observation/dropsonde.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | "T"
6 | "z"
7 | "abs_species-H2O"
8 |
9 |
10 | 100991.714773996
11 | 100118.556362501
12 | 98859.1326574627
13 | 97447.1792793992
14 | 95912.5130513097
15 | 94245.3827277987
16 | 92435.1522220354
17 | 90477.5419908952
18 | 88369.5269449006
19 | 86096.4534870001
20 | 83655.776593574
21 | 81043.8567465965
22 | 78259.9020132852
23 | 75297.0137584802
24 | 72159.0170324597
25 | 68855.839496041
26 | 65486.7349102027
27 | 62170.9128415888
28 | 58936.5773128153
29 | 55792.3991428444
30 | 52739.7526332444
31 | 49778.119471599
32 | 46910.549232156
33 | 44140.2804668532
34 | 41465.5097778122
35 | 38884.2710114648
36 | 36405.5032496594
37 | 34026.8144066115
38 | 31742.8596914261
39 | 29556.1285333279
40 | 27470.3254191222
41 | 25488.7224155002
42 | 23604.2415477494
43 | 21810.9156267841
44 | 20111.654774372
45 | 18509.696890414
46 | 16999.9202398639
47 | 15627.1118335754
48 | 14433.9247288444
49 | 13401.9535181492
50 |
51 |
52 |
53 |
54 |
55 |
56 | 300.314115594592
57 | 300.915330372873
58 | 298.782312710637
59 | 297.87411501143
60 | 296.774035926772
61 | 295.468103462684
62 | 293.899684949495
63 | 293.722830361289
64 | 292.509383509685
65 | 290.004325813321
66 | 291.232271683131
67 | 290.331672981929
68 | 289.127795984874
69 | 288.461313749925
70 | 286.567846897965
71 | 285.742079170901
72 | 284.508628125183
73 | 281.00804993402
74 | 278.814059988081
75 | 276.349252248762
76 | 273.500463859992
77 | 269.67472613962
78 | 266.822655297673
79 | 262.036218763523
80 | 258.954221987858
81 | 255.384988206163
82 | 250.795313692145
83 | 247.982466459245
84 | 245.528285350701
85 | 242.50712954472
86 | 238.279417795557
87 | 234.888612083524
88 | 230.867300106856
89 | 227.615188981353
90 | 223.969615336887
91 | 220.952525418782
92 | 215.569689644477
93 | 210.398553130983
94 | 206.288461464893
95 | 203.153408978163
96 | 25.547341549291
97 | 102.453953700783
98 | 214.224428733893
99 | 340.739264091559
100 | 479.741135858856
101 | 632.656472940361
102 | 801.060679747995
103 | 986.068051768615
104 | 1188.90479919974
105 | 1412.00128119815
106 | 1657.00720511918
107 | 1926.26118536755
108 | 2222.34076413855
109 | 2547.9738575083
110 | 2905.17349756291
111 | 3296.67714184786
112 | 3713.83095152188
113 | 4142.75693748272
114 | 4579.36015279314
115 | 5022.55042152067
116 | 5472.7646246716
117 | 5930.27152110412
118 | 6394.30634571177
119 | 6864.08837890625
120 | 7339.79788072974
121 | 7821.60314185218
122 | 8308.05660605642
123 | 8800.01743985066
124 | 9299.02158592022
125 | 9804.49139890418
126 | 10315.510506689
127 | 10830.9006066786
128 | 11352.2669170008
129 | 11881.4726648921
130 | 12416.8489180033
131 | 12955.7686540722
132 | 13496.094402482
133 | 14018.3408721654
134 | 14501.4999330234
135 | 14944.3979319345
136 | 0.0264307447277379
137 | 0.0248531839334627
138 | 0.0290274537961586
139 | 0.0246460524271902
140 | 0.0237457494839289
141 | 0.0195948415713455
142 | 0.0214977838780675
143 | 0.023192978784024
144 | 0.0176328373717528
145 | 0.0159647697511969
146 | 0.0136071479898486
147 | 0.00701667673311904
148 | 0.00511076782711294
149 | 0.00371916509870556
150 | 0.00210145532531236
151 | 0.000549513831319697
152 | 0.000387273481173217
153 | 0.000520133120816913
154 | 0.000927094164051217
155 | 0.000808087526052102
156 | 0.000762713032760954
157 | 0.000631177500790877
158 | 0.000448501481590075
159 | 0.000320447281074285
160 | 0.000150942233913906
161 | 9.96347020025363e-05
162 | 8.51211601336836e-05
163 | 7.11754914919099e-05
164 | 7.29824938614383e-05
165 | 4.17257583985655e-05
166 | 3.03399538338396e-05
167 | 2.24581888729241e-05
168 | 2.9441107824495e-05
169 | 3.68162345410877e-05
170 | 1.51035150045871e-05
171 | 1.07382384452592e-05
172 | 1.17604593018267e-05
173 | 6.75492089457961e-06
174 | 5.5514524196455e-06
175 | 4.90342904540925e-06
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/observation/lat.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16.0021457672119
5 | 16.0043563842773
6 | 16.006555557251
7 | 16.0087547302246
8 | 16.0109691619873
9 | 16.0131778717041
10 | 16.0153770446777
11 | 16.017578125
12 | 16.0198040008545
13 | 16.0220031738281
14 | 16.0242042541504
15 | 16.0264148712158
16 | 16.0286273956299
17 | 16.0308265686035
18 | 16.0330390930176
19 | 16.0352382659912
20 | 16.0374374389648
21 | 16.0396633148193
22 | 16.0418605804443
23 | 16.0440635681152
24 | 16.0462760925293
25 | 16.0484886169434
26 | 16.0506858825684
27 | 16.0528984069824
28 | 16.0550975799561
29 | 16.0573120117188
30 | 16.0595264434814
31 | 16.0617237091064
32 | 16.0639209747314
33 | 16.0661487579346
34 | 16.0683479309082
35 | 16.0705471038818
36 | 16.0727596282959
37 | 16.07497215271
38 | 16.077169418335
39 | 16.0793838500977
40 | 16.0815830230713
41 | 16.0837821960449
42 | 16.0860080718994
43 | 16.0882053375244
44 | 16.0904064178467
45 | 16.0926342010498
46 | 16.0948333740234
47 | 16.0970306396484
48 | 16.0992298126221
49 | 16.1014404296875
50 | 16.1036548614502
51 | 16.1058540344238
52 | 16.1080646514893
53 | 16.1102638244629
54 | 16.112476348877
55 | 16.114688873291
56 | 16.1168880462646
57 | 16.1190872192383
58 | 16.1213130950928
59 | 16.123514175415
60 | 16.12571144104
61 | 16.1279258728027
62 | 16.1301383972168
63 | 16.1323356628418
64 | 16.1345500946045
65 | 16.1367473602295
66 | 16.1389465332031
67 | 16.1411743164062
68 | 16.1438026428223
69 | 16.1460037231445
70 | 16.1482162475586
71 | 16.150426864624
72 | 16.1526260375977
73 | 16.1548385620117
74 | 16.157039642334
75 | 16.159252166748
76 | 16.1614646911621
77 | 16.1636638641357
78 | 16.1658611297607
79 | 16.1680889129639
80 | 16.1702861785889
81 | 16.1724834442139
82 | 16.1746978759766
83 | 16.176908493042
84 | 16.1791076660156
85 | 16.1813220977783
86 | 16.1835193634033
87 | 16.185718536377
88 | 16.1875038146973
89 | 16.1897144317627
90 | 16.1919136047363
91 | 16.19411277771
92 | 16.1963386535645
93 | 16.1985378265381
94 | 16.2007369995117
95 | 16.2033805847168
96 | 16.2055912017822
97 | 16.2077903747559
98 | 16.2100028991699
99 | 16.2122001647949
100 | 16.2144165039062
101 | 16.2166290283203
102 | 16.2188262939453
103 | 16.2210273742676
104 | 16.2232532501221
105 | 16.2254524230957
106 | 16.2276515960693
107 | 16.2298641204834
108 | 16.2320766448975
109 | 16.2342758178711
110 | 16.2364864349365
111 | 16.2386875152588
112 | 16.2408866882324
113 | 16.2431106567383
114 | 16.2453136444092
115 | 16.2475109100342
116 | 16.2497234344482
117 | 16.2519378662109
118 | 16.2541351318359
119 | 16.2563438415527
120 | 16.2585430145264
121 | 16.2607574462891
122 | 16.2629680633545
123 | 16.2651691436768
124 | 16.2673683166504
125 | 16.2695922851562
126 | 16.2717952728271
127 | 16.2739925384521
128 | 16.2761898040771
129 | 16.2784156799316
130 | 16.2806148529053
131 | 16.2828159332275
132 | 16.2850284576416
133 | 16.2872257232666
134 | 16.2894382476807
135 | 16.2916507720947
136 | 16.2938499450684
137 | 16.2960510253906
138 | 16.2982769012451
139 | 16.3004741668701
140 | 16.3026752471924
141 | 16.3048858642578
142 | 16.3071002960205
143 | 16.3092994689941
144 | 16.3115100860596
145 | 16.3137092590332
146 | 16.3159217834473
147 | 16.3181343078613
148 | 16.3203372955322
149 | 16.3225345611572
150 | 16.3247585296631
151 | 16.3269577026367
152 | 16.3291568756104
153 | 16.331371307373
154 | 16.3335704803467
155 | 16.3357810974121
156 | 16.3379917144775
157 | 16.3401927947998
158 | 16.3423919677734
159 | 16.3446140289307
160 | 16.3468170166016
161 | 16.3490142822266
162 | 16.3512287139893
163 | 16.3534412384033
164 | 16.3556385040283
165 | 16.3578510284424
166 | 16.3600482940674
167 | 16.3622627258301
168 | 16.3644618988037
169 | 16.3666763305664
170 | 16.3688735961914
171 | 16.371072769165
172 | 16.3732986450195
173 | 16.3754978179932
174 | 16.3776969909668
175 | 16.3799209594727
176 | 16.3821201324463
177 | 16.3843193054199
178 | 16.386531829834
179 | 16.3887348175049
180 | 16.3909435272217
181 | 16.3931560516357
182 | 16.3953552246094
183 | 16.397554397583
184 | 16.3997821807861
185 | 16.4019794464111
186 | 16.4041786193848
187 | 16.4063892364502
188 | 16.4086036682129
189 | 16.4108047485352
190 | 16.4130153656006
191 | 16.4152145385742
192 | 16.4174270629883
193 | 16.419641494751
194 | 16.421838760376
195 | 16.4240398406982
196 | 16.4262619018555
197 | 16.4284591674805
198 | 16.4306602478027
199 | 16.4328727722168
200 | 16.4350700378418
201 | 16.4372825622559
202 | 16.4394950866699
203 | 16.4416961669922
204 | 16.4438953399658
205 | 16.4461059570312
206 | 16.4483184814453
207 | 16.4505157470703
208 | 16.4527168273926
209 | 16.4549446105957
210 | 16.4571418762207
211 | 16.4593410491943
212 | 16.4615535736084
213 | 16.4637660980225
214 | 16.4659652709961
215 | 16.4681758880615
216 | 16.4703750610352
217 | 16.4725761413574
218 | 16.4748001098633
219 | 16.4770011901855
220 | 16.4792003631592
221 | 16.4814109802246
222 | 16.4836254119873
223 | 16.4858226776123
224 | 16.4880352020264
225 | 16.490234375
226 | 16.4924449920654
227 | 16.4946575164795
228 | 16.4968585968018
229 | 16.4990577697754
230 | 16.5012836456299
231 | 16.5034809112549
232 | 16.5056800842285
233 | 16.5078926086426
234 | 16.510103225708
235 | 16.5123023986816
236 | 16.5145149230957
237 | 16.5167140960693
238 | 16.518913269043
239 | 16.5211410522461
240 | 16.5233383178711
241 | 16.5255374908447
242 | 16.5277633666992
243 | 16.5299625396729
244 | 16.5321598052979
245 | 16.5343608856201
246 | 16.5365734100342
247 | 16.5387840270996
248 | 16.5409832000732
249 | 16.5431976318359
250 | 16.5453948974609
251 | 16.5476093292236
252 | 16.5498180389404
253 | 16.5520191192627
254 | 16.554220199585
255 | 16.5564441680908
256 | 16.5586433410645
257 | 16.5608425140381
258 | 16.5630550384521
259 | 16.5652656555176
260 | 16.5674648284912
261 | 16.5696792602539
262 | 16.5718765258789
263 | 16.5740756988525
264 | 16.5763034820557
265 | 16.5785007476807
266 | 16.5806999206543
267 | 16.5829124450684
268 | 16.5851249694824
269 | 16.5873241424561
270 | 16.5895347595215
271 | 16.5917358398438
272 | 16.5939483642578
273 | 16.5961589813232
274 | 16.5983581542969
275 | 16.6005554199219
276 | 16.6027793884277
277 | 16.60498046875
278 | 16.6071796417236
279 | 16.6093940734863
280 | 16.6116046905518
281 | 16.6138038635254
282 | 16.6160163879395
283 | 16.6182136535645
284 | 16.6204128265381
285 | 16.6226234436035
286 | 16.6248340606689
287 | 16.6270370483398
288 | 16.6292343139648
289 | 16.6314640045166
290 | 16.6336612701416
291 | 16.635856628418
292 | 16.6380710601807
293 | 16.6402835845947
294 | 16.642484664917
295 | 16.6446952819824
296 | 16.6468944549561
297 | 16.6491069793701
298 | 16.6513195037842
299 | 16.6535186767578
300 | 16.6557178497314
301 | 16.6579418182373
302 | 16.6601428985596
303 | 16.6623420715332
304 | 16.6645545959473
305 | 16.6667671203613
306 | 16.6689643859863
307 | 16.6711769104004
308 | 16.6733741760254
309 | 16.6755752563477
310 | 16.6777992248535
311 | 16.6799964904785
312 | 16.6821975708008
313 | 16.6844081878662
314 | 16.6866226196289
315 | 16.6888198852539
316 | 16.6910305023193
317 | 16.6932315826416
318 | 16.6954441070557
319 | 16.6976566314697
320 | 16.6998538970947
321 | 16.7020530700684
322 | 16.7042808532715
323 | 16.7064781188965
324 | 16.7086772918701
325 | 16.7108764648438
326 | 16.7131023406982
327 | 16.7153015136719
328 | 16.7174987792969
329 | 16.7197113037109
330 | 16.7219104766846
331 | 16.72412109375
332 | 16.7263374328613
333 | 16.7285346984863
334 | 16.73073387146
335 | 16.7329597473145
336 | 16.7351570129395
337 | 16.7373580932617
338 | 16.7395668029785
339 | 16.7417793273926
340 | 16.7439804077148
341 | 16.7461929321289
342 | 16.7483921051025
343 | 16.7506065368652
344 | 16.752815246582
345 | 16.755012512207
346 | 16.7572154998779
347 | 16.7594394683838
348 | 16.7616405487061
349 | 16.7638378143311
350 | 16.7660465240479
351 | 16.7682476043701
352 | 16.7704582214355
353 | 16.7726726531982
354 | 16.7748699188232
355 | 16.7770690917969
356 | 16.779296875
357 | 16.7814922332764
358 | 16.78369140625
359 | 16.7859039306641
360 | 16.7881164550781
361 | 16.7903175354004
362 | 16.7925281524658
363 | 16.7947273254395
364 | 16.7969379425049
365 | 16.7991371154785
366 | 16.8013496398926
367 | 16.8035507202148
368 | 16.8057479858398
369 | 16.8079738616943
370 | 16.810173034668
371 | 16.8123722076416
372 | 16.8145980834961
373 | 16.8167953491211
374 | 16.8189964294434
375 | 16.8212070465088
376 | 16.8234062194824
377 | 16.8256187438965
378 | 16.8278293609619
379 | 16.8300304412842
380 | 16.8322296142578
381 | 16.8344554901123
382 | 16.8366546630859
383 | 16.8388519287109
384 | 16.8410625457764
385 | 16.8432769775391
386 | 16.8454742431641
387 | 16.8476867675781
388 | 16.8498840332031
389 | 16.8520965576172
390 | 16.8543071746826
391 | 16.8565082550049
392 | 16.8587055206299
393 | 16.8609313964844
394 | 16.8631324768066
395 | 16.8653297424316
396 | 16.8675441741943
397 | 16.8697395324707
398 | 16.8719539642334
399 | 16.8741645812988
400 | 16.8763656616211
401 | 16.8785629272461
402 | 16.8812198638916
403 | 16.8834190368652
404 | 16.8856163024902
405 | 16.8878135681152
406 | 16.8900394439697
407 | 16.8922424316406
408 | 16.894437789917
409 | 16.8966484069824
410 | 16.8988628387451
411 | 16.9010620117188
412 | 16.9032745361328
413 | 16.9054718017578
414 | 16.9076728820801
415 | 16.9098968505859
416 | 16.9120979309082
417 | 16.9142951965332
418 | 16.9165191650391
419 | 16.9187183380127
420 | 16.920919418335
421 | 16.922700881958
422 | 16.924898147583
423 | 16.9271125793457
424 | 16.9293231964111
425 | 16.9315223693848
426 | 16.9337215423584
427 | 16.9363765716553
428 | 16.9385757446289
429 | 16.9407730102539
430 | 16.9429874420166
431 | 16.945198059082
432 | 16.9473991394043
433 | 16.9496097564697
434 | 16.9518089294434
435 | 16.954008102417
436 | 16.9562320709229
437 | 16.9584312438965
438 | 16.9606304168701
439 | 16.9628582000732
440 | 16.9650554656982
441 | 16.9672546386719
442 | 16.9694652557373
443 | 16.9716625213623
444 | 16.973876953125
445 | 16.97607421875
446 | 16.9782867431641
447 | 16.9804840087891
448 | 16.9826984405518
449 | 16.9849090576172
450 | 16.9871101379395
451 | 16.9893054962158
452 | 16.9915313720703
453 | 16.9937324523926
454 | 16.9959297180176
455 | 16.998140335083
456 |
457 |
458 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/prepare_airborne_data.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Thu Jan 2 19:53:32 2025
5 |
6 | THIS SCRIPT IS NOT INTENDED TO BE USED FOR THE EXERCISE!
7 | ITS ONLY PURPOSE IS TO CREATE THE DATA FOR THE EXERCISE BY THE DEVELOPER!
8 |
9 | Prepare data for exercise - Non-linear inversion
10 | Read in data from GEM tropical pacific scene and extract
11 | a slice of the data for given latitude and altitude range for
12 | a simulation of an airborne radiometer observation.
13 |
14 | To keep it simple the slice will be simply the center in x of the data.
15 |
16 | It also creates a dropsonde data set by selecting one of the profiles.
17 |
18 | The data will be saved in ARTS XML format.
19 |
20 | The data is also plotted for a quick check.
21 |
22 | The data is saved in the folder 'observation' in the current directory.
23 |
24 | The data is saved in the following files:
25 | - atmospheres_true.xml
26 | - aux1d_true.xml
27 | - aux2d_true.xml
28 | - dropsonde.xml
29 |
30 | The plots are saved in the folder 'check_plots' in the current directory.
31 |
32 | The plots are saved in the following files:
33 | - profiles.pdf
34 | - columns.pdf
35 | - dropsonde.pdf
36 |
37 | @author: Manfred Brath
38 | """
39 |
40 | import os
41 | from copy import deepcopy
42 | import numpy as np
43 | import xarray as xa
44 | from scipy.interpolate import interp1d
45 | import matplotlib.pyplot as plt
46 |
47 | # import seaborn as sns
48 |
49 | import pyarts as pa
50 |
51 | from typhon.physics import e_eq_mixed_mk
52 | from typhon.constants import gas_constant_water_vapor
53 |
54 | # %% paths / constants
55 |
56 | data_folder = (
57 | "/scratch/u237/user_data/mbrath/EarthCARE_Scenes/39320_test_data/"
58 | )
59 |
60 | # lat_range = [15.5, 16.5] # °
61 | lat_range = [16, 17]
62 | alt_max = 15e3 # m
63 |
64 | # Amount of Oxygen
65 | O2vmr = 0.2095
66 |
67 | # Amount of Nitrogen
68 | N2vmr = 0.7808
69 |
70 | #T emperature offset of dropsonde
71 | T_offset=1. #K
72 |
73 | # %% load data
74 |
75 |
76 | file_list = os.listdir(data_folder)
77 |
78 | # get rid of non_nc files
79 | files = [file for file in file_list if ".nc" in file]
80 |
81 | # estimate common prefix
82 | idx = [
83 | i for i in range(min(len(files[0]), len(files[1]))) if files[0][i] == files[1][i]
84 | ]
85 |
86 |
87 | # get variable names from list
88 | variable_names = [file[idx[-1] + 1 : -3] for file in files]
89 |
90 |
91 | # %% variable translation
92 | translator = {
93 | "water_content_cloud": "scat_species-LWC-mass_density",
94 | "water_content_ice": "scat_species-IWC-mass_density",
95 | "water_content_rain": "scat_species-RWC-mass_density",
96 | "water_content_snow": "scat_species-SWC-mass_density",
97 | "water_content_graupel": "scat_species-GWC-mass_density",
98 | "water_content_hail": "scat_species-HWC-mass_density",
99 | "number_concentration_cloud": "scat_species-LWC-number_density",
100 | "number_concentration_ice": "scat_species-IWC-number_density",
101 | "number_concentration_rain": "scat_species-RWC-number_density",
102 | "number_concentration_snow": "scat_species-SWC-number_density",
103 | "number_concentration_graupel": "scat_species-GWC-number_density",
104 | "number_concentration_hail": "scat_species-HWC-number_density",
105 | "relative_humidity": "abs_species-H2O",
106 | "temperature": "T",
107 | "height_thermodynamic": "z",
108 | "pressure_thermodynamic": "p_grid",
109 | }
110 |
111 |
112 | # Atmospheric field names
113 | atm_fieldnames = [
114 | "T",
115 | "z",
116 | "scat_species-LWC-mass_density",
117 | "scat_species-IWC-mass_density",
118 | "scat_species-RWC-mass_density",
119 | "scat_species-SWC-mass_density",
120 | "scat_species-GWC-mass_density",
121 | "scat_species-HWC-mass_density",
122 | "scat_species-LWC-number_density",
123 | "scat_species-IWC-number_density",
124 | "scat_species-RWC-number_density",
125 | "scat_species-SWC-number_density",
126 | "scat_species-GWC-number_density",
127 | "scat_species-HWC-number_density",
128 | "abs_species-H2O",
129 | "abs_species-O2",
130 | "abs_species-N2",
131 | ]
132 |
133 | # %%
134 |
135 | # first read latitude
136 | lat_idx = [i for i in range(len(files)) if "latitude" in files[i]][0]
137 | lat = xa.load_dataarray(data_folder + files[lat_idx]).to_numpy()
138 | central_idx = np.size(lat, axis=0) // 2
139 |
140 | latitude = lat[central_idx, :]
141 | lat_range_idx = np.where(
142 | np.logical_and(latitude > lat_range[0], latitude < lat_range[1])
143 | )[0]
144 |
145 |
146 | # %% now read data
147 |
148 | rawdata = {}
149 | cnt = 0
150 | N_nvars = len(variable_names)
151 |
152 | for var in variable_names:
153 |
154 | cnt += 1
155 | print(f"\n{cnt} of {N_nvars}")
156 | print(f"reading {var}")
157 |
158 | if var in translator:
159 | varname = translator[var]
160 | print(f"rename {var} -> {varname}")
161 | else:
162 | varname = var
163 | idx = [i for i in range(len(files)) if var in files[i]][0]
164 |
165 | temp = xa.open_dataarray(data_folder + files[idx])
166 | if temp.ndim == 3:
167 | rawdata[varname] = np.float64(
168 | temp[:, central_idx, lat_range_idx].to_numpy()[::-1, :]
169 | )
170 | # we have flipped the vertical directions as in ARTS 2.6 the pressure
171 | # must be ordered from high to low
172 |
173 | elif temp.ndim == 2:
174 | rawdata[varname] = np.float64(temp[central_idx, lat_range_idx].to_numpy())
175 | else:
176 | raise ValueError("There should be only 2d and 3d data...mmmh")
177 |
178 |
179 | # %% get atms on the same altitude grid
180 |
181 | N_profiles = len(rawdata["latitude"])
182 |
183 | z_default = np.mean(rawdata["z"], axis=1)
184 | z_default = z_default[z_default < alt_max]
185 |
186 | z_org = rawdata["z"] * 1.0
187 |
188 | for key in rawdata:
189 |
190 | if rawdata[key].ndim == 2:
191 |
192 | if np.size(rawdata[key], axis=0) >= len(z_default):
193 |
194 | print(f"reinterpolating {key}")
195 |
196 | data = np.zeros((len(z_default), N_profiles))
197 | for i in range(N_profiles):
198 | F_int = interp1d(
199 | z_org[:, i], rawdata[key][:, i], fill_value="extrapolate"
200 | )
201 | data[:, i] = F_int(z_default)
202 |
203 | rawdata[key] = data
204 |
205 |
206 | # %% now prepare data for arts
207 | # This means we have to convert relative humidity to vmr and
208 | # create batch_atm_compactand aux data
209 |
210 |
211 | # get all 2d variables that is not atm_fieldnames
212 | aux2d_fieldnames = [
213 | var
214 | for var in rawdata.keys()
215 | if var not in atm_fieldnames
216 | and np.size(rawdata[var], axis=0) == np.size(rawdata["p_grid"], axis=0)
217 | ]
218 |
219 | # get all 1d variables that is not atm_fieldnames
220 | aux1d_fieldnames = [
221 | var
222 | for var in rawdata.keys()
223 | if var not in atm_fieldnames and rawdata[var].ndim == 1
224 | ]
225 |
226 | batch_atms = pa.arts.ArrayOfGriddedField4()
227 | batch_aux2d = pa.arts.ArrayOfGriddedField2()
228 | batch_aux1d = pa.arts.ArrayOfGriddedField1()
229 |
230 | # allocate atm object
231 | atm = pa.arts.GriddedField4()
232 | atm.set_grid(0, atm_fieldnames)
233 | atm.set_grid(1, rawdata["p_grid"][:, 0])
234 | atm.data = np.zeros((len(atm.grids[0]), len(atm.grids[1]), 1, 1))
235 |
236 | # allocate aux2d object
237 | aux2d = pa.arts.GriddedField2()
238 | aux2d.set_grid(0, aux2d_fieldnames)
239 | aux2d.set_grid(1, rawdata["p_grid"][:, 0])
240 | aux2d.data = np.zeros((len(aux2d.grids[0]), len(aux2d.grids[1])))
241 |
242 | # allocate aux1d object
243 | aux1d = pa.arts.GriddedField1()
244 | aux1d.set_grid(0, aux1d_fieldnames)
245 | aux1d.data = np.zeros(len(aux1d.grids[0]))
246 |
247 |
248 | for i in range(N_profiles):
249 |
250 | if i % 50 == 0:
251 | print(f"processing profile {i} of {N_profiles}")
252 |
253 | atm.set_grid(1, rawdata["p_grid"][:, i])
254 | atm.data = np.zeros((len(atm.grids[0]), len(atm.grids[1]), 1, 1))
255 |
256 | for j, var in enumerate(atm_fieldnames):
257 | if var in rawdata:
258 | atm.data[j, :, 0, 0] = rawdata[var][:, i]
259 |
260 | # Convert mass densities from g m^{-3} to kg m^{-3}
261 | if "mass_density" in var:
262 | atm.data[j, :, 0, 0] /= 1000
263 |
264 | # convert relative humidity to vmr
265 | if var == "abs_species-H2O":
266 | temp = (
267 | atm.data[j, :, 0, 0]
268 | * e_eq_mixed_mk(atm.data[0, :, 0, 0])
269 | / atm.grids[1][:]
270 | )
271 | atm.data[j, :, 0, 0] = temp
272 |
273 | if var == "abs_species-O2":
274 | atm.data[j, :, 0, 0] = O2vmr
275 |
276 | if var == "abs_species-N2":
277 | atm.data[j, :, 0, 0] = N2vmr
278 |
279 |
280 | batch_atms.append(deepcopy(atm))
281 |
282 | # now the aux data
283 | for j, var in enumerate(aux2d_fieldnames):
284 | aux2d.data[j, :] = rawdata[var][:, i]
285 |
286 | batch_aux2d.append(deepcopy(aux2d))
287 |
288 | for j, var in enumerate(aux1d_fieldnames):
289 | aux1d.data[j] = rawdata[var][i]
290 |
291 | batch_aux1d.append(deepcopy(aux1d))
292 |
293 |
294 | # %% before we save the data, let's do some checks
295 |
296 | # calculate water columns and plot them
297 |
298 | columns = {}
299 | col_names = ["LWP", "IWP", "RWP", "SWP", "GWP", "HWP", "IWV"]
300 | content_names = ["LWC", "IWC", "RWC", "SWC", "GWC", "HWC", "H2O"]
301 | for name in col_names:
302 | columns[name] = np.zeros(N_profiles)
303 |
304 |
305 | for i in range(N_profiles):
306 |
307 | for j, name in enumerate(col_names):
308 |
309 | names = [str(name) for name in batch_atms[i].grids[0]]
310 | idx = [k for k in range(len(names)) if content_names[j] in names[k]]
311 |
312 | if len(idx) > 1:
313 | idx = [idx_k for idx_k in idx if "mass" in names[idx_k]][0]
314 | else:
315 | idx = idx[0]
316 |
317 | if name == "IWV":
318 | # WV=rawdata.vmr_h2o./T.*p/R_h2o;
319 | density = (
320 | batch_atms[i].data[idx, :, 0, 0]
321 | / batch_atms[i].data[0, :, 0, 0]
322 | * batch_atms[i].grids[1]
323 | / gas_constant_water_vapor
324 | )
325 |
326 | columns[name][i] = np.trapezoid(density, x=batch_atms[i].data[1, :, 0, 0])
327 | else:
328 |
329 | # breakpoint()
330 |
331 | columns[name][i] = np.trapezoid(
332 | batch_atms[i].data[idx, :, 0, 0],
333 | x=batch_atms[i].data[1, :, 0, 0],
334 | )
335 |
336 |
337 | # %% plot vertical coluns for check for checks
338 |
339 | lat = np.array([a.data[0] for a in batch_aux1d])
340 |
341 |
342 | # plt.style.use('ggplot')
343 | fig, ax = plt.subplots(4, 2, figsize=(16, 10))
344 |
345 |
346 | for i, name in enumerate(col_names):
347 | ax_k = ax[i // 2, i % 2]
348 | ax_k.plot(lat, columns[name], label=name)
349 | ax_k.set_title(name)
350 | ax_k.set_xlabel("latitude / °")
351 | ax_k.set_ylabel("column / kg m$^{-1}$ ")
352 |
353 |
354 | # %% Create 'dropsonde data'
355 | # simply select one of these profiles
356 | # to keep it simple we simply take the middle
357 |
358 | idx_selected = N_profiles // 2
359 |
360 | dropsonde = pa.arts.GriddedField4()
361 | dropsonde.set_grid(0, ["T", "z", "abs_species-H2O"])
362 | dropsonde.set_grid(1, batch_atms[idx_selected].grids[1][:])
363 | dropsonde.data = np.zeros((3, len(dropsonde.grids[1]), 1, 1))
364 | dropsonde.data[0, :, 0, 0] = batch_atms[idx_selected].data[0, :, 0, 0]
365 | dropsonde.data[1, :, 0, 0] = batch_atms[idx_selected].data[1, :, 0, 0]
366 | dropsonde.data[2, :, 0, 0] = batch_atms[idx_selected].data[14, :, 0, 0]
367 |
368 | # add some noise
369 | rng = np.random.default_rng(12345)
370 | T_noise_free = dropsonde.data[0, :, 0, 0] * 1.0
371 | dropsonde.data[0, :, 0, 0] += rng.normal(0, 0.5, len(dropsonde.grids[1]))+T_offset
372 |
373 | vmr_noise_free = dropsonde.data[2, :, 0, 0] * 1.0
374 | temp = np.log10(dropsonde.data[2, :, 0, 0])
375 | temp += rng.normal(0, 0.05, len(dropsonde.grids[1]))
376 | dropsonde.data[2, :, 0, 0] = 10**temp
377 |
378 |
379 | # plot dropsonde data
380 | fig2, ax2 = plt.subplots(1, 2, figsize=(10, 5))
381 |
382 | ax2[0].plot(dropsonde.data[0, :, 0, 0], dropsonde.grids[1] / 1e3, label="obs") # T
383 | ax2[0].plot(T_noise_free, dropsonde.grids[1] / 1e3, label="true")
384 | ax2[0].set_title("Temperature")
385 | ax2[0].set_xlabel("T / K")
386 | ax2[0].set_ylabel("p / hPa")
387 | ax2[0].set_yscale("log")
388 | ax2[0].invert_yaxis()
389 | ax2[0].legend()
390 |
391 | ax2[1].loglog(dropsonde.data[2, :, 0, 0], dropsonde.grids[1] / 1e3, label="obs") # H2O
392 | ax2[1].loglog(vmr_noise_free, dropsonde.grids[1] / 1e3, label="true")
393 | ax2[1].set_title("H2O")
394 | ax2[1].set_xlabel("H2O / vmr")
395 | ax2[1].set_ylabel("p / hPa")
396 | ax2[1].invert_yaxis()
397 | ax2[1].legend()
398 |
399 |
400 |
401 | # %% now plot profles for check
402 |
403 | plot_vars = ["T", "abs_species-H2O"] + [
404 | name for name in atm_fieldnames if "mass_density" in name
405 | ]
406 | # plot_vars=['T']
407 |
408 | fig1, ax1 = plt.subplots(4, 2, figsize=(16, 10), sharey=True, sharex=True)
409 |
410 | p_grid = np.mean([a.grids[1] for a in batch_atms], axis=0)
411 |
412 |
413 | for i, name in enumerate(plot_vars):
414 |
415 | print(f"{name} - {i}")
416 |
417 | ax_k = ax1[i // 2, i % 2]
418 |
419 | names = [str(name) for name in batch_atms[0].grids[0]]
420 | idx = [k for k in range(len(names)) if name in names[k]][0]
421 |
422 | print(f"idx: {idx}")
423 |
424 | data = np.array([a.data[idx, :, 0, 0] for a in batch_atms])
425 |
426 | if name == "T":
427 | data -= np.mean(data, axis=0)
428 | # data -= dropsonde.data[0,:,0,0]
429 | cmap = "YlOrRd"
430 | pcm = ax_k.pcolormesh(
431 | lat,
432 | p_grid / 1e3,
433 | data.T,
434 | cmap=cmap,
435 | clim=[np.min(data), np.max(data)],
436 | rasterized=True,
437 | )
438 |
439 | else:
440 | data[data < 1e-10] = 1e-10
441 | data = np.log10(data)
442 | cmap = "Blues"
443 | pcm = ax_k.pcolormesh(
444 | lat, p_grid / 1e3, data.T, cmap=cmap, clim=[-6, 1], rasterized=True
445 | )
446 |
447 | ax_k.set_title(name)
448 | ax_k.set_xlabel("latitude / °")
449 | ax_k.set_ylabel("p / hPa")
450 | ax_k.set_yscale("log")
451 |
452 | cbar = fig1.colorbar(pcm, ax=ax_k)
453 |
454 | if name == "T":
455 | cbar.set_label("T - T$_{mean}$ / K")
456 | elif "mass_density" in name:
457 | cbar.set_label(" 10$^x$ kg m$^{-3}$")
458 | elif name == "abs_species-H2O":
459 | cbar.set_label("10$^x$ vmr")
460 |
461 |
462 | ax_k.invert_yaxis()
463 |
464 |
465 |
466 | # %% save data
467 |
468 | batch_atms.savexml("atmosphere/atmospheres_true.xml")
469 | batch_aux1d.savexml("atmosphere/aux1d_true.xml")
470 | batch_aux2d.savexml("atmosphere/aux2d_true.xml")
471 |
472 | dropsonde.savexml("observation/dropsonde.xml")
473 |
474 | # %% save figures
475 |
476 | fig1.savefig("check_plots/profiles.pdf")
477 | fig.savefig("check_plots/columns.pdf")
478 | fig2.savefig("check_plots/dropsonde.pdf")
479 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/simulate_airborne_measurement.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Simulate airborne radiometric measurements from a HALO-like airplane.
5 |
6 | This script simulates radiometric measurements of atmospheric radiation in three frequency bands:
7 | - H2O (22 GHz)
8 | - O2_low (50 GHz)
9 | - O2_high (118 GHz)
10 |
11 | The simulation assumes:
12 | - Aircraft altitude of 15 km
13 | - Nadir-looking geometry (180 degree line of sight)
14 | - Known constant surface reflectivity of 0.4
15 | - Known constant surface temperature of 300K
16 |
17 | The script:
18 | 1. Loads atmospheric data and auxiliary information
19 | 2. Simulates clean measurements for each frequency band
20 | 3. Adds random measurement noise based on specified NeDT
21 | 4. Saves results as XML files
22 | 5. Generates plots of simulated measurements vs latitude
23 |
24 | Files:
25 | Input:
26 | - atmosphere/atmospheres_true.xml: Atmospheric profiles
27 | - atmosphere/aux1d_true.xml: Auxiliary data including latitudes
28 |
29 | Output:
30 | - observation/y_obs_*.xml: Simulated noisy measurements
31 | - observation/f_grid_*.xml: Frequency grids
32 | - observation/lat.xml: Latitude points
33 | - check_plots/airborne_measurement.pdf: Visualization of results
34 |
35 | Dependencies:
36 | - numpy: For numerical operations
37 | - matplotlib: For plotting
38 | - pyarts: For XML handling
39 | - nonlin_oem: Custom module for radiative transfer calculations
40 |
41 | Created on Sat Jan 4 00:42:52 2025
42 | @author: Manfred Brath
43 | """
44 |
45 | import numpy as np
46 | import matplotlib.pyplot as plt
47 | from pyarts import xml
48 |
49 | import nonlin_oem as nlo
50 |
51 |
52 | # =============================================================================
53 | # %% paths and constants
54 | # =============================================================================
55 |
56 |
57 | # atmospheric data
58 | atms = xml.load("atmosphere/atmospheres_true.xml")
59 | auxs = xml.load("atmosphere/aux1d_true.xml")
60 |
61 | # surface reflectivity
62 | # It is assumed that the surface reflectivity is known and constant
63 | surface_reflectivity = 0.4
64 |
65 | # surface temperature
66 | # It is assumed that the surface temperature is known and constant
67 | surface_temperature = 300.0 # K
68 |
69 | # define sensor positions and line of sight
70 | # we assume a HALO like airplane with a sensor at 15 km altitude and a line of sight of 180 degrees
71 | sensor_altitude = 15000.
72 | sensor_los = 180.
73 |
74 | # set the random number generator
75 | rng = np.random.default_rng(12345)
76 |
77 |
78 | # latitude
79 | lat = np.array([auxs[i].data[0] for i in range(len(auxs))])
80 |
81 | # =============================================================================
82 | # %% simulate the observation
83 |
84 |
85 | bands = ["K", "V", "F"]
86 | suffixes = ["22GHz", "50GHz", "118GHz"]
87 |
88 |
89 | results = {}
90 | for band, suffix in zip(bands, suffixes):
91 |
92 | print(f"\n{band}\n")
93 |
94 | # set the "channels" (freqquency grid) for the simulation
95 | sensor_description_fine, NeDT, Accuracy, FWHM_Antenna=nlo.Hamp_channels([band], rel_mandatory_grid_spacing=1./1.)
96 |
97 | # # setup the basics
98 | # ws = nlo.basic_setup(f_grid)
99 |
100 | y = np.zeros((len(atms), np.size(sensor_description_fine,0)))
101 |
102 | for i in range(len(atms)):
103 | if i % 10 == 0:
104 | print(f"Simulating measurement for atm {i}")
105 |
106 | atm = atms[i]
107 | y[i, :], _ = nlo.Forward_model(
108 | [],
109 | atm,
110 | surface_reflectivity,
111 | surface_temperature,
112 | sensor_altitude,
113 | sensor_los,
114 | sensor_description=sensor_description_fine
115 | )
116 |
117 |
118 | y_obs = y + rng.normal(0, NeDT, y.shape)
119 |
120 |
121 | results[("y_" + band)] = y
122 | results[("y_obs_" + band)] = y_obs
123 | results[("f_grid_" + band)] = sensor_description_fine[:,0]+sensor_description_fine[:,1]+sensor_description_fine[:,2]
124 | results[('NeDT_'+band)]=NeDT
125 | results[(band + "_suffix")] = suffix
126 | results[('sensor_description_' + band)] = sensor_description_fine
127 |
128 | Y = nlo.pa.arts.Matrix(y)
129 | Y_obs = nlo.pa.arts.Matrix(y_obs)
130 | SensorCharacteristics = nlo.pa.arts.Matrix(sensor_description_fine)
131 |
132 | Y_obs.savexml(f"observation/y_obs_{suffix}.xml")
133 | SensorCharacteristics.savexml(
134 | f"observation/SensorCharacteristics_{suffix}.xml"
135 | )
136 |
137 | print("ddd")
138 |
139 |
140 | Lat = nlo.pa.arts.Vector(lat)
141 | Lat.savexml("observation/lat.xml")
142 |
143 |
144 | # %% plot results
145 |
146 | cmap = np.array(
147 | [
148 | [0, 0.44701, 0.74101, 1],
149 | [0.85001, 0.32501, 0.09801, 1],
150 | [0.92901, 0.69401, 0.12501, 1],
151 | [0.49401, 0.18401, 0.55601, 1],
152 | [0.46601, 0.67401, 0.18801, 1],
153 | [0.30101, 0.74501, 0.93301, 1],
154 | [0.63501, 0.07801, 0.18401, 1],
155 | ]
156 | )
157 |
158 |
159 | fig, ax = plt.subplots(3, 1, figsize=(16, 10))
160 |
161 | for i, band in enumerate(bands):
162 |
163 | ax[i].set_prop_cycle(color=cmap)
164 | sen_desc=results[f"sensor_description_{band}"]
165 |
166 | for j in range(np.size(sen_desc,0)):
167 | if sen_desc[j,1]+sen_desc[j,2]>0:
168 | label=f"{sen_desc[j,0]/1e9:0.2f}"+" $\pm$ "+f"{(sen_desc[j,1]+sen_desc[j,2])/1e9:0.2f} GHz"
169 | else:
170 | label=f"{sen_desc[j,0]/1e9:0.2f}"
171 |
172 | ax[i].plot(lat, results[(f"y_{band}")][:, j], label=label)
173 |
174 | ax[i].set_prop_cycle(color=cmap)
175 | for j in range(np.size(sen_desc,0)):
176 | if sen_desc[j,1]+sen_desc[j,2]>0:
177 | label=f"{sen_desc[j,0]/1e9:0.2f}"+" $\pm$ "+f"{(sen_desc[j,1]+sen_desc[j,2])/1e9:0.2f} GHz"
178 | else:
179 | label=f"{sen_desc[j,0]/1e9:0.2f}"
180 | ax[i].plot(
181 | lat, results[(f"y_obs_{band}")][:, j], "--", label=f"{label} (obs)"
182 | )
183 |
184 | ax[i].set_title(band)
185 | ax[i].legend()
186 | ax[i].set_xlabel("Latitude")
187 | ax[i].set_ylabel("Brightness temperature [K]")
188 | ax[i].grid()
189 |
190 |
191 | fig.tight_layout()
192 | fig.savefig("check_plots/airborne_measurement.pdf")
193 |
--------------------------------------------------------------------------------
/exercises/06-non_lin_inversion/temperature_retrieval.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Temperature Retrieval Analysis Script
5 |
6 | This script performs temperature profile retrievals from radiometric observations
7 | in the 50 GHz range using the Optimal Estimation Method (OEM).
8 |
9 | The script:
10 | 1. Loads atmospheric data (true profiles, a priori data from dropsondes)
11 | 2. Loads observation data (frequencies, measurements, location)
12 | 3. Performs single profile retrieval with diagnostics
13 | 4. Performs retrieval for a complete observation segment
14 | 5. Creates visualization plots of the results
15 |
16 | Key Features:
17 | - Single profile retrieval with averaging kernels and gain matrix analysis
18 | - Multiple profile retrieval along a latitude segment
19 | - Comparison between retrieved, true and a priori profiles
20 | - Uncertainty estimation and visualization
21 | - Observation fitting analysis and residuals
22 |
23 | Required Data Files:
24 | - atmosphere/atmospheres_true.xml: True atmospheric profiles
25 | - observation/dropsonde.xml: A priori and background data
26 | - observation/SensorCharacteristics_50GHz.xml: Sensor characteristics
27 | - observation/y_obs_50GHz.xml: Observation vector
28 | - observation/lat.xml: Latitude information
29 |
30 | Dependencies:
31 | numpy
32 | matplotlib
33 | pyarts
34 | nonlin_oem
35 |
36 | @author: Manfred Brath
37 | Created: Tue Jan 7 12:13:59 2025
38 |
39 | """
40 |
41 | import numpy as np
42 | import matplotlib.pyplot as plt
43 |
44 | import pyarts as pa
45 | import nonlin_oem as nlo
46 |
47 |
48 | # =============================================================================
49 | # %% read data
50 | # =============================================================================
51 |
52 | # surface temperature
53 | surface_temperature = 300 # K
54 |
55 | # surface reflectivity
56 | surface_reflectivity = 0.4
57 |
58 | # sensor position and line of sight
59 | sensor_pos = 15e3
60 | sensor_los = 180.0
61 |
62 | # load true data
63 | atms = pa.xml.load("atmosphere/atmospheres_true.xml")
64 |
65 | # load dropsonde data this will serve as a priori and background data
66 | dropsonde = pa.xml.load("observation/dropsonde.xml")
67 |
68 | # load frequency data for 50 GHz channels
69 | sensor_characteristics=pa.xml.load("observation/SensorCharacteristics_50GHz.xml")[:]
70 |
71 | # load measurement vector
72 | y_obs = pa.xml.load("observation/y_obs_50GHz.xml")
73 |
74 | lat = pa.xml.load("observation/lat.xml")
75 |
76 | NeDT = 0.2 # K
77 |
78 | # %% do the retrieval for one observation
79 |
80 | # select obervation idx
81 | idx = 328
82 | y = y_obs[idx, :]
83 |
84 | # create and add a priori covariance matrix
85 | delta_x = 6
86 | correlation_length = nlo.set_correlation_length(
87 | dropsonde.get("z", keep_dims=False), len_sfc=500, len_toa=2e3
88 | )
89 | S_a = nlo.create_apriori_covariance_matrix(
90 | dropsonde.get("T", keep_dims=False),
91 | dropsonde.get("z", keep_dims=False),
92 | delta_x,
93 | correlation_length,
94 | )
95 |
96 | # Define Se and its invers
97 | S_y = pa.arts.Sparse(np.diag(np.ones(np.size(sensor_characteristics,0)) * NeDT))
98 |
99 | #Temperature retrieval for selected observation
100 | T_ret, DeltaT, y_fit, A, G = nlo.temperature_retrieval(
101 | y,
102 | [],
103 | sensor_pos,
104 | sensor_los,
105 | dropsonde,
106 | surface_temperature,
107 | surface_reflectivity,
108 | S_y,
109 | S_a,
110 | Diagnostics=True,
111 | Verbosity=True,
112 | sensor_description=sensor_characteristics
113 | )
114 |
115 |
116 | # %% plot results for the single observation
117 |
118 | T_apr = dropsonde.get("T", keep_dims=False)
119 | T_true = atms[idx].get("T", keep_dims=False)
120 | z_true = atms[idx].get("z", keep_dims=False)
121 | z_ret = dropsonde.get("z", keep_dims=False)
122 | DeltaT_apriori = np.sqrt(np.diag(S_a))
123 |
124 | # Frequencies of the channels
125 | f_grid=sensor_characteristics[:,0]+sensor_characteristics[:,1]+sensor_characteristics[:,2]
126 |
127 | fig, ax = plt.subplots(1, 3, figsize=(14.14, 10))
128 |
129 | #plot difference to true temperature
130 | ax[0].plot(T_ret - T_true, z_ret, "s-", color="r", label="ret")
131 | ax[0].fill_betweenx(
132 | z_ret, T_ret - T_true - DeltaT, T_ret - T_true + DeltaT, alpha=0.3, color="r"
133 | )
134 | ax[0].plot(T_apr - T_true, z_ret, "o-", color="g", label="apriori")
135 | ax[0].fill_betweenx(
136 | z_ret,
137 | T_apr - T_true - DeltaT_apriori,
138 | T_apr - T_true + DeltaT_apriori,
139 | alpha=0.3,
140 | color="g",
141 | )
142 | ax[0].set_title("Difference to T$_{true}$")
143 | ax[0].set_xlabel("Temperature [K]")
144 | ax[0].set_ylabel("Altitude [m]")
145 | ax[0].legend()
146 |
147 | # Plot true temperature profile
148 | ax[1].plot(T_true, z_true, "x-", label="truth")
149 | ax[1].set_xlabel("Temperature [K]")
150 | ax[1].set_ylabel("Altitude [m]")
151 | ax[1].set_title("T$_{true}$")
152 |
153 | ax[2].plot(f_grid, y, "x-", label="obs")
154 | ax[2].plot(f_grid, y_fit, "s-", label="fit")
155 | ax[2].set_xlabel("Frequency [GHz]")
156 | ax[2].set_ylabel("Brightness temperature [K]")
157 | ax[2].legend()
158 | fig.tight_layout()
159 |
160 |
161 | #new figure
162 | fig2, ax2 = plt.subplots(1, 2, figsize=(14.14, 10))
163 |
164 | #plot averaging kernels
165 | colormap = plt.cm.YlOrRd
166 | colors = [colormap(i) for i in np.linspace(0, 1, np.size(A, 1))]
167 | for i in range(np.size(A, 1)):
168 | ax2[0].plot(
169 | A[:, i],
170 | z_ret,
171 | color=colors[i],
172 | )
173 | A_sum = np.sum(A, axis=1)
174 | ax2[0].plot(A_sum / 5, z_ret, color="k", label="$\Sigma A_{i}$/5")
175 | ax2[0].legend()
176 | ax2[0].set_xlabel("Altitude [m]")
177 | ax2[0].set_ylabel("Altitude [m]")
178 | ax2[0].set_title("Averaging Kernels")
179 |
180 |
181 | #plot gain matrix
182 | for i in range(np.size(G, 1)):
183 | ax2[1].plot(G[:, i], z_ret, label=f"{f_grid[i]/1e9:.2f}GHz")
184 | ax2[1].set_title("Gain Matrix")
185 | ax2[1].legend()
186 | ax2[1].set_xlabel("Contributions / KK$^{-1}$")
187 | ax2[1].set_ylabel("Altitude [m]")
188 | fig.tight_layout()
189 |
190 |
191 | # %% now do the retrieval for the whole segment
192 |
193 | #allocate
194 | DeltaT_all = np.zeros((np.size(y_obs, 0), dropsonde.get("z", keep_dims=False).size))
195 | T_all = np.zeros((np.size(y_obs, 0), dropsonde.get("z", keep_dims=False).size))
196 | y_fit_all = np.zeros((np.size(y_obs, 0), len(f_grid)))
197 | A_summed_all = np.zeros((np.size(y_obs, 0), dropsonde.get("z", keep_dims=False).size))
198 | G_summed_all = np.zeros((np.size(y_obs, 0), dropsonde.get("z", keep_dims=False).size))
199 |
200 |
201 | #loop over the observations
202 | for i in range(np.size(y_obs, 0)):
203 |
204 | print(f"...retrieving profile {i} of {np.size(y_obs, 0)}")
205 |
206 | y = y_obs[i, :]
207 | T_ret, DeltaT, y_fit, A, G = nlo.temperature_retrieval(
208 | y,
209 | [],
210 | sensor_pos,
211 | sensor_los,
212 | dropsonde,
213 | surface_temperature,
214 | surface_reflectivity,
215 | S_y,
216 | S_a,
217 | Diagnostics=True,
218 | sensor_description=sensor_characteristics
219 | )
220 |
221 |
222 | DeltaT_all[i, :] = DeltaT
223 | T_all[i, :] = T_ret
224 | y_fit_all[i, :] = y_fit
225 | A_summed_all[i, :] = np.sum(A, axis=1)
226 | G_summed_all[i, :] = np.sum(G, axis=1)
227 |
228 |
229 |
230 | # %% plot the whole segment
231 |
232 | fig, ax = plt.subplots(2, 2, figsize=(14.14, 10))
233 |
234 | #plot difference to a priori
235 | cmap = "YlOrRd"
236 | data = T_all - T_apr # T_all.mean(0)
237 | data_max = np.max(np.abs(data))
238 | pcm = ax[0, 0].pcolormesh(
239 | lat, z_ret / 1e3, data.T, cmap="RdBu_r", clim=[-data_max, data_max], rasterized=True
240 | )
241 | fig.colorbar(pcm, ax=ax[0, 0], label="$\Delta T$ [K]")
242 | ax[0, 0].set_xlabel("Latitude [deg]")
243 | ax[0, 0].set_ylabel("Altitude [km]")
244 | ax[0, 0].set_title("Difference to a priori")
245 |
246 | data = DeltaT_all
247 | pcm = ax[0, 1].pcolormesh(
248 | lat,
249 | z_ret / 1e3,
250 | data.T,
251 | cmap=cmap,
252 | clim=[np.min(data), np.max(data)],
253 | rasterized=True,
254 | )
255 |
256 | #plot retrieved terperature uncertainty
257 | fig.colorbar(pcm, ax=ax[0, 1], label="$\Delta T$ [K]")
258 | ax[0, 1].set_xlabel("Latitude [deg]")
259 | ax[0, 1].set_ylabel("Altitude [km]")
260 | ax[0, 1].set_title("Retrieved Temperature uncertainty")
261 |
262 | #plot observed and fitted brightness temperatures
263 | for i in range(np.size(y_obs, 1)):
264 | ax[1, 0].plot(lat, y_obs[:, i], label=f"{f_grid[i]/1e9:.2f}$\,$GHz, obs")
265 | ax[1, 0].set_prop_cycle(None)
266 | for i in range(np.size(y_fit_all, 1)):
267 | ax[1, 0].plot(
268 | lat, y_fit_all[:, i], "--"
269 | )
270 | ax[1, 0].set_prop_cycle(None)
271 | ax[1, 0].set_xlabel("Latitude [deg]")
272 | ax[1, 0].set_ylabel("Brightness temperature [K]")
273 | ax[1, 0].legend()
274 |
275 | #plot residuals
276 | ax[1, 1].plot(lat, y_obs - y_fit_all, "-", label="residual")
277 | ax[1, 1].set_xlabel("Latitude [deg]")
278 | ax[1, 1].set_ylabel("Brightness temperature [K]")
279 | ax[1, 1].set_title("Residuals")
280 |
281 | fig.tight_layout()
282 |
--------------------------------------------------------------------------------
/exercises/07-olr/olr.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": null,
6 | "metadata": {},
7 | "outputs": [],
8 | "source": [
9 | "%matplotlib widget"
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {},
15 | "source": [
16 | "## Exercise No. 6 -- Outgoing Longwave Radiation (OLR)\n",
17 | "\n",
18 | "#### 1)\n",
19 | "\n",
20 | "Run ARTS on the Jupyter notebook olr.ipynb . This will calculate the spectrum\n",
21 | "of outgoing longwave radiation for a midlatitude-summer atmosphere.\n",
22 | "Our calculation trades accuracy for computational efficiency. For\n",
23 | "example, we use only water vapor and carbon dioxide as absorbers.\n",
24 | "We use only 300 frequency grid points and approximately 54,000 spectral\n",
25 | "lines, whereas for accurate calculations one needs at least 10,000\n",
26 | "frequency grid points and 500,000 spectral lines, taking into account\n",
27 | "absorbing species like ozone and methane. The script plots the spectral\n",
28 | "irradiance, in SI units, as a function of wavenumber. Planck curves\n",
29 | "for different temperatures are shown for comparison. We integrate\n",
30 | "the whole spectrum to quantify the power per square that is emitted\n",
31 | "by the atmosphere (value in title).\n",
32 | "\n",
33 | "* How would the OLR spectrum look in units of brightness temperature?\n",
34 | "* How would the Planck curves look in units of brightness temperature?\n",
35 | "* Find the $\\text{CO}_{2}$ absorption band and the regions of $\\text{H}_{2}\\text{O}$ \n",
36 | "absorption. From which height in the atmosphere does the radiation in the $\\text{CO}_{2}$ band originate?\n",
37 | "* Are there window regions?\n",
38 | "* What will determine the OLR in the window regions?\n",
39 | "* Use the plot to explain the atmospheric greenhouse effect."
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": null,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "import os\n",
49 | "import matplotlib.pyplot as plt\n",
50 | "import numpy as np\n",
51 | "from pyarts import xml\n",
52 | "from pyarts.arts import convert\n",
53 | "from olr_module import calc_olr_from_atmfield, Change_T_with_RH_const, cmap2rgba, planck\n",
54 | "\n",
55 | "os.makedirs(\"plots\", exist_ok=True)"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "# Read input atmosphere\n",
65 | "atmfield = xml.load(\"input/midlatitude-summer.xml\")\n",
66 | "\n",
67 | "# Scale the CO2 concentration\n",
68 | "atmfield.scale(\"abs_species-CO2\", 1)\n",
69 | "\n",
70 | "# Add a constant value to the temperature\n",
71 | "atmfield.set(\"T\", atmfield.get(\"T\") + 0)\n",
72 | "\n",
73 | "# Add a constant value to the temperature but \n",
74 | "# without changing relative humidity \n",
75 | "atmfield = Change_T_with_RH_const(atmfield,DeltaT=0)\n",
76 | "\n",
77 | "# Calculate the outgoing-longwave radiation\n",
78 | "f, olr = calc_olr_from_atmfield(atmfield, verbosity=0)"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {},
85 | "outputs": [],
86 | "source": [
87 | "# Plotting.\n",
88 | "import matplotlib\n",
89 | "font = {'size' : 12}\n",
90 | "matplotlib.rc('font', **font)\n",
91 | "\n",
92 | "wn = convert.freq2kaycm(f)\n",
93 | "\n",
94 | "temps = [225, 250, 275, atmfield.get(\"T\", keep_dims=False)[0]]\n",
95 | "temp_colors = cmap2rgba(\"plasma\", len(temps))\n",
96 | "\n",
97 | "fig, ax = plt.subplots()\n",
98 | "for t, color in sorted(zip(temps, temp_colors)):\n",
99 | " ax.plot(\n",
100 | " wn, np.pi * planck(f, t), label=f\"{t:3.1f} K\", color=color\n",
101 | " )\n",
102 | "ax.plot(wn, olr, color=\"C0\", label=\"Irradiance\")\n",
103 | "ax.legend()\n",
104 | "ax.set_title(rf\"OLR={np.trapezoid(olr, f):3.2f} $\\sf Wm^{{-2}}$\")\n",
105 | "ax.set_xlim(wn.min(), wn.max())\n",
106 | "ax.set_xlabel(r\"Wavenumber [$\\sf cm^{-1}$]\")\n",
107 | "ax.set_ylabel(r\"Irradiance [$\\sf Wm^{-2}Hz^{-1}$]\")\n",
108 | "ax.set_ylim(bottom=0)\n",
109 | "fig.savefig(\"plots/olr.pdf\")"
110 | ]
111 | },
112 | {
113 | "cell_type": "markdown",
114 | "metadata": {},
115 | "source": [
116 | "#### 2) \n",
117 | "\n",
118 | "Investigate how the OLR changes for different atmospheric conditions\n",
119 | "by modifying the input data. \n",
120 | "Use `atmfield.scale(...)`,\n",
121 | "`atmfield.set(...)` and `Change_T_with_RH_const(...)` to change the atmospheric data:\n",
122 | "\n",
123 | "* Add $1\\,\\,\\text{K}$ to the temperature.\n",
124 | "* Add $1\\,\\,\\text{K}$ to the temperature but hold relative humidity constant.\n",
125 | "* Increase the $\\text{CO}_{2}$ conentration by a factor of $2$.\n",
126 | "* Increase the $\\text{H}_{2}\\text{O}$ conentration by a factor of $1.2$.\n",
127 | " \n",
128 | "1) Change it, and calculate and plot the spectrum for each change. \n",
129 | "2) Compare the spectra of the changed atmosphere with the unchanged OLR spectrum. Where\n",
130 | " do the changes occur? Explain the differnt effects of the changed atmospheres.\n",
131 | "3) Compare the OLR numbers, which is the more potent greenhouse gas,\n",
132 | "$\\text{CO}_{2}$ or $\\text{H}_{2}\\text{O}$?"
133 | ]
134 | }
135 | ],
136 | "metadata": {
137 | "kernelspec": {
138 | "display_name": "Python 3 (ipykernel)",
139 | "language": "python",
140 | "name": "python3"
141 | },
142 | "language_info": {
143 | "codemirror_mode": {
144 | "name": "ipython",
145 | "version": 3
146 | },
147 | "file_extension": ".py",
148 | "mimetype": "text/x-python",
149 | "name": "python",
150 | "nbconvert_exporter": "python",
151 | "pygments_lexer": "ipython3",
152 | "version": "3.12.5"
153 | },
154 | "vscode": {
155 | "interpreter": {
156 | "hash": "6bd45fef6a38d15b43f43de43ba5066924911f80576952f97fb08adaede44831"
157 | }
158 | }
159 | },
160 | "nbformat": 4,
161 | "nbformat_minor": 4
162 | }
163 |
--------------------------------------------------------------------------------
/exercises/07-olr/olr.py:
--------------------------------------------------------------------------------
1 | # %%
2 | """Simulate and plot Earth's outgoing longwave radiation (OLR)."""
3 | import pyarts.workspace
4 | import numpy as np
5 |
6 |
7 | def calc_olr_from_profiles(
8 | pressure_profile,
9 | temperature_profile,
10 | h2o_profile,
11 | N2=0.78,
12 | O2=0.21,
13 | CO2=400e-6,
14 | CH4=1.8e-6,
15 | O3=0.0,
16 | surface_altitude=0.0,
17 | nstreams=10,
18 | fnum=300,
19 | fmin=1e6,
20 | fmax=75e12,
21 | verbosity=0,
22 | version='2.6.8'
23 | ):
24 | """Calculate the outgoing-longwave radiation for a given atmosphere profiles.
25 |
26 | Parameters:
27 |
28 | pressure_profile (ndarray): Pressure profile [Pa].
29 | temperature_profile (ndarray): Temperature profile [K].
30 | h2o_profile (ndarray): Water vapor profile [VMR].
31 | N2 (float): Nitrogen volume mixing ratio. Defaults to 0.78.
32 | O2 (float): Oxygen volume mixing ratio. Defaults to 0.21.
33 | CO2 (float): Carbon dioxide volume mixing ratio. Defaults to 400 ppm.
34 | CH4 (float): Methane volume mixing ratio. Defaults to 1.8 ppm.
35 | O3 (float): Ozone volume mixing ratio. Defaults to 0.
36 | surface_altitude (float): Surface altitude [m]. Defaults to 0.
37 | nstreams (int): Even number of streams to integrate the radiative fluxes.
38 | fnum (int): Number of points in frequency grid.
39 | fmin (float): Lower frequency limit [Hz].
40 | fmax (float): Upper frequency limit [Hz].
41 | verbosity (int): Reporting levels between 0 (only error messages)
42 | and 3 (everything).
43 |
44 | Returns:
45 | ndarray, ndarray: Frequency grid [Hz], OLR [Wm^-2]
46 |
47 | """
48 |
49 | pyarts.cat.download.retrieve(verbose=True, version=version)
50 |
51 | if fmin < 1e6:
52 | raise RuntimeError('fmin must be >= 1e6 Hz')
53 |
54 | ws = pyarts.workspace.Workspace(verbosity=0)
55 | ws.water_p_eq_agendaSet()
56 | ws.gas_scattering_agendaSet()
57 | ws.PlanetSet(option="Earth")
58 |
59 | ws.verbositySetScreen(ws.verbosity, verbosity)
60 |
61 | # Number of Stokes components to be computed
62 | ws.IndexSet(ws.stokes_dim, 1)
63 |
64 | # No jacobian calculation
65 | ws.jacobianOff()
66 |
67 | ws.abs_speciesSet(
68 | species=[
69 | "H2O, H2O-SelfContCKDMT400, H2O-ForeignContCKDMT400",
70 | "CO2, CO2-CKDMT252",
71 | "CH4",
72 | "O2,O2-CIAfunCKDMT100",
73 | "N2, N2-CIAfunCKDMT252, N2-CIArotCKDMT252",
74 | "O3",
75 | ]
76 | )
77 |
78 | # Read line catalog
79 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
80 |
81 | # Load CKDMT400 model data
82 | ws.ReadXML(ws.predefined_model_data, "model/mt_ckd_4.0/H2O.xml")
83 |
84 | # Read cross section data
85 | ws.ReadXsecData(basename="lines/")
86 |
87 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
88 | ws.abs_lines_per_speciesTurnOffLineMixing()
89 |
90 | # Create a frequency grid
91 | ws.VectorNLinSpace(ws.f_grid, int(fnum), float(fmin), float(fmax))
92 |
93 | # Throw away lines outside f_grid
94 | ws.abs_lines_per_speciesCompact()
95 |
96 | # Calculate absorption
97 | ws.propmat_clearsky_agendaAuto()
98 |
99 | # Weakly reflecting surface
100 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.0)
101 |
102 | # Atmosphere and surface
103 | ws.Touch(ws.lat_grid)
104 | ws.Touch(ws.lon_grid)
105 | ws.lat_true = np.array([0.0])
106 | ws.lon_true = np.array([0.0])
107 |
108 | ws.AtmosphereSet1D()
109 | ws.p_grid = pressure_profile
110 | ws.t_field = temperature_profile[:, np.newaxis, np.newaxis]
111 |
112 | vmr_field = np.zeros((6, len(pressure_profile), 1, 1))
113 | vmr_field[0, :, 0, 0] = h2o_profile
114 | vmr_field[1, :, 0, 0] = CO2
115 | vmr_field[2, :, 0, 0] = CH4
116 | vmr_field[3, :, 0, 0] = O2
117 | vmr_field[4, :, 0, 0] = N2
118 | vmr_field[5, :, 0, 0] = O3
119 | ws.vmr_field = vmr_field
120 |
121 | ws.z_surface = np.array([[surface_altitude]])
122 | ws.p_hse = 100000
123 | ws.z_hse_accuracy = 100.0
124 | ws.z_field = 16e3 * (5 - np.log10(pressure_profile[:, np.newaxis, np.newaxis]))
125 | ws.atmfields_checkedCalc()
126 | ws.z_fieldFromHSE()
127 |
128 | # Set surface temperature equal to the lowest atmosphere level
129 | ws.surface_skin_t = ws.t_field.value[0, 0, 0]
130 |
131 | # Output radiance not converted
132 | ws.StringSet(ws.iy_unit, "1")
133 |
134 | # set cloudbox to full atmosphere
135 | ws.cloudboxSetFullAtm()
136 |
137 | # set particle scattering to zero, because we want only clear sky
138 | ws.scat_data_checked = 1
139 | ws.Touch(ws.scat_data)
140 | ws.pnd_fieldZero()
141 |
142 | # No sensor properties
143 | ws.sensorOff()
144 |
145 | # No jacobian calculations
146 | ws.jacobianOff()
147 |
148 | # Check model atmosphere
149 | ws.scat_data_checkedCalc()
150 | ws.atmfields_checkedCalc()
151 | ws.atmgeom_checkedCalc()
152 | ws.cloudbox_checkedCalc()
153 | ws.lbl_checkedCalc()
154 |
155 | # Perform RT calculations
156 | ws.spectral_irradiance_fieldDisort(nstreams=nstreams, emission=1)
157 |
158 | olr = ws.spectral_irradiance_field.value[:, -1, 0, 0, 1][:]
159 |
160 | return ws.f_grid.value[:], olr
161 |
162 |
163 | # %% Run module as script
164 |
165 | if __name__ == "__main__":
166 | import matplotlib.pyplot as plt
167 |
168 | # generate example atmosphere
169 | # This atmosphere is not intended to be fully realistic, but to be simply
170 | # an example for the calculation of the OLR.
171 |
172 | # set pressure grid
173 | pressure_profile = np.linspace(1000e2, 1e2, 80)
174 |
175 | # create water vapor profile
176 | # Water vapor is simply define by a 1st order
177 | # polynomial in log-log space
178 | # log h2o = a + b * log pressure
179 | b = 4
180 | a = -6 - b * 4
181 | logH2O = a + b * np.log10(pressure_profile)
182 | H2O_profile = 10**logH2O
183 |
184 | # create temperature profile
185 | # Temperature is simply define by a 1st order
186 | # polynomial of log pressure
187 | # T = a + b * log pressure
188 | # For pressure < 100 hPa, the temperature is set to 200 K
189 | b = 100
190 | a = 200 - b * 4
191 | temperature_profile = a + b * np.log10(pressure_profile)
192 | temperature_profile[
193 | pressure_profile < 100e2
194 | ] = 200 # set temperature to 200 K below 100 hPa
195 |
196 | # plot atmosphere profiles
197 | fig, ax = plt.subplots(1, 2)
198 | ax[0].plot(temperature_profile, pressure_profile / 100, label="Temperature")
199 | ax[0].set_xlabel("Temperature [K]")
200 | ax[0].set_ylabel("Pressure [hPa]")
201 | ax[0].invert_yaxis()
202 |
203 | ax[1].plot(H2O_profile, pressure_profile / 100, label="Water vapor")
204 | ax[1].set_xlabel("Water vapor [VMR]")
205 | ax[1].set_ylabel("Pressure [hPa]")
206 | ax[1].invert_yaxis()
207 |
208 | # %% calulate OLR from atmosphere profiles
209 | f_grid, olr = calc_olr_from_profiles(
210 | pressure_profile, temperature_profile, H2O_profile
211 | )
212 |
213 | # %% plot OLR
214 | fig, ax = plt.subplots()
215 | ax.plot(f_grid / 1e12, olr, label="Irradiance")
216 | ax.set_title(rf"OLR={np.trapz(olr, f_grid):3.2f} $\sf Wm^{{-2}}$")
217 | ax.set_xlim(f_grid.min() / 1e12, f_grid.max() / 1e12)
218 | ax.set_xlabel(r"Frequency [$\sf THz$]")
219 | ax.set_ylabel(r"Irradiance [$\sf Wm^{-2}Hz^{-1}$]")
220 | ax.set_ylim(bottom=0)
221 |
222 | plt.show()
223 |
--------------------------------------------------------------------------------
/exercises/07-olr/olr_module.py:
--------------------------------------------------------------------------------
1 | # %%
2 | """Simulate and plot Earth's outgoing longwave radiation (OLR)."""
3 | import pyarts.workspace
4 | from pyarts.arts import constants
5 | import numpy as np
6 | import matplotlib.pyplot as plt
7 | from matplotlib import colors
8 |
9 |
10 | # %% Helper functions taken from the typhon package
11 | def cmap2rgba(cmap=None, N=None, interpolate=True):
12 | """Convert a colormap into a list of RGBA values.
13 |
14 | Parameters:
15 | cmap (str): Name of a registered colormap.
16 | N (int): Number of RGBA-values to return.
17 | If ``None`` use the number of colors defined in the colormap.
18 | interpolate (bool): Toggle the interpolation of values in the
19 | colormap. If ``False``, only values from the colormap are
20 | used. This may lead to the re-use of a color, if the colormap
21 | provides less colors than requested. If ``True``, a lookup table
22 | is used to interpolate colors (default is ``True``).
23 |
24 | Returns:
25 | ndarray: RGBA-values.
26 |
27 | Examples:
28 | >>> cmap2rgba('viridis', 5)
29 | array([[ 0.267004, 0.004874, 0.329415, 1. ],
30 | [ 0.229739, 0.322361, 0.545706, 1. ],
31 | [ 0.127568, 0.566949, 0.550556, 1. ],
32 | [ 0.369214, 0.788888, 0.382914, 1. ],
33 | [ 0.993248, 0.906157, 0.143936, 1. ]])
34 | """
35 | cmap = plt.get_cmap(cmap)
36 |
37 | if N is None:
38 | N = cmap.N
39 |
40 | nlut = N if interpolate else None
41 |
42 | if interpolate and isinstance(cmap, colors.ListedColormap):
43 | # `ListedColormap` does not support lookup table interpolation.
44 | cmap = colors.LinearSegmentedColormap.from_list("", cmap.colors)
45 | return cmap(np.linspace(0, 1, N))
46 |
47 | return plt.get_cmap(cmap.name, lut=nlut)(np.linspace(0, 1, N))
48 |
49 |
50 | def e_eq_water_mk(T):
51 | r"""Calculate the equilibrium vapor pressure of water over liquid water.
52 |
53 | .. math::
54 | \ln(e_\mathrm{liq}) &=
55 | 54.842763 - \frac{6763.22}{T} - 4.21 \cdot \ln(T) \\
56 | &+ 0.000367 \cdot T
57 | + \tanh \left(0.0415 \cdot (T - 218.8)\right) \\
58 | &\cdot \left(53.878 - \frac{1331.22}{T}
59 | - 9.44523 \cdot \ln(T)
60 | + 0.014025 \cdot T \right)
61 |
62 | Parameters:
63 | T (float or ndarray): Temperature [K].
64 |
65 | Returns:
66 | float or ndarray: Equilibrium vapor pressure [Pa].
67 |
68 | See also:
69 | :func:`~typhon.physics.e_eq_ice_mk`
70 | Calculate the equilibrium vapor pressure of water over ice.
71 | :func:`~typhon.physics.e_eq_mixed_mk`
72 | Calculate the vapor pressure of water over the mixed phase.
73 |
74 | References:
75 | Murphy, D. M. and Koop, T. (2005): Review of the vapour pressures of
76 | ice and supercooled water for atmospheric applications,
77 | Quarterly Journal of the Royal Meteorological Society 131(608):
78 | 1539–1565. doi:10.1256/qj.04.94
79 |
80 | """
81 | if np.any(T <= 0):
82 | raise ValueError("Temperatures must be larger than 0 Kelvin.")
83 |
84 | # Give the natural log of saturation vapor pressure over water in Pa
85 |
86 | e = (
87 | 54.842763
88 | - 6763.22 / T
89 | - 4.21 * np.log(T)
90 | + 0.000367 * T
91 | + np.tanh(0.0415 * (T - 218.8))
92 | * (53.878 - 1331.22 / T - 9.44523 * np.log(T) + 0.014025 * T)
93 | )
94 |
95 | return np.exp(e)
96 |
97 |
98 | def relative_humidity2vmr(RH, p, T, e_eq=None):
99 | r"""Convert relative humidity into water vapor VMR.
100 |
101 | .. math::
102 | x = \frac{\mathrm{RH} \cdot e_s(T)}{p}
103 |
104 | Note:
105 | By default, the relative humidity is calculated with respect to
106 | saturation over liquid water in accordance to the WMO standard for
107 | radiosonde observations.
108 | You can use :func:`~typhon.physics.e_eq_mixed_mk` to calculate
109 | relative humidity with respect to saturation over the mixed-phase
110 | following the IFS model documentation.
111 |
112 | Parameters:
113 | RH (float or ndarray): Relative humidity.
114 | p (float or ndarray): Pressue [Pa].
115 | T (float or ndarray): Temperature [K].
116 | e_eq (callable): Function to calculate the equilibrium vapor
117 | pressure of water in Pa. The function must implement the
118 | signature ``e_eq = f(T)`` where ``T`` is temperature in Kelvin.
119 | If ``None`` the function :func:`~typhon.physics.e_eq_water_mk` is
120 | used.
121 |
122 | Returns:
123 | float or ndarray: Volume mixing ratio [unitless].
124 |
125 | See also:
126 | :func:`~typhon.physics.vmr2relative_humidity`
127 | Complement function (returns RH for given VMR).
128 | :func:`~typhon.physics.e_eq_water_mk`
129 | Used to calculate the equilibrium water vapor pressure.
130 |
131 | Examples:
132 | >>> relative_humidity2vmr(0.75, 101300, 300)
133 | 0.026185323887350429
134 | """
135 | if e_eq is None:
136 | e_eq = e_eq_water_mk
137 |
138 | return RH * e_eq(T) / p
139 |
140 |
141 | def vmr2relative_humidity(vmr, p, T, e_eq=None):
142 | r"""Convert water vapor VMR into relative humidity.
143 |
144 | .. math::
145 | \mathrm{RH} = \frac{x \cdot p}{e_s(T)}
146 |
147 | Note:
148 | By default, the relative humidity is calculated with respect to
149 | saturation over liquid water in accordance to the WMO standard for
150 | radiosonde observations.
151 | You can use :func:`~typhon.physics.e_eq_mixed_mk` to calculate
152 | relative humidity with respect to saturation over the mixed-phase
153 | following the IFS model documentation.
154 |
155 | Parameters:
156 | vmr (float or ndarray): Volume mixing ratio,
157 | p (float or ndarray): Pressure [Pa].
158 | T (float or ndarray): Temperature [K].
159 | e_eq (callable): Function to calculate the equilibrium vapor
160 | pressure of water in Pa. The function must implement the
161 | signature ``e_eq = f(T)`` where ``T`` is temperature in Kelvin.
162 | If ``None`` the function :func:`~typhon.physics.e_eq_water_mk` is
163 | used.
164 |
165 | Returns:
166 | float or ndarray: Relative humidity [unitless].
167 |
168 | See also:
169 | :func:`~typhon.physics.relative_humidity2vmr`
170 | Complement function (returns VMR for given RH).
171 | :func:`~typhon.physics.e_eq_water_mk`
172 | Used to calculate the equilibrium water vapor pressure.
173 |
174 | Examples:
175 | >>> vmr2relative_humidity(0.025, 1013e2, 300)
176 | 0.71604995533615401
177 | """
178 | if e_eq is None:
179 | e_eq = e_eq_water_mk
180 |
181 | return vmr * p / e_eq(T)
182 |
183 |
184 | def planck(f, T):
185 | """Calculate black body radiation for given frequency and temperature.
186 |
187 | Parameters:
188 | f (float or ndarray): Frquencies [Hz].
189 | T (float or ndarray): Temperature [K].
190 |
191 | Returns:
192 | float or ndarray: Radiances.
193 |
194 | """
195 | c = constants.c
196 | h = constants.h
197 | k = constants.k
198 |
199 | return 2 * h * f**3 / (c**2 * (np.exp(np.divide(h * f, (k * T))) - 1))
200 |
201 |
202 | # %% main functions
203 | def Change_T_with_RH_const(atmfield, DeltaT=0.0):
204 | """Change the temperature everywhere in the atmosphere by a value of DeltaT
205 | but without changing the relative humidity. This results in a changed
206 | volume mixing ratio of water vapor.
207 |
208 | Parameters:
209 | atmfield (GriddedField4): Atmosphere field.
210 | DeltaT (float): Temperature change [K].
211 |
212 | Returns:
213 | GriddedField4: Atmosphere field
214 | """
215 |
216 | # water vapor
217 | vmr = atmfield.get("abs_species-H2O")
218 |
219 | # Temperature
220 | T = atmfield.get("T")
221 |
222 | # Reshape pressure p, so that p has the same dimensions
223 | p = atmfield.grids[1][:].reshape(T.shape)
224 |
225 | # Calculate relative humidity
226 | rh = vmr2relative_humidity(vmr, p, T)
227 |
228 | # Calculate water vapor volume mixing ratio for changed temperature
229 | vmr = relative_humidity2vmr(rh, p, T + DeltaT)
230 |
231 | # update atmosphere field
232 | atmfield.set("T", T + DeltaT)
233 | atmfield.set("abs_species-H2O", vmr)
234 |
235 | return atmfield
236 |
237 |
238 | def calc_olr_from_atmfield(
239 | atmfield,
240 | nstreams=10,
241 | fnum=300,
242 | fmin=1.0,
243 | fmax=75e12,
244 | species="default",
245 | verbosity=0,
246 | ):
247 | """Calculate the outgoing-longwave radiation for a given atmosphere.
248 |
249 | Parameters:
250 | atmfield (GriddedField4): Atmosphere field.
251 | nstreams (int): Even number of streams to integrate the radiative fluxes.
252 | fnum (int): Number of points in frequency grid.
253 | fmin (float): Lower frequency limit [Hz].
254 | fmax (float): Upper frequency limit [Hz].
255 | species (List of strings): List fo absorption species. Defaults to "default"
256 | verbosity (int): Reporting levels between 0 (only error messages)
257 | and 3 (everything).
258 |
259 | Returns:
260 | ndarray, ndarray: Frequency grid [Hz], OLR [Wm^-2]
261 | """
262 |
263 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
264 |
265 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
266 | ws.water_p_eq_agendaSet()
267 | ws.gas_scattering_agendaSet()
268 | ws.PlanetSet(option="Earth")
269 |
270 | ws.verbositySetScreen(ws.verbosity, verbosity)
271 |
272 | # Number of Stokes components to be computed
273 | ws.IndexSet(ws.stokes_dim, 1)
274 |
275 | # No jacobian calculation
276 | ws.jacobianOff()
277 |
278 | # Clearsky = No scattering
279 | ws.cloudboxOff()
280 |
281 | # Definition of species
282 | if species == "default":
283 | ws.abs_speciesSet(
284 | species=[
285 | "H2O, H2O-SelfContCKDMT400, H2O-ForeignContCKDMT400",
286 | "CO2, CO2-CKDMT252",
287 | ]
288 | )
289 | else:
290 | ws.abs_speciesSet(species=species)
291 |
292 | # Read line catalog
293 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
294 |
295 | # Load CKDMT400 model data
296 | ws.ReadXML(ws.predefined_model_data, "model/mt_ckd_4.0/H2O.xml")
297 |
298 | # Read cross section data
299 | ws.ReadXsecData(basename="lines/")
300 |
301 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
302 |
303 | # Create a frequency grid
304 | ws.VectorNLinSpace(ws.f_grid, int(fnum), float(fmin), float(fmax))
305 |
306 | # Throw away lines outside f_grid
307 | ws.abs_lines_per_speciesCompact()
308 |
309 | # Calculate absorption
310 | ws.propmat_clearsky_agendaAuto()
311 |
312 | # Weakly reflecting surface
313 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.0)
314 |
315 | # Atmosphere and surface
316 | ws.atm_fields_compact = atmfield
317 | ws.AtmosphereSet1D()
318 | ws.AtmFieldsAndParticleBulkPropFieldFromCompact()
319 |
320 | # Set surface height and temperature equal to the lowest atmosphere level
321 | ws.Extract(ws.z_surface, ws.z_field, 0)
322 | ws.surface_skin_t = ws.t_field.value[0, 0, 0]
323 |
324 | # Output radiance not converted
325 | ws.StringSet(ws.iy_unit, "1")
326 |
327 | # set cloudbox to full atmosphere
328 | ws.cloudboxSetFullAtm()
329 |
330 | # set particle scattering to zero, because we want only clear sky
331 | ws.scat_data_checked = 1
332 | ws.Touch(ws.scat_data)
333 | ws.pnd_fieldZero()
334 |
335 | # No sensor properties
336 | ws.sensorOff()
337 |
338 | # No jacobian calculations
339 | ws.jacobianOff()
340 |
341 | # Check model atmosphere
342 | ws.scat_data_checkedCalc()
343 | ws.atmfields_checkedCalc()
344 | ws.atmgeom_checkedCalc()
345 | ws.cloudbox_checkedCalc()
346 | ws.lbl_checkedCalc()
347 |
348 | # Perform RT calculations
349 | ws.spectral_irradiance_fieldDisort(nstreams=nstreams, emission=1)
350 |
351 | olr = ws.spectral_irradiance_field.value[:, -1, 0, 0, 1][:]
352 |
353 | return ws.f_grid.value[:], olr
354 |
--------------------------------------------------------------------------------
/exercises/08_heating_rates/heating_rates.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "\n",
10 | "Manfred Brath, Oliver Lemke\n",
11 | "\n",
12 | "## Exercise 8: Heating rate"
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": 1,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "%matplotlib widget\n",
22 | "\n",
23 | "\n",
24 | "\n",
25 | "import os\n",
26 | "import matplotlib.pyplot as plt\n",
27 | "import numpy as np\n",
28 | "from pyarts import xml\n",
29 | "from heating_rates_module import (calc_spectral_irradiance, calc_irradiance,\n",
30 | " integrate_spectral_irradiance)\n",
31 | "\n",
32 | "os.makedirs(\"plots\", exist_ok=True)"
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {},
38 | "source": [
39 | "The heating rate denotes the change of atmospheric temperature with time due\n",
40 | "to gain or loss of energy. Here, we consider only the gain or loss due to\n",
41 | "radiation.\n",
42 | "The heating rate including only radiation is \n",
43 | "\n",
44 | "$$\\frac{\\partial T\\left(z\\right)}{\\partial t}=-\\frac{1}{\\rho\\left( z \\right) c_p }\\frac{\\partial}{\\partial z}F_{net}\\left( z \\right)$$\n",
45 | "\n",
46 | "with $\\rho\\left( z \\right)$ the density of dry air (To keep it simple, we assume dry air. \n",
47 | "In reality the air is not dry. Nonetheless, the differences are small.), $c_p = 1.0035\\, \\text{J}\\, \\text{kg}^{-1} \\text{K}^{-1}$ the specific heat \n",
48 | "capacity of dry air and $F_{net}$ the net radiation flux. \n",
49 | "The net radiation flux is \n",
50 | "\n",
51 | "$$F_{net}=F_{up}-F_{down}$$\n",
52 | "\n",
53 | "with $F_{up}$ and $F_{down}$ the up- and downward radiation flux (irradiance), respectively. \n",
54 | "The density of dry air is \n",
55 | "\n",
56 | "$$\\rho =\\frac{p}{R_s\\,T}$$\n",
57 | "\n",
58 | "with pressure $p$, temperature $T$ and the specific gas constant \n",
59 | "$R_s = 287.058\\, \\text{J}\\,\\text{kg}^{-1} \\text{K}^{-1}$."
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {},
65 | "source": [
66 | "Before we start, we need to load an atmosphere."
67 | ]
68 | },
69 | {
70 | "cell_type": "code",
71 | "execution_count": 2,
72 | "metadata": {},
73 | "outputs": [],
74 | "source": [
75 | "# Read input atmosphere\n",
76 | "atmfield = xml.load(\"input/midlatitude-summer.xml\")"
77 | ]
78 | },
79 | {
80 | "cell_type": "markdown",
81 | "metadata": {},
82 | "source": [
83 | "### 1)\n",
84 | "Run the next cell. This will calculate the upward and downward longwave radiation fluxes. \n",
85 | "Here, we will consider only the longwave flux. Calculate the net flux and plot upward, \n",
86 | "downward and net flux together in one figure against altitude. Explain the plot. "
87 | ]
88 | },
89 | {
90 | "cell_type": "code",
91 | "execution_count": 3,
92 | "metadata": {},
93 | "outputs": [],
94 | "source": [
95 | "# Calculate the radiation irradiance (flux)\n",
96 | "\n",
97 | "z, p, T, flux_downward, flux_upward = calc_irradiance(atmfield)"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": 4,
103 | "metadata": {},
104 | "outputs": [],
105 | "source": [
106 | "# Calculate net flux and plot up-, down- and net flux\n"
107 | ]
108 | },
109 | {
110 | "cell_type": "markdown",
111 | "metadata": {},
112 | "source": [
113 | "### 2)\n",
114 | "Implement the function `calc_heatingrates(...)`. Use the function to calculate \n",
115 | "the heating rate. Plot the heating rate against altitude and explain the plot. How would \n",
116 | "a heating rate in thermal equilibrium assuming only longwave radiation look like? \n",
117 | "Why is the heating rate so much higher in the stratosphere than in the troposphere?"
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": 5,
123 | "metadata": {},
124 | "outputs": [],
125 | "source": [
126 | "# Implement heating rate function\n",
127 | "\n",
128 | "def calc_heatingrates(z, p, T, Fnet):\n",
129 | " \"\"\"Calculate the heating rate.\n",
130 | "\n",
131 | " Parameters:\n",
132 | " z (ndarray): Altitude [m].\n",
133 | " p (ndarray): Pressure [Pa].\n",
134 | " T (ndarray): Temperature [K].\n",
135 | " Fnet (ndarray): Net flux [W m^-2].\n",
136 | "\n",
137 | " Returns:\n",
138 | " ndarray, ndarray:\n",
139 | " Heating rate [K/d], Altitude [m].\n",
140 | " \"\"\"\n",
141 | "\n",
142 | "\n",
143 | "\n",
144 | "\n",
145 | " return NotImplementedError"
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": 6,
151 | "metadata": {},
152 | "outputs": [],
153 | "source": [
154 | "# Calculate heating rate and plot it\n",
155 | "\n",
156 | "# HR,zp_lay = calc_heatingrates(z, p, T, net_flux)\n"
157 | ]
158 | },
159 | {
160 | "cell_type": "markdown",
161 | "metadata": {},
162 | "source": [
163 | "### 3)\n",
164 | "Calculate the spectral upward, downward and net flux using the function `calc_spectral_irradiance`."
165 | ]
166 | },
167 | {
168 | "cell_type": "code",
169 | "execution_count": 7,
170 | "metadata": {},
171 | "outputs": [],
172 | "source": [
173 | "# Calculate the spectral irradiance (spectral flux)\n",
174 | "\n",
175 | "f, z, p, T, spectral_flux_downward, spectral_flux_upward = calc_spectral_irradiance( atmfield, verbosity=1)\n"
176 | ]
177 | },
178 | {
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": [
182 | "### 4)\n",
183 | "Use the function `integrate_spectral_irradiance(...)` to integrate \n",
184 | "the spectral irradiance over three continuing bands:\n",
185 | "\n",
186 | "* the far infrared\n",
187 | "* the $\\text{CO}_2$-band\n",
188 | "* the window-region and above.\n",
189 | "\n",
190 | "Calculate the heating rate for each band and plot them together with the total \n",
191 | "heating rate from Task 2. Compare the band heating rates with the total \n",
192 | "heating rate and explain differences."
193 | ]
194 | },
195 | {
196 | "cell_type": "code",
197 | "execution_count": 8,
198 | "metadata": {},
199 | "outputs": [],
200 | "source": [
201 | "# calculate the heating rate for each band and plot them together with the other heating rate\n"
202 | ]
203 | }
204 | ],
205 | "metadata": {
206 | "kernelspec": {
207 | "display_name": "Python 3 (ipykernel)",
208 | "language": "python",
209 | "name": "python3"
210 | },
211 | "language_info": {
212 | "codemirror_mode": {
213 | "name": "ipython",
214 | "version": 3
215 | },
216 | "file_extension": ".py",
217 | "mimetype": "text/x-python",
218 | "name": "python",
219 | "nbconvert_exporter": "python",
220 | "pygments_lexer": "ipython3",
221 | "version": "3.12.5"
222 | },
223 | "vscode": {
224 | "interpreter": {
225 | "hash": "6bd45fef6a38d15b43f43de43ba5066924911f80576952f97fb08adaede44831"
226 | }
227 | }
228 | },
229 | "nbformat": 4,
230 | "nbformat_minor": 4
231 | }
232 |
--------------------------------------------------------------------------------
/exercises/08_heating_rates/heating_rates_module.py:
--------------------------------------------------------------------------------
1 | """Simulate and plot Earth's outgoing longwave radiation (OLR)."""
2 | import numpy as np
3 | import pyarts.workspace
4 |
5 |
6 | def calc_spectral_irradiance(
7 | atmfield, nstreams=4, fnum=300, fmin=1.0, fmax=97e12, verbosity=0
8 | ):
9 | """Calculate the spectral downward and upward irradiance for a given atmosphere.
10 | Irradiandce is defined as positive quantity independent of direction.
11 |
12 | Parameters:
13 | atmfield (GriddedField4): Atmosphere field.
14 | nstreams (int): Even number of streams to integrate the radiative fluxes.
15 | fnum (int): Number of points in frequency grid.
16 | fmin (float): Lower frequency limit [Hz].
17 | fmax (float): Upper frequency limit [Hz].
18 | verbosity (int): Reporting levels between 0 (only error messages)
19 | and 3 (everything).
20 |
21 | Returns:
22 | ndarray, ndarray, ndarray, ndarray, ndarray, ndarray :
23 | Frequency grid [Hz], altitude [m], pressure [Pa], temperature [K],
24 | spectral downward irradiance [Wm^-2 Hz^-1],
25 | spectral upward irradiance [Wm^-2 Hz^-1].
26 | """
27 |
28 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
29 |
30 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
31 | ws.water_p_eq_agendaSet()
32 | ws.gas_scattering_agendaSet()
33 | ws.PlanetSet(option="Earth")
34 |
35 | ws.verbositySetScreen(ws.verbosity, verbosity)
36 |
37 | # Number of Stokes components to be computed
38 | ws.IndexSet(ws.stokes_dim, 1)
39 |
40 | # No jacobian calculations
41 | ws.jacobianOff()
42 |
43 | # Definition of species
44 | ws.abs_speciesSet(
45 | species=[
46 | "H2O, H2O-SelfContCKDMT400, H2O-ForeignContCKDMT400",
47 | "CO2, CO2-CKDMT252",
48 | ]
49 | )
50 |
51 | # Read line catalog
52 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
53 |
54 | # Load CKDMT400 model data
55 | ws.ReadXML(ws.predefined_model_data, "model/mt_ckd_4.0/H2O.xml")
56 |
57 | # Read cross section data
58 | ws.ReadXsecData(basename="lines/")
59 |
60 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
61 | ws.abs_lines_per_speciesTurnOffLineMixing()
62 |
63 | # Create a frequency grid
64 | ws.VectorNLinSpace(ws.f_grid, int(fnum), float(fmin), float(fmax))
65 |
66 | # Throw away lines outside f_grid
67 | ws.abs_lines_per_speciesCompact()
68 |
69 | # Calculate absorption
70 | ws.propmat_clearsky_agendaAuto()
71 |
72 | # Weakly reflecting surface
73 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.0)
74 | ws.surface_rtprop_agendaSet(option="Specular_NoPol_ReflFix_SurfTFromt_surface")
75 |
76 | # Atmosphere and surface
77 | ws.atm_fields_compact = atmfield
78 | ws.AtmosphereSet1D()
79 | ws.AtmFieldsAndParticleBulkPropFieldFromCompact()
80 |
81 | # Set surface height and temperature equal to the lowest atmosphere level
82 | ws.Extract(ws.z_surface, ws.z_field, 0)
83 | ws.surface_skin_t = ws.t_field.value[0, 0, 0]
84 |
85 | # Output radiance not converted
86 | ws.StringSet(ws.iy_unit, "1")
87 |
88 | # set cloudbox to full atmosphere
89 | ws.cloudboxSetFullAtm()
90 |
91 | # set particle scattering to zero, because we want only clear sky
92 | ws.scat_data_checked = 1
93 | ws.Touch(ws.scat_data)
94 | ws.pnd_fieldZero()
95 |
96 | # Check model atmosphere
97 | ws.scat_data_checkedCalc()
98 | ws.atmfields_checkedCalc()
99 | ws.atmgeom_checkedCalc()
100 | ws.cloudbox_checkedCalc()
101 | ws.lbl_checkedCalc()
102 |
103 | # Perform RT calculations
104 | ws.spectral_irradiance_fieldDisort(nstreams=nstreams, emission=1)
105 |
106 | spectral_flux_downward = -ws.spectral_irradiance_field.value[:, :, 0, 0, 0].copy()
107 | spectral_flux_upward = ws.spectral_irradiance_field.value[:, :, 0, 0, 1].copy()
108 |
109 | # spectral_flux_downward[np.isnan(spectral_flux_downward)] = 0.
110 | # spectral_flux_upward[np.isnan(spectral_flux_upward)] = 0.
111 |
112 | # set outputs
113 | f = ws.f_grid.value[:].copy()
114 | z = ws.z_field.value[:].copy().squeeze()
115 | p = atmfield.grids[1][:].squeeze().copy()
116 | T = atmfield.get("T")[:].squeeze().copy()
117 |
118 | return f, z, p, T, spectral_flux_downward, spectral_flux_upward
119 |
120 |
121 | def calc_irradiance(atmfield, nstreams=2, fnum=300, fmin=1.0, fmax=97e12, verbosity=0):
122 | """Calculate the downward and upward irradiance for a given atmosphere.
123 | Irradiandce is defined as positive quantity independent of direction.
124 |
125 | Parameters:
126 | atmfield (GriddedField4): Atmosphere field.
127 | nstreams (int): Even number of streams to integrate the radiative fluxes.
128 | fnum (int): Number of points in frequency grid.
129 | fmin (float): Lower frequency limit [Hz].
130 | fmax (float): Upper frequency limit [Hz].
131 | verbosity (int): Reporting levels between 0 (only error messages)
132 | and 3 (everything).
133 |
134 | Returns:
135 | ndarray, ndarray, ndarray, ndarray, ndarray :
136 | Altitude [m], pressure [Pa], temperature [K],
137 | downward irradiance [Wm^-2], upward irradiance [Wm^-2].
138 | """
139 |
140 | f, z, p, T, spectral_flux_downward, spectral_flux_upward = calc_spectral_irradiance(
141 | atmfield,
142 | nstreams=nstreams,
143 | fnum=fnum,
144 | fmin=fmin,
145 | fmax=fmax,
146 | verbosity=verbosity,
147 | )
148 |
149 | # calculate flux
150 | flux_downward = np.trapz(spectral_flux_downward, f, axis=0)
151 | flux_upward = np.trapz(spectral_flux_upward, f, axis=0)
152 |
153 | return z, p, T, flux_downward, flux_upward
154 |
155 |
156 | def integrate_spectral_irradiance(f, spectral_flux, fmin=-np.inf, fmax=np.inf):
157 | """Calculate the integral of the spectral iradiance from fmin to fmax.
158 |
159 | Parameters:
160 | f (ndarray): Frequency grid [Hz].
161 | spectral_flux (ndarray): Spectral irradiance [Wm^-2 Hz^-1].
162 | fmin (float): Lower frequency limit [Hz].
163 | fmax (float): Upper frequency limit [Hz].
164 |
165 | Returns:
166 | ndarray irradiance [Wm^-2].
167 | """
168 |
169 | logic = np.logical_and(fmin <= f, f < fmax)
170 |
171 | flux = np.trapezoid(spectral_flux[logic, :], f[logic], axis=0)
172 |
173 | return flux
174 |
--------------------------------------------------------------------------------
/exercises/09-scattering/input/pndfield_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 1.1000000e+05
6 | 2.4684211e+04
7 | 2.4368421e+04
8 | 2.4052632e+04
9 | 2.3736842e+04
10 | 2.3421053e+04
11 | 2.3105263e+04
12 | 2.2789474e+04
13 | 2.2473684e+04
14 | 2.2157895e+04
15 | 2.1842105e+04
16 | 2.1526316e+04
17 | 2.1210526e+04
18 | 2.0894737e+04
19 | 2.0578947e+04
20 | 2.0263158e+04
21 | 1.9947368e+04
22 | 1.9631579e+04
23 | 1.9315789e+04
24 | 1.0000000e-05
25 |
26 |
27 | 0.0000000e+00
28 |
29 |
30 | 0.0000000e+00
31 |
32 |
33 | 0.0000000e+00
34 | 0.0000000e+00
35 | 0.0000000e+00
36 | 3.5000000e+04
37 | 3.5000000e+04
38 | 3.5000000e+04
39 | 3.5000000e+04
40 | 3.5000000e+04
41 | 3.5000000e+04
42 | 3.5000000e+04
43 | 3.5000000e+04
44 | 3.5000000e+04
45 | 3.5000000e+04
46 | 3.5000000e+04
47 | 3.5000000e+04
48 | 3.5000000e+04
49 | 3.5000000e+04
50 | 0.0000000e+00
51 | 0.0000000e+00
52 | 0.0000000e+00
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/exercises/09-scattering/scattering.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Advanced radiation and remote sensing\n",
8 | "\n",
9 | "\n",
10 | "Manfred Brath, Oliver Lemke\n",
11 | "\n",
12 | "## Exercise 8: Scattering"
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": 1,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "%matplotlib widget\n",
22 | "\n",
23 | "\n",
24 | "import os\n",
25 | "import numpy as np\n",
26 | "import matplotlib.pyplot as plt\n",
27 | "from scattering_module import argclosest, scattering\n",
28 | "from matplotlib.ticker import StrMethodFormatter\n",
29 | "\n",
30 | "os.makedirs(\"plots\", exist_ok=True)"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "### 1)\n",
38 | "Run the next cell. This will simulate the radiation field at a frequency of $229\\,\\text{GHz}$ \n",
39 | "for an atmosphere with an ice cloud as well for clear-sky. Since this \n",
40 | "is a one-dimensional simulation (vertical dimension only), the calculated \n",
41 | "radiation fields have two dimensions: altitude (pressure) and zenith angle. "
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "metadata": {},
47 | "source": [
48 | "Plot the two radiation fields in the atmosphere at a zenith angle of $180{^\\circ}$. \n",
49 | "Here, the zenith angle describes the viewing direction. This means that you are \n",
50 | "looking at the upward directed radiation. The unit is brightness temperature. \n",
51 | "\n",
52 | "* Describe the difference between cloudy and clear-sky radiation. \n",
53 | "* Guess where the ice cloud is located in the atmosphere based on the\n",
54 | "two radiation fields? \n",
55 | "* Explain the difference between cloudy and clear-sky radiation."
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "# Run ARTS simulation\n",
65 | "p, zenith_angles, ifield, ifield_clearsky = scattering()\n",
66 | "\n",
67 | "# Control parameters\n",
68 | "zenith_angle = 180.0 # viewing angle [degree, 180° = upward radiation]\n",
69 | "pressure_level = None # pressure level [Pa]\n",
70 | "\n",
71 | "ia, zenith_angle = argclosest(zenith_angles, zenith_angle, retvalue=True)\n",
72 | "\n",
73 | "# Plot Tb vs height for a specific viewing angle\n",
74 | "f0, a0 = plt.subplots()\n",
75 | "a0.plot(ifield_clearsky[:, ia], p / 100, label=\"Clear-sky\")\n",
76 | "a0.plot(ifield[:, ia], p / 100, label=\"Scattering\")\n",
77 | "a0.grid()\n",
78 | "a0.set_ylim(p.max() / 100, p.min() / 100)\n",
79 | "a0.set_ylabel(\"Pressure [hPa]\")\n",
80 | "a0.set_xlabel(r\"$T_\\mathrm{B}$ [K]\")\n",
81 | "a0.legend()\n",
82 | "a0.set_title(rf\"$T_\\mathrm{{B}}$ at $\\Theta$ = {zenith_angle:.0f}°\")\n",
83 | "\n",
84 | "# Plot Tb vs Viewing angle for a specific pressure level:\n",
85 | "if pressure_level is not None:\n",
86 | " ip, pressure_level = argclosest(p, pressure_level, retvalue=True)\n",
87 | "\n",
88 | " f1, a1 = plt.subplots(subplot_kw=dict(projection=\"polar\"))\n",
89 | " a1.plot(np.deg2rad(zenith_angles), ifield_clearsky[ip, :], label=\"Clear-sky\")\n",
90 | " a1.plot(np.deg2rad(zenith_angles), ifield[ip, :], label=\"Scattering\")\n",
91 | " a1.legend(loc=\"upper right\")\n",
92 | " a1.set_theta_offset(np.deg2rad(+90))\n",
93 | " a1.set_theta_direction(-1)\n",
94 | " a1.set_thetagrids(np.arange(0, 181, 45), ha=\"left\")\n",
95 | " a1.text(0.01, 0.75, r\"$T_\\mathrm{B}$\", transform=a1.transAxes)\n",
96 | " a1.yaxis.set_major_formatter(StrMethodFormatter(\"{x:g} K\"))\n",
97 | " a1.set_thetamin(0)\n",
98 | " a1.set_thetamax(180)\n",
99 | " a1.set_xlabel(r\"Viewing angle $\\Theta$\")\n",
100 | " a1.set_title(rf\"$T_\\mathrm{{B}}$ at p = {pressure_level/100:.0f} hPa\")\n",
101 | "\n",
102 | "plt.show()"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "### 2) \n",
110 | "Change the zenith angle (`zenith_angle`) from $180{^\\circ}$\n",
111 | "to $0{^\\circ}$ and rerun the previous cell.\n",
112 | "\n",
113 | "* Describe and explain the difference.\n",
114 | "* Why is the brightness temperature at the top of the atmosphere so\n",
115 | "low?\n",
116 | "\n",
117 | "### 3)\n",
118 | "Now you will look at the radiation fields as a function of zenith\n",
119 | "angle (viewing direction) at a fixed pressure level. In the Jupyter\n",
120 | "notebook, change the variable `pressure_level` from `None`\n",
121 | "to a pressure level in $\\left[\\text{Pa}\\right]$, which is within\n",
122 | "the ice cloud and rerun previous cell within the notebook.\n",
123 | "\n",
124 | "* Explain the shape of the radiation field without the cloud.\n",
125 | "* How does the radiation field with the cloud differ? \n",
126 | "\n",
127 | "### 4)\n",
128 | "Make the same calculation as in task 3 but with a less or a more dense\n",
129 | "ice cloud. To do that, you have to call the function `scattering()`\n",
130 | "within your script with the argument `ice_water_path` set\n",
131 | "to your desired value in $\\left[\\text{kg}\\,\\text{m}^{-2}\\right]$.\n",
132 | "The ice water path is the vertically integrated mass content of ice.\n",
133 | "In task 3, the function `scattering()` used a default value\n",
134 | "of $2\\,\\text{kg}\\,\\text{m}^{-2}$. "
135 | ]
136 | },
137 | {
138 | "cell_type": "markdown",
139 | "metadata": {},
140 | "source": []
141 | }
142 | ],
143 | "metadata": {
144 | "kernelspec": {
145 | "display_name": "Python 3 (ipykernel)",
146 | "language": "python",
147 | "name": "python3"
148 | },
149 | "language_info": {
150 | "codemirror_mode": {
151 | "name": "ipython",
152 | "version": 3
153 | },
154 | "file_extension": ".py",
155 | "mimetype": "text/x-python",
156 | "name": "python",
157 | "nbconvert_exporter": "python",
158 | "pygments_lexer": "ipython3",
159 | "version": "3.12.5"
160 | },
161 | "vscode": {
162 | "interpreter": {
163 | "hash": "6bd45fef6a38d15b43f43de43ba5066924911f80576952f97fb08adaede44831"
164 | }
165 | }
166 | },
167 | "nbformat": 4,
168 | "nbformat_minor": 4
169 | }
170 |
--------------------------------------------------------------------------------
/exercises/09-scattering/scattering_module.py:
--------------------------------------------------------------------------------
1 | """Perform a DOIT scattering radiation with ARTS.
2 |
3 | Based on a script by Jakob Doerr.
4 | """
5 | import numpy as np
6 | import pyarts
7 |
8 |
9 | "function taken from typhon.physics"
10 |
11 |
12 | def radiance2planckTb(f, r):
13 | """Convert spectral radiance to Planck brightness temperture.
14 |
15 | Parameters:
16 | f (float or ndarray): Frequency [Hz].
17 | r (float or ndarray): Spectral radiance [W/(m2*Hz*sr)].
18 |
19 | Returns:
20 | float or ndarray: Planck brightness temperature [K].
21 | """
22 | c = pyarts.arts.constants.c
23 | k = pyarts.arts.constants.k
24 | h = pyarts.arts.constants.h
25 |
26 | return h / k * f / np.log(np.divide((2 * h / c**2) * f**3, r) + 1)
27 |
28 |
29 | def argclosest(array, value, retvalue=False):
30 | """Returns the index of the closest value in array."""
31 | idx = np.abs(array - value).argmin()
32 |
33 | return (idx, array[idx]) if retvalue else idx
34 |
35 |
36 | def setup_doit_agendas(ws):
37 | # Calculation of the phase matrix
38 | @pyarts.workspace.arts_agenda(ws=ws, set_agenda=True)
39 | def pha_mat_spt_agenda(ws):
40 | # Optimized option:
41 | ws.pha_mat_sptFromDataDOITOpt()
42 | # Alternative option:
43 | # ws.pha_mat_sptFromMonoData
44 |
45 | @pyarts.workspace.arts_agenda(ws=ws, set_agenda=True)
46 | def doit_scat_field_agenda(ws):
47 | ws.doit_scat_fieldCalcLimb()
48 | # Alternative: use the same za grids in RT part and scattering integral part
49 | # ws.doit_scat_fieldCalc()
50 |
51 | @pyarts.workspace.arts_agenda(ws=ws, set_agenda=True)
52 | def doit_rte_agenda(ws):
53 | # Sequential update for 1D
54 | ws.cloudbox_fieldUpdateSeq1D()
55 |
56 | @pyarts.workspace.arts_agenda(ws=ws, set_agenda=True)
57 | def spt_calc_agenda(ws):
58 | ws.opt_prop_sptFromMonoData()
59 |
60 | @pyarts.workspace.arts_agenda(ws=ws, set_agenda=True)
61 | def doit_conv_test_agenda(ws):
62 | ws.doit_conv_flagAbsBT(
63 | epsilon=np.array([0.01]), max_iterations=100, nonconv_return_nan=1
64 | )
65 | ws.Print(ws.doit_iteration_counter, 0)
66 |
67 | ws.doit_conv_test_agenda = doit_conv_test_agenda
68 |
69 | @pyarts.workspace.arts_agenda(ws=ws, set_agenda=True)
70 | def doit_mono_agenda(ws):
71 | # Prepare scattering data for DOIT calculation (Optimized method):
72 | ws.Ignore(ws.f_grid)
73 | ws.DoitScatteringDataPrepare()
74 | # Perform iterations: 1. scattering integral. 2. RT calculations with
75 | # fixed scattering integral field, 3. convergence test
76 | ws.cloudbox_field_monoIterate(accelerated=1)
77 |
78 | ws.iy_cloudbox_agendaSet(option="LinInterpField")
79 |
80 |
81 | def scattering(
82 | ice_water_path=2.0, num_viewing_angles=19, phase="ice", radius=1.5e2, verbosity=0
83 | ):
84 | """Perform a radiative transfer simulation with a simple cloud.
85 |
86 | Parameters:
87 | ice_water_path (float): Integrated ice water in cloud box [kg/m^2].
88 | num_viewing_angles (int): Number of zenith viewing angles.
89 | phase (str): Hydrometeor phase "ice" or "liquid".
90 | radius (float): Particle radius.
91 | verbosity (int): Reporting levels between 0 (only error messages)
92 | and 3 (everything).
93 |
94 | Returns:
95 | ndarray, ndarray, ndarray, ndarray:
96 | Pressure [hPa],
97 | Viewing angles [degree],
98 | Radiation field [K],
99 | Radiation field clear-sky [K].
100 |
101 | """
102 |
103 | pyarts.cat.download.retrieve(verbose=bool(verbosity))
104 |
105 | ws = pyarts.workspace.Workspace(verbosity=verbosity)
106 | ws.water_p_eq_agendaSet()
107 | ws.PlanetSet(option="Earth")
108 |
109 | ws.verbositySetScreen(ws.verbosity, verbosity)
110 |
111 | # Agenda settings
112 |
113 | # (standard) emission calculation
114 | ws.iy_main_agendaSet(option="Emission")
115 |
116 | # cosmic background radiation
117 | ws.iy_space_agendaSet(option="CosmicBackground")
118 |
119 | # standard surface agenda (i.e., make use of surface_rtprop_agenda)
120 | ws.iy_surface_agendaSet(option="UseSurfaceRtprop")
121 |
122 | # sensor-only path
123 | ws.ppath_agendaSet(option="FollowSensorLosPath")
124 |
125 | # no refraction
126 | ws.ppath_step_agendaSet(option="GeometricPath")
127 |
128 | ws.VectorSet(ws.f_grid, np.array([229.0e9])) # Define f_grid
129 |
130 | ws.IndexSet(ws.stokes_dim, 1) # Set stokes dim
131 |
132 | ws.AtmosphereSet1D()
133 |
134 | ws.jacobianOff() # No jacobian calculations
135 |
136 | ws.sensorOff() # No sensor
137 |
138 | # Set the maximum propagation step to 250m.
139 | ws.NumericSet(ws.ppath_lmax, 250.0)
140 |
141 | # Set absorption species
142 | ws.abs_speciesSet(
143 | species=["H2O-PWR98", "O3", "O2-PWR98", "N2-SelfContStandardType"]
144 | )
145 |
146 | # Read atmospheric data
147 | ws.ReadXML(ws.batch_atm_fields_compact, "input/chevallierl91_all_extract.xml")
148 | ws.Extract(ws.atm_fields_compact, ws.batch_atm_fields_compact, 1)
149 |
150 | # Add constant profiles for O2 and N2
151 | ws.atm_fields_compactAddConstant(
152 | name="abs_species-O2", value=0.2095, condensibles=["abs_species-H2O"]
153 | )
154 | ws.atm_fields_compactAddConstant(
155 | name="abs_species-N2", value=0.7808, condensibles=["abs_species-H2O"]
156 | )
157 |
158 | ws.AtmFieldsAndParticleBulkPropFieldFromCompact()
159 | ws.atmfields_checkedCalc()
160 |
161 | # Read Catalog (needed for O3):
162 | ws.abs_lines_per_speciesReadSpeciesSplitCatalog(basename="lines/")
163 |
164 | # ws.abs_lines_per_speciesLineShapeType(option=lineshape)
165 | ws.abs_lines_per_speciesCutoff(option="ByLine", value=750e9)
166 | # ws.abs_lines_per_speciesNormalization(option=normalization)
167 |
168 | # absorption from LUT
169 | ws.propmat_clearsky_agendaAuto()
170 |
171 | ws.abs_lookupSetup()
172 | ws.lbl_checkedCalc()
173 |
174 | ws.abs_lookupCalc()
175 |
176 | ws.propmat_clearsky_agenda_checkedCalc()
177 |
178 | # Set surface reflectivity (= 1 - emissivity)
179 | ws.surface_rtprop_agendaSet(option="Specular_NoPol_ReflFix_SurfTFromt_surface")
180 | ws.VectorSetConstant(ws.surface_scalar_reflectivity, 1, 0.0)
181 |
182 | # Extract particle mass from scattering meta data.
183 | scat_xml = f"scattering/H2O_{phase}/MieSphere_R{radius:.5e}um"
184 | scat_meta = pyarts.arts.ScatteringMetaData()
185 | scat_meta.readxml(scat_xml + ".meta")
186 | particle_mass = float(scat_meta.mass)
187 |
188 | # Load scattering data and PND field.
189 | ws.ScatSpeciesInit()
190 | ws.ScatElementsPndAndScatAdd(
191 | scat_data_files=[scat_xml], pnd_field_files=["./input/pndfield_input.xml"]
192 | )
193 | ws.scat_dataCalc()
194 | ws.scat_data_checkedCalc()
195 |
196 | # Set the extent of the cloud box.
197 | ws.cloudboxSetManually(
198 | p1=101300.0, p2=1000.0, lat1=0.0, lat2=0.0, lon1=0.0, lon2=0.0
199 | )
200 |
201 | # Trim pressure grid to match cloudbox.
202 | bottom, top = ws.cloudbox_limits.value
203 | p = ws.p_grid.value[bottom : top + 1].copy()
204 | z = ws.z_field.value[bottom : top + 1, 0, 0].copy()
205 |
206 | ws.pnd_fieldCalcFrompnd_field_raw()
207 |
208 | # Calculate the initial ice water path (IWP).
209 | iwp0 = np.trapz(particle_mass * ws.pnd_field.value[0, :, 0, 0], z)
210 |
211 | # Scale the PND field to get the desired IWP.
212 | ws.Tensor4Multiply(ws.pnd_field, ws.pnd_field, ice_water_path / iwp0)
213 |
214 | # Get case-specific surface properties from corresponding atmospheric fields
215 | ws.Extract(ws.z_surface, ws.z_field, 0)
216 | ws.Extract(ws.t_surface, ws.t_field, 0)
217 |
218 | # Consistency checks
219 | ws.atmgeom_checkedCalc()
220 | ws.cloudbox_checkedCalc()
221 |
222 | setup_doit_agendas(ws)
223 |
224 | ws.doit_za_interpSet(interp_method="linear")
225 |
226 | ws.DOAngularGridsSet(
227 | N_za_grid=num_viewing_angles, N_aa_grid=37, za_grid_opt_file=""
228 | )
229 |
230 | # Use lookup table for absorption calculation
231 | ws.propmat_clearsky_agendaAuto(use_abs_lookup=1)
232 |
233 | # Scattering calculation
234 | ws.DoitInit()
235 | ws.DoitGetIncoming(rigorous=0)
236 | ws.cloudbox_fieldSetClearsky()
237 | ws.DoitCalc()
238 |
239 | ifield = np.squeeze(ws.cloudbox_field.value[:].squeeze())
240 | ifield = radiance2planckTb(ws.f_grid.value, ifield)
241 |
242 | # Clear-sky
243 | ws.Tensor4Multiply(ws.pnd_field, ws.pnd_field, 0.0)
244 | ws.DoitCalc()
245 |
246 | ifield_clear = np.squeeze(ws.cloudbox_field.value[:].squeeze())
247 | ifield_clear = radiance2planckTb(ws.f_grid.value, ifield_clear)
248 |
249 | return p, ws.za_grid.value[:].copy(), ifield, ifield_clear
250 |
--------------------------------------------------------------------------------
/script/AdvRaRe_script.bib:
--------------------------------------------------------------------------------
1 | @Article{hodnebrog13:_global_RG,
2 | author = {{\O} Hodnebrog and others},
3 | title = {Global warming potentials and radiative efficiencies
4 | of halocarbons and related compounds: A
5 | comprehensive review},
6 | journal = RG,
7 | year = 2013,
8 | volume = 51,
9 | pages = {300--378},
10 | doi = {10.1002/rog.20013}
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/script/AdvRaRe_script_print.tex:
--------------------------------------------------------------------------------
1 | \documentclass[a4paper,fleqn]{article}
2 | %% Seitenaufbau
3 | \usepackage[top=3cm, bottom=2.5cm, left=3.5cm, right=3.5cm]{geometry}
4 |
5 | %% Schriftbild
6 | \usepackage{lmodern} % Latin Modern Zeichensatz
7 | \usepackage[utf8]{inputenc} % Unterstuetzung von Umlauten im Quelltext
8 | \usepackage[T1]{fontenc} % Korrekte Umlaute im Output
9 | %\renewcommand{\familydefault}{\sfdefault} % Serifenlose Schrift
10 | %\usepackage{euler} % Serifenlose Schrift in Formelumgebungen
11 | \usepackage{setspace}\onehalfspacing % 1.5-facher Zeilenabstand
12 | \renewcommand{\arraystretch}{1.5} % 1.5-facher Zeilenabstand (Tabellen)
13 | \setlength{\parindent}{0pt} % Keine Einrueckung am Beginng von Absaetzen
14 | \setlength{\parskip}{1ex}
15 | \usepackage{fancyhdr}
16 | \pagestyle{fancy}
17 | \renewcommand{\headrulewidth}{0.5pt}
18 | \renewcommand{\sectionmark}[1]{\markright{\thesection\ #1}}
19 | \lhead{\rightmark}
20 | \chead{}
21 | \cfoot{\thepage} % Richtige Schriftart fuer Seitenzahlen
22 | \sloppy % Weniger strikte Silbentrennung
23 |
24 | %% Verlinkung von Inhaltsverzeichnis, Bildern und Formeln
25 | \usepackage[pagebackref]{hyperref} % Verlinkung von URLs und Referenzen
26 | %\usepackage{color} % Definition von Linkfarben
27 | %\definecolor{DarkRed}{rgb}{0.5,0,0}
28 | %\hypersetup{
29 | % colorlinks,
30 | % citecolor=DarkRed,
31 | % linkcolor=DarkRed,
32 | % urlcolor=blue}
33 |
34 | \input{AdvRaRe_script}
35 |
--------------------------------------------------------------------------------
/script/AdvRaRe_script_screen.tex:
--------------------------------------------------------------------------------
1 | \documentclass[a5paper,landscape,fleqn,12pt]{article}
2 | %% Seitenaufbau
3 | \usepackage[top=2cm, bottom=1.5cm, left=2.5cm, right=2.5cm]{geometry}
4 |
5 | %% Schriftbild
6 | \usepackage{lmodern} % Latin Modern Zeichensatz
7 | \usepackage[utf8]{inputenc} % Unterstuetzung von Umlauten im Quelltext
8 | \usepackage[T1]{fontenc} % Korrekte Umlaute im Output
9 | \renewcommand{\familydefault}{\sfdefault} % Serifenlose Schrift
10 | \usepackage{euler} % Serifenlose Schrift in Formelumgebungen
11 | \usepackage{setspace}\onehalfspacing % 1.5-facher Zeilenabstand
12 | \renewcommand{\arraystretch}{1.5} % 1.5-facher Zeilenabstand (Tabellen)
13 | \setlength{\parindent}{0pt} % Keine Einrueckung am Beginng von Absaetzen
14 | \setlength{\parskip}{1ex}
15 | \usepackage{fancyhdr}
16 | \pagestyle{fancy}
17 | \renewcommand{\headrulewidth}{0.5pt}
18 | \renewcommand{\sectionmark}[1]{\markright{\thesection\ #1}}
19 | \lhead{\rightmark}
20 | \chead{}
21 | \cfoot{\thepage} % Richtige Schriftart fuer Seitenzahlen
22 | \sloppy % Weniger strikte Silbentrennung
23 |
24 | %% Verlinkung von Inhaltsverzeichnis, Bildern und Formeln
25 | \usepackage[pagebackref]{hyperref} % Verlinkung von URLs und Referenzen
26 | \usepackage{color} % Definition von Linkfarben
27 | \definecolor{DarkRed}{rgb}{0.5,0,0}
28 | \hypersetup{
29 | colorlinks,
30 | citecolor=DarkRed,
31 | linkcolor=DarkRed,
32 | urlcolor=blue}
33 |
34 | \input{AdvRaRe_script}
35 |
--------------------------------------------------------------------------------
/script/Makefile:
--------------------------------------------------------------------------------
1 | # Variables
2 | TC = latexmk
3 | TFLAGS = -e "$$pdflatex=q/pdflatex -interaction=nonstopmode/" -pdf -bibtex
4 |
5 | TARGETS = AdvRaRe_script_print.pdf AdvRaRe_script_screen.pdf
6 |
7 | .PHONY: all $(TARGETS)
8 |
9 | all: $(TARGETS)
10 |
11 | $(TARGETS): %.pdf : %.tex
12 | $(TC) $(TFLAGS) $<
13 |
14 | clean:
15 | $(TC) -silent -c
16 |
17 | cleanall:
18 | $(TC) -silent -C
19 |
20 | # vim:ft=make
21 | #
22 |
--------------------------------------------------------------------------------
/script/figures/Buehler_et_al_OLR_spectra.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Buehler_et_al_OLR_spectra.png
--------------------------------------------------------------------------------
/script/figures/Energy_levels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Energy_levels.png
--------------------------------------------------------------------------------
/script/figures/Fig_moments_inertia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Fig_moments_inertia.png
--------------------------------------------------------------------------------
/script/figures/Reduced_mass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Reduced_mass.png
--------------------------------------------------------------------------------
/script/figures/Transitions_vib_rot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Transitions_vib_rot.png
--------------------------------------------------------------------------------
/script/figures/Vibration_parabol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Vibration_parabol.png
--------------------------------------------------------------------------------
/script/figures/Vibration_parabol_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/Vibration_parabol_2.png
--------------------------------------------------------------------------------
/script/figures/buehler_portrait_sketch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/buehler_portrait_sketch.png
--------------------------------------------------------------------------------
/script/figures/interaction_types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/interaction_types.png
--------------------------------------------------------------------------------
/script/figures/perturbedLayer_EmissionTransmission.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/perturbedLayer_EmissionTransmission.png
--------------------------------------------------------------------------------
/script/figures/plotTypes_Jacobian.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/plotTypes_Jacobian.png
--------------------------------------------------------------------------------
/script/figures/rotating_mass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/rotating_mass.png
--------------------------------------------------------------------------------
/script/figures/schematic_energy_states.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/schematic_energy_states.png
--------------------------------------------------------------------------------
/script/figures/transition_types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atmtools/arts-lectures/3dc28d627560c60ede546a59f24c518d093dff08/script/figures/transition_types.png
--------------------------------------------------------------------------------
/script/j_abbr.bib:
--------------------------------------------------------------------------------
1 | %
2 | % In our bibtex files journal names are given as acronyms, for
3 | % example, GRL for Geophysical Research Letters.
4 | %
5 | % This file defines the conversion between these acronyms and
6 | % abbreviated journal names.
7 | %
8 | % This file should be the default choice but the conversion to
9 | % full journal names is found in the file journals.full.
10 | %
11 | % To use abbreviated journal names, put:
12 | %
13 | % \input{journals.abbr}
14 | %
15 | % at the start of the bibtex file. To use full journal names, use
16 | %
17 | % \input{journals.full}
18 | %
19 | % Please add new journals to these files when needed (in both
20 | % files!). The same acronym shall of course be used in journal.abbr
21 | % and journal.full.
22 | %
23 |
24 | @string{A = {Atmosf.}}
25 | @string{AA = {A\&A}}
26 | @string{AASS = {Astronomy \& Astrophysics Suppl. S.}}
27 | @string{ACA = {Acta Astron.}}
28 | @string{ACP = {Atmos. Chem. Phys.}}
29 | @string{ACPD = {Atmos. Chem. Phys. Discuss.}}
30 | @string{ACS = {Atm. Clim. Sci.}}
31 | @string{ADA = {Adv. Astron.}}
32 | @string{ADGEO = {Adv. Geosci.}}
33 | @string{AE = {Atmos. Environ.}}
34 | @string{AG = {Ann. Geophys.}}
35 | @string{ANJ = {Astronom. J.}}
36 | @string{AI = {Acta Inform.}}
37 | @string{AJ = {Astrophys. J.}}
38 | @string{AJL = {Astrophys. J. Lett.}}
39 | @string{AJSS = {Astrophys. J. Suppl. S.}}
40 | @string{AL = {Astrophys. J.}}
41 | @string{AJP = {Am. J. Phys.}}
42 | @string{AM = {Ann. Met.}}
43 | @string{AMM = {Aus. Met. Mag.}}
44 | @string{AMT = {Atmos. Meas. Tech.}}
45 | @string{AMTD = {Atmos. Meas. Tech. Discuss.}}
46 | @string{AO = {Appl. Opt.}}
47 | @string{AOP = {Atmos. Oce. Phy.(engl. transl.)}}
48 | @string{APL = {Appl. Phys. Lett.}}
49 | @string{AR = {Atmos. Res.}}
50 | @string{AREE = {Annu. Rev. Energy Environ.}}
51 | @string{ARPC = {Annu. Rev. Phys. Chem.}}
52 | @string{AS = {Am. Sci.}}
53 | @string{ASL = {Atm. Sci. Lett.}}
54 | @string{ASP = {Appl. Sign. Proc.}}
55 | @string{ASR = {Adv. Space. Res.}}
56 | @string{AST = {Aerosol Sci. Technol.}}
57 | @string{ATOC = {Atmos. -- Oce.}}
58 |
59 | @string{BAMS = {Bull. Amer. Met. Soc.}}
60 | @string{BAS = {Bull. Atom. Sci.}}
61 | @string{BLM = {Boun.-Lay. Met.}}
62 | @string{BPA = {Beitr. Phys. Atm.}}
63 |
64 | @string{CC = {Climate Change}}
65 | @string{CCCR = {Curr. Clim. Change Rep.}}
66 | @string{CD = {Climate Dynamics}}
67 | @string{COP = {Clim. Past}}
68 | @string{CG = {Comput. Geosci.}}
69 | @string{CJC = {Can. J. Chem.}}
70 | @string{CJP = {Can. J. Phys.}}
71 | @string{CP = {Contemp. Phys.}}
72 | @string{CPH = {Chem. Phys.}}
73 | @string{CPL = {Chem. Phys. Lett.}}
74 | @string{CR = {Climate Research}}
75 | @string{CRP = {Comptes Rendus Phys.}}
76 | @string{CS = {Curr. Sci.}}
77 | @string{CSB = {Chin. Sci. Bul.}}
78 | @string{CSPH = {Chemosph.}}
79 | @string{CSR = {Chem. Soc. Rev.}}
80 |
81 | @string{EECT = {Energy Emiss. Control Technol.}}
82 | @string{EF = {Earth's Fut.}}
83 | @string{EFM = {Env. Fluid Mech.}}
84 | @string{EL = {Elec. Lett.}}
85 | @string{EM = {Experiment. Math.}}
86 | @string{EMP = {Earth, Moon and Planets}}
87 | @string{EOS = {Eos}}
88 | @string{ENN = {Env. News Net.}}
89 | @string{EPA = {Earth and Planet. Astrophys.}}
90 | @string{EPJC = {Eur. Phys. J. Conferences}}
91 | @string{EPJST = {Eur. Phys. J. Spec. Top.}}
92 | @string{EPS = {Earth Planets Space}}
93 | @string{ERL = {Environ. Res. Lett.}}
94 | @string{ESD = {Earth Syst. Dynamics}}
95 | @string{ESS = {Earth and Space Sci.}}
96 | @string{ESSD = {Earth Syst. Sci. Data}}
97 | @string{EST = {Environ. Sci. Technol.}}
98 | @string{ETA = {Eos Trans. AGU}}
99 |
100 | @string{FES = {Front. Earth Sci.}}
101 | @string{FIP = {Front. in Phys.}}
102 |
103 | @string{GDJ = {Geosci. Data J.}}
104 | @string{GJI = {Geophys. J. Int.}}
105 | @string{GI = {Geosci. Instr., Methods and Data Syst.}}
106 | @string{GP = {Geophysica}}
107 | @string{GMD = {Geosci. Model Dev.}}
108 | @string{GMDD = {Geosci. Model Dev. Discuss.}}
109 | @string{GRL = {Geophys. Res. Lett.}}
110 | @string{HESSD = {Hydrol. Earth Syst. Sci. Discuss.}}
111 | @string{HESS = {Hydrol. Earth Syst. Sci.}}
112 | @string{IAOP = {Izv. Atmos. Oce. Phys.}}
113 | @string{ICARUS = {Icarus}}
114 | @string{IJAEOG = {Int. J. of Applied Earth Observation and Geoinformation}}
115 | @string{IJC = {Int. J. Climatol.}}
116 | @string{IJES = {Int. J. of Earth Sci.}}
117 | @string{IJRS = {Int. J. Remote Sensing}}
118 | @string{IJRSP = {Indian J. of Radio \& Space Physics}}
119 | @string{IJIMW = {Int. J. Inf. Millim. Waves}}
120 | @string{IJSC = {Int. J. Sat. Comm.}}
121 | @string{IP = {Infrared Phys.}}
122 | @string{IPT = {Infrared Phys. \& Tech.}}
123 | @string{I3EGRS = {IEEE Geosci. Remote Sens.}}
124 | @string{I3EGRSL = {IEEE Geosci. Remote Sens. Let.}}
125 | @string{I3EJOE = {IEEE J. Oce. Eng.}}
126 | @string{I3EJSTARS = {IEEE J. Sel. Top. Appl. Rem. Sens.}}
127 | @string{I3ETAP = {IEEE Trans. Antennas Propag.}}
128 | @string{I3ETC = {IEEE T. Comm.}}
129 | @string{I3ETGE = {IEEE T. Geosci. Ele.}}
130 | @string{I3ETGRS = {IEEE T. Geosci. Remote}}
131 | @string{I3ETMTT = {IEEE T. Microw. Theory}}
132 | @string{I3ETNN = {IEEE T. Ne. Netw.}}
133 | @string{I3ETIP = {IEEE T. Image Proc.}}
134 |
135 | @string{JAMES = {J. Adv. Model. Earth Syst.}}
136 | @string{JAC = {J. Atmos. Chem.}}
137 | @string{JAES = {J. Aerosol Sci.}}
138 | @string{JAM = {J. Appl. Meteorol.}}
139 | @string{JAMC = {J. Appl. Meteorol. Clim.}}
140 | @string{JAO = {J. Appl. Opt.}}
141 | @string{JAOT = {J. Atmos. Oceanic Technol.}}
142 | @string{JAP = {J. Appl. Phys.}}
143 | @string{JARS = {J. Appl. Rem. Sens.}}
144 | @string{JAS = {J. Atmos. Sci.}}
145 | @string{JASTP = {J. Atm. Sol.-Terr. Phys.}}
146 | @string{JATP = {J. Atm. Terr. Phys.}}
147 | @string{JAWMA = {J. Air \& Waste Manage. Assoc.}}
148 | @string{JC = {J. Climate}}
149 | @string{JH = {J. Hydrology}}
150 | @string{JCAM = {J. Clim. Appl. Mete.}}
151 | @string{JCG = {J. Cryst. Growth}}
152 | @string{JCP = {J. Chem. Phys.}}
153 | @string{JES = {J. Earth Simul.}}
154 | @string{JCSFT = {J. Chem. Soc. Far. Trans.}}
155 | @string{JFM = {J. Fluid Mech.}}
156 | @string{JG = {J. Geodesy}}
157 | @string{JGR = {J. Geophys. Res.}}
158 | @string{JGRa = {J. Geophys. Res.: Atm.}}
159 | @string{JGRo = {J. Geophys. Res.: Oceans}}
160 | @string{JGRP = {J. Geophys. Res.: Planets}}
161 | @string{JIMTW = {J. Infrared Milli. Terahz Waves}}
162 | @string{JLTP = {J. Low Temp. Phys.}}
163 | @string{JM = {J. Meteorol.}}
164 | @string{JMS = {J. Molec. Struct.}}
165 | @string{JMSL = {J. Molec. Liqu.}}
166 | @string{JMSp = {J. Molec. Spectro.}}
167 | @string{JMSJ = {J. Meteorol. Soc. Jpn.}}
168 | @string{JOA = {J. Opt. A: Pure Appl. Opt.}}
169 | @string{JOH = {J. Hydrometeorol.}}
170 | @string{JOSA = {J. Optical Soc. o. Am.}}
171 | @string{JOSAA = {J. Optical Soc. o. Am. A}}
172 | @string{JOSAB = {J. Optical Soc. o. Am. B}}
173 | @string{JOTA = {J. Opt. Theory and Appl.}}
174 | @string{JPC = {J. Phys. Chem.}}
175 | @string{JPCA = {J. Phys. Chem. A}}
176 | @string{JPCM = {J. Phys.: Condens. Matter}}
177 | @string{JPAGP = {J. of Phys. A: Gen. Phys.}}
178 | @string{JPAMG = {J. of Phys. A: Math. Gen.}}
179 | @string{JPESI = {J. of Phys. E: Sci. Instrum.}}
180 | @string{JCOP = {J. of Comp. Phys.}}
181 | @string{JCTE = {J. of Comm. Tech. and Elec.}}
182 | @string{JPIVF = {J. Phys. IV France}}
183 | @string{JPPA = {J. Photochem. Photobiol. A: Chem}}
184 | @string{JPO = {J. Phys. Ocea.}}
185 | @string{JQSRT = {J. Quant. Spectrosc. Radiat. Transfer}}
186 |
187 | @string{MA = {Met. Appl.}}
188 | @string{MAP = {Met. Atm. Phys.}}
189 | @string{MBT = {Met. Bul., Tai.}}
190 | @string{MC = {Mon. Corr.}}
191 | @string{MM = {Meteor. Monog.}}
192 | @string{MMS = {Multiscale Model. Simul.}}
193 | @string{MNRAS = {Mon. Not. R. Astron. Soc.}}
194 | @string{MP = {Mol. Phys.}}
195 | @string{MR = {Met. Rund.}}
196 | @string{MRRSE = {Micro. Radiat. Rem. Sen. Env.}}
197 | @string{MSSP = {Mech. Sys. and Sig. Proc.}}
198 | @string{MST = {Meas. Sci. Technol.}}
199 | @string{MWR = {Mon. Weather Rev.}}
200 | @string{MZ = {Met. Zeit.}}
201 |
202 | @string{N = {Nature}}
203 | @string{NCC = {Nature Clim. Change}}
204 | @string{NCPGH = {Nat. Clin. Pract. Gastr.}}
205 | @string{NG = {Nature Geosci.}}
206 | @string{NP = {Nature Phys.}}
207 | @string{NHESS = {Nat. Hazards and Earth Syst. Sci}}
208 | @string{NW = {Naturwissenschaften}}
209 | @string{NWD = {Nat. Weat. Dig.}}
210 | @string{NPG = {Nonlin. Processes. Geophys.}}
211 |
212 | @string{OE = {Opt. Express}}
213 | @string{OLE = {Opt. Las. Eng.g}}
214 | @string{OS = {Opt. Spectro.}}
215 |
216 | @string{P = {Physics}}
217 | @string{PAC = {Pure Appl. Chem.}}
218 | @string{PASP = {Publ. of the Astron. Soc. of Pacific}}
219 | @string{PD = {Physica D}}
220 | @string{PCE = {Phys. Chem. Earth}}
221 | @string{PERS = {Photogr. Eng. and Rem. Sens.}}
222 | @string{PLA = {Phys. Let. A}}
223 | @string{PMG = {Papers in Meteorol. and Geophys.}}
224 | @string{PNAS = {Proc. Nat. Aca. Sci.}}
225 | @string{PPG = {Prog. Phys. Geog.}}
226 | @string{PPSB = {Proc. Phys. Soc. B}}
227 | @string{PR = {Phys. Rev.}}
228 | @string{PRA = {Phys. Rev. A}}
229 | @string{PRD = {Phys. Rev. D}}
230 | @string{PRE = {Phys. Rev. E}}
231 | @string{PRL = {Phys. Rev. L}}
232 | @string{PRSA = {Proc. R. Soc. A}}
233 | @string{PRSLA = {Proc. R. Soc. Lond. A}}
234 | @string{PSPIE = {Proc. of SPIE}}
235 | @string{PSS = {Planet. Space Sci.}}
236 | @string{PT = {Phys. Today}}
237 | @string{PTRSLA = {Phil. Trans. R. Soc. Lond. A}}
238 | @string{PTRSA = {Phil. Trans. R. Soc. A}}
239 | @string{PW = {Phys. World}}
240 | @string{PU = {Phys. Usp.}}
241 |
242 |
243 | @string{QJRMS = {Q. J. R. Meteorol. Soc.}}
244 |
245 | @string{RESE = {Rem. Sens.}}
246 | @string{RG = {Rev. Geophys.}}
247 | @string{RMP = {Rev. Mod. Phys.}}
248 | @string{RPP = {Rep. Prog. Phys.}}
249 | @string{RQE = {Radiophys. Quant. Elec.}}
250 | @string{RS = {Radio Sci.}}
251 | @string{RSE = {Rem. Sen. Env.}}
252 | @string{RSI = {Rev. Sci. Inst.}}
253 | @string{RSR = {Rem. Sen. Rev.}}
254 | @string{RSPR = {Rem. Sen. Po. Reg.}}
255 |
256 | @string{S = {Science}}
257 | @string{SA = {Scient. Amer.}}
258 | @string{SD = {Sci. Data}}
259 | @string{SG = {Sur. Geophy.}}
260 | @string{SOLA = {Sci. Onl. Let. Atm.}}
261 | @string{SP = {Sov. Phys. Uspekhi}}
262 | @string{SOP = {Sol. Phys.}}
263 | @string{SSR = {Space Sci. Rev.}}
264 | @string{SW = {Spek. Wissen.}}
265 | @string{SAPAMBS = {Spectro. Acta Part A: Molec. and Biomolec. Spectro.}}
266 |
267 | @string{T = {Tellus}}
268 | @string{TA = {Tellus A: Dyn. Meteo. and Ocean.}}
269 | @string{TAC = {Theor. Appl. Climatol.}}
270 | @string{TAO = {Terr. Atmos. and Ocean. Sci. J.}}
271 | @string{TKDD = {ACM Trans. on Knowl. Disc. from Data}}
272 | @string{TOMS = {ACM Trans. on Math. Softw.}}
273 |
274 | @string{US = {Urb. Sci.}}
275 |
276 | @string{W = {Weather}}
277 | @string{WF = {Weat. and Fore.}}
278 | @string{WIRCC = {Wiley Interd. Rev.: Climate Change}}
279 | @string{WRR = {Wat. Res. Res.}}
280 | @string{WRM = {Waves Rand. Med.}}
281 |
282 | @string{Z = {Zeit}}
283 |
284 |
--------------------------------------------------------------------------------
/test/Makefile:
--------------------------------------------------------------------------------
1 | TARGETS = 01-absorption.pdf \
2 | 02-lineshape.pdf \
3 | 04-rtcalc.pdf \
4 | 05-jacobian.pdf \
5 | 06-oem.pdf \
6 | 07-olr.pdf \
7 | 08-scattering.pdf \
8 | 09-heating_rates.pdf
9 |
10 | SOURCES = ../exercises/01-molecule_spectra/absorption.ipynb \
11 | ../exercises/02-line_shape/lineshape.ipynb \
12 | ../exercises/04-rtcalc/rtcalc.ipynb \
13 | ../exercises/05-jacobian/jacobian.ipynb \
14 | ../exercises/06-inversion/oem.ipynb \
15 | ../exercises/07-olr/olr.ipynb \
16 | ../exercises/08-scattering/scattering.ipynb \
17 | ../exercises/09_heating_rates/heating_rates.ipynb
18 |
19 | CONVERT = jupyter nbconvert --log-level=ERROR --to pdf --output-dir=. --output=$@ --execute
20 |
21 |
22 | all: convert
23 |
24 | define make-exercise-target
25 | convert:: $1
26 | $1: $2
27 | endef
28 |
29 | ILIST = $(shell for x in {1..$(words $(TARGETS))}; do echo $$x; done)
30 | $(foreach i,$(ILIST), \
31 | $(eval $(call make-exercise-target, \
32 | $(word $(i),$(TARGETS)), \
33 | $(word $(i),$(SOURCES)))))
34 |
35 | $(TARGETS):
36 | TEXINPUTS=$(PWD)/$(dir $<): $(CONVERT) $<
37 |
38 | clean:
39 | rm -f $(TARGETS)
40 |
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | This directory is meant for testing.
2 |
3 | `make` runs all exercises and converts them into PDFs.
4 |
5 | Note that you need to set the environment variable `ARTS_DATA_PATH`
6 | correctly to the locations of the catalogs.
7 |
--------------------------------------------------------------------------------