Run each scheme#

We test the following:

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import xarray as xr

import crt1d as crt

Examine available schemes#

crt.print_config()
scheme IDs available: 2s, 4s, bf, bl, g77, n79, zq, zq_pa
crt1d base dir
    /home/docs/checkouts/readthedocs.org/user_builds/crt1d/envs/latest/lib/python3.10/site-packages/crt1d
looking for input data for provided cases in
    /home/docs/checkouts/readthedocs.org/user_builds/crt1d/envs/latest/lib/python3.10/site-packages/crt1d/data
df_schemes = pd.DataFrame(crt.solvers.AVAILABLE_SCHEMES).T

BL_args = set(a for a in df_schemes.loc[df_schemes.name == "bl"].args[0])
df_schemes["args_minus_BL"] = df_schemes["args"].apply(
    lambda x: list(set(x).symmetric_difference(BL_args))
)

print("B-L args:", ", ".join(BL_args))

df_schemes.drop(columns=["args", "solver"])  # solver memory address not very informative
B-L args: leaf_t, leaf_r, psi, lai, I_df0_all, I_dr0_all, K_b_fn
/tmp/ipykernel_786/685711968.py:3: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
  BL_args = set(a for a in df_schemes.loc[df_schemes.name == "bl"].args[0])
module_name name short_name long_name options args_minus_BL
2s _solve_2s 2s 2s Dickinson–Sellers two-stream [] [G_fn, mla, soil_r]
4s _solve_4s 4s 4s Tian et al. four-stream [mu_s] [G_fn, soil_r]
bf _solve_bf bf BF Bodin & Franklin improved Goudriaan [] [soil_r]
bl _solve_bl bl B–L Beer–Lambert [] []
g77 _solve_g77 g77 G77 Goudriaan (1977) [] [soil_r]
n79 _solve_n79 n79 N79 Norman (1979) [tau_d_method] [soil_r]
zq _solve_zq zq ZQ Zhao & Qualls multi-scattering [] [G_fn, soil_r]
zq_pa _solve_zq_pa zq_pa ZQ-pA Zhao & Qualls multi-scattering (pyAPES) [] [soil_r, clump]

👆 We see that all schemes take at least one more argument than what B–L (bl) requires. The B–L scheme makes the black soil assumption and so does not need soil_r. Additionally, some schemes have some parameters on top of the required arguments (column options).

Run#

Run the default case (which is loaded automatically when model object is created).

scheme_names = list(crt.solvers.AVAILABLE_SCHEMES)

ms = []
for scheme_name in scheme_names:  # run all available
    m = crt.Model(scheme_name, nlayers=60).run().calc_absorption()
    ms.append(m)

dsets = [m.to_xr() for m in ms]

Examine default case#

We plot the leaf area profile, leaf/soil optical property spectra, and top-of-canopy spectral, using the plotting methods attached to the Model instance.

m0 = ms[0]

m0.plot_canopy()
m0.plot_leafsoil_spectra()
m0.plot_toc_spectra()
../_images/249a2817e37a3547ff6d9393e6122c7ce81c912c0aa2f1bfa18261ce72a61a36.png ../_images/078efa3ccf81b8900c4bcee155ee1b68b0894ef87147602aeae65bc0f9f50072.png ../_images/f9cd3c1a483f332bc7a276ab7235b9e5869a0255dc593bfa9eecc23ff0f1338c.png

Compare results#

In crt1d.diagnostics, there are some functions that can be used to compare a set of model output datasets.

Plots – single band#

We demonstrate the function crt1d.diagnostics.plot_compare_band().

crt.diagnostics.plot_compare_band(dsets, band_name="PAR", marker=None)
../_images/0d16a44efe0abee40b3da670a10256c768217d9b2cc9507a9ec7ee7f06123b21.png
crt.diagnostics.plot_compare_band(
    dsets, band_name="solar", marker=None,
    legend_outside=False, ds_labels="scheme_short_name"
)
../_images/6a1b715a1a68d3af5904378f73674db201806db117b3e86b18f1e95aa246c543.png

Optionally, we can compare to a reference, in order to better see the differences among schemes.

crt.diagnostics.plot_compare_band(
    dsets, band_name="solar", marker=None,
    legend_outside=False, ds_labels="scheme_short_name",
    ref="2s"
)
../_images/94fb30601c4818d7693c76b6b020fdf743511bca332f2a436d4b628496221ea5.png

With ref_relative=True, the differences are computed as relative error (indicated by \(\Delta_r\)). Otherwise (above), they are absolute (indicated by \(\Delta\)). Relative error allows us to better see the differences in the lower regions of the canopy where there is less light.

