Generating a Table of Global Fluxes

This notebook reads time series data from Make Timeseries.ipynb and generates a table containing values listed in issue #6

This notebook uses several python packages

The watermark package shows the version number used to help others recreate this environment.

import pandas as pd
import ann_avg_utils as aau

# Set pandas option and get pint units
pd.set_option('display.max_colwidth', 75)
units, final_units = aau.get_pint_units()

%load_ext watermark
%watermark -d -iv -m -g -h
Compiler    : GCC 9.3.0
OS          : Linux
Release     : 3.10.0-1127.18.2.el7.x86_64
Machine     : x86_64
Processor   : x86_64
CPU cores   : 72
Architecture: 64bit

Hostname: crhtc50

Git hash: d554567d1893e0b92b8e04d39cdfe8ccf8aefef4

pandas: 1.2.4
json  : 2.0.9
sys   : 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 16:07:37) 
[GCC 9.3.0]

Copy data that is shared with time series plotting from aau

global_vars = aau.global_vars()

xp_dir = global_vars['xp_dir']
vars = global_vars['vars']
experiments = global_vars['experiments']
experiment_longnames = global_vars['experiment_longnames']
experiment_span = global_vars['experiment_span']
experiment_dict = global_vars['experiment_dict']
time_slices = global_vars['time_slices']

Read output from Make Timeseries.ipynb

Files were written by xpersist and are read in using xr.open_dataset

%%time

ann_avg, cesm_units = aau.get_ann_means_and_units(xp_dir, vars, experiments, experiment_longnames, units)
CPU times: user 1.46 s, sys: 180 ms, total: 1.64 s
Wall time: 3.65 s

Reduce Data Sets

Data has been reduced to annual means, but the netcdf files contain every year in the dataset. For generating tables, we want to look at specific time periods.

# aau.print_exp_time_bounds(ann_avg[vars[0]], time_slices)

Define labels for rows in each table

Also determine correct number of digits to write each value out to

table_specs = aau.get_table_specs(final_units, o2_levs=[5, 20, 60, 80])

Average over all ensemble members and time (for proper time period)

