Skip to content

Commit dd4de5d

Browse files
authored
Linear in propagation and source (#1077)
This introduce a RTE forward option that assumes both the source function and the propagation matrix changes linearly along the path. There is a gross simplification involved with the use of a Dawson function call that integrates the exponential. This is implemented elementwise, so it is likely it will cause problems for strong polarization.
2 parents 680d83f + 64ea68f commit dd4de5d

32 files changed

Lines changed: 2161 additions & 87 deletions

doc/arts/concept.radiative_transfer.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,54 @@ The derivative contribution is then given by
460460
&+& \frac{\partial T_{N-1}} {\partial \vec{x}_{N}} \left(I_{N-1} - J_{N-1}\right)
461461
&+& \frac{\partial \Lambda_{N-1}} {\partial \vec{x}_{N}} \left(J_{N-1} - J_{N}\right)
462462
&\Bigr]
463+
464+
Linear propagation matrix and source function
465+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
466+
467+
This relaxes the assumption of constant propagation matrix within
468+
the layer but is otherwise similar to linear in source.
469+
The indexing is the same for the
470+
transmittance and incoming background radiation as above - in layers. However, the source
471+
function and propagation matrix indexing below is now shifted to represent levels - the same indexing
472+
as we use for the spectral radiance term.
473+
474+
Linear in source means that we assume :math:`J = J_0 + (J_1 - J_0) \frac{r}{r_x}`,
475+
where :math:`r` is the distance through the layer and :math:`r_x` is the total
476+
thickness of the layer. With a linearly varying propagation matrix :math:`K = K_0 + (K_1 - K_0) \frac{r}{r_x}`, we define
477+
:math:`T_0 = \exp\left(- \overline{K_{0, 1}} r_x\right)`, with the overline indicate
478+
the arithmetic mean, and get
479+
480+
.. math::
481+
I_1 = J_1 + T_0 \left(I_0 - J_0\right) + \left(\Lambda_0^{(0)} + \Lambda_0^{(1)}\right) \left(J_0 - J_1\right).
482+
483+
This is a classical solution to the radiative transfer equation to improve
484+
numerical stability when the propagation matrix :math:`K` is
485+
large. Going through the motion of extending this to multiple steps as for the
486+
constant source case is then just a matter of adding 1 extra term
487+
per layer in the dot products above. If we define
488+
489+
.. math::
490+
\Lambda_i = \Lambda_i^{(0)} + \Lambda_i^{(1)},
491+
492+
where the first term is identical to the linear source function case,
493+
and the second term is a second order correction for the linear
494+
variation of the propagation matrix, we get
495+
496+
497+
.. math::
498+
499+
\Lambda_i^{(0)} &=& \frac{1}{r_i} \overline{K_{i, i+1}}^{-1} &\left(1 - T_i\right), \\
500+
\Lambda_i^{(1)} &=& \frac{1}{r_i^2} \overline{K_{i, i+1}}^{-1} &D\left(\left[K_{i+1} - K_{i}\right] r_i \middle/ 2\right),
501+
502+
where :math:`D` is the Dawson function of a matrix.
503+
504+
The rest of the steps that follows are the same as for the linear source function
505+
case above, just replacing :math:`\Lambda_i` with the new definition.
506+
507+
.. caution::
508+
509+
The Dawson function of a matrix is not implemented in ARTS yet. Instead,
510+
the element-wise Dawson function is used as an approximation. This means
511+
that it might be better to use the linear source function method.
512+
513+
Indeed, we do not know how well this approximation performs in practice.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import os
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
import pyarts3 as pyarts
6+
7+
# Download catalogs
8+
pyarts.data.download()
9+
10+
ws = pyarts.workspace.Workspace()
11+
12+
# %% Sampled frequency range
13+
line_f0 = 118750348044.712
14+
ws.freq_grid = np.linspace(-50e6, 50e6, 1001) + line_f0
15+
16+
# %% Species and line absorption
17+
ws.abs_speciesSet(species=["O2-66"])
18+
ws.ReadCatalogData()
19+
ws.abs_bandsSelectFrequencyByLine(fmin=40e9, fmax=120e9)
20+
ws.abs_bandsSetZeeman(species="O2-66", fmin=118e9, fmax=119e9)
21+
ws.WignerInit()
22+
23+
# %% Use the automatic agenda setter for propagation matrix calculations
24+
ws.spectral_propmat_agendaAuto()
25+
26+
# %% Grids and planet
27+
ws.surf_fieldPlanet(option="Earth")
28+
ws.surf_field[pyarts.arts.SurfaceKey("t")] = 295.0
29+
ws.atm_fieldRead(
30+
toa=100e3, basename="planets/Earth/afgl/tropical/", missing_is_zero=1
31+
)
32+
ws.atm_fieldSchmidthFieldFromIGRF(time="2000-03-11 14:39:37")
33+
34+
# %% Checks and settings
35+
ws.spectral_rad_transform_operatorSet(option="Tb")
36+
37+
# %% Core calculations
38+
pos = [100e3, 0, 0]
39+
los = [180.0, 0.0]
40+
ws.ray_pathGeometric(pos=pos, los=los, max_stepsize=1000.0)
41+
ws.spectral_radClearskyLinearInTauAndPropEmission()
42+
ws.spectral_radApplyUnitFromSpectralRadiance()
43+
44+
# %% Show results
45+
fig, ax = pyarts.plot(ws.spectral_rad, freqs=(
46+
ws.freq_grid - line_f0) / 1e6)
47+
[a.set_xlabel("Frequency offset [MHz]") for a in ax.flatten()]
48+
[a.set_ylabel("Spectral radiance [K]") for a in ax.flatten()]
49+
fig.suptitle(f"Zeeman effect of {round(line_f0 / 1e6)} MHz O$_2$ line")
50+
51+
if "ARTS_HEADLESS" not in os.environ:
52+
plt.show()
53+
54+
# %% Test
55+
56+
assert np.allclose(
57+
ws.spectral_rad[::100],
58+
np.array(
59+
[[ 2.27830141e+02, 4.25489128e-04, 1.02599187e-04, 5.68881918e-02],
60+
[ 2.30907190e+02, 6.59070807e-04, 1.59097448e-04, 7.03735321e-02],
61+
[ 2.34847968e+02, 1.16702319e-03, 2.82212718e-04, 9.33486760e-02],
62+
[ 2.40400744e+02, 2.61388422e-03, 6.34254900e-04, 1.40005198e-01],
63+
[ 2.49821088e+02, 9.73795166e-03, 2.38733210e-03, 2.69700244e-01],
64+
[ 2.08949873e+02, 2.37747404e+01, 1.64966784e+00, 5.47066239e-06],
65+
[ 2.49820521e+02, 9.74149272e-03, 2.38822964e-03, -2.69777785e-01],
66+
[ 2.40399650e+02, 2.61568960e-03, 6.34700330e-04, -1.40081856e-01],
67+
[ 2.34846390e+02, 1.16822346e-03, 2.82506203e-04, -9.34247505e-02],
68+
[ 2.30905136e+02, 6.59975316e-04, 1.59317615e-04, -7.04496271e-02],
69+
[ 2.27827622e+02, 4.26220137e-04, 1.02776641e-04, -5.69642694e-02]]
70+
),
71+
), "Values have drifted from expected results in spectral radiance"

