How to use OceanVal

Validating a simulation involves three steps: register the observational datasets, matchup the simulation output against them, and generate statistics and an HTML report. Always create a new directory before running OceanVal for a new simulation, and run all commands from within it. Once you have reports for multiple simulations, you can also compare them side by side.

Step 1: Register observational datasets

Register the observational datasets you want to validate against by specifying the location of the data files and any necessary metadata, using oceanval.add_point_comparison and oceanval.add_gridded_comparison.

Setting up gridded observational data

Register a gridded (NetCDF) dataset by pointing OceanVal at a directory of observation files and saying whether the data is a climatology:

python
oceanval.add_gridded_comparison(
    name="oxygen",
    source="CMEMS",
    source_info="Gridded observations from the Copernicus Marine Environment Monitoring Service",
    short_name="oxygen concentration",
    model_variable="oxygen",
    obs_variable="O2_concentration",
    obs_path="/path/to/obs_data/",
    climatology=False,
)

Required parameters:

Optional parameters
  • source_info

    Additional information about the source, e.g. publication details.

  • short_name

    A short name for the observational variable, e.g. "temp".

  • short_title

    A short title for plots, e.g. "Nitrate Concentration".

  • long_name

    A long name for the variable, e.g. "sea surface temperature".

  • vertical

    Whether to carry out vertical validation. Defaults to False (surface only).

  • start / end

    First / last year of observations to use. Defaults to all available years.

  • obs_multiplier

    Multiplier applied to observational data, e.g. for unit conversion. Defaults to 1.0.

  • obs_adder

    Value added to observational data, e.g. 273.15 to convert Kelvin to Celsius. Defaults to 0.0.

  • recipe

    A built-in recipe dict, e.g. {"temperature": "woa23"}, providing standard metadata and file locations automatically.

  • thredds

    Whether obs_path is a remote OPeNDAP/THREDDS URL rather than a local file or directory. Defaults to False.

  • file_check

    Whether to check that obs_path exists and its variables are valid. Defaults to True.

Be consistent

If you register the same variable separately for point and gridded data, give both the same short_name, long_name and short_title — plots and statistics need consistent labelling. Inconsistent labels raise an error.

How does OceanVal handle gridded data?

Gridded data is converted to one of: a time series of multi-year monthly averages, a climatological monthly average, or a climatological annual average per grid cell.

  • Multi-year observational data → OceanVal computes a multi-year observational average for a like-for-like comparison.
  • Single-year monthly data → OceanVal generates a comparable climatological monthly average from the simulation, based on the year range you specify.
  • Single-year, single-timestep data → treated as a climatological annual average, matched against a model climatological annual average.

The simulation output is always regridded to the observational grid.

Using built-in recipes for gridded data

Built-in recipe definitions make it easy to register standard observational datasets — especially useful for a standard climatology such as WOA23 or GLODAP, without manually specifying all the metadata.

python
oceanval.add_gridded_comparison(
    name="temperature",
    source="WOA23",
    model_variable="temp",
    recipe={"temperature": "woa23"},
    start=2005, end=2014,
    climatology=True,
)

This uses the built-in metadata and file locations for the WOA23 temperature climatology. recipe also works for salinity, oxygen, nitrate, phosphate, silicate, chlorophyll and pH — see all recipes. The underlying helper can also be used directly:

python
recipe = oceanval.parsers.find_recipe({"temperature": "woa23"}, start=2005, end=2014)
print(recipe["source"], recipe["obs_variable"])

Browse the full recipe catalogue to see every supported observational dataset, with the exact call to use for each.

Setting up point (in-situ) observational data

Register an in-situ dataset by pointing OceanVal at a CSV (or directory of CSVs) and naming the model variable to compare it against:

python
oceanval.add_point_comparison(
    name="nitrate",
    source="ICES",
    source_info="In-situ observations from the International Council for the Exploration of the Sea",
    short_name="nitrate concentration",
    model_variable="temp",
    obs_path="/path/to/obs_data/",
)

Required parameters:

Optional parameters
  • source_info

    Additional information about the source, e.g. publication details.

  • short_name

    A short name for the observational variable, e.g. "temp".

  • short_title

    A short title for plots, e.g. "Nitrate Concentration".

  • long_name

    A long name for the variable, e.g. "sea surface temperature".

  • vertical

    Whether to carry out vertical validation. Defaults to False (surface only).

  • start / end

    First / last year of observations to use. Defaults to all available years.

  • obs_multiplier

    Multiplier applied to observational data, e.g. for unit conversion. Defaults to 1.0.

  • obs_adder

    Value added to observational data, e.g. 273.15 to convert Kelvin to Celsius. Defaults to 0.0.

  • binning

    Spatially bin data to a [lon_bin_size, lat_bin_size] resolution in degrees. Off by default.