diagnostic_values = aau.compute_diagnostic_values(experiments,
                                                  table_specs,
                                                  ann_avg,
                                                  time_slices,
                                                  cesm_units,
                                                  final_units,
                                                  experiment_span,
                                                  verbose=False)
   * Can not compute SiO2_FLUX_100m for cesm1_PI
   * Can not compute NHx_SURFACE_EMIS for cesm1_PI
   * Can not compute SedDenitrif for cesm1_PI
   * Can not compute ponToSed for cesm1_PI
   * Can not compute DON_RIV_FLUX for cesm1_PI
   * Can not compute DONr_RIV_FLUX for cesm1_PI
   * Can not compute NO3_RIV_FLUX for cesm1_PI
   * No additional denitrification terms for cesm1_PI
   * Can not compute SiO2_FLUX_100m for cesm1_PI_esm
   * Can not compute NHx_SURFACE_EMIS for cesm1_PI_esm
   * Can not compute SedDenitrif for cesm1_PI_esm
   * Can not compute ponToSed for cesm1_PI_esm
   * Can not compute DON_RIV_FLUX for cesm1_PI_esm
   * Can not compute DONr_RIV_FLUX for cesm1_PI_esm
   * Can not compute NO3_RIV_FLUX for cesm1_PI_esm
   * No additional denitrification terms for cesm1_PI_esm
   * Can not compute SiO2_FLUX_100m for cesm1_hist
   * Can not compute NHx_SURFACE_EMIS for cesm1_hist
   * Can not compute SedDenitrif for cesm1_hist
   * Can not compute ponToSed for cesm1_hist
   * Can not compute DON_RIV_FLUX for cesm1_hist
   * Can not compute DONr_RIV_FLUX for cesm1_hist
   * Can not compute NO3_RIV_FLUX for cesm1_hist
   * No additional denitrification terms for cesm1_hist
   * Can not compute SiO2_FLUX_100m for cesm1_hist_esm
   * Can not compute NHx_SURFACE_EMIS for cesm1_hist_esm
   * Can not compute SedDenitrif for cesm1_hist_esm
   * Can not compute ponToSed for cesm1_hist_esm
   * Can not compute DON_RIV_FLUX for cesm1_hist_esm
   * Can not compute DONr_RIV_FLUX for cesm1_hist_esm
   * Can not compute NO3_RIV_FLUX for cesm1_hist_esm
   * No additional denitrification terms for cesm1_hist_esm
   * Can not compute SiO2_FLUX_100m for cesm1_RCP85
   * Can not compute NHx_SURFACE_EMIS for cesm1_RCP85
   * Can not compute SedDenitrif for cesm1_RCP85
   * Can not compute ponToSed for cesm1_RCP85
   * Can not compute DON_RIV_FLUX for cesm1_RCP85
   * Can not compute DONr_RIV_FLUX for cesm1_RCP85
   * Can not compute NO3_RIV_FLUX for cesm1_RCP85
   * No additional denitrification terms for cesm1_RCP85
   * Can not compute SiO2_FLUX_100m for cesm1_hist_RCP85
   * Can not compute NHx_SURFACE_EMIS for cesm1_hist_RCP85
   * Can not compute SedDenitrif for cesm1_hist_RCP85
   * Can not compute ponToSed for cesm1_hist_RCP85
   * Can not compute DON_RIV_FLUX for cesm1_hist_RCP85
   * Can not compute DONr_RIV_FLUX for cesm1_hist_RCP85
   * Can not compute NO3_RIV_FLUX for cesm1_hist_RCP85
   * No additional denitrification terms for cesm1_hist_RCP85

Actually make the tables

def make_table(table_specs, table_vars, test_exps):
    table_dict = dict()
    table_dict['Flux or Concentration'] = []
    for varname in table_vars:
        # Unpack dictionaries in diag_columns
        table_key = table_specs[varname]['key']
        units = table_specs[varname]['units']
        rounding = table_specs[varname]['rounding']
        # Get row header
        if units:
            row_header = f'{table_key} ({units})'
        else:
            row_header = table_key
        table_dict['Flux or Concentration'].append(row_header)

        # Get values rounded to correct number of digits
        for exp in test_exps:
            if experiment_longnames[exp] not in table_dict:
                table_dict[experiment_longnames[exp]] = []
            # Workaround to drop decimal place when rounding to nearest integer
            if exp != 'diff':
                round_to = rounding
            else:
                round_to = 3
            format = f'0.{round_to}f'
            try:
                rounded_val = f'{diagnostic_values[exp][table_key].magnitude:{format}}'
                table_dict[experiment_longnames[exp]].append(rounded_val)
                # Add asterisk denoting CESM1 integrals are 150m, not full depth
                if ('cesm1' in exp) and (varname in ['NPP', 'NPP_diat']):
                    table_dict[experiment_longnames[exp]][-1] = table_dict[experiment_longnames[exp]][-1] + '*'
            except:
                table_dict[experiment_longnames[exp]].append('-')
    return(table_dict)
if 'cesm1_PI_esm' in diagnostic_values:
    if global_vars["include_marg_seas"]:
        print('Comparison of cesm1_PI_esm')
#         let var = fg_co2[d=2]
#         show var var
#          VAR = FG_CO2[D=2]
#         list var_integral_PgC_year
#                      VARIABLE : (1E-9 * 12 * 1E-15 * 86400 * 365) * VAR_MUL_AREA[I=@SUM,J=@SUM]
#                      X        : 0.5 to 320.5
#                      Y        : 0.5 to 384.5
#                      ENSEMBLE : 0421
#                  -0.02491
        if table_specs['CO2']['key'] in diagnostic_values["cesm1_PI_esm"]:
            print(f'FG_CO2: {diagnostic_values["cesm1_PI_esm"][table_specs["CO2"]["key"]].magnitude:0.5f} (should be -0.02491)')

#         let var = POC_FLUX_IN_100m[d=2]
#         show var var
#          VAR = POC_FLUX_IN_100M[D=2]
#         list/prec=6 var_integral_PgC_year
#                      VARIABLE : (1E-9 * 12 * 1E-15 * 86400 * 365) * VAR_MUL_AREA[I=@SUM,J=@SUM]
#                      X        : 0.5 to 320.5
#                      Y        : 0.5 to 384.5
#                      ENSEMBLE : 0421
#                   8.06490
        if table_specs['POC']['key'] in diagnostic_values["cesm1_PI_esm"]:
            print(f'POC_FLUX_IN_100m: {diagnostic_values["cesm1_PI_esm"][table_specs["POC"]["key"]].magnitude:0.5f} (should be 8.06490)')

#     let var = photoC_diat_zint_100m[d=2]+photoC_sp_zint_100m[d=2]+photoC_diaz_zint_100m[d=2]
#     show var var
#      VAR = PHOTOC_DIAT_ZINT_100M[D=2]+PHOTOC_SP_ZINT_100M[D=2]+PHOTOC_DIAZ_ZINT_100M[D=2]
#     list/prec=6 var_integral_PgC_year
#                  VARIABLE : (1E-9 * 12 * 1E-15 * 86400 * 365) * VAR_MUL_AREA[I=@SUM,J=@SUM]
#                  X        : 0.5 to 320.5
#                  Y        : 0.5 to 384.5
#                  ENSEMBLE : 0421
#               55.4878
        if table_specs['NPP_100m']['key'] in diagnostic_values["cesm1_PI_esm"]:
            print(f'photoC_diat_zint_100m: {diagnostic_values["cesm1_PI_esm"][table_specs["NPP_100m"]["key"]].magnitude:0.4f} (should be 55.4878)')
    else:
        print("Comparison of CESM1 values is only done when including marginal seas in global averages")
else:
    print('No comparisons done, since cesm1_PI_esm experiment not included')
Comparison of CESM1 values is only done when including marginal seas in global averages
# Keith L's table
table_vars = ['CO2', 'NPP_100m', 'POC']

# Match number of digits in orginal paper
table_specs['CO2']['rounding'] = 3
table_specs['NPP_100m']['rounding'] = 2

# Add difference column
new_exp = 'diff'
diagnostic_values[new_exp] = dict()
experiment_longnames[new_exp] = 'Difference'
for table_key in table_specs:
    table_spec = table_specs[table_key]
    if ('cesm1_hist' in diagnostic_values) and ('cesm1_PI' in diagnostic_values):
        try:
            # If key has not been populated, we want a dash here
            diagnostic_values[new_exp][table_spec['key']] = diagnostic_values['cesm1_hist'][table_spec['key']] - diagnostic_values['cesm1_PI'][table_spec['key']]
        except:
            diagnostic_values[new_exp][table_spec['key']] = '-'
    else:
        diagnostic_values[new_exp][table_spec['key']] = '-'

pd.DataFrame(make_table(table_specs, table_vars, ['cesm1_PI', 'cesm1_hist', new_exp])).set_index("Flux or Concentration")
preindustrial (CESM1) 1981-2005 (CESM1) Difference
Flux or Concentration
Air-sea CO$_2$ flux (PgC/yr) -0.023 1.773 1.796
Net primary production, top 100m (PgC/yr) 55.35 55.53 0.178
Sinking POC at 100 m (PgC/yr) 8.07 8.00 -0.074
# Keith M's original table
# We use a different set of preindustrial years
# Also, maybe he uses equal weighting for month -> year instead of number of days per month?

table_specs['CO2']['rounding'] = 2
table_specs['NPP_100m']['rounding'] = 1