crt.diagnostics.plot_compare_band(
    dsets[1:], band_name="solar", marker=None,
    legend_outside=False, ds_labels="scheme_short_name",
    ref=dsets[0], ref_relative=True, ref_label="a fancy 2s run"
)
../_images/1b123fd72ad03f2f67c5d709657438a50eb85cfe56653d41d3983c4fd167fcdc.png

👆 Note that the reference does not have to belong to dsets if passed as an xarray.Dataset.

Plots – spectra#

We demonstrate the function crt1d.diagnostics.plot_compare_spectra().

crt.diagnostics.plot_compare_spectra(dsets)
../_images/7306b0a12d04a4755592157ae10fefe5c7f8bc86989577246fc47629bdd28614.png

👆 It is hard to see differences this way. Like with crt1d.diagnostics.plot_compare_band(), we have some options to help illuminate the differences.

crt.diagnostics.plot_compare_spectra(dsets, ref="2s")

crt.diagnostics.plot_compare_spectra(
    dsets,
    ref="2s",
    norm=mpl.colors.SymLogNorm(10, base=10),
)
../_images/6524536369878cc288640159d4eaa852e7b9820ba44eb474461c7e29c68e0620.png ../_images/a53f64fa49b4b5b304e03a1d51cbf2c9f986c7fac3d173b3862a748d8dc9242c.png

Optionally, we can plot the reference spectra like normal, so that it is clear what the differences are relative to. In the plot below, we highlight how the spectrum changes from top-of-canopy to below by using the toc_relative option.

crt.diagnostics.plot_compare_spectra(
    dsets[:3] + dsets[5:],
    toc_relative=True,
    ref="2s", ref_plot=True
)
../_images/b236ee12c8d8d37794cf25568bd1c27653a333cb85b064750b50aaee902345d1.png

Plots – xarray#

Note that with the magic of xarray.plot.FacetGrid (examples here), we can make similar plots to the above.

xr.concat(
    [ds["I_d"] for ds in dsets],
    dim=xr.DataArray(name="exp", dims="exp", data=scheme_names)
).plot(
    x="wl", y="z", col="exp", col_wrap=3
);
../_images/822aff2f08a709182505ff1d27422c52e254d28c461eb2d87b9cfe75b676e0b8.png

👆 Merging the whole dataset is a bit more awkward since we have dataset attribute conflicts (though we can use combine_attrs='drop_conflicts' if we have xarray v0.17+) and some datasets have extra variables. Above we get around these problems by merging a selected variable instead.

Other diagnostic tools#

crt.diagnostics.compare_ebal(dsets).astype(float).round(3)
incoming outgoing (reflected) soil absorbed layerwise abs sum in-out-soil canopy abs
2s 982.509 140.117 139.381 703.011 703.011 703.011
4s 982.509 138.916 145.774 697.820 697.820 697.820
bf 982.509 244.540 156.812 581.157 581.157 581.157
bl 982.509 0.000 159.327 823.182 823.182 823.182
g77 1081.284 128.517 82.768 869.999 869.999 869.999
n79 982.509 167.410 131.162 683.937 683.937 683.937
zq 986.077 157.436 136.143 692.498 692.498 692.498
zq_pa 985.920 153.011 135.760 697.149 697.149 697.149

Model output datasets#

See the variables page for more information about the output variables.

dsets[0]
<xarray.Dataset> Size: 616kB
Dimensions:   (z: 60, wl: 107, zm: 59, wle: 108)
Coordinates:
  * z         (z) float64 480B 0.5 3.966 4.922 5.609 ... 18.34 18.68 19.11 20.0
  * wl        (wl) float64 856B 0.3025 0.3075 0.3125 0.3175 ... 2.405 2.475 2.55
  * zm        (zm) float64 472B 2.233 4.444 5.266 5.888 ... 18.51 18.89 19.55
  * wle       (wle) float64 864B 0.3 0.305 0.31 0.315 0.32 ... 2.36 2.45 2.5 2.6
