esgvoc 2.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. esgvoc/__init__.py +3 -0
  2. esgvoc/api/__init__.py +91 -0
  3. esgvoc/api/data_descriptors/EMD_models/__init__.py +66 -0
  4. esgvoc/api/data_descriptors/EMD_models/arrangement.py +21 -0
  5. esgvoc/api/data_descriptors/EMD_models/calendar.py +5 -0
  6. esgvoc/api/data_descriptors/EMD_models/cell_variable_type.py +20 -0
  7. esgvoc/api/data_descriptors/EMD_models/component_type.py +5 -0
  8. esgvoc/api/data_descriptors/EMD_models/coordinate.py +52 -0
  9. esgvoc/api/data_descriptors/EMD_models/grid_mapping.py +19 -0
  10. esgvoc/api/data_descriptors/EMD_models/grid_region.py +19 -0
  11. esgvoc/api/data_descriptors/EMD_models/grid_type.py +19 -0
  12. esgvoc/api/data_descriptors/EMD_models/horizontal_computational_grid.py +56 -0
  13. esgvoc/api/data_descriptors/EMD_models/horizontal_grid_cells.py +230 -0
  14. esgvoc/api/data_descriptors/EMD_models/horizontal_subgrid.py +41 -0
  15. esgvoc/api/data_descriptors/EMD_models/horizontal_units.py +5 -0
  16. esgvoc/api/data_descriptors/EMD_models/model.py +139 -0
  17. esgvoc/api/data_descriptors/EMD_models/model_component.py +115 -0
  18. esgvoc/api/data_descriptors/EMD_models/reference.py +61 -0
  19. esgvoc/api/data_descriptors/EMD_models/resolution.py +48 -0
  20. esgvoc/api/data_descriptors/EMD_models/temporal_refinement.py +19 -0
  21. esgvoc/api/data_descriptors/EMD_models/truncation_method.py +17 -0
  22. esgvoc/api/data_descriptors/EMD_models/vertical_computational_grid.py +91 -0
  23. esgvoc/api/data_descriptors/EMD_models/vertical_coordinate.py +5 -0
  24. esgvoc/api/data_descriptors/EMD_models/vertical_units.py +19 -0
  25. esgvoc/api/data_descriptors/__init__.py +159 -0
  26. esgvoc/api/data_descriptors/activity.py +72 -0
  27. esgvoc/api/data_descriptors/archive.py +5 -0
  28. esgvoc/api/data_descriptors/area_label.py +30 -0
  29. esgvoc/api/data_descriptors/branded_suffix.py +30 -0
  30. esgvoc/api/data_descriptors/branded_variable.py +21 -0
  31. esgvoc/api/data_descriptors/citation_url.py +5 -0
  32. esgvoc/api/data_descriptors/contact.py +5 -0
  33. esgvoc/api/data_descriptors/conventions.py +28 -0
  34. esgvoc/api/data_descriptors/creation_date.py +18 -0
  35. esgvoc/api/data_descriptors/data_descriptor.py +127 -0
  36. esgvoc/api/data_descriptors/data_specs_version.py +25 -0
  37. esgvoc/api/data_descriptors/date.py +5 -0
  38. esgvoc/api/data_descriptors/directory_date.py +22 -0
  39. esgvoc/api/data_descriptors/drs_specs.py +38 -0
  40. esgvoc/api/data_descriptors/experiment.py +215 -0
  41. esgvoc/api/data_descriptors/forcing_index.py +21 -0
  42. esgvoc/api/data_descriptors/frequency.py +48 -0
  43. esgvoc/api/data_descriptors/further_info_url.py +5 -0
  44. esgvoc/api/data_descriptors/grid.py +43 -0
  45. esgvoc/api/data_descriptors/horizontal_label.py +20 -0
  46. esgvoc/api/data_descriptors/initialization_index.py +27 -0
  47. esgvoc/api/data_descriptors/institution.py +80 -0
  48. esgvoc/api/data_descriptors/known_branded_variable.py +75 -0
  49. esgvoc/api/data_descriptors/license.py +31 -0
  50. esgvoc/api/data_descriptors/member_id.py +9 -0
  51. esgvoc/api/data_descriptors/mip_era.py +26 -0
  52. esgvoc/api/data_descriptors/model_component.py +32 -0
  53. esgvoc/api/data_descriptors/models_test/models.py +17 -0
  54. esgvoc/api/data_descriptors/nominal_resolution.py +50 -0
  55. esgvoc/api/data_descriptors/obs_type.py +5 -0
  56. esgvoc/api/data_descriptors/organisation.py +22 -0
  57. esgvoc/api/data_descriptors/physics_index.py +21 -0
  58. esgvoc/api/data_descriptors/product.py +16 -0
  59. esgvoc/api/data_descriptors/publication_status.py +5 -0
  60. esgvoc/api/data_descriptors/realization_index.py +24 -0
  61. esgvoc/api/data_descriptors/realm.py +16 -0
  62. esgvoc/api/data_descriptors/regex.py +5 -0
  63. esgvoc/api/data_descriptors/region.py +35 -0
  64. esgvoc/api/data_descriptors/resolution.py +7 -0
  65. esgvoc/api/data_descriptors/source.py +120 -0
  66. esgvoc/api/data_descriptors/source_type.py +5 -0
  67. esgvoc/api/data_descriptors/sub_experiment.py +5 -0
  68. esgvoc/api/data_descriptors/table.py +28 -0
  69. esgvoc/api/data_descriptors/temporal_label.py +20 -0
  70. esgvoc/api/data_descriptors/time_range.py +17 -0
  71. esgvoc/api/data_descriptors/title.py +5 -0
  72. esgvoc/api/data_descriptors/tracking_id.py +67 -0
  73. esgvoc/api/data_descriptors/variable.py +56 -0
  74. esgvoc/api/data_descriptors/variant_label.py +25 -0
  75. esgvoc/api/data_descriptors/vertical_label.py +20 -0
  76. esgvoc/api/project_specs.py +143 -0
  77. esgvoc/api/projects.py +1253 -0
  78. esgvoc/api/py.typed +0 -0
  79. esgvoc/api/pydantic_handler.py +146 -0
  80. esgvoc/api/report.py +127 -0
  81. esgvoc/api/search.py +171 -0
  82. esgvoc/api/universe.py +434 -0
  83. esgvoc/apps/__init__.py +6 -0
  84. esgvoc/apps/cmor_tables/__init__.py +7 -0
  85. esgvoc/apps/cmor_tables/cvs_table.py +948 -0
  86. esgvoc/apps/drs/__init__.py +0 -0
  87. esgvoc/apps/drs/constants.py +2 -0
  88. esgvoc/apps/drs/generator.py +429 -0
  89. esgvoc/apps/drs/report.py +540 -0
  90. esgvoc/apps/drs/validator.py +312 -0
  91. esgvoc/apps/ga/__init__.py +104 -0
  92. esgvoc/apps/ga/example_usage.py +315 -0
  93. esgvoc/apps/ga/models/__init__.py +47 -0
  94. esgvoc/apps/ga/models/netcdf_header.py +306 -0
  95. esgvoc/apps/ga/models/validator.py +491 -0
  96. esgvoc/apps/ga/test_ga.py +161 -0
  97. esgvoc/apps/ga/validator.py +277 -0
  98. esgvoc/apps/jsg/json_schema_generator.py +341 -0
  99. esgvoc/apps/jsg/templates/template.jinja +241 -0
  100. esgvoc/apps/test_cv/README.md +214 -0
  101. esgvoc/apps/test_cv/__init__.py +0 -0
  102. esgvoc/apps/test_cv/cv_tester.py +1611 -0
  103. esgvoc/apps/test_cv/example_usage.py +216 -0
  104. esgvoc/apps/vr/__init__.py +12 -0
  105. esgvoc/apps/vr/build_variable_registry.py +71 -0
  106. esgvoc/apps/vr/example_usage.py +60 -0
  107. esgvoc/apps/vr/vr_app.py +333 -0
  108. esgvoc/cli/clean.py +304 -0
  109. esgvoc/cli/cmor.py +46 -0
  110. esgvoc/cli/config.py +1300 -0
  111. esgvoc/cli/drs.py +267 -0
  112. esgvoc/cli/find.py +138 -0
  113. esgvoc/cli/get.py +155 -0
  114. esgvoc/cli/install.py +41 -0
  115. esgvoc/cli/main.py +60 -0
  116. esgvoc/cli/offline.py +269 -0
  117. esgvoc/cli/status.py +79 -0
  118. esgvoc/cli/test_cv.py +258 -0
  119. esgvoc/cli/valid.py +147 -0
  120. esgvoc/core/constants.py +17 -0
  121. esgvoc/core/convert.py +0 -0
  122. esgvoc/core/data_handler.py +206 -0
  123. esgvoc/core/db/__init__.py +3 -0
  124. esgvoc/core/db/connection.py +40 -0
  125. esgvoc/core/db/models/mixins.py +25 -0
  126. esgvoc/core/db/models/project.py +102 -0
  127. esgvoc/core/db/models/universe.py +98 -0
  128. esgvoc/core/db/project_ingestion.py +231 -0
  129. esgvoc/core/db/universe_ingestion.py +172 -0
  130. esgvoc/core/exceptions.py +33 -0
  131. esgvoc/core/logging_handler.py +26 -0
  132. esgvoc/core/repo_fetcher.py +345 -0
  133. esgvoc/core/service/__init__.py +41 -0
  134. esgvoc/core/service/configuration/config_manager.py +196 -0
  135. esgvoc/core/service/configuration/setting.py +363 -0
  136. esgvoc/core/service/data_merger.py +634 -0
  137. esgvoc/core/service/esg_voc.py +77 -0
  138. esgvoc/core/service/resolver_config.py +56 -0
  139. esgvoc/core/service/state.py +324 -0
  140. esgvoc/core/service/string_heuristics.py +98 -0
  141. esgvoc/core/service/term_cache.py +108 -0
  142. esgvoc/core/service/uri_resolver.py +133 -0
  143. esgvoc-2.0.2.dist-info/METADATA +82 -0
  144. esgvoc-2.0.2.dist-info/RECORD +147 -0
  145. esgvoc-2.0.2.dist-info/WHEEL +4 -0
  146. esgvoc-2.0.2.dist-info/entry_points.txt +2 -0
  147. esgvoc-2.0.2.dist-info/licenses/LICENSE.txt +519 -0