test_exps = ['cesm1_PI_esm', 'cesm1_hist_esm', 'cesm1_RCP45', 'cesm1_RCP85']
table_vars = ['NPP', 'NPP_100m', 'POC', 'CaCO3', 'rain', 'Nfix', 'Ndep', 'denitrif',
              'Ncycle', 'CO2', 'NPP_diat', 'NPP_diat_100m', 'O2', 'o2_under_20']

pd.DataFrame(make_table(table_specs, table_vars, test_exps)).set_index("Flux or Concentration")
preindustrial (CESM1, BPRP) 1990s (CESM1) RCP 4.5 2090s (CESM1) RCP 8.5 2090s (CESM1)
Flux or Concentration
Net primary production, full depth (PgC/yr) 55.8* 56.3* - 54.0*
Net primary production, top 100m (PgC/yr) 55.3 55.8 - 53.5
Sinking POC at 100 m (PgC/yr) 8.05 8.05 - 7.20
Sinking CaCO$_3$ at 100 m (PgC/yr) 0.756 0.750 - 0.723
Rain ratio (CaCO$_3$/POC) at 100 m 0.094 0.093 - 0.100
Nitrogen fixation (TgN/yr) 176.7 173.6 - 143.7
Nitrogen deposition (TgN/yr) 6.6 29.1 - 30.0
Water Column Denitrification (TgN/yr) 189.7 193.1 - 187.5
N cycle imbalance (TgN/yr) -6.4 9.6 - -13.8
Air-sea CO$_2$ flux (PgC/yr) -0.02 2.18 - 4.71
Diatom primary production, full depth (\%) 35* 35* - 32*
Diatom primary production, top 100m (\%) 34 34 - 32
Mean ocean oxygen ($\mu$M) 192 190 - 183
OMZ volume (O$_2$ $<$20 $\mu$M) (10$^1$$^5$ m$^3$) 37 38 - 46
# Updated table for our paper
table_specs['CO2']['rounding'] = 2
table_specs['NPP_100m']['rounding'] = 1
table_specs['SiO2']['rounding'] = 1

# test_exps = ['cesm2_PI', 'cesm2_hist', 'cesm2_SSP1-2.6', 'cesm2_SSP2-4.5', 'cesm2_SSP3-7.0', 'cesm2_SSP5-8.5']
test_exps = ['cesm1_PI', 'cesm1_hist_RCP85', 'cesm1_RCP85', 'cesm2_PI', 'cesm2_hist', 'cesm2_SSP5-8.5']
table_vars = ['NPP',
#               'NPP_100m',
              'POC',
              'CaCO3',
              'SiO2',
              'rain',
              'Nfix',
              'Ndep',
              'denitrif',
              'denitrif2',
              'Nbury',
              'Nemis',
              'rivflux',
              'Ncycle',
              'CO2',
              'NPP_diat_100m',
              'NPP_diat',
#               'NPP_diat_100m',
#               'O2',
#               'o2_under_20',
#               'o2_under_5',
#               'o2_under_60',
#               'o2_under_80'
             ]