How does OceanVal handle variable names?

name can be anything — it's only used internally to keep track of things and name files. Reports and plots use short_name, long_name and short_title for labelling, so set these for a better-looking report. You can only validate a variable using a single gridded and a single point dataset at a time.

Step 2: Matchup model output with observations

Once your datasets are registered, pair the model output against them with oceanval.matchup:

python
oceanval.matchup(
    sim_dir="/path/to/simulation/output/",
    start=2000, end=2010,
    cores=4,
    lon_lim=[-80, 0], lat_lim=[20, 60],
    thickness="cell_thickness",
)

Required parameters:

Optional parameters
  • n_dirs_down

    Directory levels to search down for output files. Defaults to 2, assuming a YYYY/MM/ structure.

  • overwrite / ask

    Whether to overwrite existing matchup files, and whether to confirm first. ask defaults to True.

  • cache

    Whether to cache intermediate results. Defaults to False.

  • exclude

    Strings that should not appear in any simulation file paths.

  • require

    Strings that must appear in a simulation file path for it to be included — useful when multiple simulations share a directory.

  • out_dir

    Directory to save matchup files in. Defaults to the execution directory.

  • point_time_res

    Time resolution for point matchups. Defaults to ["year", "month", "day"]; set to ["month", "day"] to compare climatological output with observations.

  • n_check

    Number of files checked when identifying the file naming convention.

  • as_missing

    A float or [min, max] range of values to treat as missing in the model output.

  • strict_names

    Whether to strictly enforce variable naming conventions. Defaults to True.

Requirements for simulation folder structure

Simulation output must be in a single directory, or in subdirectories following a YYYY/MM/-style structure — subdirectories must contain only integers. If your structure differs, create symbolic links in a single directory. If multiple simulations share a directory, use require to filter file paths to the one you want.

Note: for monthly-resolution simulations, set point_time_res=["year", "month"] when matching up in-situ observations — otherwise day-of-year will almost never match and few matchups will be found.

Summing simulation output: to compare observations against the sum of multiple model variables, set something like "var1+var2+var3" as model_variable.

How does OceanVal handle in-situ data?

Behaviour depends on what's provided: year, month, day, depth.

  • No depth → assumed to be a surface dataset. With depth and vertical=True, OceanVal interpolates to all available depths; otherwise only the top 5 m is used.
  • Year, month and day → matched against model output for the exact date.
  • Year and month only → matched against the monthly average from the simulation.
  • Year only → matched against the annual average from the simulation.
  • No time information → matched against the simulation's multi-year average.

To ignore year information (e.g. comparing a 1-year simulation against multiple years of observations), set point_time_res=["month", "day"] in oceanval.matchup.

Where does OceanVal save matchup files?

By default, in the directory where oceanval.matchup is run: oceanval_matchups/gridded (.nc files) and oceanval_matchups/point (.csv files). Model output is named "model" and observations "observation" in each file.

Step 3: Build the validation report

Once matched, compute statistics and generate plots with oceanval.validate, run from the same directory as the matchup step:

python
oceanval.validate()
Optional parameters
  • lon_lim / lat_lim

    Longitude / latitude limits for the validation region.

  • region

    Region being validated — currently "global" or "nwes" (Northwest European Shelf).

  • concise

    Whether to generate a concise HTML summary page. Defaults to True.

  • fixed_scale

    Whether to use a fixed colour scale for the seasonal plots, capping min/max to the 2nd/98th percentile. Defaults to False.

  • data_dir / out_dir

    Where matchup data is read from / the report is written to. Default to the current directory.

This generates and opens an HTML page you can view in a web browser.


Beyond the basics

Comparing validation outputs from multiple simulations

Once you have validation reports for different simulations, compare them with oceanval.compare, which summarises the differences between them:

python
oceanval.compare(
    model_dict={
        "model_a": "/path/to/model_a",
        "model_b": "/path/to/model_b",
    },
    view=True,
    ask=True,
)

model_dict maps a short name for each model to its validation output directory. The HTML comparison report is written to oceanval_comparison/compare/_build/html/notebooks/comparison_seasonal.html.

Can I access and use OceanVal's validation code?

Yes. OceanVal uses Jupyter notebooks to run validation calculations and generate plots. They live in the oceanval_report/notebooks directory of the report output — copy and edit them for a more customised validation. They're designed for internal use, so not especially user-friendly, but should be clear enough.