diff --git a/docs/source/conf.py b/docs/source/conf.py index 2af96ce82..f2b184903 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -4,6 +4,8 @@ https://www.sphinx-doc.org/en/master/usage/configuration.html """ +from sphinx_gallery.sorting import ExplicitOrder + # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information @@ -20,6 +22,7 @@ "sphinx.ext.napoleon", "sphinx.ext.doctest", "sphinx.ext.extlinks", + "sphinx_gallery.gen_gallery", ] # -- Options for HTML output ------------------------------------------------- @@ -61,3 +64,28 @@ # Met Office internal webserver. r"https://wwwspice/.+", ] + +# -- Sphinx gallery config ---------------------------------------------------- + +sphinx_gallery_conf = { + "plot_gallery": True, + "examples_dirs": "reference/cset_gallery/examples", # input scripts + "gallery_dirs": "reference/cset_gallery/generated", # output pages + "filename_pattern": r"\.py$", + "image_scrapers": ("matplotlib",), + "thumbnail_size": (800, 600), + "capture_repr": (), # disable capture of printed / returned output + "nested_sections": False, + "subsection_order": ExplicitOrder( + [ + "reference/cset_gallery/examples/spatial", + "reference/cset_gallery/examples/line", + "reference/cset_gallery/examples/custom", + ] + ), +} + +suppress_warnings = [ + "toc.not_included", + "toc.excluded", +] diff --git a/docs/source/index.rst b/docs/source/index.rst index 53c35565c..1b270586a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -26,6 +26,9 @@ Useful links `Source Code`_ | `Issue Tracker`_ | Releases_ | `Discussion Forum`_ + +For examples of CSET outputs, see :doc:`reference/cset_gallery/index`. + For information on how to use CSET, see :doc:`getting-started/index`. For information on getting involved as a developer, see diff --git a/docs/source/reference/cli.rst b/docs/source/reference/cli.rst index abaac2605..a2f6aea6a 100644 --- a/docs/source/reference/cli.rst +++ b/docs/source/reference/cli.rst @@ -1,5 +1,5 @@ -CLI Usage -========= +Command Line Usage (CLI) +======================== .. _cset-bake-command: diff --git a/docs/source/reference/cset_gallery/examples/README.rst b/docs/source/reference/cset_gallery/examples/README.rst new file mode 100644 index 000000000..a8e5234a2 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/README.rst @@ -0,0 +1 @@ +.. orphan diff --git a/docs/source/reference/cset_gallery/examples/custom/README.rst b/docs/source/reference/cset_gallery/examples/custom/README.rst new file mode 100644 index 000000000..2d77c89de --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/custom/README.rst @@ -0,0 +1,2 @@ +Customising CSET outputs +======================== diff --git a/docs/source/reference/cset_gallery/examples/custom/plot_surface_spatial_cutout.py b/docs/source/reference/cset_gallery/examples/custom/plot_surface_spatial_cutout.py new file mode 100644 index 000000000..0e6f3bc6e --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/custom/plot_surface_spatial_cutout.py @@ -0,0 +1,69 @@ +""" +Trim edge gridcells +=================== + +Generate spatial map of a 2D field over selected sub-region of data with domain edges trimmed. + +Spatial maps are generated using either CSET operators :py:mod:`CSET.operators.plot.spatial_pcolormesh_plot` or :py:mod:`CSET.operators.plot.spatial_contour_plot`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_spatial_plot_sequence.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- See :doc:`/reference/cset_gallery/generated/spatial/plot_surface_spatial` for general settings. +- Set ``SUBAREA_TYPE`` and ``SUBAREA_EXTENT`` to select trim widths, and use ``SUBAREA_NAME`` to control labelling. +- Example to generate spatial maps for selected sub-area of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_spatial_plot_sequence + cset bake -i "input_data_path" -o "my_output_path" + -r generic_surface_spatial_plot_sequence + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" + --METHOD="" + --SUBAREA_TYPE='gridcells' --SUBAREA_EXTENT=[3, 2, 3, 1] --SUBAREA_NAME='' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set ``SELECT_SUBAREA`` to ``True``, set ``SUBAREA_TYPE`` to ``gridcells`` and set ``SUBAREA_EXTENT``. +- Set other required configuration options on ``Diagnostics / Surface (2D) fields`` panel. + +:: + + SELECT_SUBAREA = True + SPATIAL_SURFACE_FIELD = True + SUBAREA_TYPE = 'gridcells' + SUBAREA_EXTENT = [3, 2, 3, 1] + SUBAREA_NAME = '' + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_path = "../../../../../../tests/test_data/air_temp.nc" + +# Read selected variable(s) of interest. +# Use read_cube parameters to control edge trim selection. +cube = cset_read.read_cubes( + file_path, + ["temperature_at_screen_level"], + subarea_type="gridcells", + subarea_extent=[3, 2, 3, 1], +)[0] + +# Plot single example frame using spatial_contour_plot +cset_plot.spatial_contour_plot(cube[-1]) diff --git a/docs/source/reference/cset_gallery/examples/custom/plot_surface_spatial_global_cutout.py b/docs/source/reference/cset_gallery/examples/custom/plot_surface_spatial_global_cutout.py new file mode 100644 index 000000000..6d6bd15db --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/custom/plot_surface_spatial_global_cutout.py @@ -0,0 +1,69 @@ +""" +Select lat-lon subarea +====================== + +Generate spatial map of a 2D field over selected sub-region of data. + +Spatial maps are generated using either CSET operators :py:mod:`CSET.operators.plot.spatial_pcolormesh_plot` or :py:mod:`CSET.operators.plot.spatial_contour_plot`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_spatial_plot_sequence.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- See :doc:`/reference/cset_gallery/generated/spatial/plot_surface_spatial_global` for general settings. +- Set ``SUBAREA_TYPE`` and ``SUBAREA_EXTENT`` to select sub-region, and use ``SUBAREA_NAME`` to control labelling. +- Example to generate spatial maps for selected sub-area of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_spatial_plot_sequence + cset bake -i "input_data_path" -o "my_output_path" + -r generic_surface_spatial_plot_sequence + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" + --METHOD="" + --SUBAREA_TYPE='realworld' --SUBAREA_EXTENT=[-40.0, 40.0, -20.0, 55.0] --SUBAREA_NAME='Africa' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set ``SELECT_SUBAREA`` to ``True``, choose ``SUBAREA_TYPE`` and set ``SUBAREA_EXTENT`` and ``SUBAREA_NAME``. +- Set other required configuration options on ``Diagnostics / Surface (2D) fields`` panel. + +:: + + SELECT_SUBAREA = True + SPATIAL_SURFACE_FIELD = True + SUBAREA_TYPE = 'realworld' + SUBAREA_EXTENT = [-40.0, 40.0, -20.0, 55.0] + SUBAREA_NAME = 'Africa' + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_path = "../../../../../../tests/test_data/air_temperature_global.nc" + +# Read selected variable(s) of interest. +# Use read_cube parameters to control subarea selection. +cube = cset_read.read_cube( + file_path, + ["temperature_at_screen_level"], + subarea_type="realworld", + subarea_extent=[-40.0, 40.0, -20.0, 55.0], +) + +# Plot single example frame using spatial_pcolormesh_plot +cset_plot.spatial_pcolormesh_plot(cube) diff --git a/docs/source/reference/cset_gallery/examples/line/README.rst b/docs/source/reference/cset_gallery/examples/line/README.rst new file mode 100644 index 000000000..4df0d86f6 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/line/README.rst @@ -0,0 +1,4 @@ +.. orphan + +Line plots +========== diff --git a/docs/source/reference/cset_gallery/examples/line/plot_line_histogram.py b/docs/source/reference/cset_gallery/examples/line/plot_line_histogram.py new file mode 100644 index 000000000..7f69061a0 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/line/plot_line_histogram.py @@ -0,0 +1,65 @@ +""" +Histogram plot +============== + +Generate histogram of region-averaged field. + +Line are generated using either CSET operators :py:mod:`CSET.operators.plot.plot_histogram_series`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_histogram_series.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Use ``SEQUENCE="realization"`` to generate one histogram for all times. +- Use ``SEQUENCE="time"`` for a histogram at each output time. +- Example to generate full domain histogram of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_histogram_series + cset bake -i "input_data_path" ["input_data_path2" "input_data_path3" "..."] -o "my_output_path" + -r generic_surface_histogram_series.yaml + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" "my_model_label2" "my_model_label3" "..." + --SEQUENCE="realization" + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel. +- Set ``HISTOGRAM_SURFACE_FIELD_SEQUENCE=False`` to generate one histogram for all times. +- Set ``HISTOGRAM_SURFACE_FIELD_SEQUENCE=True`` for a histogram at each output time. + +:: + + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + HISTOGRAM_SURFACE_FIELD = True + HISTOGRAM_SURFACE_FIELD_SEQUENCE = False + + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_paths = "../../../../../../tests/test_data/air_temperature_global.nc" + +# Read selected variable(s) of interest +cubes = cset_read.read_cubes(file_paths, ["temperature_at_screen_level"]) + +# Plot domain histogram +cset_plot.plot_histogram_series(cubes, sequence_coordinate="realization") diff --git a/docs/source/reference/cset_gallery/examples/line/plot_line_power_spectra.py b/docs/source/reference/cset_gallery/examples/line/plot_line_power_spectra.py new file mode 100644 index 000000000..5dd21c493 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/line/plot_line_power_spectra.py @@ -0,0 +1,69 @@ +""" +Power spectra plot +================== + +Generate power spectra of region-averaged field. + +Line are generated using either CSET operators :py:mod:`CSET.operators.plot.plot_line_series`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_power_spectrum_series.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Use ``SINGLE_PLOT=True`` to generate one plot for all times. +- Use ``SINGLE_PLOT=False`` for a plot at each output time. +- Example to generate full-domain power spectra of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_domain_mean_time_series + cset bake -i "input_data_path" ["input_data_path2" "input_data_path3" "..."] -o "my_output_path" + -r generic_surface_domain_mean_time_series + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" "my_model_label2" "my_model_label3" "..." + --SINGLE_PLOT="True" + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel. +- Set ``SPECTRUM_SURFACE_FIELD_SEQUENCE=False`` to generate spectrum for all times. +- Set ``SPECTRUM_SURFACE_FIELD_SEQUENCE=True`` for spectrum at each output time. + +:: + + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + SPECTRUM_SURFACE_FIELD = True + SPECTRUM_SURFACE_FIELD_SEQUENCE = False + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.plot as cset_plot +import CSET.operators.power_spectrum as cset_spectra +import CSET.operators.read as cset_read + +# Set path to input data +file_paths = "../../../../../../tests/test_data/air_temperature_global.nc" + +# Read selected variable(s) of interest +cubes = cset_read.read_cubes(file_paths, ["temperature_at_screen_level"]) + +# Compute domain power spectrum +spectra = cset_spectra.calculate_power_spectrum(cubes) + +# Plot power spectrum as line plot +cset_plot.plot_line_series( + spectra, series_coordinate="physical_wavenumber", single_plot=True +) diff --git a/docs/source/reference/cset_gallery/examples/line/plot_line_timeseries.py b/docs/source/reference/cset_gallery/examples/line/plot_line_timeseries.py new file mode 100644 index 000000000..40f695d6b --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/line/plot_line_timeseries.py @@ -0,0 +1,68 @@ +""" +Time series plot +================ + +Generate time series of region-averaged field. + +Line are generated using either CSET operators :py:mod:`CSET.operators.plot.plot_line_series`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_domain_mean_time_series.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Example to generate full-domain spatial maps of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_domain_mean_time_series + cset -v bake -i "input_data_path" ["input_data_path2" "input_data_path3" "..."] -o "my_output_path" + -r generic_surface_domain_mean_time_series.yaml + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" "my_model_label2" "my_model_label3" "..." + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel: + +:: + + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + TIMESERIES_SURFACE_FIELD = True + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.collapse as cset_collapse +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_paths = [ + "../../../../../../tests/test_data/long_forecast_air_temp_fcst_1.nc", + "../../../../../../tests/test_data/long_forecast_air_temp_fcst_a.nc", +] + +# Read selected variable(s) of interest +cubes = cset_read.read_cubes( + file_paths, ["temperature_at_screen_level"], model_names=["model_1", "model_a"] +) + +# Collapse input data over selected dimensions +collapsed_cubes = cset_collapse.collapse( + cubes, ["grid_latitude", "grid_longitude"], "MEAN" +) + +# Plot domain mean time series +cset_plot.plot_line_series(collapsed_cubes) diff --git a/docs/source/reference/cset_gallery/examples/line/plot_line_vertical.py b/docs/source/reference/cset_gallery/examples/line/plot_line_vertical.py new file mode 100644 index 000000000..07bdf9020 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/line/plot_line_vertical.py @@ -0,0 +1,63 @@ +""" +Vertical profile plot +===================== + +Generate vertical profile of region-averaged field. + +Line are generated using either CSET operators :py:mod:`CSET.operators.plot.plot_vertical_line_series`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_level_domain_mean_vertical_profile_series.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Example to generate domain mean vertical profiles of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_level_domain_mean_vertical_profile_series + cset -v bake -i "input_data_path" ["input_data_path2" "input_data_path3" "..."] -o "my_output_path" + -r generic_level_domain_mean_vertical_profile_series + --VARNAME="air_temperature" + --MODEL_NAME="my_model_label" "my_model_label2" "my_model_label3" "..." + --LEVELTYPE="pressure" + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel: + +:: + + PRESSURE_LEVEL_FIELDS = ['air_temperature', , ...] + PRESSURE_LEVELS = ['1000','850', , ...] + PROFILE_PLEVEL = True + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.collapse as cset_collapse +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_paths = "../../../../../../tests/test_data/transect_out_umpl.nc" + +# Read selected variable(s) of interest +cubes = cset_read.read_cubes(file_paths, ["air_temperature"]) + +# Collapse input data over selected dimensions +collapsed_cubes = cset_collapse.collapse(cubes, ["longitude", "time"], "MEAN") + +# Plot domain mean time series +cset_plot.plot_vertical_line_series(collapsed_cubes, series_coordinate="pressure") diff --git a/docs/source/reference/cset_gallery/examples/spatial/README.rst b/docs/source/reference/cset_gallery/examples/spatial/README.rst new file mode 100644 index 000000000..702349758 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/spatial/README.rst @@ -0,0 +1,4 @@ +.. orphan + +Spatial plots +============= diff --git a/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial.py b/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial.py new file mode 100644 index 000000000..4967b9465 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial.py @@ -0,0 +1,58 @@ +""" +Regional spatial plot +===================== + +Generate spatial map of a 2D field (regional data example). + +Spatial maps are generated using either CSET operators :py:mod:`CSET.operators.plot.spatial_pcolormesh_plot` or :py:mod:`CSET.operators.plot.spatial_contour_plot`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_spatial_plot_sequence.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Example to generate full-domain spatial maps of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_spatial_plot_sequence + cset bake -i "input_data_path" -o "my_output_path" + -r generic_surface_spatial_plot_sequence + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" + --METHOD="" + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel: + +:: + + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + SPATIAL_SURFACE_FIELD = True + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_path = "../../../../../../tests/test_data/air_temp.nc" + +# Read selected variable(s) of interest +cube = cset_read.read_cubes(file_path, ["temperature_at_screen_level"])[0] + +# Plot single example frame (final time output only, using spatial_contour_plot) +cset_plot.spatial_contour_plot(cube[-1]) diff --git a/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial_difference.py b/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial_difference.py new file mode 100644 index 000000000..f1a756b9b --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial_difference.py @@ -0,0 +1,75 @@ +""" +Spatial difference plot +======================= + +Generate spatial map of a 2D field difference (regional data example). + +Differences are calculated using the CSET operator :py:mod:`CSET.operators.misc.difference`. +Spatial maps are generated using either CSET operators :py:mod:`CSET.operators.plot.spatial_pcolormesh_plot` or :py:mod:`CSET.operators.plot.spatial_contour_plot`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_spatial_difference.yaml`` + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Example to generate full-domain spatial maps of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_spatial_difference + cset bake -i "input_data_path_1" "input_data_path_2" -o "my_output_path" + -r generic_surface_spatial_difference + --VARNAME="air_temperature" + --BASE_MODEL="Model 1" + --OTHER_MODEL="Model 2" + --MODEL_NAME="my_model_label" + --METHOD="" + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel: + +:: + + SURFACE_FIELDS = ['air_temperature', , ...] + SPATIAL_DIFFERENCE_SURFACE_FIELD = True + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.constraints as cset_constrain +import CSET.operators.misc as cset_misc +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_path = [ + "/home/users/mike.bush/repos/CSET/tests/test_data/air_temp.nc", + "/home/users/mike.bush/repos/CSET//tests/test_data/air_temp_a.nc", +] + +# Generating constraints that Iris can understand that can be used later in the code +constraint_nomethods = cset_constrain.generate_cell_methods_constraint([]) +constraint_var = cset_constrain.generate_var_constraint("air_temperature") +both_constraints = cset_constrain.combine_constraints( + constraint=constraint_nomethods, additional_constraint_1=constraint_var +) + +# Read selected variable(s) of interest +cubes = cset_read.read_cubes(file_path, constraint=both_constraints) + +# +diff = cset_misc.difference(cubes) + +# Plot single example frame (final time output only, using spatial_contour_plot) +cset_plot.spatial_contour_plot(diff[-1]) diff --git a/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial_global.py b/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial_global.py new file mode 100644 index 000000000..ffedff952 --- /dev/null +++ b/docs/source/reference/cset_gallery/examples/spatial/plot_surface_spatial_global.py @@ -0,0 +1,58 @@ +""" +Global spatial plot +=================== + +Generate spatial map of a 2D field (global data example). + +Spatial maps are generated using either CSET operators :py:mod:`CSET.operators.plot.spatial_pcolormesh_plot` or :py:mod:`CSET.operators.plot.spatial_contour_plot`. + +General functionality is provided using :doc:`CSET recipe ` ``generic_surface_spatial_plot_sequence.yaml``. + + +A) Using *cset bake* on the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Access recipe file using ``cset cookbook``. +- Set required recipe inputs on command-line (or as environment variables for greater flexibility). +- Example to generate full-domain spatial maps of ``VARNAME`` for all output times: + +.. code-block:: + + cset cookbook generic_surface_spatial_plot_sequence + cset bake -i "input_data_path" -o "my_output_path" + -r generic_surface_spatial_plot_sequence + --VARNAME="temperature_at_screen_level" + --MODEL_NAME="my_model_label" + --METHOD="" + --SUBAREA_TYPE='None' --SUBAREA_EXTENT='None' --SUBAREA_NAME='None' + [-s STYLE_FILE] [--plot-resolution PLOT_RESOLUTION] [--skip-write] + + +B) Configuring the *cset_workflow* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Update workflow configuration settings via ``rose edit`` GUI or in ``rose-suite.conf`` file. +- Complete ``General setup options`` and ``Cycling and Model options`` details - see :doc:`/usage/workflow-configure`. +- Set required configuration options on ``Diagnostics / Surface (2D) fields`` panel: + +:: + + SURFACE_FIELDS = ['temperature_at_screen_level', , ...] + SPATIAL_SURFACE_FIELD = True + + +C) Example python code +^^^^^^^^^^^^^^^^^^^^^^ +""" + +import CSET.operators.plot as cset_plot +import CSET.operators.read as cset_read + +# Set path to input data +file_path = "../../../../../../tests/test_data/air_temperature_global.nc" + +# Read selected variable(s) of interest +cube = cset_read.read_cube(file_path, ["temperature_at_screen_level"]) + +# Plot single example frame using spatial_pcolormesh_plot +cset_plot.spatial_pcolormesh_plot(cube) diff --git a/docs/source/reference/cset_gallery/index.rst b/docs/source/reference/cset_gallery/index.rst new file mode 100644 index 000000000..898800ffb --- /dev/null +++ b/docs/source/reference/cset_gallery/index.rst @@ -0,0 +1,16 @@ +:orphan: + + +CSET Gallery +============ + +This gallery demonstrates example CSET functionality. + +The gallery is divided into sections as described below. All entries show the code used to produce the example plot. Additionally there are links to download the code directly as source or as part of a jupyter notebook, these links are at the bottom of the page. + +Examples use test_data provided with CSET release. + +.. toctree:: + :maxdepth: 1 + + generated/index diff --git a/docs/source/reference/index.rst b/docs/source/reference/index.rst index 642b8314c..e63eae3f5 100644 --- a/docs/source/reference/index.rst +++ b/docs/source/reference/index.rst @@ -8,6 +8,7 @@ components. :maxdepth: 1 glossary + cset_gallery/index cli recipe-format operators diff --git a/pyproject.toml b/pyproject.toml index 721c8b27a..129411f27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,7 @@ src = ["src", "test"] [tool.ruff.lint] extend-select = ["B", "D", "I"] +per-file-ignores = {"docs/source/reference/cset_gallery/examples/**/*.py" = ["D"]} [tool.ruff.lint.pydocstyle] convention = "numpy" diff --git a/src/CSET/operators/plot.py b/src/CSET/operators/plot.py index 051e438bd..b7d67e6dc 100644 --- a/src/CSET/operators/plot.py +++ b/src/CSET/operators/plot.py @@ -21,6 +21,7 @@ import logging import math import os +import sys from typing import Literal import cartopy.crs as ccrs @@ -74,6 +75,11 @@ ############################ +def in_sphinx_gallery(): + """Test if running plot code in sphinx-gallery context.""" + return "sphinx_gallery" in sys.modules + + def _append_to_plot_index(plot_index: list) -> list: """Add plots into the plot index, returning the complete plot index.""" with open("meta.json", "r+t", encoding="UTF-8") as fp: @@ -690,9 +696,10 @@ def _plot_and_save_spatial_plot( logging.debug("Set colorbar ticks and labels.") # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved spatial plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved spatial plot to %s", filename) + plt.close(fig) def _plot_and_save_postage_stamp_spatial_plot( @@ -815,10 +822,10 @@ def _plot_and_save_postage_stamp_spatial_plot( # Overall figure title. fig.suptitle(title, fontsize=16) - - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved contour postage stamp plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved contour postage stamp plot to %s", filename) + plt.close(fig) def _plot_and_save_line_series( @@ -932,9 +939,10 @@ def _plot_and_save_line_series( ax.legend(handles=handles, loc="best", ncol=1, frameon=False, fontsize=16) # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved line plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved line plot to %s", filename) + plt.close(fig) def _plot_and_save_line_power_spectrum_series( @@ -1057,9 +1065,10 @@ def _plot_and_save_line_power_spectrum_series( ax.legend(handles=handles, loc="best", ncol=1, frameon=False, fontsize=16) # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved line plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved line plot to %s", filename) + plt.close(fig) def _plot_and_save_vertical_line_series( @@ -1199,9 +1208,10 @@ def _plot_and_save_vertical_line_series( ax.legend(handles=handles, loc="best", ncol=1, frameon=False, fontsize=16) # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved line plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved line plot to %s", filename) + plt.close(fig) def _plot_and_save_scatter_plot( @@ -1273,9 +1283,10 @@ def _plot_and_save_scatter_plot( ax.autoscale() # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved scatter plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved scatter plot to %s", filename) + plt.close(fig) def _plot_and_save_vector_plot( @@ -1383,9 +1394,10 @@ def _plot_and_save_vector_plot( iplt.quiver(cube_u[::step, ::step], cube_v[::step, ::step], pivot="middle") # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved vector plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved vector plot to %s", filename) + plt.close(fig) def _plot_and_save_histogram_series( @@ -1491,9 +1503,10 @@ def _plot_and_save_histogram_series( ax.legend(loc="best", ncol=1, frameon=False, fontsize=16) # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved histogram plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved histogram plot to %s", filename) + plt.close(fig) def _plot_and_save_postage_stamp_histogram_series( @@ -1550,10 +1563,10 @@ def _plot_and_save_postage_stamp_histogram_series( # Overall figure title. fig.suptitle(title, fontsize=16) - - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved histogram postage stamp plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved histogram postage stamp plot to %s", filename) + plt.close(fig) def _plot_and_save_postage_stamps_in_single_plot_histogram_series( @@ -1662,9 +1675,10 @@ def _plot_and_save_scattermap_plot( cbar.set_label(label=f"{cube.name()} ({cube.units})", size=20) # Save plot. - fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) - logging.info("Saved geographical scatter plot to %s", filename) - plt.close(fig) + if not in_sphinx_gallery(): + fig.savefig(filename, bbox_inches="tight", dpi=_get_plot_resolution()) + logging.info("Saved geographical scatter plot to %s", filename) + plt.close(fig) def _spatial_plot( @@ -1715,7 +1729,7 @@ def _spatial_plot( TypeError If the cube isn't a single cube. """ - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", cube.name()) # Ensure we've got a single cube. cube = check_single_cube(cube) @@ -1887,8 +1901,8 @@ def spatial_pcolormesh_plot( def spatial_multi_pcolormesh_plot( cube: iris.cube.Cube, - overlay_cube: iris.cube.Cube, - contour_cube: iris.cube.Cube, + overlay_cube: iris.cube.Cube | None = None, + contour_cube: iris.cube.Cube | None = None, filename: str = None, sequence_coordinate: str = "time", stamp_coordinate: str = "realization", @@ -1916,14 +1930,15 @@ def spatial_multi_pcolormesh_plot( Iris cube of the data to plot. It should have two spatial dimensions, such as lat and lon, and may also have a another two dimension to be plotted sequentially and/or as postage stamp plots. - overlay_cube: Cube + overlay_cube: Cube, optional Iris cube of the data to plot as an overlay on top of basis cube. It should have two spatial dimensions, such as lat and lon, and may also have a another two dimension to be plotted sequentially and/or as postage stamp plots. This is likely to be a masked cube in order not to hide the underlying basis cube. - contour_cube: Cube + If not provided, output plot generated without overlay cube. + contour_cube: Cube, optional Iris cube of the data to plot as a contour overlay on top of basis cube and overlay_cube. It should have two spatial dimensions, such as lat and lon, and may also have a another two dimension to be - plotted sequentially and/or as postage stamp plots. + plotted sequentially and/or as postage stamp plots. If not provided, output plot generated without contours. filename: str, optional Name of the plot to write, used as a prefix for plot sequences. Defaults to the recipe name. @@ -2003,7 +2018,7 @@ def plot_line_series( If the cube isn't a Cube or CubeList. """ # Ensure we have a name for the plot file. - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", iter_maybe(cube)[0].name()) num_models = get_num_models(cube) @@ -2201,7 +2216,7 @@ def plot_vertical_line_series( If the cube isn't a Cube or CubeList. """ # Ensure we have a name for the plot file. - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", iter_maybe(cubes)[0].name()) cubes = iter_maybe(cubes) # Initialise empty list to hold all data from all cubes in a CubeList @@ -2429,7 +2444,7 @@ def qq_plot( ) # Ensure we have a name for the plot file. - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", "qq") title = f"{recipe_title}" if filename is None: @@ -2508,7 +2523,7 @@ def scatter_plot( raise ValueError("cube_y must be 1D.") # Ensure we have a name for the plot file. - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", "Scatter") title = f"{recipe_title}" if filename is None: @@ -2537,7 +2552,7 @@ def vector_plot( **kwargs, ) -> iris.cube.CubeList: """Plot a vector plot based on the input u and v components.""" - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", "Vector_plot") # Cubes must have a matching sequence coordinate. try: @@ -2634,7 +2649,7 @@ def plot_histogram_series( TypeError If the cube isn't a Cube or CubeList. """ - recipe_title = get_recipe_metadata().get("title", "Untitled") + recipe_title = get_recipe_metadata().get("title", "Histogram") cubes = iter_maybe(cubes) # Ensure we have a name for the plot file. diff --git a/tests/conftest.py b/tests/conftest.py index b0ae5143d..7a66ae644 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -298,7 +298,7 @@ def long_forecast_multi_day(long_forecast_multi_day_read_only): def long_forecast_many_cubes_read_only(): """Get long_forecast_may_cubes to run tests on. It is NOT safe to modify.""" return read.read_cubes( - "tests/test_data/long_forecast_air_temp_fcst_*.nc", "air_temperature" + "tests/test_data/long_forecast_air_temp_fcst_[123].nc", "air_temperature" ) diff --git a/tests/operators/test_plot.py b/tests/operators/test_plot.py index 2126119ab..5e73efd36 100644 --- a/tests/operators/test_plot.py +++ b/tests/operators/test_plot.py @@ -192,18 +192,43 @@ def test_spatial_contour_plot(cube, tmp_working_dir): def test_contour_plot_sequence(cube, tmp_working_dir): """Plot sequence of contour plots.""" plot.spatial_contour_plot(cube, sequence_coordinate="time") - assert Path("untitled_20220921030000.png").is_file() - assert Path("untitled_20220921040000.png").is_file() - assert Path("untitled_20220921050000.png").is_file() + assert Path("air_temperature_20220921030000.png").is_file() + assert Path("air_temperature_20220921040000.png").is_file() + assert Path("air_temperature_20220921050000.png").is_file() def test_spatial_multi_variable_plot(cube, tmp_working_dir): """Plot spatial plot with multiple input variables.""" # Here assume cube provides cube, overlay_cube and contour_cube. plot.spatial_multi_pcolormesh_plot(cube, cube, cube, sequence_coordinate="time") - assert Path("untitled_20220921030000.png").is_file() - assert Path("untitled_20220921040000.png").is_file() - assert Path("untitled_20220921050000.png").is_file() + assert Path("air_temperature_20220921030000.png").is_file() + assert Path("air_temperature_20220921040000.png").is_file() + assert Path("air_temperature_20220921050000.png").is_file() + + +def test_spatial_multi_variable_plot_nolayers(cube, tmp_working_dir): + """Plot spatial plot with single input cube only.""" + # Call spatial_multi_pcolormesh_plot with only cube as input. + plot.spatial_multi_pcolormesh_plot(cube[0], sequence_coordinate="time") + assert Path("air_temperature_20220921030000.png").is_file() + + +def test_spatial_multi_variable_plot_overlay_only(cube, tmp_working_dir): + """Plot spatial plot with based and overlay cube only.""" + # Call spatial_multi_pcolormesh_plot with only cube and overlay_cube. + plot.spatial_multi_pcolormesh_plot( + cube[0], overlay_cube=cube[0], sequence_coordinate="time" + ) + assert Path("air_temperature_20220921030000.png").is_file() + + +def test_spatial_multi_variable_plot_contour_only(cube, tmp_working_dir): + """Plot spatial plot with based and contour cube only.""" + # Call spatial_multi_pcolormesh_plot with only cube and contour_cube. + plot.spatial_multi_pcolormesh_plot( + cube[0], contour_cube=cube[0], sequence_coordinate="time" + ) + assert Path("air_temperature_20220921030000.png").is_file() @pytest.mark.slow @@ -247,7 +272,7 @@ def test_postage_stamp_contour_plot(ensemble_cube, tmp_working_dir): # Get a single time step. ensemble_cube_3d = next(ensemble_cube.slices_over("time")) plot.spatial_contour_plot(ensemble_cube_3d) - assert Path("untitled_20221201100000.png").is_file() + assert Path("air_temperature_20221201100000.png").is_file() def test_postage_stamp_contour_plot_sequence_coord_check(cube, tmp_working_dir): @@ -282,17 +307,17 @@ def test_spatial_pcolormesh_levels(cube, tmp_working_dir, caplog): message_matchB = True assert message_matchA assert message_matchB - assert Path("untitled_20220921030000.png").is_file() - assert Path("untitled_20220921040000.png").is_file() - assert Path("untitled_20220921050000.png").is_file() + assert Path("surface_microphysical_rainfall_rate_20220921030000.png").is_file() + assert Path("surface_microphysical_rainfall_rate_20220921040000.png").is_file() + assert Path("surface_microphysical_rainfall_rate_20220921050000.png").is_file() def test_pcolormesh_plot_sequence(cube, tmp_working_dir): """Plot sequence of pcolormesh plots.""" plot.spatial_pcolormesh_plot(cube, sequence_coordinate="time") - assert Path("untitled_20220921030000.png").is_file() - assert Path("untitled_20220921040000.png").is_file() - assert Path("untitled_20220921050000.png").is_file() + assert Path("air_temperature_20220921030000.png").is_file() + assert Path("air_temperature_20220921040000.png").is_file() + assert Path("air_temperature_20220921050000.png").is_file() def test_pcolormesh_plot_global(global_cube, caplog, tmp_working_dir): @@ -311,7 +336,7 @@ def test_postage_stamp_pcolormesh_plot(ensemble_cube, tmp_working_dir): # Get a single time step. ensemble_cube_3d = next(ensemble_cube.slices_over("time")) plot.spatial_pcolormesh_plot(ensemble_cube_3d) - assert Path("untitled_20221201100000.png").is_file() + assert Path("air_temperature_20221201100000.png").is_file() def test_postage_stamp_pcolormesh_plot_sequence_coord_check(cube, tmp_working_dir): @@ -350,13 +375,13 @@ def test_plot_line_series(cube, tmp_working_dir): """Save a line series plot.""" cube = collapse.collapse(cube, ["grid_latitude", "grid_longitude"], "MEAN") plot.plot_line_series(cube) - assert Path("untitled_20220921030000_20220921050000.png").is_file() + assert Path("air_temperature_20220921030000_20220921050000.png").is_file() def test_plot_power_spectrum(power_spectrum_cube_readonly, tmp_working_dir): """Save a power_spectrum plot using line series plot.""" plot.plot_line_series(power_spectrum_cube_readonly, series_coordinate="frequency") - assert Path("untitled_20220601000000.png").is_file() + assert Path("power_spectra_20220601000000.png").is_file() def test_plot_line_series_with_filename(cube, tmp_working_dir): @@ -594,8 +619,8 @@ def test_plot_vertical_line_series(vertical_profile_cube, tmp_working_dir): plot.plot_vertical_line_series( vertical_profile_cube, series_coordinate="pressure", sequence_coordinate="time" ) - assert Path("untitled_20240116060000.png").is_file() - assert Path("untitled_20240116090000.png").is_file() + assert Path("air_temperature_20240116060000.png").is_file() + assert Path("air_temperature_20240116090000.png").is_file() def test_plot_vertical_line_series_with_filename( @@ -663,8 +688,8 @@ def test_plot_vertical_line_series_ensemble(vertical_profile_cube, tmp_working_d plot.plot_vertical_line_series( cubes, series_coordinate="pressure", sequence_coordinate="time" ) - assert Path("untitled_20240116060000.png").is_file() - assert Path("untitled_20240116090000.png").is_file() + assert Path("air_temperature_20240116060000.png").is_file() + assert Path("air_temperature_20240116090000.png").is_file() def test_plot_histogram_no_sequence_coordinate(histogram_cube, tmp_working_dir): @@ -808,7 +833,7 @@ def test_scatter_plot(cube, vertical_profile_cube, tmp_working_dir): cube_y, cube_x, ) - assert Path("untitled.png").is_file() + assert Path("scatter.png").is_file() def test_scatter_plot_with_filename(cube, vertical_profile_cube, tmp_working_dir): @@ -832,7 +857,7 @@ def test_scatter_plot_no_one_to_one_line(cube, vertical_profile_cube, tmp_workin cube_x, one_to_one=False, ) - assert Path("untitled.png").is_file() + assert Path("scatter.png").is_file() def test_scatter_plot_too_many_x_dimensions( @@ -984,7 +1009,7 @@ def test_qq_plot(cube, tmp_working_dir): percentiles=[0, 50, 100], model_names=["a", "b"], ) - assert Path("untitled.png").is_file() + assert Path("qq.png").is_file() def test_qq_plot_named(cube, tmp_working_dir): @@ -1049,7 +1074,7 @@ def test_qq_plot_different_data_shape_regrid(cube, tmp_working_dir): percentiles=[0, 50, 100], model_names=["a", "b"], ) - assert Path("untitled.png").is_file() + assert Path("qq.png").is_file() def test_qq_plot_grid_staggering_regrid(cube, tmp_working_dir): @@ -1064,4 +1089,4 @@ def test_qq_plot_grid_staggering_regrid(cube, tmp_working_dir): percentiles=[0, 50, 100], model_names=["a", "b"], ) - assert Path("untitled.png").is_file() + assert Path("qq.png").is_file() diff --git a/tests/test_data/air_temp_a.nc b/tests/test_data/air_temp_a.nc new file mode 100644 index 000000000..3b22c4739 Binary files /dev/null and b/tests/test_data/air_temp_a.nc differ diff --git a/tests/test_data/long_forecast_air_temp_fcst_a.nc b/tests/test_data/long_forecast_air_temp_fcst_a.nc new file mode 100644 index 000000000..2ff9846d4 Binary files /dev/null and b/tests/test_data/long_forecast_air_temp_fcst_a.nc differ