our_table = pd.DataFrame(make_table(table_specs, table_vars, test_exps))
our_table.set_index("Flux or Concentration")
preindustrial (CESM1) 1990 - 2014 (CESM1) RCP 8.5 2090s (CESM1) preindustrial (CESM2) 1990-2014 (CESM2) SSP5-8.5 2090s (CESM2)
Flux or Concentration
Net primary production, full depth (PgC/yr) 55.9* 56.1* 54.0* 48.2 48.8 49.8
Sinking POC at 100 m (PgC/yr) 8.07 7.98 7.20 6.98 7.05 6.69
Sinking CaCO$_3$ at 100 m (PgC/yr) 0.757 0.748 0.723 0.767 0.767 0.808
Sinking SiO$_2$ at 100 m (Tmol/yr) - - - 77.6 78.3 69.9
Rain ratio (CaCO$_3$/POC) at 100 m 0.094 0.094 0.100 0.110 0.109 0.121
Nitrogen fixation (TgN/yr) 175.3 169.4 143.7 240.6 243.1 285.0
Nitrogen deposition (TgN/yr) 6.6 29.6 30.0 13.3 37.2 38.3
Water Column Denitrification (TgN/yr) 190.0 193.7 187.5 184.8 191.6 255.8
Sediment Denitrification (TgN/yr) - - - 67 71 68
Nitrogen Burial to Sediment (TgN/yr) - - - 24 27 22
Nitrogen surface emissions (TgN/yr) - - - 6 5 3
Nitrogen River Flux (TgN/yr) - - - 13 25 25
N cycle imbalance (TgN/yr) -8.0 5.2 -13.8 -14.1 10.3 -0.8
Air-sea CO$_2$ flux (PgC/yr) -0.02 2.03 4.71 -0.04 2.04 5.33
Diatom primary production, top 100m (\%) 34 34 32 35 37 31
Diatom primary production, full depth (\%) 35* 35* 32* 36 37 31
print(our_table.to_latex(index=False, escape=False))
\begin{tabular}{lllllll}
\toprule
                      Flux or Concentration & preindustrial (CESM1) & 1990 - 2014 (CESM1) & RCP 8.5 2090s (CESM1) & preindustrial (CESM2) & 1990-2014 (CESM2) & SSP5-8.5 2090s (CESM2) \\
\midrule
Net primary production, full depth (PgC/yr) &                 55.9* &               56.1* &                 54.0* &                  48.2 &              48.8 &                   49.8 \\
              Sinking POC at 100 m (PgC/yr) &                  8.07 &                7.98 &                  7.20 &                  6.98 &              7.05 &                   6.69 \\
         Sinking CaCO$_3$ at 100 m (PgC/yr) &                 0.757 &               0.748 &                 0.723 &                 0.767 &             0.767 &                  0.808 \\
         Sinking SiO$_2$ at 100 m (Tmol/yr) &                     - &                   - &                     - &                  77.6 &              78.3 &                   69.9 \\
         Rain ratio (CaCO$_3$/POC) at 100 m &                 0.094 &               0.094 &                 0.100 &                 0.110 &             0.109 &                  0.121 \\
                 Nitrogen fixation (TgN/yr) &                 175.3 &               169.4 &                 143.7 &                 240.6 &             243.1 &                  285.0 \\
               Nitrogen deposition (TgN/yr) &                   6.6 &                29.6 &                  30.0 &                  13.3 &              37.2 &                   38.3 \\
      Water Column Denitrification (TgN/yr) &                 190.0 &               193.7 &                 187.5 &                 184.8 &             191.6 &                  255.8 \\
          Sediment Denitrification (TgN/yr) &                     - &                   - &                     - &                    67 &                71 &                     68 \\
       Nitrogen Burial to Sediment (TgN/yr) &                     - &                   - &                     - &                    24 &                27 &                     22 \\
        Nitrogen surface emissions (TgN/yr) &                     - &                   - &                     - &                     6 &                 5 &                      3 \\
               Nitrogen River Flux (TgN/yr) &                     - &                   - &                     - &                    13 &                25 &                     25 \\
                 N cycle imbalance (TgN/yr) &                  -8.0 &                 5.2 &                 -13.8 &                 -14.1 &              10.3 &                   -0.8 \\
               Air-sea CO$_2$ flux (PgC/yr) &                 -0.02 &                2.03 &                  4.71 &                 -0.04 &              2.04 &                   5.33 \\
   Diatom primary production, top 100m (\%) &                    34 &                  34 &                    32 &                    35 &                37 &                     31 \\
 Diatom primary production, full depth (\%) &                   35* &                 35* &                   32* &                    36 &                37 &                     31 \\
\bottomrule
\end{tabular}