Data variables: (12/21)
    I_dr      (z, wl) float64 51kB 0.001447 0.006402 0.01758 ... 0.544 0.2569
    I_df_d    (z, wl) float64 51kB 0.001012 0.004119 ... 0.02288 0.009896
    I_df_u    (z, wl) float64 51kB 0.0002705 0.001157 ... 0.01064 0.001819
    F         (z, wl) float64 51kB 0.004104 0.01737 0.04577 ... 0.646 0.2969
    I_d       (z, wl) float64 51kB 0.002459 0.01052 0.02803 ... 0.5669 0.2668
    dwl       (wl) float64 856B 0.005 0.005 0.005 0.005 ... 0.09 0.09 0.05 0.1
    ...        ...
    laim      (zm) float64 472B 3.966 3.898 3.831 3.763 ... 0.1695 0.1017 0.0339
    f_slm     (zm) float64 472B 0.1267 0.1313 0.136 ... 0.9155 0.9484 0.9825
    psi       float64 8B 0.3491
    sza       float64 8B 20.0
    G         float64 8B 0.4894
    K_b       float64 8B 0.5208
Attributes:
    info:               
    scheme_name:        2s
    scheme_long_name:   Dickinson–Sellers two-stream
    scheme_short_name:  2s
    crt1d_version:      0.3.dev6+gc700499
dsets[0]["zm"]
<xarray.DataArray 'zm' (zm: 59)> Size: 472B
array([ 2.233243,  4.444249,  5.265545,  5.888072,  6.406822,  6.859524,
        7.265752,  7.637189,  7.981456,  8.303839,  8.608181,  8.897375,
        9.173663,  9.438827,  9.694307,  9.94129 , 10.180769, 10.413585,
       10.640457, 10.862012, 11.078795, 11.29129 , 11.499927, 11.705094,
       11.907143, 12.106395, 12.303146, 12.497673, 12.690234, 12.881074,
       13.070427, 13.258519, 13.445569, 13.631793, 13.817406, 14.002623,
       14.187664, 14.372752, 14.558122, 14.744021, 14.93071 , 15.118471,
       15.307616, 15.498485, 15.691464, 15.886992, 16.085578, 16.287821,
       16.494442, 16.706323, 16.924573, 17.150628, 17.386407, 17.6346  ,
       17.899194, 18.186622, 18.508678, 18.892952, 19.552798])
Coordinates:
  * zm       (zm) float64 472B 2.233 4.444 5.266 5.888 ... 18.51 18.89 19.55
Attributes:
    long_name:  Height above ground
    units:      m
# Some schemes generate extra outputs (in addition to the required)
ms[scheme_names.index("zq")].out_extra
{'I_df_d_ss_scheme': array([[0.0011174 , 0.00453624, 0.01147541, ..., 0.05775764, 0.0050126 ,
         0.00090819],
        [0.0011775 , 0.00477837, 0.01208304, ..., 0.0591903 , 0.00514565,
         0.00093767],
        [0.00124104, 0.00503426, 0.01272508, ..., 0.06067554, 0.00528461,
         0.00096874],
        ...,
        [0.02539346, 0.10176728, 0.25397733, ..., 0.15556606, 0.02216082,
         0.00902544],
        [0.02686378, 0.10764841, 0.26862401, ..., 0.15305175, 0.02250769,
         0.00944879],
        [0.02843666, 0.11394001, 0.28429375, ..., 0.15073189, 0.02287754,
         0.00989642]]),
 'I_df_u_ss_scheme': array([[0.00015918, 0.0007042 , 0.0019335 , ..., 0.08350631, 0.0152426 ,
         0.00719909],
        [0.0002663 , 0.00113788, 0.00302736, ..., 0.09210497, 0.0155031 ,
         0.00696519],
        [0.00026468, 0.00112976, 0.00300279, ..., 0.08850114, 0.01472284,
         0.00655977],
        ...,
        [0.00186484, 0.00761463, 0.01937933, ..., 0.11479556, 0.0107946 ,
         0.00193982],
        [0.00196119, 0.00800401, 0.02035995, ..., 0.11827098, 0.01112857,
         0.00199167],
        [0.0020628 , 0.00841452, 0.02139341, ..., 0.12186427, 0.01147647,
         0.00204659]]),
 'F_ss_scheme': array([[0.00409313, 0.0172935 , 0.04552318, ..., 0.67748594, 0.11260302,
         0.05026394],
        [0.00448293, 0.01888997, 0.04959841, ..., 0.71174317, 0.11598109,
         0.05107883],
        [0.00466408, 0.01963916, 0.05152978, ..., 0.7222107 , 0.11738256,
         0.05159781],
        ...,
        [0.06603977, 0.26974079, 0.68668028, ..., 3.49608316, 0.60535961,
         0.27671251],
        [0.06958724, 0.28411388, 0.72296522, ..., 3.60421941, 0.6261088 ,
         0.28681965],
        [0.07336524, 0.29941604, 0.76158274, ..., 3.71679759, 0.64762855,
         0.29731055]])}