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.
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.
Register a gridded (NetCDF) dataset by pointing OceanVal at a directory of observation files and saying whether the data is a climatology:
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:
namerequiredA name for the dataset, e.g. "temperature".
sourcerequiredThe source of the observational data, e.g. "CMEMS".
model_variablerequiredThe name of the model variable to compare against the observations.
obs_pathrequiredPath to the directory containing the observational data files.
obs_variableName of the variable in the observational files. If omitted, OceanVal assumes a single variable is present.
climatologyrequiredWhether the observational data is a climatology.
source_infoAdditional information about the source, e.g. publication details.
short_nameA short name for the observational variable, e.g. "temp".
short_titleA short title for plots, e.g. "Nitrate Concentration".
long_nameA long name for the variable, e.g. "sea surface temperature".
verticalWhether to carry out vertical validation. Defaults to False (surface only).
start / endFirst / last year of observations to use. Defaults to all available years.
obs_multiplierMultiplier applied to observational data, e.g. for unit conversion. Defaults to 1.0.
obs_adderValue added to observational data, e.g. 273.15 to convert Kelvin to Celsius. Defaults to 0.0.
recipeA built-in recipe dict, e.g. {"temperature": "woa23"}, providing standard metadata and file locations automatically.
threddsWhether obs_path is a remote OPeNDAP/THREDDS URL rather than a local file or directory. Defaults to False.
file_checkWhether to check that obs_path exists and its variables are valid. Defaults to True.
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.
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.
The simulation output is always regridded to the observational grid.
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.
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:
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.
Register an in-situ dataset by pointing OceanVal at a CSV (or directory of CSVs) and naming the model variable to compare it against:
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:
namerequiredA name for the dataset, e.g. "temperature" — used internally to keep track of things. Letters and numbers only.
sourcerequiredThe source of the observational data, e.g. "NOAA".
model_variablerequiredThe name of the model variable to compare against the observations.
obs_pathrequiredPath to a file or directory containing the observational data. If a directory, ensure it only contains files relevant to this variable — OceanVal recursively uses all NetCDF files inside it.
source_infoAdditional information about the source, e.g. publication details.
short_nameA short name for the observational variable, e.g. "temp".
short_titleA short title for plots, e.g. "Nitrate Concentration".
long_nameA long name for the variable, e.g. "sea surface temperature".
verticalWhether to carry out vertical validation. Defaults to False (surface only).
start / endFirst / last year of observations to use. Defaults to all available years.
obs_multiplierMultiplier applied to observational data, e.g. for unit conversion. Defaults to 1.0.
obs_adderValue added to observational data, e.g. 273.15 to convert Kelvin to Celsius. Defaults to 0.0.
binningSpatially bin data to a [lon_bin_size, lat_bin_size] resolution in degrees. Off by default.
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.
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:
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)".
Once your datasets are registered, pair the model output against them with oceanval.matchup:
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:
sim_dirrequiredPath to the directory containing the model simulation output files.
start / endrequiredFirst / last year of the simulation to use for validation.
coresNumber of CPU cores to use for parallel processing.
lon_lim / lat_limLongitude / latitude limits for the validation region, e.g. [-180, 180].
thicknessvertical only"z_level" or a variable name containing cell thickness — required for vertical validation.
n_dirs_downDirectory levels to search down for output files. Defaults to 2, assuming a YYYY/MM/ structure.
overwrite / askWhether to overwrite existing matchup files, and whether to confirm first. ask defaults to True.
cacheWhether to cache intermediate results. Defaults to False.
excludeStrings that should not appear in any simulation file paths.
requireStrings that must appear in a simulation file path for it to be included — useful when multiple simulations share a directory.
out_dirDirectory to save matchup files in. Defaults to the execution directory.
point_time_resTime resolution for point matchups. Defaults to ["year", "month", "day"]; set to ["month", "day"] to compare climatological output with observations.
n_checkNumber of files checked when identifying the file naming convention.
as_missingA float or [min, max] range of values to treat as missing in the model output.
strict_namesWhether to strictly enforce variable naming conventions. Defaults to True.
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.
Behaviour depends on what's provided: year, month, day, depth.
vertical=True, OceanVal interpolates to all available depths; otherwise only the top 5 m is used.start to end.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.
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.
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.
Once matched, compute statistics and generate plots with oceanval.validate, run from the same directory as the matchup step:
oceanval.validate()
lon_lim / lat_limLongitude / latitude limits for the validation region.
subregionsThe 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.
conciseWhether to generate a concise HTML summary page. Defaults to True.
fixed_scaleWhether to use a fixed colour scale for the seasonal plots, capping min/max to the 2nd/98th percentile. Defaults to False.
data_dir / out_dirWhere matchup data is read from / the report is written to. Default to the current directory.
pdfWhether 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.
wordWhether 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.
zipWhether 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.
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:
oceanval.validate(subregions="my_regions.nc")
The file should be set up as follows:
One variable per region, each a 2D map on a longitude/latitude grid. It is regridded to the model grid using nearest neighbour, so it does not need to match the model grid.
Grid cells in the region are 1, and all other cells are missing values. A 0 is also accepted: it is set to missing and ignored in the regional summaries. Any other value is rejected.
Each region is named in the report by the variable's long_name attribute, or by the variable name if it has no long_name.
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.
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:
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:
Equations are real, editable Word equations rather than pictures, so you can lift a formula straight into a paper or report.
Figures and tables are centred, the page is A4, and every page carries a page number.
In the combined document each page becomes a numbered chapter, so figures and tables read as 1.1, 1.2, 2.1 and so on, and cross-references in the text follow the same numbering. The single-page files number their own figures from 1, matching the HTML page they came from.
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.
Once you have validation reports for different simulations, compare them with oceanval.compare, which summarises the differences between them:
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.
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.