examples/2-clearsky-radiative-transfer/2-zeeman/9-zeeman-linear-src.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@
5656
assert np.allclose(
5757
ws.spectral_rad[::100],
5858
np.array(
59-
[[2.27742531e+02, 4.26846049e-04, 1.02930910e-04, 5.69747534e-02],
60-
[2.30824032e+02, 6.60877182e-04, 1.59541966e-04, 7.04658183e-02],
61-
[2.34769234e+02, 1.16969415e-03, 2.82878204e-04, 9.34339220e-02],
62-
[2.40325915e+02, 2.61895652e-03, 6.35550519e-04, 1.40077284e-01],
63-
[2.49750254e+02, 9.75738899e-03, 2.39258319e-03, 2.69811017e-01],
64-
[2.09105759e+02, 2.38744101e+01, 1.64300556e+00, 5.42840543e-06],
65-
[2.49749687e+02, 9.76093079e-03, 2.39348131e-03, -2.69888391e-01],
66-
[2.40324823e+02, 2.62076265e-03, 6.35996235e-04, -1.40153790e-01],
67-
[2.34767658e+02, 1.17089544e-03, 2.83171984e-04, -9.35098578e-02],
68-
[2.30821982e+02, 6.61782801e-04, 1.59762430e-04, -7.05417543e-02],
69-
[2.27740017e+02, 4.27578039e-04, 1.03108621e-04, -5.70506086e-02]]
59+
[[ 2.27827140e+02, 4.25383147e-04, 1.02572526e-04, 5.68785875e-02],
60+
[ 2.30903679e+02, 6.58907241e-04, 1.59056068e-04, 7.03617869e-02],
61+
[ 2.34843814e+02, 1.16673604e-03, 2.82139468e-04, 9.33332773e-02],
62+
[ 2.40395699e+02, 2.61325506e-03, 6.34092170e-04, 1.39982571e-01],
63+
[ 2.49814592e+02, 9.73578709e-03, 2.38675820e-03, 2.69659250e-01],
64+
[ 2.08948687e+02, 2.37725593e+01, 1.64969081e+00, 5.47121870e-06],
65+
[ 2.49814025e+02, 9.73932755e-03, 2.38765555e-03, -2.69736783e-01],
66+
[ 2.40394605e+02, 2.61506011e-03, 6.34537500e-04, -1.40059220e-01],
67+
[ 2.34842236e+02, 1.16793609e-03, 2.82432888e-04, -9.34093435e-02],
68+
[ 2.30901627e+02, 6.59811580e-04, 1.59276187e-04, -7.04378734e-02],
69+
[ 2.27824621e+02, 4.26114017e-04, 1.02749941e-04, -5.69546563e-02]]
7070
),
7171
), "Values have drifted from expected results in spectral radiance"

