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 ...@@ -25,6 +25,12 @@ earthdiagnostics.ocean.cutsection
:show-inheritance: :show-inheritance:
:members: :members:
earthdiagnostics.ocean.density
------------------------------
.. automodule:: earthdiagnostics.ocean.density
:show-inheritance:
:members:
earthdiagnostics.ocean.gyres earthdiagnostics.ocean.gyres
---------------------------- ----------------------------
.. automodule:: earthdiagnostics.ocean.gyres .. automodule:: earthdiagnostics.ocean.gyres
......
...@@ -63,9 +63,9 @@ copyright = u"2020, BSC-CNS Earth Sciences Department" ...@@ -63,9 +63,9 @@ copyright = u"2020, BSC-CNS Earth Sciences Department"
# built documents.source ~/vi # built documents.source ~/vi
# #
# The short X.Y version. # The short X.Y version.
version = "3.4" version = "3.5"
# The full version, including alpha/beta/rc tags. # 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 # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
......
...@@ -315,6 +315,16 @@ Options: ...@@ -315,6 +315,16 @@ Options:
4. Domain = ocean: 4. Domain = ocean:
Variable's domain Variable's domain
density
~~~~~~~
Compute the total potential density anomaly. See :class:`~earthdiagnostics.ocean.density.Density`
Options:
********
This diagnostic has no options
gyres gyres
~~~~~ ~~~~~
...@@ -356,14 +366,14 @@ See :class:`~earthdiagnostics.ocean.heatcontentlayer.HeatContentLayer` ...@@ -356,14 +366,14 @@ See :class:`~earthdiagnostics.ocean.heatcontentlayer.HeatContentLayer`
Options: Options:
******** ********
3. Min depth: 1. Min depth:
Minimum depth for the calculation in meteres Minimum depth for the calculation in meteres
4. Max depth: 2. Max depth:
Maximum depth for the calculation in meters Maximum depth for the calculation in meters
5. Basin = 'Global': 3. Basins = ['Global']:
Basin to calculate the heat content on. List of basins to calculate the heat content on.
interpolate interpolate
~~~~~~~~~~~ ~~~~~~~~~~~
...@@ -381,8 +391,8 @@ Options: ...@@ -381,8 +391,8 @@ Options:
1. Target grid: 1. Target grid:
New grid for the data New grid for the data
2. Variable: 2. VariableList:
Variable to interpolate List of variables to interpolate
3. Domain = ocean: 3. Domain = ocean:
Variable's domain Variable's domain
......
...@@ -892,10 +892,15 @@ class Cmor3Convention(DataConvention): ...@@ -892,10 +892,15 @@ class Cmor3Convention(DataConvention):
self.config.cmor.version, self.config.cmor.version,
) )
if self.config.cmor.version == "latest": 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) versions.sort(reverse=True)
self.config.cmor.version = versions[0] 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 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): ...@@ -407,6 +407,7 @@ class WorkManager(object):
from .ocean.sivolume import Sivolume from .ocean.sivolume import Sivolume
from .ocean.sivol2d import Sivol2d from .ocean.sivol2d import Sivol2d
from .ocean.zonalmean import ZonalMean from .ocean.zonalmean import ZonalMean
from .ocean.density import Density
Diagnostic.register(MixedLayerSaltContent) Diagnostic.register(MixedLayerSaltContent)
Diagnostic.register(Siasiesiv) Diagnostic.register(Siasiesiv)
...@@ -433,6 +434,7 @@ class WorkManager(object): ...@@ -433,6 +434,7 @@ class WorkManager(object):
Diagnostic.register(Sivolume) Diagnostic.register(Sivolume)
Diagnostic.register(Sivol2d) Diagnostic.register(Sivol2d)
Diagnostic.register(ZonalMean) Diagnostic.register(ZonalMean)
Diagnostic.register(Density)
class Downloader(object): class Downloader(object):
......
...@@ -9,4 +9,5 @@ dependencies: ...@@ -9,4 +9,5 @@ dependencies:
- cdo - cdo
- nco - nco
- eccodes - eccodes
- six
- iris>=2.4 - iris>=2.4
...@@ -24,14 +24,14 @@ REQUIREMENTS = { ...@@ -24,14 +24,14 @@ REQUIREMENTS = {
"cdo>=1.3.4", "cdo>=1.3.4",
"cfgrib", "cfgrib",
"dask[array]", "dask[array]",
"diagonals>=0.2" "diagonals>=0.3",
"netCDF4", "netCDF4",
"nco>=0.0.3", "nco>=0.0.3",
"numba", "numba",
"numpy", "numpy",
"psutil", "psutil",
"openpyxl", "openpyxl",
"scitools-iris>=2.2", "scitools-iris>=2.4",
"six", "six",
"xxhash", "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