|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | 5 | import textwrap |
6 | | -from typing import TYPE_CHECKING, Any, cast |
| 6 | +from dataclasses import dataclass |
| 7 | +from typing import TYPE_CHECKING, Any, Literal, cast |
7 | 8 |
|
8 | 9 | import numpy as np |
9 | 10 | import xarray as xr |
10 | 11 |
|
| 12 | +from parcels._python import isinstance_noimport |
| 13 | + |
11 | 14 | if TYPE_CHECKING: |
12 | 15 | from parcels import Field, FieldSet, ParticleSet |
13 | 16 | from parcels._core.field import VectorField |
| 17 | + from parcels._core.model import ModelData |
14 | 18 |
|
15 | 19 |
|
16 | 20 | def fieldset_repr(fieldset: FieldSet) -> str: |
@@ -177,3 +181,69 @@ def _format_list_items_multiline(items: list[str] | dict, level: int = 1, with_b |
177 | 181 |
|
178 | 182 | def is_builtin_object(obj): |
179 | 183 | return obj.__class__.__module__ == "builtins" |
| 184 | + |
| 185 | + |
| 186 | +@dataclass |
| 187 | +class _FieldSetDescriptionRow: |
| 188 | + type_: Literal["Field", "VectorField", "Context value"] |
| 189 | + model_id: int | None |
| 190 | + name: str |
| 191 | + interp_method: str | None |
| 192 | + |
| 193 | + def to_dict(self) -> dict[str, str]: |
| 194 | + return { |
| 195 | + "Type": self.type_, |
| 196 | + "Dataset origin": str(self.model_id) if self.model_id is not None else "-", |
| 197 | + "Name": self.name, |
| 198 | + "Interp method": str(self.interp_method) if self.interp_method is not None else "-", |
| 199 | + } |
| 200 | + |
| 201 | + |
| 202 | +def _print_table(rows: list[_FieldSetDescriptionRow]) -> str: |
| 203 | + import pandas as pd |
| 204 | + |
| 205 | + dicts = [r.to_dict() for r in rows] |
| 206 | + return pd.DataFrame(dicts).sort_values(["Dataset origin", "Type", "Name"]).to_markdown(index=False) |
| 207 | + |
| 208 | + |
| 209 | +def fieldset_describe(fieldset: FieldSet) -> str: |
| 210 | + rows: list[_FieldSetDescriptionRow] = [] |
| 211 | + models: dict[int, int] = {} # mapping of memory ID to a human readable ID |
| 212 | + |
| 213 | + assert fieldset._fields is not None |
| 214 | + |
| 215 | + for field in fieldset._fields.values(): |
| 216 | + model_id: int |
| 217 | + |
| 218 | + # Set human readable model ID |
| 219 | + parent_id = id(_get_parent_model(field)) |
| 220 | + models[parent_id] = models.get(parent_id, len(models)) |
| 221 | + model_id = models[parent_id] |
| 222 | + |
| 223 | + type_ = field.__class__.__name__ |
| 224 | + assert type_ in ("Field", "VectorField") |
| 225 | + |
| 226 | + rows.append( |
| 227 | + _FieldSetDescriptionRow( |
| 228 | + type_=type_, |
| 229 | + model_id=model_id, |
| 230 | + name=field.name, |
| 231 | + interp_method=repr(field.interp_method), |
| 232 | + ) |
| 233 | + ) |
| 234 | + for k, v in fieldset.context.items(): |
| 235 | + rows.append( |
| 236 | + _FieldSetDescriptionRow( |
| 237 | + type_="Context value", |
| 238 | + model_id=None, |
| 239 | + name=k, |
| 240 | + interp_method=repr(v), |
| 241 | + ) |
| 242 | + ) |
| 243 | + return _print_table(rows) |
| 244 | + |
| 245 | + |
| 246 | +def _get_parent_model(field: Field | VectorField) -> ModelData: |
| 247 | + if isinstance_noimport(field, "Field"): |
| 248 | + return field.model |
| 249 | + return field.U.model |
0 commit comments