esgvoc/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ import esgvoc.core.logging_handler # noqa
2
+
3
+ __version__ = "2.0.2"
esgvoc/api/__init__.py ADDED
@@ -0,0 +1,91 @@
1
+ from esgvoc.api.project_specs import (
2
+ DrsPart,
3
+ DrsSpecification,
4
+ DrsType,
5
+ ProjectSpecs,
6
+ )
7
+ from esgvoc.api.projects import (
8
+ find_collections_in_project,
9
+ find_items_in_project,
10
+ find_terms_in_all_projects,
11
+ find_terms_in_collection,
12
+ find_terms_in_project,
13
+ get_all_collections_in_project,
14
+ get_all_projects,
15
+ get_all_terms_in_all_projects,
16
+ get_all_terms_in_collection,
17
+ get_all_terms_in_project,
18
+ get_collection_from_data_descriptor_in_all_projects,
19
+ get_collection_from_data_descriptor_in_project,
20
+ get_collection_in_project,
21
+ get_project,
22
+ get_term_in_collection,
23
+ get_term_in_project,
24
+ valid_term,
25
+ valid_term_in_all_projects,
26
+ valid_term_in_collection,
27
+ valid_term_in_project,
28
+ )
29
+ from esgvoc.api.report import (
30
+ ProjectTermError,
31
+ UniverseTermError,
32
+ ValidationError,
33
+ ValidationErrorVisitor,
34
+ ValidationReport,
35
+ )
36
+ from esgvoc.api.search import MatchingTerm
37
+ from esgvoc.api.universe import (
38
+ find_data_descriptors_in_universe,
39
+ find_items_in_universe,
40
+ find_terms_in_data_descriptor,
41
+ find_terms_in_universe,
42
+ get_all_data_descriptors_in_universe,
43
+ get_all_terms_in_data_descriptor,
44
+ get_all_terms_in_universe,
45
+ get_data_descriptor_in_universe,
46
+ get_term_in_data_descriptor,
47
+ get_term_in_universe,
48
+ )
49
+
50
+ __all__ = [
51
+ "DrsPart",
52
+ "DrsSpecification",
53
+ "DrsType",
54
+ "find_collections_in_project",
55
+ "find_data_descriptors_in_universe",
56
+ "find_items_in_project",
57
+ "find_items_in_universe",
58
+ "find_terms_in_all_projects",
59
+ "find_terms_in_collection",
60
+ "find_terms_in_data_descriptor",
61
+ "find_terms_in_project",
62
+ "find_terms_in_universe",
63
+ "get_all_collections_in_project",
64
+ "get_all_data_descriptors_in_universe",
65
+ "get_all_projects",
66
+ "get_all_terms_in_all_projects",
67
+ "get_all_terms_in_collection",
68
+ "get_all_terms_in_data_descriptor",
69
+ "get_all_terms_in_project",
70
+ "get_all_terms_in_universe",
71
+ "get_collection_from_data_descriptor_in_all_projects",
72
+ "get_collection_from_data_descriptor_in_project",
73
+ "get_collection_in_project",
74
+ "get_data_descriptor_in_universe",
75
+ "get_project",
76
+ "get_term_in_collection",
77
+ "get_term_in_data_descriptor",
78
+ "get_term_in_project",
79
+ "get_term_in_universe",
80
+ "MatchingTerm",
81
+ "ProjectSpecs",
82
+ "ProjectTermError",
83
+ "UniverseTermError",
84
+ "valid_term",
85
+ "valid_term_in_all_projects",
86
+ "valid_term_in_collection",
87
+ "valid_term_in_project",
88
+ "ValidationError",
89
+ "ValidationErrorVisitor",
90
+ "ValidationReport"
91
+ ]
@@ -0,0 +1,66 @@
1
+ """
2
+ EMD (Essential Model Documentation) Pydantic Models - Version 1.0
3
+
4
+ This package implements the EMD v1.0 specification.
5
+ For specification details, see: Essential Model Documentation (EMD) - version 1.0.docx
6
+
7
+ Major changes from v0.993:
8
+ - Horizontal grid changed from flat to nested 3-level structure
9
+ (HorizontalComputationalGrid → HorizontalSubgrid → HorizontalGridCells)
10
+ - Field renames: coordinate → vertical_coordinate, native_*_grid → *_computational_grid
11
+ - New required description fields in Model, ModelComponent, and grids
12
+ - New spatial_refinement field in HorizontalGridCells
13
+ """
14
+
15
+ # Core EMD models
16
+ from .model import Model
17
+ from .model_component import EMDModelComponent
18
+ from .reference import Reference
19
+
20
+ # Grid models (v1.0 nested structure)
21
+ from .horizontal_computational_grid import HorizontalComputationalGrid
22
+ from .horizontal_grid_cells import HorizontalGridCells
23
+ from .horizontal_subgrid import HorizontalSubgrid
24
+ from .vertical_computational_grid import VerticalComputationalGrid
25
+ from .vertical_units import VerticalUnits
26
+
27
+ # Supporting models
28
+ from .resolution import EMDResolution
29
+
30
+ # Controlled Vocabulary (CV) models - EMD Section 7
31
+ from .arrangement import Arrangement
32
+ from .calendar import Calendar
33
+ from .cell_variable_type import CellVariableType
34
+ from .component_type import ComponentType
35
+ from .coordinate import Coordinate
36
+ from .grid_mapping import GridMapping
37
+ from .grid_region import GridRegion
38
+ from .grid_type import GridType
39
+ from .temporal_refinement import TemporalRefinement
40
+ from .truncation_method import TruncationMethod
41
+
42
+ __all__ = [
43
+ # Core models
44
+ "Model",
45
+ "EMDModelComponent",
46
+ "Reference",
47
+ # Grid models
48
+ "HorizontalComputationalGrid",
49
+ "HorizontalSubgrid",
50
+ "HorizontalGridCells",
51
+ "VerticalComputationalGrid",
52
+ "VerticalUnits",
53
+ # Supporting
54
+ "EMDResolution",
55
+ # CV models (Section 7)
56
+ "Arrangement",
57
+ "Calendar",
58
+ "CellVariableType",
59
+ "ComponentType",
60
+ "Coordinate",
61
+ "GridMapping",
62
+ "GridRegion",
63
+ "GridType",
64
+ "TemporalRefinement",
65
+ "TruncationMethod",
66
+ ]
@@ -0,0 +1,21 @@
1
+ """
2
+ EMD v1.0 Section 7.3 - arrangement CV
3
+
4
+ Horizontal grid arrangement types (Arakawa grids).
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class Arrangement(PlainTermDataDescriptor):
11
+ """
12
+ Horizontal grid arrangement (EMD v1.0 Section 7.3).
13
+
14
+ Options: arakawa_a, arakawa_b, arakawa_c, arakawa_d, arakawa_e
15
+
16
+ A grid arrangement describes the relative locations of mass- and velocity-related quantities
17
+ on the computed grid (for instance Collins et al. (2013), and for unstructured grids
18
+ Thuburn et al. (2009)).
19
+ """
20
+
21
+ pass
@@ -0,0 +1,5 @@
1
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
2
+
3
+
4
+ class Calendar(PlainTermDataDescriptor):
5
+ pass
@@ -0,0 +1,20 @@
1
+ """
2
+ EMD v1.0 Section 7.4 - cell_variable_type CV
3
+
4
+ Types of physical variables carried at grid cells.
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class CellVariableType(PlainTermDataDescriptor):
11
+ """
12
+ Cell variable type (EMD v1.0 Section 7.4).
13
+
14
+ Options: mass, x_velocity, y_velocity, velocity
15
+
16
+ Types of physical variables that are carried at, or representative of conditions at,
17
+ cells of a horizontal subgrid.
18
+ """
19
+
20
+ pass
@@ -0,0 +1,5 @@
1
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
2
+
3
+
4
+ class ComponentType(PlainTermDataDescriptor):
5
+ pass
@@ -0,0 +1,52 @@
1
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
2
+
3
+
4
+ class Coordinate(PlainTermDataDescriptor):
5
+ """
6
+ Native vertical grid coordinate type. The coordinate types are all CF standard names (except where indicated) with the same definitions. See section 5.2 Native vertical grid properties.
7
+ Options for the native vertical grid Coordinate property:
8
+ • none
9
+ ◦ (Not a standard name) There is no vertical dimension.
10
+ • height
11
+ ◦ Height is the vertical distance above the earth's surface.
12
+ • geopotential_height
13
+ ◦ Geopotential height is the geopotential divided by the standard acceleration due to gravity.
14
+ • air_pressure
15
+ ◦ Air pressure is the pressure that exists in the medium of air.
16
+ • air_potential_temperature
17
+ ◦ Air potential temperature is the temperature a parcel of air would have if moved dry adiabatically to a standard pressure.
18
+ • atmosphere_ln_pressure_coordinate
19
+ ◦ Parametric atmosphere natural log pressure coordinate.
20
+ • atmosphere_sigma_coordinate
21
+ ◦ Parametric atmosphere sigma coordinate.
22
+ • atmosphere_hybrid_sigma_pressure_coordinate
23
+ ◦ Parametric atmosphere hybrid sigma pressure coordinate.
24
+ • atmosphere_hybrid_height_coordinate
25
+ ◦ Parametric atmosphere hybrid height coordinate.
26
+ • atmosphere_sleve_coordinate
27
+ ◦ Parametric atmosphere smooth vertical level coordinate.
28
+ • depth
29
+ ◦ Depth is the vertical distance below the earth's surface.
30
+ • sea_water_pressure
31
+ ◦ Sea water pressure is the pressure that exists in the medium of sea water.
32
+ • sea_water_potential_temperature
33
+ ◦ Sea water potential temperature is the temperature a parcel of sea water would have if moved adiabatically to sea level pressure.
34
+ • ocean_sigma_coordinate
35
+ ◦ Parametric ocean sigma coordinate.
36
+ • ocean_s_coordinate
37
+ ◦ Parametric ocean s-coordinate.
38
+ • ocean_s_coordinate_g1
39
+ ◦ Parametric ocean s-coordinate, generic form 1.
40
+ • ocean_s_coordinate_g2
41
+ ◦ Parametric ocean s-coordinate, generic form 2.
42
+ • ocean_sigma_z_coordinate
43
+ ◦ Parametric ocean sigma over z coordinate.
44
+ • ocean_double_sigma_coordinate
45
+ ◦ Parametric ocean double sigma coordinate.
46
+ • land_ice_sigma_coordinate
47
+ ◦ Land ice (glaciers, ice-caps and ice-sheets resting on bedrock and also includes ice-shelves) sigma coordinate.
48
+ • z*
49
+ ◦ (Not a standard name) The z* coordinate of Adcroft and Campin (2004).
50
+ """
51
+
52
+ pass
@@ -0,0 +1,19 @@
1
+ """
2
+ EMD v1.0 Section 7.7 - grid_mapping CV
3
+
4
+ Grid mapping (coordinate reference system) types.
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class GridMapping(PlainTermDataDescriptor):
11
+ """
12
+ Grid mapping (EMD v1.0 Section 7.7).
13
+
14
+ Options: latitude_longitude, lambert_conformal_conic, etc.
15
+
16
+ The name of the coordinate reference system of the horizontal coordinates.
17
+ """
18
+
19
+ pass
@@ -0,0 +1,19 @@
1
+ """
2
+ EMD v1.0 Section 7.5 - region CV
3
+
4
+ Horizontal grid region types.
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class GridRegion(PlainTermDataDescriptor):
11
+ """
12
+ Horizontal grid region (EMD v1.0 Section 7.5).
13
+
14
+ Options: global, antarctica, greenland, limited_area, 30S-90S, etc.
15
+
16
+ A region is a contiguous part of the Earth's surface which spans the horizontal grid cells.
17
+ """
18
+
19
+ pass
@@ -0,0 +1,19 @@
1
+ """
2
+ EMD v1.0 Section 7.6 - grid_type CV
3
+
4
+ Horizontal grid types.
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class GridType(PlainTermDataDescriptor):
11
+ """
12
+ Horizontal grid type (EMD v1.0 Section 7.6).
13
+
14
+ Options: regular_latitude_longitude, regular_gaussian, reduced_gaussian, tripolar, etc.
15
+
16
+ A grid type describes the method for distributing grid points over the sphere.
17
+ """
18
+
19
+ pass
@@ -0,0 +1,56 @@
1
+ """
2
+ Horizontal computational grid description (EMD v1.0 Section 4.1.1).
3
+
4
+ A model component's horizontal computational grid is composed of one or more
5
+ horizontal subgrids, on which different sets of variables are calculated.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import List
11
+
12
+ from pydantic import Field, field_validator
13
+
14
+ from esgvoc.api.data_descriptors.data_descriptor import DataDescriptor
15
+
16
+ from .arrangement import Arrangement
17
+ from .horizontal_subgrid import HorizontalSubgrid
18
+
19
+
20
+ class HorizontalComputationalGrid(DataDescriptor):
21
+ """
22
+ Horizontal computational grid description (EMD v1.0 Section 4.1.1).
23
+
24
+ A model component's horizontal computational grid is composed of one or more
25
+ horizontal subgrids, on which different sets of variables are calculated.
26
+ When the computational grid relies on more than one subgrid, it is referred to
27
+ as a "staggered" grid. For most staggered grids, the velocity-related variables
28
+ are calculated on a subgrid offset from the mass-related variables (e.g. pressure,
29
+ temperature, water vapour and other mass constituents).
30
+ """
31
+
32
+ arrangement: str | Arrangement = Field(
33
+ description="A characterisation of the grid staggering defining the relative positions of computed "
34
+ "mass-related and velocity-related variables. Taken from 7.3 arrangement CV. "
35
+ "Options: 'arakawa_a', 'arakawa_b', 'arakawa_c', 'arakawa_d', 'arakawa_e'. "
36
+ "E.g. 'arakawa_c'"
37
+ )
38
+
39
+ horizontal_subgrids: List[HorizontalSubgrid] = Field(
40
+ description="All of the subgrids, of which there must be at least one, used to construct the "
41
+ "horizontal computational grid. Each subgrid is associated with one or more variable types "
42
+ "(mass-related, velocity-related, etc.), consistent with the arrangement property.",
43
+ min_length=1,
44
+ )
45
+
46
+ @field_validator("horizontal_subgrids")
47
+ @classmethod
48
+ def validate_at_least_one_subgrid(cls, v):
49
+ """Validate that there is at least one horizontal subgrid."""
50
+ if not v or len(v) < 1:
51
+ raise ValueError("At least one horizontal subgrid must be provided")
52
+ return v
53
+
54
+ def accept(self, visitor):
55
+ """Accept a data descriptor visitor."""
56
+ return visitor.visit_plain_term(self)
@@ -0,0 +1,230 @@
1
+ """
2
+ Horizontal grid cells description (EMD v1.0 Section 4.1.3).
3
+
4
+ Horizontal grid cells are described by a coordinate system, cell resolutions,
5
+ as well as a number of other grid features.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import textwrap
11
+ from typing import Optional
12
+
13
+ from pydantic import BaseModel, Field, field_validator
14
+
15
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
16
+ from esgvoc.api.data_descriptors.region import Region
17
+
18
+ from .grid_mapping import GridMapping
19
+ from .grid_type import GridType
20
+ from .temporal_refinement import TemporalRefinement
21
+ from .truncation_method import TruncationMethod
22
+
23
+
24
+ class HorizontalGridCells(PlainTermDataDescriptor):
25
+ """
26
+ Horizontal grid cells description (EMD v1.0 Section 4.1.3).
27
+
28
+ Horizontal grid cells are described by a coordinate system, cell resolutions,
29
+ as well as a number of other grid features. The description does not include
30
+ any information on whether or not the grid cells form part of a model component's
31
+ computational grid, and so may be used to describe an arbitrary output grid.
32
+ """
33
+
34
+ region: Region | str = Field(
35
+ description="The geographical region, or regions, over which the component is simulated. "
36
+ "A region is a contiguous part of the Earth's surface, and may include areas for which "
37
+ "no calculations are made (such as ocean areas for a land surface component). "
38
+ "Taken from 7.5 region CV. E.g. 'global', 'antarctica', 'greenland', 'limited_area'"
39
+ )
40
+
41
+ grid_type: str | GridType = Field(
42
+ description="The horizontal grid type, i.e. the method of distributing grid cells over the region. "
43
+ "Taken from 7.6 grid_type CV. E.g. 'regular_latitude_longitude', 'tripolar'"
44
+ )
45
+
46
+ description: Optional[str] = Field(
47
+ default=None,
48
+ description="A description of the grid. A description is only required if there is information "
49
+ "that is not covered by any of the other properties. Omit when not required.",
50
+ )
51
+
52
+ grid_mapping: Optional[str | GridMapping] = Field(
53
+ default=None,
54
+ description="The name of the coordinate reference system of the horizontal coordinates. "
55
+ "Taken from 7.7 grid_mapping CV. E.g. 'latitude_longitude', 'lambert_conformal_conic'. "
56
+ "Can be None or empty for certain grid types (e.g., tripolar grids).",
57
+ )
58
+
59
+ temporal_refinement: str | TemporalRefinement = Field(
60
+ description="The grid temporal refinement, indicating how the distribution of grid cells varies with time. "
61
+ "Taken from 7.8 temporal_refinement CV. E.g. 'static'"
62
+ )
63
+
64
+ spatial_refinement: Optional[str] = Field(
65
+ default=None,
66
+ description="The grid spatial refinement, indicating how the distribution of grid cells varies with space. "
67
+ "NEW in EMD v1.0. Omit when not applicable.",
68
+ )
69
+
70
+ x_resolution: Optional[float] = Field(
71
+ default=None,
72
+ description=textwrap.dedent(
73
+ """
74
+ The size of grid cells in the X direction.
75
+
76
+ Cells for which no calculations are made are included (such as ocean areas
77
+ for a land surface component).
78
+
79
+ The X direction for a grid defined by spherical polar coordinates is longitude.
80
+
81
+ The value's physical units are given by the horizontal_units property.
82
+
83
+ Report only when cell sizes are identical or else reasonably uniform (in their given units).
84
+ When cells sizes in the X direction are not identical, a representative value should be
85
+ provided and this fact noted in the description property.
86
+ If the cell sizes vary by more than 25%, set this to None.
87
+ """
88
+ ),
89
+ gt=0,
90
+ )
91
+
92
+ y_resolution: Optional[float] = Field(
93
+ default=None,
94
+ description=textwrap.dedent(
95
+ """
96
+ The size of grid cells in the Y direction.
97
+
98
+ Cells for which no calculations are made are included (such as ocean areas
99
+ for a land surface component).
100
+
101
+ The Y direction for a grid defined by spherical polar coordinates is latitude.
102
+
103
+ The value's physical units are given by the horizontal_units property.
104
+
105
+ Report only when cell sizes are identical or else reasonably uniform (in their given units).
106
+ When cells sizes in the Y direction are not identical, a representative value should be
107
+ provided and this fact noted in the description property.
108
+ If the cell sizes vary by more than 25%, set this to None.
109
+ """
110
+ ),
111
+ gt=0,
112
+ )
113
+
114
+ horizontal_units: Optional[str] = Field(
115
+ default=None,
116
+ description=textwrap.dedent(
117
+ """
118
+ The physical units of the x_resolution and y_resolution property values.
119
+
120
+ If x_resolution and y_resolution are None, set this to None.
121
+ """
122
+ ),
123
+ )
124
+
125
+ southernmost_latitude: Optional[float] = Field(
126
+ default=None,
127
+ description=textwrap.dedent(
128
+ """
129
+ The southernmost grid cell latitude, in degrees north.
130
+
131
+ Cells for which no calculations are made are included.
132
+ The southernmost latitude may be shared by multiple cells.
133
+
134
+ If the southernmost latitude is not known (e.g. the grid is adaptive), use None.
135
+ """
136
+ ),
137
+ ge=-90.0,
138
+ le=90.0,
139
+ )
140
+
141
+ westernmost_longitude: Optional[float] = Field(
142
+ default=None,
143
+ description=textwrap.dedent(
144
+ """
145
+ The westernmost grid cell longitude, in degrees east, of the southernmost grid cell(s).
146
+
147
+ Cells for which no calculations are made are included.
148
+ The westernmost longitude is the smallest longitude value of the cells
149
+ that share the latitude given by the southernmost_latitude.
150
+
151
+ If the westernmost longitude is not known (e.g. the grid is adaptive), use None.
152
+ """
153
+ ),
154
+ ge=0.0,
155
+ le=360.0,
156
+ )
157
+
158
+ n_cells: Optional[int] = Field(
159
+ default=None,
160
+ description=textwrap.dedent(
161
+ """
162
+ The total number of cells in the horizontal grid.
163
+
164
+ If the total number of grid cells is not constant, set to None.
165
+ """
166
+ ),
167
+ ge=1,
168
+ )
169
+
170
+ truncation_method: Optional[str | TruncationMethod] = Field(
171
+ default=None,
172
+ description="The method for truncating the spherical harmonic representation of a spectral model "
173
+ "when reporting on this grid. If the grid is not used for reporting spherical harmonic "
174
+ "representations, set to None.",
175
+ )
176
+
177
+ truncation_number: Optional[int] = Field(
178
+ default=None,
179
+ description="The zonal (east-west) wave number at which a spectral model is truncated when "
180
+ "reporting on this grid. If the grid is not used for reporting spectral models, set to None.",
181
+ )
182
+
183
+ resolution_range_km: Optional[list[float]] = Field(
184
+ default=None,
185
+ description=textwrap.dedent(
186
+ """
187
+ The minimum and maximum resolution (in km) of cells of the horizontal grid.
188
+
189
+ Should be calculated according to the algorithm implemented by
190
+ https://github.com/PCMDI/nominal_resolution/blob/master/lib/api.py
191
+ You need to take the min and max of the array that is returned
192
+ when using the returnMaxDistance of the mean_resolution function.
193
+ """
194
+ ),
195
+ min_length=2,
196
+ max_length=2,
197
+ )
198
+
199
+ @field_validator("horizontal_units", mode="after")
200
+ @classmethod
201
+ def validate_horizontal_units(cls, v, info):
202
+ """Validate horizontal_units."""
203
+ resolution_fields = {"x_resolution", "y_resolution"}
204
+ has_resolution = any(info.data.get(field) is not None for field in resolution_fields)
205
+ if has_resolution:
206
+ if not v:
207
+ raise ValueError("horizontal_units is required when x_resolution or y_resolution are set")
208
+
209
+ allowed_values = {"km", "degree"}
210
+ if v not in allowed_values:
211
+ msg = f"horizontal_units must be one of {allowed_values}. Received: {v}"
212
+ raise ValueError(msg)
213
+ elif v:
214
+ msg = f"If all of {resolution_fields} are None, then horizontal_units must also be None. Received: {v}"
215
+ raise ValueError(msg)
216
+
217
+ return v
218
+
219
+ @field_validator("resolution_range_km")
220
+ @classmethod
221
+ def validate_resolution_range(cls, v):
222
+ """Validate that resolution range has exactly 2 values and min <= max."""
223
+ if v is not None:
224
+ if len(v) != 2:
225
+ raise ValueError("resolution_range_km must contain exactly 2 values [min, max]")
226
+ if v[0] > v[1]:
227
+ raise ValueError("resolution_range_km: minimum must be <= maximum")
228
+ if any(val <= 0 for val in v):
229
+ raise ValueError("resolution_range_km values must be > 0")
230
+ return v
@@ -0,0 +1,41 @@
1
+ """
2
+ Horizontal subgrid description (EMD v1.0 Section 4.1.2).
3
+
4
+ A horizontal subgrid describes the grid cells at one of the stagger positions
5
+ of a horizontal computational grid.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import List
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
15
+
16
+ from .cell_variable_type import CellVariableType
17
+ from .horizontal_grid_cells import HorizontalGridCells
18
+
19
+
20
+ class HorizontalSubgrid(PlainTermDataDescriptor):
21
+ """
22
+ Horizontal subgrid description (EMD v1.0 Section 4.1.2).
23
+
24
+ A horizontal subgrid describes the grid cells at one of the stagger positions
25
+ of a horizontal computational grid. Often the locations of mass-related and
26
+ velocity-related variables differ, so more than one horizontal subgrid will
27
+ be defined as part of a horizontal computational grid.
28
+ """
29
+
30
+ cell_variable_type: List[str | CellVariableType] = Field(
31
+ description="The types of physical variables that are carried at, or representative of conditions at, "
32
+ "the cells described by this horizontal subgrid. Taken from 7.4 cell_variable_type CV. "
33
+ "Options: 'mass', 'x_velocity', 'y_velocity', 'velocity'. "
34
+ "E.g. ['mass'], ['x_velocity'], ['mass', 'x_velocity', 'y_velocity'], ['mass', 'velocity']. "
35
+ "Can be an empty list in certain cases.",
36
+ default_factory=list,
37
+ )
38
+
39
+ horizontal_grid_cells: HorizontalGridCells = Field(
40
+ description="A description of the characteristics and location of the grid cells of this subgrid."
41
+ )
@@ -0,0 +1,5 @@
1
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
2
+
3
+
4
+ class HorizontalUnits(PlainTermDataDescriptor):
5
+ pass