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:
  • 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

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: object

Raster 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