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. A variable can be validated against several gridded and point datasets at once — see below.

Validating a variable against more than one dataset

Call oceanval.add_gridded_comparison or oceanval.add_point_comparison once per dataset, with the same name and a different source. Each source is matched up and reported separately:

python
oceanval.add_gridded_comparison(
    name="temperature",
    model_variable="votemper",
    recipe={"temperature": "cobe2"},
)
oceanval.add_gridded_comparison(
    name="temperature",
    model_variable="votemper",
    recipe={"temperature": "nsbc"},
)

Every call for a variable must use the same model_variable and labels. Registering a source that is already registered for that variable replaces it, so re-running a script or notebook cell is safe. Where a variable has more than one source, the summary page labels it with the source, e.g. "Temperature (COBE2)".

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.
  • Month (and optionally day), but no year → matched against the simulation's average for that month (or day) over the years from start to end.
  • No time information → matched against the simulation's average over the years from start to end.

To ignore year information and compare observations with the simulation's average for their month and day, set point_time_res=["month", "day"] in oceanval.matchup. Only observations from the years between start and end are used.

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.

A note on handling high-resolution gridded observational data

OceanVal will automatically regrid observational data that spans more than 90° longitude or 90° latitude to a spatial resolution of 0.5° by 0.5°. This will make minimal differences to validation statistics, but will ensure that high-resolution spatial data does not cause processes such as plotting to crash.

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.

  • subregions

    The sub-regions used for the regional summaries: "global", "nwes" (Northwest European Shelf), or a path to your own regions netCDF file. See Using your own regions below.

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

  • pdf

    Whether to also generate PDF downloads of the report (a per-page PDF and a combined PDF of the whole report). Defaults to False, since generating them is slow and most users only need the HTML report.

  • word

    Whether to also generate Word versions of the report — one per page, plus a combined oceanval_report.docx — with figures, tables and editable Word equations. Defaults to False. Requires pandoc.

  • zip

    Whether to also bundle the report into a zip archive (oceanval_report.zip) in the output directory, containing just the files needed to view the complete report — the HTML pages, plus the PDFs and Word file if those were generated. Defaults to False.

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

Using your own regions

The report summarises model performance in each region: a map of the regions, the seasonal cycle of the spatial average, and a table of bias, RMSD and correlations. Instead of the built-in "global" or "nwes" regions, you can supply your own netCDF file:

python
oceanval.validate(subregions="my_regions.nc")

The file should be set up as follows:

A copy of the file is saved with the results, so oceanval.compare can also compare regions across simulations. When comparing, use the same regions file for every simulation. OceanVal cannot tell two different custom files apart.

Word and PDF versions of the report

The report is HTML by default. If you need something to edit, circulate or drop into a deliverable, ask for a Word version as well:

python
oceanval.validate(word=True)

This writes oceanval_report.docx — the whole report as a single document — plus one .docx per page, alongside the HTML files. The report itself gains download buttons for them, so you can pick up the Word file straight from the page you are reading.

The Word version is built to be worked on rather than just read:

Word output needs pandoc installed (conda install -c conda-forge pandoc). If it is missing, OceanVal warns and still builds the HTML report.

PDF works the same way, via pdf=True, and the two can be combined. Both are off by default because they add time to the build and most people only need the HTML.


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.

Comparison reports take word=True and pdf=True as well, producing the same Word and PDF versions as a validation report.

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.