canopy.util
canopy.util.checks
- canopy.util.checks.check_disjoint_coords(fields: List[Field], raise_exception: bool = False) bool[source]
Check if no coordinates overlap between fields
- Parameters:
fields (List[Field]) – List of fields whose coordinates to check
raise_exception (bool) – If True, an exception is raised if the check fails
- Return type:
True if no coordinates overlap, False otherwise
- canopy.util.checks.check_field_contains_layers(field: Field, layers: str | Iterable[str], name: str = 'field', raise_exception: bool = False) bool[source]
Check if field contains required layers
- Parameters:
field (Field) – The field whose layers to check
layers (str | list[str]) – A string or a list of strings, identifying the required layer(s)
name (str = 'field') – The name of the field for logging purposes
raise_exception (bool) – If True, an exception is raised if the check does not pass (default: False)
- canopy.util.checks.check_grids_have_same_axes(fields: List[Field], raise_exception: bool = False) bool[source]
Check if grids have same axes
- canopy.util.checks.check_indices_match(field1: Field, field2: Field)[source]
Check if the indices of the DataFrames of two fields match up to a given tolerance
- Parameters:
Notes
Absolute and relative tolerances are defined as in Numpy, i.e., two numbers a and b are equivalent if the following equation is fulfilled:
absolute(a - b) <= (atol + rtol * absolute(b))
See https://numpy.org/doc/stable/reference/generated/numpy.isclose.html#numpy.isclose
- canopy.util.checks.check_spatial_coords_match(field1: Field, field2: Field, atol: float = 1e-07, rtol: float = 0.0, raise_exception: bool = False) bool[source]
Check if spatial coordinates of two fields match up to given tolerance
- Parameters:
field1 (Field) – The first of the two fields whose coordinates to compare
field2 (Field) – The second of the two fields whose coordinates to compare
atol (float) – Absolute tolerance to apply in the comparison
rtol (float) – Relative tolerance to apply in the comparison
raise_exception (bool) – If True, an exception is raised if the check does not pass (default: False)
Notes
Absolute and relative tolerances are defined as in Numpy, i.e., two numbers a and b are equivalent if the following equation is fulfilled:
absolute(a - b) <= (atol + rtol * absolute(b))
See https://numpy.org/doc/stable/reference/generated/numpy.isclose.html#numpy.isclose
- canopy.util.checks.check_time_series_consistency(field: Field, raise_exception: bool = False) bool[source]
Check if all spatial locations in a field have the same time series
- Parameters:
field (Field) – The field whose time series to check
raise_exception (bool) – If True, an exception is raised if the check does not pass (default: False)
- canopy.util.checks.check_time_series_match(fields: List[Field], raise_exception: bool = False)[source]
Check if fields have the same time frequency and span
- Parameters:
fields (List[Field]) – A list of fields
raise_exception (bool) – If True, an exception is raised if the check does not pass (default: False)
- Return type:
True if time series match, False otherwise
canopy.util.compare_ts
Statistically compare fields along the time axis
- canopy.util.compare_ts.compare_ts(field1: Field, field2: Field, layers1: list[str] | str | None = None, layers2: list[str] | str | None = None) DataFrame[source]
Statistically compare two fields along the time axes
NaN values in either of the series are ignored. The field indices must be equal.
- The comparison calculates the following metrics:
ME: Mean Error
MAE: Mean Absolute Error
MSE: Mean Squared Error
RMSE: Root Mean Squared Error
r: Correlation coefficient
R2: Coefficient of determination
- Parameters:
field1 (Field) – One of the two fields to compare. In the case of observation vs. simulation, this field represents the OBSERVED DATA.
field2 (Field) – One of the two fields to compare. In the case of observation vs. simulation, this field represents the MODELED DATA.
layers1 (str | list[str]) – A string or list of strings to select the layers from field1 to be compared. If None, all of the field’s layers are used.
layers2 (str | list[str]) – A string or list of strings to select the layers from field2 to be compared. If None, all of the field’s layers are used.
- Return type:
A pandas DataFrame with the metrics for each gridcell, and each pairs of layers.
Notes
The layers are compared in the order that they are passed to the function, and their name has no effect on the comparison. For example, if layers1 = [‘C3G’, ‘C4G’] and layers2 = [‘C4G’, ‘C3G’], field1’s ‘C3G’ layer will be compared to field2’s ‘C4G’ layer, and viceversa.
canopy.util.raster
Raster data structure for map visualization with pcolormesh.
- class canopy.util.raster.Raster(field: Field, layer: str, timeop: str = 'av')[source]
Bases:
objectRaster data for map plotting with ax.pcolormesh.
Uses 1D center arrays (x, y) with same dimensions as vmap rows/columns. Compatible with pcolormesh using shading=’nearest’ or ‘auto’.
- property x: ndarray
1D array of x (lon) cell centers. Same length as vmap columns.
- property x_edges: ndarray
1D array of x (lon) cell edges. For use with plotting packages that need edges.
- property y: ndarray
1D array of y (lat) cell centers. Same length as vmap rows.
- property y_edges: ndarray
1D array of y (lat) cell edges. For use with plotting packages that need edges.
canopy.util.make_lines
Create a DataFrame from a Field for line plotting.
- canopy.util.make_lines.make_lines(field: Field, axis: str = 'time', flatten_columns: bool = False, layers: List[str] | None = None) DataFrame[source]
Create a pandas DataFrame with the field’s data unstacked along the specified axis
This DataFrame is much easier to use to plot lines (e.g. with df.plot())
- Parameters:
field (Field) – The field from which to create the DataFrame
axis (str) – The axis along which to unstack the data
flatten_columns (bool, optional) – If True, flatten MultiIndex columns to simple strings. Default is False.
layers (Optional[List[str]], optional) – Layer names to include when flatten_columns is True. Defaults to field.layers.
canopy.util.overlap
- canopy.util.overlap.overlap(field_left: Field, field_right: Field, match: Literal['coordinates', 'labels'] = 'coordinates', atol: float | None = None, use_index: Literal['left', 'right'] | None = None) tuple[Field, Field][source]
Calculate entries with overlapping indices between two fields.
This function takes two fields, and returns another two fields with the data corresponding to where the spatial and time coordinates of the original fields overlap. The overlap allows for a certain tolerance in the spatial coordinates.
- Parameters:
field_left (Field) – The first field object
field_right (Field) – The second field object
match (Literal['coordinates', 'labels']) – How to match the spatial part of the index, whether throug coordinates (the default) or through labels. If ‘coordinates’ is selected, the ‘label’ index level, if present, is ignored. If ‘label’ is selected, the spatial coordinates are ignored.
atol (Optional[float] = None) – If None (the default value), spatial coordinates matching is exact. Otherwise the coordinates are matched within atol (absolute tolerance).
use_index (Optional[Literal['left', 'right']] = None) – If None, each returned field’s index corresponds with the original fields. Otherwise, the returned fields have an index based on the first or second field. This only makes a difference when spatial coordinates are matched up to a tolerance.
- Return type:
Two fields holding the data from the original fields, but only where the original indices overlap.
Notes
Matching labels or coordinates exactly is much faster than allowing for tolerance, so use atol=None, rather than atol=0, if you want an exact match.
Example
# This will match field entries with slightly offset spatial coordinates import canopy as cp
# Load (dummy) data sources: my_source = cp.get_source(“/path/to/some/data”, “lpj_guess”) my_other_source = cp.get_source(“/path/to/some/other/data”, “lpj_guess”)
# Load fields field_left = my_source.load_field(‘anpp’) field_right = my_other_source.load_field(‘anpp’)
print(field_left.data) # TrBE C4G # lon lat time # 1.0 1.0 1989 0 0 # 1990 1 2 # 2.0 1989 2 4 # 1990 3 6 # 2.0 3.0 1989 4 8 # 1990 5 10 # 1991 6 12
print(field_right.data) # TrBE C4G # lon lat time # 1.1 2.1 1990 1 2 # 2.1 3.1 1990 3 4 # 1991 5 6 # 1992 7 8 # 3.1 -3.1 1984 9 10
# Exact coordinate matching leads to empty fields: field_left_overlap, field_right_overlap = cp.overlap(field_left, field_right) print(field_left) # # Field is empty! # # History # ——- # [1] 2025-11-10 19:05:56: overlap: sliced with overlapping rows from another field (description: Pretend Annual NPP just for showcasing cp.overlap). The index intersection was calculated exactly. Field was sliced to empty.
# Allowing some tolerance does the trick. Let’s allow a tolerance of 0.2 degrees in the spatial coordinates field_left_overlap, field_right_overlap = cp.overlap(field_left, field_right, atol=0.2)
print(field_left_overlap.data) # TrBE C4G # lon lat time # 1.0 2.0 1990 3 6 # 2.0 3.0 1990 5 10 # 1991 6 12
print(field_right_overlap.data) # TrBE C4G # lon lat time # 1.1 2.1 1990 1 2 # 2.1 3.1 1990 3 4 # 1991 5 6
# We can specify that the second field’s coordinates should be used for both returned fields. # Having the same coordinates in both fields can help comparing them.
field_left_overlap, field_right_overlap = cp.overlap(field_left, field_right, atol=0.2, use_index=”second”) print(field_left_overlap.data) # TrBE C4G # lon lat time # 1.1 2.1 1990 3 6 # 2.1 3.1 1990 5 10 # 1991 6 12
print(field_right_overlap.data) # TrBE C4G # lon lat time # 1.1 2.1 1990 1 2 # 2.1 3.1 1990 3 4 # 1991 5 6
canopy.util.unite
- canopy.util.unite.unite(fields: List[Field], checks: str = '') Field[source]
Concatenate fields with common layers along the time or spatial axes
The fields must fulfill the following basic requirements: - Their time series must have the same frequency - Their grids must be of the same type and compatible (see documentation for grid compatibility) - All fields must have at least one overlapping layer
- Parameters:
fields (List[Field]) – A list of fields to unite
checks (str) – A string specifying what checks to run. Each character represents a check: ‘t’: check that fields identical time series ‘c’: check that fields have contiguous time series ‘g’: check that all gridcells overlap between fields ‘d’: (disjoint) check that there are no common gridcells
- Return type:
A field object with the layers common to all passed fields concatenated along the coordinate axes
Notes
In case of overlapping coordinates/time periods, the first occurrence in concatenation order is kept.
Examples
import canopy as cp
# Uniting a historical run with a scenario run # ——————————————–
aaet_hist = cp.Field.from_file(‘/path/to/historical/run/aaet.out’) aaet_ssp126 = cp.Field.from_file(‘/path/to/ssp126/run/aaet.out’)
# In this case we might want to check that there are no missing gridcells # between the 2 runs and that the time series connect with no gaps aaet_full2 = cp.unite([aaet_hist, aaet_ssp126], chekcs = ‘cg’)
# Uniting pieces of a simulation broken down into smaller spatial domains # ———————————————————————– anpp1 = cp.Field.from_file(‘/path/to/run1/anpp.out’) anpp2 = cp.Field.from_file(‘/path/to/run2/anpp.out’) anpp3 = cp.Field.from_file(‘/path/to/run3/anpp.out’)
# In this case we might want to check that the time series of all the pieces are the same # and that the fields do not overlap spatially anpp = cp.unite([anpp1, anpp2, anpp3], checks=’td’)
canopy.util.join
- canopy.util.join.join(fields: List[Field], checks: str = '') Field[source]
Join two or more fields with non-overlapping layers
The fields must fulfill the following basic requirements: - Their time series must have the same frequency - Their grids must be of the same type and compatible (see documentation for grid compatibility) - Fields must have no overlapping layers
Entries with non-overlapping spatio-temporal coordinates are completed with NaNs.
- Parameters:
fields (List[Field]) – A list of fields to join
checks (str) – A string specifying what checks to run. Each character represents a check: ‘t’: check that fields have identical time series ‘g’: check that all gridcells overlap between fields ‘i’: check that indices are identical (supersedes both checks above)
- Return type:
A unique field object with the layers of all passed fields