src/core/rtepack/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ add_library(rtepack STATIC
1111
rtepack_spectral_matrix.cc
1212
)
1313
target_include_directories(rtepack PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
14-
target_link_libraries(rtepack PUBLIC matpack physics)
14+
target_link_libraries(rtepack PUBLIC matpack physics Faddeeva)

src/core/rtepack/rtepack_mueller_matrix.h

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,38 @@ struct muelmat final : mat44 {
1212
constexpr muelmat(Numeric tau = 1.0) noexcept
1313
: mat44{tau, 0, 0, 0, 0, tau, 0, 0, 0, 0, tau, 0, 0, 0, 0, tau} {}
1414

15-
constexpr muelmat(Numeric a,
16-
Numeric b,
17-
Numeric c,
18-
Numeric d,
19-
Numeric e,
20-
Numeric f,
21-
Numeric g,
22-
Numeric h,
23-
Numeric i,
24-
Numeric j,
25-
Numeric k,
26-
Numeric l,
27-
Numeric m,
28-
Numeric n,
29-
Numeric o,
30-
Numeric p) noexcept
31-
: mat44{a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p} {}
15+
constexpr muelmat(Numeric m00,
16+
Numeric m01,
17+
Numeric m02,
18+
Numeric m03,
19+
Numeric m10,
20+
Numeric m11,
21+
Numeric m12,
22+
Numeric m13,
23+
Numeric m20,
24+
Numeric m21,
25+
Numeric m22,
26+
Numeric m23,
27+
Numeric m30,
28+
Numeric m31,
29+
Numeric m32,
30+
Numeric m33) noexcept
31+
: mat44{m00,
32+
m01,
33+
m02,
34+
m03,
35+
m10,
36+
m11,
37+
m12,
38+
m13,
39+
m20,
40+
m21,
41+
m22,
42+
m23,
43+
m30,
44+
m31,
45+
m32,
46+
m33} {}
3247

3348
constexpr muelmat(std::array<Numeric, 16> data) noexcept : mat44{data} {}
3449

src/core/rtepack/rtepack_multitype.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,26 @@ constexpr stokvec operator*(const propmat &k, const stokvec s) {
128128
a * s4 + d * s1 - s2 * v - s3 * w};
129129
}
130130

131+
//! Element-wise product between a propmat and a muelmat
132+
constexpr muelmat elem_prod(const propmat &a, const muelmat &b) {
133+
return muelmat{a.A() * b[0, 0],
134+
a.B() * b[0, 1],
135+
a.C() * b[0, 2],
136+
a.D() * b[0, 3],
137+
a.B() * b[1, 0],
138+
a.A() * b[1, 1],
139+
a.U() * b[1, 2],
140+
a.V() * b[1, 3],
141+
a.C() * b[2, 0],
142+
-a.U() * b[2, 1],
143+
a.A() * b[2, 2],
144+
a.W() * b[2, 3],
145+
a.D() * b[3, 0],
146+
-a.V() * b[3, 1],
147+
-a.W() * b[3, 2],
148+
a.A() * b[3, 3]};
149+
}
150+
131151
//! Transform a matrix of shape (N, 4) to a list of Stokes (absorption) vectors
132152
stokvec_vector to_stokvec_vector(const ConstMatrixView &v);
133153

src/core/rtepack/rtepack_propagation_matrix.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "rtepack_propagation_matrix.h"
22

3+
#include <Faddeeva.hh>
4+
35
namespace rtepack {
46
propmat_vector operator*(Numeric x, const propmat_vector_const_view &y) {
57
propmat_vector z(y.size());
@@ -8,6 +10,16 @@ propmat_vector operator*(Numeric x, const propmat_vector_const_view &y) {
810
}
911
return z;
1012
}
13+
14+
propmat dawson(const propmat &pm) {
15+
return {Faddeeva::Dawson(pm.A()),
16+
Faddeeva::Dawson(pm.B()),
17+
Faddeeva::Dawson(pm.C()),
18+
Faddeeva::Dawson(pm.D()),
19+
Faddeeva::Dawson(pm.U()),
20+
Faddeeva::Dawson(pm.V()),
21+
Faddeeva::Dawson(pm.W())};
22+
}
1123
} // namespace rtepack
1224

1325
void xml_io_stream<rtepack::propmat>::write(std::ostream &os,

src/core/rtepack/rtepack_propagation_matrix.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ constexpr propmat avg(const propmat &a, const propmat &b) {
103103
std::midpoint(a.W(), b.W())};
104104
}
105105

106+
//! Element-wise dawson function of a propmat matrix (FIXME: implement as matrix?)
107+
propmat dawson(const propmat &pm);
108+
106109
using propmat_vector = matpack::data_t<propmat, 1>;
107110
using propmat_vector_view = matpack::view_t<propmat, 1>;
108111
using propmat_vector_const_view = matpack::view_t<const propmat, 1>;

0 commit comments

Comments
 (0)