|
| 1 | +# pylint: disable=unused-argument |
| 2 | +# pylint: disable=too-many-arguments |
| 3 | +# pylint: disable=too-many-function-args |
| 4 | +# pylint: disable=R0917 |
| 5 | +# pylint: disable=E1121 |
| 6 | +# flake8: noqa |
| 7 | +"""ESMValTool CMORizer for TROPFLUX data. |
| 8 | +
|
| 9 | +Tier |
| 10 | + Tier 2: other freely-available dataset. |
| 11 | +
|
| 12 | +Source |
| 13 | + https://incois.gov.in/tropflux |
| 14 | +
|
| 15 | +Last access |
| 16 | + 20250115 |
| 17 | +
|
| 18 | +Download and processing instructions |
| 19 | + Register and sign-in here: |
| 20 | +
|
| 21 | + https://incois.gov.in/tropflux/DataHome.jsp |
| 22 | +
|
| 23 | + Data is available per year from 1979 to 2018 |
| 24 | + at daily and monthly frequency. |
| 25 | +
|
| 26 | + Products availables are: |
| 27 | +
|
| 28 | + - Air Temperature at 2m |
| 29 | + - Latent Heat Flux |
| 30 | + - Long Wave Radiation |
| 31 | + - Meridional Wind Stress |
| 32 | + - Net Surface Heat Flux |
| 33 | + - Sea Surface Temperature |
| 34 | + - Sensible Heat Flux |
| 35 | + - Shortwave Radiation |
| 36 | + - Specific Humidity at 2m |
| 37 | + - Wind Speed at 10m |
| 38 | + - Wind Stress magnitude |
| 39 | + - Zonal Wind Stress |
| 40 | +
|
| 41 | +Caveats |
| 42 | +""" |
| 43 | + |
| 44 | +import logging |
| 45 | +import re |
| 46 | +from copy import deepcopy |
| 47 | +from pathlib import Path |
| 48 | + |
| 49 | +import cf_units |
| 50 | +import iris |
| 51 | +from esmvalcore.cmor.table import CMOR_TABLES |
| 52 | +from iris import NameConstraint |
| 53 | +from iris.util import equalise_attributes |
| 54 | + |
| 55 | +from esmvaltool.cmorizers.data import utilities as utils |
| 56 | + |
| 57 | +logger = logging.getLogger(__name__) |
| 58 | + |
| 59 | +# scitools-iris.readthedocs.io/en/stable/generated/api/iris.html#iris.Future |
| 60 | +try: |
| 61 | + iris.FUTURE.date_microseconds = True |
| 62 | + iris.FUTURE.save_split_attrs = True |
| 63 | +except AttributeError as e: |
| 64 | + # Handle cases where FUTURE or the attributes don't exist |
| 65 | + logger.warning("AttributeError: %s", e) |
| 66 | +except (TypeError, ValueError) as e: |
| 67 | + # Handle specific errors if these might occur |
| 68 | + logger.warning("TypeError or ValueError: %s", e) |
| 69 | +except BaseException as e: |
| 70 | + # Fallback for rare or unknown issues, but avoid catching Exception |
| 71 | + logger.warning("An unexpected error occurred: %s", e) |
| 72 | + |
| 73 | + |
| 74 | +def _fix_coordinates(cube, definition): |
| 75 | + cube = utils.fix_coords(cube) |
| 76 | + |
| 77 | + for coord_def in definition.coordinates.values(): |
| 78 | + axis = coord_def.axis |
| 79 | + coord = cube.coord(axis=axis) |
| 80 | + if axis == 'Z': |
| 81 | + coord.convert_units(coord_def.units) |
| 82 | + coord.standard_name = coord_def.standard_name |
| 83 | + coord.var_name = coord_def.out_name |
| 84 | + coord.long_name = coord_def.long_name |
| 85 | + coord.points = coord.core_points().astype('float64') |
| 86 | + if coord.var_name == 'plev': |
| 87 | + coord.attributes['positive'] = 'down' |
| 88 | + |
| 89 | + return cube |
| 90 | + |
| 91 | + |
| 92 | +def _extract_variable(short_name, var, cfg, raw_filepaths, out_dir): |
| 93 | + attributes = deepcopy(cfg['attributes']) |
| 94 | + attributes['mip'] = var['mip'] |
| 95 | + cmor_table = CMOR_TABLES[attributes['project_id']] |
| 96 | + definition = cmor_table.get_variable(var['mip'], short_name) |
| 97 | + cmor_info = cfg['cmor_table'].get_variable(var['mip'], short_name) |
| 98 | + |
| 99 | + if cmor_info.positive != '': |
| 100 | + attributes['positive'] = cmor_info.positive |
| 101 | + |
| 102 | + # load data |
| 103 | + raw_var = var.get('raw', short_name) |
| 104 | + cubes = iris.load(raw_filepaths, NameConstraint(var_name=raw_var)) |
| 105 | + equalise_attributes(cubes) |
| 106 | + for cube in cubes: |
| 107 | + cube.rename(cubes[0].name()) |
| 108 | + cube = cubes.concatenate_cube() |
| 109 | + utils.set_global_atts(cube, attributes) |
| 110 | + |
| 111 | + # Set correct names |
| 112 | + cube.var_name = definition.short_name |
| 113 | + if definition.standard_name: |
| 114 | + cube.standard_name = definition.standard_name |
| 115 | + cube.long_name = definition.long_name |
| 116 | + |
| 117 | + utils.fix_var_metadata(cube, cmor_info) |
| 118 | + |
| 119 | + # fix time units |
| 120 | + cube.coord('time').convert_units( |
| 121 | + cf_units.Unit('days since 1950-1-1 00:00:00', calendar='gregorian')) |
| 122 | + |
| 123 | + cube = _fix_coordinates(cube, definition) |
| 124 | + |
| 125 | + if raw_var == 'taux': |
| 126 | + cube.data = -1 * cube.data |
| 127 | + |
| 128 | + utils.save_variable( |
| 129 | + cube, |
| 130 | + short_name, |
| 131 | + out_dir, |
| 132 | + attributes, |
| 133 | + unlimited_dimensions=['time'], |
| 134 | + ) |
| 135 | + |
| 136 | +def cmorization(in_dir, out_dir, cfg, cfg_user, start_date, end_date): |
| 137 | + """Run CMORizer for TROPFLUX.""" |
| 138 | + for (short_name, var) in cfg['variables'].items(): |
| 139 | + logger.info("CMORizing variable '%s'", short_name) |
| 140 | + short_name = var['short_name'] |
| 141 | + raw_filenames = Path(in_dir).rglob("*.nc") |
| 142 | + filenames = [] |
| 143 | + for raw_filename in raw_filenames: |
| 144 | + if re.search(var["file"], str(raw_filename)): |
| 145 | + filenames.append(raw_filename) |
| 146 | + |
| 147 | + _extract_variable(short_name, var, cfg, filenames, out_dir) |
0 commit comments