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
@@ -0,0 +1,139 @@
1
+ """
2
+ Top-level model description (EMD v1.0 Section 2).
3
+
4
+ The following properties provide a top-level description of the model as a whole.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import List, Optional
10
+
11
+ from pydantic import Field, field_validator
12
+
13
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
14
+
15
+ from .calendar import Calendar
16
+ from .component_type import ComponentType
17
+ from .model_component import EMDModelComponent
18
+ from .reference import Reference
19
+
20
+
21
+ class Model(PlainTermDataDescriptor):
22
+ """
23
+ Top-level model description (EMD v1.0 Section 2).
24
+
25
+ The following properties provide a top-level description of the model as a whole.
26
+ """
27
+
28
+ name: str = Field(
29
+ description="The name of the top-level model. For CMIP7, this name will be registered as the model's source_id.",
30
+ min_length=1,
31
+ )
32
+
33
+ family: str = Field(
34
+ description="The top-level model's 'family' name. Use 'none' to indicate that there is no such family.",
35
+ min_length=1,
36
+ )
37
+
38
+ dynamic_components: List[str | ComponentType] = Field(
39
+ description="The model components that are dynamically simulated within the top-level model. "
40
+ "Taken from 7.1 component CV.",
41
+ min_length=1,
42
+ )
43
+
44
+ prescribed_components: List[str | ComponentType] = Field(
45
+ description="The components that are represented in the top-level model with prescribed values. "
46
+ "Taken from 7.1 component CV.",
47
+ default_factory=list,
48
+ )
49
+
50
+ omitted_components: List[str | ComponentType] = Field(
51
+ description="The components that are wholly omitted from the top-level model. Taken from 7.1 component CV.",
52
+ default_factory=list,
53
+ )
54
+
55
+ description: str = Field(
56
+ description="A scientific overview of the top-level model. The description should include a brief mention "
57
+ "of all the components listed in the 7.1 component CV, whether dynamically simulated, prescribed, or omitted.",
58
+ min_length=1,
59
+ default="",
60
+ )
61
+
62
+ calendar: List[str | Calendar] = Field(
63
+ description="The calendar, or calendars, that define which dates are permitted in the top-level model. "
64
+ "Taken from 7.2 calendar CV.",
65
+ min_length=1,
66
+ )
67
+
68
+ release_year: int = Field(
69
+ description="The year in which the top-level model being documented was released, "
70
+ "or first used for published simulations.",
71
+ ge=1900,
72
+ le=2100,
73
+ )
74
+
75
+ references: List[str | Reference] = Field(
76
+ description="One or more references to published work for the top-level model as a whole.", min_length=1
77
+ )
78
+
79
+ model_components: List[str | EMDModelComponent] = Field(
80
+ description="The model components that dynamically simulate processes within the model."
81
+ )
82
+
83
+ @field_validator("model_components")
84
+ @classmethod
85
+ def validate_same_dynamic_components(cls, v, info):
86
+ """Validate that model_components has the same length as dynamic_components."""
87
+ if "dynamic_components" in info.data:
88
+ dynamic_components = info.data["dynamic_components"]
89
+ if len(v) != len(dynamic_components):
90
+ raise ValueError(
91
+ f"Number of model_components ({len(v)}) must equal number of dynamic_components({
92
+ len(dynamic_components)
93
+ })"
94
+ )
95
+ return v
96
+
97
+ @field_validator("name", "family", "description", mode="before")
98
+ @classmethod
99
+ def validate_non_empty_strings(cls, v):
100
+ """Validate that string fields are not empty."""
101
+ if isinstance(v, str):
102
+ if not v.strip():
103
+ raise ValueError("Field cannot be empty")
104
+ return v.strip()
105
+ return v
106
+
107
+ @field_validator("dynamic_components", "prescribed_components", "omitted_components", mode="before")
108
+ @classmethod
109
+ def validate_component_lists(cls, v):
110
+ """Validate component lists contain valid strings or ComponentType objects."""
111
+ if v is None:
112
+ return []
113
+ # Filter out empty strings, keep ComponentType objects
114
+ cleaned = []
115
+ for item in v:
116
+ if isinstance(item, str):
117
+ if item.strip():
118
+ cleaned.append(item.strip())
119
+ else:
120
+ cleaned.append(item)
121
+ return cleaned
122
+
123
+ @field_validator("calendar", mode="before")
124
+ @classmethod
125
+ def validate_calendar_list(cls, v):
126
+ """Validate calendar list contains valid strings or Calendar objects."""
127
+ if not v:
128
+ raise ValueError("At least one calendar must be specified")
129
+ # Filter out empty strings, keep Calendar objects
130
+ cleaned = []
131
+ for item in v:
132
+ if isinstance(item, str):
133
+ if item.strip():
134
+ cleaned.append(item.strip())
135
+ else:
136
+ cleaned.append(item)
137
+ if not cleaned:
138
+ raise ValueError("Calendar list cannot be empty")
139
+ return cleaned
@@ -0,0 +1,115 @@
1
+ """
2
+ Model component description (EMD v1.0 Section 3).
3
+
4
+ Properties that provide a description of individual model components.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import List, Optional
10
+
11
+ from pydantic import Field, field_validator
12
+
13
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
14
+
15
+ from .component_type import ComponentType
16
+ from .horizontal_computational_grid import HorizontalComputationalGrid
17
+ from .reference import Reference
18
+ from .vertical_computational_grid import VerticalComputationalGrid
19
+
20
+
21
+ class EMDModelComponent(PlainTermDataDescriptor):
22
+ """
23
+ Model component description (EMD v1.0 Section 3).
24
+
25
+ Properties that provide a description of individual model components.
26
+
27
+ Eight model components are defined that somewhat independently account for different
28
+ sets of interactive processes: aerosol, atmosphere, atmospheric chemistry, land surface,
29
+ land ice, ocean, ocean biogeochemistry, and sea ice.
30
+ """
31
+
32
+ component: str | ComponentType = Field(description="The type of the model component. Taken from 7.1 component CV.")
33
+
34
+ name: str = Field(description="The name of the model component.", min_length=1)
35
+
36
+ family: str = Field(
37
+ description="The model component's 'family' name. Use 'none' to indicate that there is no such family.",
38
+ min_length=1,
39
+ )
40
+
41
+ description: str = Field(
42
+ description="A scientific overview of the model component. The description should summarise the key "
43
+ "processes simulated by the model component.",
44
+ min_length=1,
45
+ )
46
+
47
+ references: List[str | Reference] = Field(
48
+ description="One or more references to published work for the model component.", min_length=1
49
+ )
50
+
51
+ code_base: str = Field(
52
+ description="A URL (preferably for a DOI) for the source code for the model component. "
53
+ "Set to 'private' if not publicly available.",
54
+ min_length=1,
55
+ )
56
+
57
+ embedded_in: Optional[str | ComponentType] = Field(
58
+ default=None,
59
+ description="The host model component (identified by its component property) in which this component "
60
+ "is 'embedded'. Taken from 7.1 component CV. Omit when this component is coupled with other components.",
61
+ )
62
+
63
+ coupled_with: Optional[List[str | ComponentType]] = Field(
64
+ default=None,
65
+ description="The model components (identified by their component properties) with which this component "
66
+ "is 'coupled'. Taken from 7.1 component CV. Omit when this component is embedded in another component.",
67
+ )
68
+
69
+ horizontal_computational_grid: HorizontalComputationalGrid = Field(
70
+ description="A standardised description of the model component's horizontal computational grid."
71
+ )
72
+
73
+ vertical_computational_grid: VerticalComputationalGrid = Field(
74
+ description="A standardised description of the model component's vertical computational grid."
75
+ )
76
+
77
+ @field_validator("component", "name", "family", "code_base", "description", mode="before")
78
+ @classmethod
79
+ def validate_non_empty_strings(cls, v):
80
+ """Validate that string fields are not empty."""
81
+ if isinstance(v, str):
82
+ if not v.strip():
83
+ raise ValueError("Field cannot be empty")
84
+ return v.strip()
85
+ return v
86
+
87
+ @field_validator("coupled_with")
88
+ @classmethod
89
+ def validate_coupling_exclusivity(cls, v, info):
90
+ """Validate that a component cannot be both embedded and coupled."""
91
+ if v is not None and info.data.get("embedded_in") is not None:
92
+ raise ValueError(
93
+ "A component cannot be both embedded_in another component and coupled_with other components"
94
+ )
95
+ return v
96
+
97
+ @field_validator("embedded_in")
98
+ @classmethod
99
+ def validate_embedding_exclusivity(cls, v, info):
100
+ """Validate that a component cannot be both embedded and coupled."""
101
+ if v is not None and info.data.get("coupled_with") is not None:
102
+ raise ValueError(
103
+ "A component cannot be both embedded_in another component and coupled_with other components"
104
+ )
105
+ return v
106
+
107
+ @field_validator("code_base", mode="before")
108
+ @classmethod
109
+ def validate_code_base_format(cls, v):
110
+ """Validate code_base is either 'private' or a URL."""
111
+ if isinstance(v, str):
112
+ v = v.strip()
113
+ if v.lower() != "private" and not (v.startswith("http://") or v.startswith("https://")):
114
+ raise ValueError('code_base must be either "private" or a valid URL starting with http:// or https://')
115
+ return v
@@ -0,0 +1,61 @@
1
+ """
2
+ Academic reference to published work for the top-level model or model components.
3
+ """
4
+
5
+ from pydantic import BaseModel, Field, field_validator
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class Reference(PlainTermDataDescriptor):
11
+ """
12
+ Academic reference to published work for the top-level model or model components.
13
+
14
+ An academic reference to published work for the top-level model or one of its model
15
+ components is defined by the following properties:
16
+
17
+ • Citation
18
+ ◦ A human-readable citation for the work.
19
+ ◦ E.g. Smith, R. S., Mathiot, P., Siahaan, A., Lee, V., Cornford, S. L., Gregory, J. M.,
20
+ et al. (2021). Coupling the U.K. Earth System model to dynamic models of the Greenland
21
+ and Antarctic ice sheets. Journal of Advances in Modeling Earth Systems, 13,
22
+ e2021MS002520. https://doi.org/10.1029/2021MS002520, 2023
23
+ • DOI
24
+ ◦ The persistent identifier (DOI) used to identify the work.
25
+ ◦ A DOI is required for all references. A reference that does not already have a DOI
26
+ (as could be the case for some technical reports, for instance) must be given one
27
+ (e.g. with a service like Zenodo).
28
+ ◦ E.g. https://doi.org/10.1029/2021MS002520
29
+ """
30
+
31
+ citation: str = Field(description="A human-readable citation for the work.", min_length=1)
32
+ doi: str = Field(
33
+ description="The persistent identifier (DOI) used to identify the work. Must be a valid DOI URL.", min_length=1
34
+ )
35
+
36
+ @field_validator("doi")
37
+ @classmethod
38
+ def validate_doi(cls, v):
39
+ """Validate that DOI follows proper format (accepts proxies like doi-org.insu.bib...)."""
40
+ # Remove all whitespace to handle formatting issues
41
+ v = "".join(v.split())
42
+
43
+ # Accept both canonical DOIs and proxy URLs
44
+ if not v.startswith("https://doi"):
45
+ raise ValueError(
46
+ 'DOI must start with "https://doi" (canonical: https://doi.org/, proxies: https://doi-...)'
47
+ )
48
+
49
+ # Ensure there's an actual identifier after the DOI prefix
50
+ if len(v) <= len("https://doi"):
51
+ raise ValueError('DOI must contain identifier after "https://doi"')
52
+
53
+ return v
54
+
55
+ @field_validator("citation")
56
+ @classmethod
57
+ def validate_citation(cls, v):
58
+ """Validate that citation is not empty."""
59
+ if not v.strip():
60
+ raise ValueError("Citation cannot be empty")
61
+ return v.strip()
@@ -0,0 +1,48 @@
1
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
2
+
3
+
4
+ class EMDResolution(PlainTermDataDescriptor):
5
+ """
6
+ The nominal resolution (in km) characterises the resolution of a model's native horizontal grid. See section 5.1 Native horizontal grid properties.
7
+ Options for the native horizontal grid nominal resolution property are defined in the following table as a function of the value of the mean resolution km property:
8
+
9
+ for mean resolution, R, in the range (km):
10
+ nominal resolution is:
11
+ 0.036 ≤ R < 0.072
12
+ 0.05 km
13
+ 0.072 ≤ R < 0.16
14
+ 0.1 km
15
+ 0.16 ≤ R < 0.36
16
+ 0.25 km
17
+ 0.36 ≤ R < 0.72
18
+ 0.5 km
19
+ 0.72 ≤ R < 1.6
20
+ 1 km
21
+ 1.6 ≤ R < 3.6
22
+ 2.5 km
23
+ 3.6 ≤ R < 7.2
24
+ 5 km
25
+ 7.2 ≤ R < 16
26
+ 10 km
27
+ 16 ≤ R < 36
28
+ 25 km
29
+ 36 ≤ R < 72
30
+ 50 km
31
+ 72 ≤ R < 160
32
+ 100 km
33
+ 160 ≤ R < 360
34
+ 250 km
35
+ 360 ≤ R < 720
36
+ 500 km
37
+ 720 ≤ R < 1600
38
+ 1000 km
39
+ 1600 ≤ R < 3600
40
+ 2500 km
41
+ 3600 ≤ R < 7200
42
+ 5000 km
43
+ 7200 ≤ R < 16000
44
+ 10000 km
45
+ 7.10. Native vertical grid "Coordinate" CV
46
+ """
47
+
48
+ mean_resolution: str
@@ -0,0 +1,19 @@
1
+ """
2
+ EMD v1.0 Section 7.8 - temporal_refinement CV
3
+
4
+ Grid temporal refinement types.
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class TemporalRefinement(PlainTermDataDescriptor):
11
+ """
12
+ Temporal refinement (EMD v1.0 Section 7.8).
13
+
14
+ Options: static, etc.
15
+
16
+ The grid temporal refinement, indicating how the distribution of grid cells varies with time.
17
+ """
18
+
19
+ pass
@@ -0,0 +1,17 @@
1
+ """
2
+ EMD v1.0 Section 7.12 - truncation_method CV
3
+
4
+ Spectral truncation method types.
5
+ """
6
+
7
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
8
+
9
+
10
+ class TruncationMethod(PlainTermDataDescriptor):
11
+ """
12
+ Truncation method (EMD v1.0 Section 7.12).
13
+
14
+ The method for truncating the spherical harmonic representation of a spectral model.
15
+ """
16
+
17
+ pass
@@ -0,0 +1,91 @@
1
+ """
2
+ Vertical computational grid description (EMD v1.0 Section 4.2).
3
+
4
+ The model component's vertical computational grid is described by a subset of the following properties.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import List, Optional
10
+
11
+ from pydantic import Field, field_validator
12
+
13
+ from esgvoc.api.data_descriptors.data_descriptor import DataDescriptor
14
+
15
+ from .coordinate import Coordinate
16
+
17
+
18
+ class VerticalComputationalGrid(DataDescriptor):
19
+ """
20
+ Vertical computational grid description (EMD v1.0 Section 4.2).
21
+
22
+ The model component's vertical computational grid is described by a subset of the following properties.
23
+ """
24
+
25
+ vertical_coordinate: str | Coordinate = Field(
26
+ description="The coordinate type of the vertical grid. Taken from 7.11 vertical_coordinate CV. If there is no vertical grid, then the value 'none' must be selected."
27
+ )
28
+ description: Optional[str] = Field(
29
+ default=None,
30
+ description="A description of the vertical grid. A description is only required if there is information that is not covered by any of the other properties. Omit when not required.",
31
+ )
32
+ n_z: Optional[int] = Field(
33
+ default=None,
34
+ description="The number of layers (i.e. grid cells) in the Z direction. Omit when not applicable or not constant. If the number of layers varies in time or across the horizontal grid, then the n_z_range property may be used instead.",
35
+ ge=1,
36
+ )
37
+ n_z_range: Optional[List[int]] = Field(
38
+ default=None,
39
+ description="The minimum and maximum number of layers for vertical grids with a time- or space-varying number of layers. Omit if the n_z property has been set.",
40
+ min_length=2,
41
+ max_length=2,
42
+ )
43
+ bottom_layer_thickness: Optional[float] = Field(
44
+ default=None,
45
+ description="The thickness of the bottom model layer (i.e. the layer closest to the centre of the Earth). The value should be reported as a dimensional (as opposed to parametric) quantity. All measurements are in metres (EMD v1.0).",
46
+ gt=0,
47
+ )
48
+ top_layer_thickness: Optional[float] = Field(
49
+ default=None,
50
+ description="The thickness of the top model layer (i.e. the layer furthest away from the centre of the Earth). The value should be reported as a dimensional (as opposed to parametric) quantity. All measurements are in metres (EMD v1.0).",
51
+ gt=0,
52
+ )
53
+ top_of_model: Optional[float] = Field(
54
+ default=None,
55
+ description="The upper boundary of the top model layer (i.e. the upper boundary of the layer that is furthest away from the centre of the Earth). The value should be relative to the lower boundary of the bottom layer of the model, or an appropriate datum (such as mean sea level). All measurements are in metres (EMD v1.0).",
56
+ )
57
+
58
+ @field_validator("vertical_coordinate", mode="before")
59
+ @classmethod
60
+ def validate_vertical_coordinate(cls, v):
61
+ """Validate that vertical_coordinate is not empty if it's a string."""
62
+ if isinstance(v, str):
63
+ if not v.strip():
64
+ raise ValueError("Vertical coordinate cannot be empty")
65
+ return v.strip()
66
+ return v
67
+
68
+ @field_validator("n_z_range")
69
+ @classmethod
70
+ def validate_n_z_range(cls, v):
71
+ """Validate that n_z_range has exactly 2 values and min <= max."""
72
+ if v is not None:
73
+ if len(v) != 2:
74
+ raise ValueError("n_z_range must contain exactly 2 values [min, max]")
75
+ if v[0] > v[1]:
76
+ raise ValueError("n_z_range: minimum must be <= maximum")
77
+ if any(val < 1 for val in v):
78
+ raise ValueError("n_z_range values must be >= 1")
79
+ return v
80
+
81
+ @field_validator("n_z")
82
+ @classmethod
83
+ def validate_n_z_exclusivity(cls, v, info):
84
+ """Validate that n_z and n_z_range are mutually exclusive."""
85
+ if v is not None and info.data.get("n_z_range") is not None:
86
+ raise ValueError("n_z and n_z_range cannot both be set")
87
+ return v
88
+
89
+ def accept(self, visitor):
90
+ """Accept a data descriptor visitor."""
91
+ return visitor.visit_plain_term(self)
@@ -0,0 +1,5 @@
1
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
2
+
3
+
4
+ class VerticalCoordinate(PlainTermDataDescriptor):
5
+ pass
@@ -0,0 +1,19 @@
1
+ """
2
+ Vertical units CV (7.14 vertical_units CV).
3
+
4
+ Physical units of vertical grid thickness and position values.
5
+ Options include: m, Pa, K
6
+ """
7
+
8
+ from esgvoc.api.data_descriptors.data_descriptor import PlainTermDataDescriptor
9
+
10
+
11
+ class VerticalUnits(PlainTermDataDescriptor):
12
+ """
13
+ Vertical units CV (7.14 vertical_units CV).
14
+
15
+ Physical units of vertical grid thickness and position values.
16
+ Options include: m, Pa, K
17
+ """
18
+
19
+ pass
@@ -0,0 +1,159 @@
1
+ from esgvoc.api.data_descriptors.EMD_models.horizontal_computational_grid import HorizontalComputationalGrid
2
+ from esgvoc.api.data_descriptors.EMD_models.horizontal_grid_cells import HorizontalGridCells
3
+ from esgvoc.api.data_descriptors.EMD_models.horizontal_subgrid import HorizontalSubgrid
4
+ from esgvoc.api.data_descriptors.EMD_models.horizontal_units import HorizontalUnits
5
+ from esgvoc.api.data_descriptors.EMD_models.vertical_computational_grid import VerticalComputationalGrid
6
+ from esgvoc.api.data_descriptors.EMD_models.vertical_coordinate import VerticalCoordinate
7
+ from esgvoc.api.data_descriptors.model_component import ModelComponent
8
+ from esgvoc.api.data_descriptors.vertical_label import VerticalLabel
9
+ from esgvoc.api.data_descriptors.variant_label import VariantLabel
10
+ from esgvoc.api.data_descriptors.variable import Variable
11
+ from esgvoc.api.data_descriptors.tracking_id import TrackingId
12
+ from esgvoc.api.data_descriptors.title import Title
13
+ from esgvoc.api.data_descriptors.time_range import TimeRange
14
+ from esgvoc.api.data_descriptors.temporal_label import TemporalLabel
15
+ from esgvoc.api.data_descriptors.table import Table
16
+ from esgvoc.api.data_descriptors.sub_experiment import SubExperiment
17
+ from esgvoc.api.data_descriptors.source_type import SourceType
18
+ from esgvoc.api.data_descriptors.source import Source
19
+ from esgvoc.api.data_descriptors.EMD_models.resolution import EMDResolution
20
+ from esgvoc.api.data_descriptors.resolution import Resolution
21
+ from esgvoc.api.data_descriptors.region import Region
22
+ from esgvoc.api.data_descriptors.regex import Regex
23
+ from esgvoc.api.data_descriptors.EMD_models.reference import Reference
24
+ from esgvoc.api.data_descriptors.realm import Realm
25
+ from esgvoc.api.data_descriptors.realization_index import RealizationIndex
26
+ from esgvoc.api.data_descriptors.publication_status import PublicationStatus
27
+ from esgvoc.api.data_descriptors.product import Product
28
+ from esgvoc.api.data_descriptors.physics_index import PhysicsIndex
29
+ from esgvoc.api.data_descriptors.organisation import Organisation
30
+ from esgvoc.api.data_descriptors.obs_type import ObsType
31
+ from esgvoc.api.data_descriptors.nominal_resolution import NominalResolution
32
+
33
+ from esgvoc.api.data_descriptors.models_test.models import CompositeTermDDex, PatternTermDDex, PlainTermDDex
34
+ from esgvoc.api.data_descriptors.EMD_models.model import Model
35
+ from esgvoc.api.data_descriptors.EMD_models.model_component import EMDModelComponent
36
+
37
+ # from esgvoc.api.data_descriptors.model_component import ModelComponent
38
+ from esgvoc.api.data_descriptors.mip_era import MipEra
39
+ from esgvoc.api.data_descriptors.member_id import MemberId
40
+ from esgvoc.api.data_descriptors.license import License
41
+ from esgvoc.api.data_descriptors.known_branded_variable import KnownBrandedVariable
42
+ from esgvoc.api.data_descriptors.institution import Institution
43
+ from esgvoc.api.data_descriptors.initialization_index import InitializationIndex
44
+ from esgvoc.api.data_descriptors.horizontal_label import HorizontalLabel
45
+ from esgvoc.api.data_descriptors.activity import Activity
46
+ from esgvoc.api.data_descriptors.archive import Archive
47
+ from esgvoc.api.data_descriptors.area_label import AreaLabel
48
+ from esgvoc.api.data_descriptors.branded_suffix import BrandedSuffix
49
+ from esgvoc.api.data_descriptors.branded_variable import BrandedVariable
50
+ from esgvoc.api.data_descriptors.EMD_models.calendar import Calendar
51
+ from esgvoc.api.data_descriptors.citation_url import CitationUrl
52
+ from esgvoc.api.data_descriptors.EMD_models.component_type import ComponentType
53
+ from esgvoc.api.data_descriptors.contact import Contact
54
+ from esgvoc.api.data_descriptors.conventions import Convention
55
+ from esgvoc.api.data_descriptors.creation_date import CreationDate
56
+ from esgvoc.api.data_descriptors.data_descriptor import DataDescriptor
57
+ from esgvoc.api.data_descriptors.data_specs_version import DataSpecsVersion
58
+ from esgvoc.api.data_descriptors.date import Date
59
+ from esgvoc.api.data_descriptors.directory_date import DirectoryDate
60
+ from esgvoc.api.data_descriptors.drs_specs import DRSSpecs
61
+ from esgvoc.api.data_descriptors.experiment import Experiment
62
+ from esgvoc.api.data_descriptors.forcing_index import ForcingIndex
63
+ from esgvoc.api.data_descriptors.frequency import Frequency
64
+ from esgvoc.api.data_descriptors.further_info_url import FurtherInfoUrl
65
+ from esgvoc.api.data_descriptors.grid import Grid
66
+ from esgvoc.api.data_descriptors.EMD_models.coordinate import Coordinate
67
+ from esgvoc.api.data_descriptors.EMD_models.arrangement import Arrangement
68
+ from esgvoc.api.data_descriptors.EMD_models.cell_variable_type import CellVariableType
69
+ from esgvoc.api.data_descriptors.EMD_models.grid_mapping import GridMapping
70
+ from esgvoc.api.data_descriptors.EMD_models.grid_region import GridRegion
71
+ from esgvoc.api.data_descriptors.EMD_models.grid_type import GridType
72
+ from esgvoc.api.data_descriptors.EMD_models.temporal_refinement import TemporalRefinement
73
+ from esgvoc.api.data_descriptors.EMD_models.truncation_method import TruncationMethod
74
+ from esgvoc.api.data_descriptors.EMD_models.vertical_units import VerticalUnits
75
+
76
+ from esgvoc.api.data_descriptors.activity import ActivityCMIP7
77
+
78
+ ActivityCMIP7.model_rebuild()
79
+
80
+ DATA_DESCRIPTOR_CLASS_MAPPING: dict[str, type[DataDescriptor]] = {
81
+ "PlainTermDDex": PlainTermDDex,
82
+ "PatternTermDDex": PatternTermDDex,
83
+ "CompositeTermDDex": CompositeTermDDex,
84
+ "activity": Activity,
85
+ "date": Date,
86
+ "directory_date": DirectoryDate,
87
+ "experiment": Experiment,
88
+ "forcing_index": ForcingIndex,
89
+ "frequency": Frequency,
90
+ "grid": Grid,
91
+ "initialization_index": InitializationIndex,
92
+ "institution": Institution,
93
+ "license": License,
94
+ "mip_era": MipEra,
95
+ "model_component": ModelComponent,
96
+ "organisation": Organisation,
97
+ "physics_index": PhysicsIndex,
98
+ "product": Product,
99
+ "realization_index": RealizationIndex,
100
+ "realm": Realm,
101
+ "resolution": Resolution,
102
+ "source": Source,
103
+ "source_type": SourceType,
104
+ "sub_experiment": SubExperiment,
105
+ "table": Table,
106
+ "time_range": TimeRange,
107
+ "variable": Variable,
108
+ "variant_label": VariantLabel,
109
+ "area_label": AreaLabel,
110
+ "temporal_label": TemporalLabel,
111
+ "vertical_label": VerticalLabel,
112
+ "horizontal_label": HorizontalLabel,
113
+ "branded_suffix": BrandedSuffix,
114
+ "branded_variable": BrandedVariable,
115
+ "publication_status": PublicationStatus,
116
+ "known_branded_variable": KnownBrandedVariable,
117
+ "calendar": Calendar,
118
+ "component_type": ComponentType,
119
+ "grid_arrangement": Arrangement, # EMD v1.0
120
+ "coordinate": Coordinate,
121
+ "grid_mapping": GridMapping, # EMD v1.0
122
+ "model_componentEMD": EMDModelComponent, # EMD v1.0
123
+ "model": Model, # EMD v1.0
124
+ "reference": Reference, # EMD v1.0
125
+ # "resolution": EMDResolution,
126
+ "grid_type": GridType, # EMD v1.0
127
+ "cell_variable_type": CellVariableType, # EMD v1.0
128
+ "truncation_method": TruncationMethod, # EMD v1.0
129
+ "vertical_units": VerticalUnits, # EMD v1.0
130
+ "grid_region": GridRegion, # EMD v1.0
131
+ "data_specs_version": DataSpecsVersion,
132
+ "further_info_url": FurtherInfoUrl,
133
+ "tracking_id": TrackingId,
134
+ "creation_date": CreationDate,
135
+ "conventions": Convention,
136
+ "title": Title,
137
+ "contact": Contact,
138
+ "region": Region,
139
+ "member_id": MemberId,
140
+ "obs_type": ObsType, # obs4Mips
141
+ "regex": Regex,
142
+ "citation_url": CitationUrl,
143
+ "archive": Archive,
144
+ "drs_specs": DRSSpecs,
145
+ "nominal_resolution": NominalResolution,
146
+ "grid_arrangement": Arrangement, # EMD v1.0
147
+ "cell_variable_type": CellVariableType, # EMD v1.0
148
+ "grid_mapping": GridMapping, # EMD v1.0
149
+ "grid_region": GridRegion, # EMD v1.0
150
+ "grid_temporal_refinement": TemporalRefinement, # EMD v1.0
151
+ "truncation_method": TruncationMethod, # EMD v1.0
152
+ "grid_type": GridType, # EMD v1.0
153
+ "horizontal_units": HorizontalUnits,
154
+ "vertical_coordinate": VerticalCoordinate,
155
+ "horizontal_grid_cell": HorizontalGridCells,
156
+ "horizontal_computational_grid": HorizontalComputationalGrid,
157
+ "horizontal_subgrid": HorizontalSubgrid,
158
+ "vertical_computational_grid": VerticalComputationalGrid,
159
+ }