Repository: ESMValGroup/ESMValCore

Issue:
derivation of total carbon stock over land
The CMIP5 variables do not include the total carbon mass content over land. This is often derived as the sum of the individual components. This normally should be the sum of cVeg and cSoil. But, checks are often necessary to ensure that all the smaller components have been considered. This leads to having a different set of components of mass content stocks for different models.


Discussion:

Patch:
diff --git a/esmvalcore/cmor/tables/custom/CMOR_ctotal.dat b/esmvalcore/cmor/tables/custom/CMOR_ctotal.dat
new file mode 100644
index 000000000..a7a2ea11f
--- /dev/null
+++ b/esmvalcore/cmor/tables/custom/CMOR_ctotal.dat
@@ -0,0 +1,21 @@
+SOURCE: CMIP5
+!============
+variable_entry:    ctotal
+!============
+modeling_realm:    land
+!----------------------------------
+! Variable attributes:
+!----------------------------------
+standard_name:
+units:             kg m-2
+cell_methods:      time: mean area: mean
+cell_measures:     area: areacella
+long_name:         Total Carbon Mass in Ecosystem
+!----------------------------------
+! Additional variable information:
+!----------------------------------
+dimensions:        longitude latitude time
+out_name:          ctotal
+type:              real
+!----------------------------------
+!
diff --git a/esmvalcore/preprocessor/_derive/ctotal.py b/esmvalcore/preprocessor/_derive/ctotal.py
new file mode 100644
index 000000000..76e539e89
--- /dev/null
+++ b/esmvalcore/preprocessor/_derive/ctotal.py
@@ -0,0 +1,31 @@
+"""Derivation of variable `ctotal`."""
+
+from iris import Constraint
+
+from ._baseclass import DerivedVariableBase
+
+
+class DerivedVariable(DerivedVariableBase):
+    """Derivation of variable `ctotal`."""
+
+    # Required variables
+    required = [
+        {
+            'short_name': 'cVeg'
+        },
+        {
+            'short_name': 'cSoil'
+        },
+    ]
+
+    @staticmethod
+    def calculate(cubes):
+        """Compute total ecosystem carbon storage."""
+        c_soil_cube = cubes.extract_strict(
+            Constraint(name='soil_carbon_content'))
+        c_veg_cube = cubes.extract_strict(
+            Constraint(name='vegetation_carbon_content'))
+        c_total_cube = c_soil_cube + c_veg_cube
+        c_total_cube.standard_name = None
+        c_total_cube.long_name = 'Total Carbon Stock'
+        return c_total_cube


Test patch:
diff --git a/tests/unit/preprocessor/_derive/test_ctotal.py b/tests/unit/preprocessor/_derive/test_ctotal.py
new file mode 100644
index 000000000..d3f8189ce
--- /dev/null
+++ b/tests/unit/preprocessor/_derive/test_ctotal.py
@@ -0,0 +1,30 @@
+"""Test derivation of `ctotal`."""
+import iris
+import numpy as np
+import pytest
+from cf_units import Unit
+
+import esmvalcore.preprocessor._derive.ctotal as ctotal
+
+
+@pytest.fixture
+def cubes():
+    c_soil_cube = iris.cube.Cube([[1.0, 2.0],
+                                  [0.0, 20.0]],
+                                 units='kg m-2',
+                                 standard_name='soil_carbon_content')
+
+    c_veg_cube = iris.cube.Cube([[10.0, 20.0],
+                                 [50.0, 100.0]],
+                                units='kg m-2',
+                                standard_name='vegetation_carbon_content')
+    return iris.cube.CubeList([c_soil_cube, c_veg_cube])
+
+
+def test_ctotal_calculation(cubes):
+    derived_var = ctotal.DerivedVariable()
+    out_cube = derived_var.calculate(cubes)
+    np.testing.assert_allclose(
+        out_cube.data, np.array([[11.0, 22.0],
+                                 [50.0, 120.0]]))
+    assert out_cube.units == Unit('kg m-2')