Commit 845a5d62 authored by Javier Vegas-Regidor's avatar Javier Vegas-Regidor
Browse files

Add density diagnostic

parent edd1f7bd
......@@ -25,6 +25,12 @@ earthdiagnostics.ocean.cutsection
:show-inheritance:
:members:
earthdiagnostics.ocean.density
------------------------------
.. automodule:: earthdiagnostics.ocean.density
:show-inheritance:
:members:
earthdiagnostics.ocean.gyres
----------------------------
.. automodule:: earthdiagnostics.ocean.gyres
......
......@@ -63,9 +63,9 @@ copyright = u"2020, BSC-CNS Earth Sciences Department"
# built documents.source ~/vi
#
# The short X.Y version.
version = "3.4"
version = "3.5"
# The full version, including alpha/beta/rc tags.
release = "3.4.1"
release = "3.5.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -315,6 +315,16 @@ Options:
4. Domain = ocean:
Variable's domain
density
~~~~~~~
Compute the total potential density anomaly. See :class:`~earthdiagnostics.ocean.density.Density`
Options:
********
This diagnostic has no options
gyres
~~~~~
......@@ -356,14 +366,14 @@ See :class:`~earthdiagnostics.ocean.heatcontentlayer.HeatContentLayer`
Options:
********
3. Min depth:
1. Min depth:
Minimum depth for the calculation in meteres
4. Max depth:
2. Max depth:
Maximum depth for the calculation in meters
5. Basin = 'Global':
Basin to calculate the heat content on.
3. Basins = ['Global']:
List of basins to calculate the heat content on.
interpolate
~~~~~~~~~~~
......@@ -381,8 +391,8 @@ Options:
1. Target grid:
New grid for the data
2. Variable:
Variable to interpolate
2. VariableList:
List of variables to interpolate
3. Domain = ocean:
Variable's domain
......
......@@ -892,10 +892,15 @@ class Cmor3Convention(DataConvention):
self.config.cmor.version,
)
if self.config.cmor.version == "latest":
versions = os.listdir(os.path.dirname(folder_path))
base_path = os.path.dirname(folder_path)
if not os.path.isdir(base_path):
base_path = base_path.replace(
'/original_files/cmorfiles/', '/cmorfiles/')
versions = os.listdir(base_path)
versions.sort(reverse=True)
self.config.cmor.version = versions[0]
folder_path = folder_path.replace('/latest/', f'/{versions[0]}/')
return self.get_cmor_folder_path(
startdate, member, domain, var, frequency, grid, cmor_var)
return folder_path
......
# coding=utf-8
"""Compute the potential density anomalies"""
import diagonals.density
import numpy as np
import iris
from earthdiagnostics.modelingrealm import ModelingRealms
from earthdiagnostics.utils import TempFile
from earthdiagnostics.diagnostic import Diagnostic
class Density(Diagnostic):
"""
Compute the total potential density anomaly
:param data_manager: data management object
:type data_manager: DataManager
:param startdate: startdate
:type startdate: str
:param member: member number
:type member: int
:param chunk: chunk's number
:type chunk: int
"""
alias = "density"
"Diagnostic alias for the configuration file"
def __init__(
self,
data_manager,
startdate,
member,
chunk,
):
Diagnostic.__init__(self, data_manager)
self.startdate = startdate
self.member = member
self.chunk = chunk
self.sigmas = [0, 2]
def __eq__(self, other):
if self._different_type(other):
return False
return (
self.startdate == other.startdate
and self.member == other.member
and self.chunk == other.chunk
)
def __str__(self):
return (
f"Density Startdate: {self.startdate} Member: {self.member} "
f"Chunk: {self.chunk}"
)
def __hash__(self):
return hash(str(self))
@classmethod
def generate_jobs(cls, diags, options):
"""
Create a job for each chunk to compute the diagnostic
:param diags: Diagnostics manager class
:type diags: Diags
:param options: This diagnostic does not require extra options
:type options: list[str]
:return:
"""
options_available = []
options = cls.process_options(options, options_available)
job_list = list()
for (
startdate,
member,
chunk,
) in diags.config.experiment.get_chunk_list():
job_list.append(
Density(
diags.data_manager,
startdate,
member,
chunk,
)
)
return job_list
def request_data(self):
"""Request data required by the diagnostic"""
self.bigthetao = self.request_chunk(
ModelingRealms.ocean,
"bigthetao",
self.startdate,
self.member,
self.chunk,
)
self.so = self.request_chunk(
ModelingRealms.ocean,
"so",
self.startdate,
self.member,
self.chunk,
)
def declare_data_generated(self):
"""Declare data to be generated by the diagnostic"""
self.sigma = {}
for sigma in self.sigmas:
self.sigma[sigma] = self.declare_chunk(
ModelingRealms.ocean,
f"sigma{sigma}",
self.startdate,
self.member,
self.chunk,
)
def compute(self):
"""Run the diagnostic"""
bigthetao = iris.load_cube(self.bigthetao.local_file)
so = iris.load_cube(self.so.local_file)
# Convert from practical to absolute
so = so / 0.99530670233846
for sigma in self.sigmas:
ref_pressure = sigma * 1000
sigma_values = []
for time in range(so.shape[0]):
sigma_values.append(diagonals.density.compute(
so[time, ...].data.astype(np.float32),
bigthetao.data[time, ...].astype(np.float32),
np.full(bigthetao.shape[1:],
ref_pressure, dtype=np.float32)
))
sigma_values = np.stack(sigma_values)
sigma_cube = bigthetao.copy(sigma_values)
sigma_cube.var_name = f'sigma{sigma}'
sigma_cube.standard_name = 'sea_water_sigma_theta'
sigma_cube.long_name = (
"potential density anomaly (potential density minus 1000 "
f"Kg/m3) with reference pressure of {ref_pressure} dbar"
)
sigma_cube.units = 'kg m-3'
temp = TempFile.get()
iris.save(sigma_cube, temp, zlib=True)
del sigma_cube
del sigma_values
self.sigma[sigma].set_local_file(temp)
......@@ -407,6 +407,7 @@ class WorkManager(object):
from .ocean.sivolume import Sivolume
from .ocean.sivol2d import Sivol2d
from .ocean.zonalmean import ZonalMean
from .ocean.density import Density
Diagnostic.register(MixedLayerSaltContent)
Diagnostic.register(Siasiesiv)
......@@ -433,6 +434,7 @@ class WorkManager(object):
Diagnostic.register(Sivolume)
Diagnostic.register(Sivol2d)
Diagnostic.register(ZonalMean)
Diagnostic.register(Density)
class Downloader(object):
......
......@@ -9,4 +9,5 @@ dependencies:
- cdo
- nco
- eccodes
- six
- iris>=2.4
......@@ -24,14 +24,14 @@ REQUIREMENTS = {
"cdo>=1.3.4",
"cfgrib",
"dask[array]",
"diagonals>=0.2"
"diagonals>=0.3",
"netCDF4",
"nco>=0.0.3",
"numba",
"numpy",
"psutil",
"openpyxl",
"scitools-iris>=2.2",
"scitools-iris>=2.4",
"six",
"xxhash",
],
......
# coding=utf-8
from unittest import TestCase
from mock import Mock
from earthdiagnostics.ocean.density import Density
class TestHeatContent(TestCase):
def setUp(self):
self.data_manager = Mock()
self.diags = Mock()
self.diags.model_version = "model_version"
self.diags.config.experiment.get_chunk_list.return_value = (
("20010101", 0, 0),
("20010101", 0, 1),
)
def test_generate_jobs(self):
jobs = Density.generate_jobs(
self.diags, ["diagnostic"]
)
self.assertEqual(len(jobs), 2)
self.assertEqual(
jobs[0],
Density(self.data_manager, "20010101", 0, 0)
)
self.assertEqual(
jobs[1],
Density(self.data_manager, "20010101", 0, 1)
)
with self.assertRaises(Exception):
Density.generate_jobs(self.diags, ["diagnostic", "0"])
def test_str(self):
diag = Density(self.data_manager, "20010101", 0, 0)
self.assertEqual(
str(diag),
"Density Startdate: 20010101 Member: 0 Chunk: 0"
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment