Source code for nowcast.workers.make_plots

# Copyright 2016-2019 Doug Latornell, 43ravens
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""GoMSS NEMO nowcast worker that produces model results visualization images for
the web site from run results.
"""
import logging
import os
from pathlib import Path
import shlex
import subprocess

# **IMPORTANT**: matplotlib must be imported before anything else that uses it
# because of the matplotlib.use() call below
import matplotlib

matplotlib.use("Agg")

import arrow
import cmocean
from nemo_nowcast import NowcastWorker
import xarray

import nowcast.figures.day_avg_tracer

NAME = "make_plots"
logger = logging.getLogger(NAME)


[docs]def main(): """Set up and run the worker. For command-line usage see: :command:`python -m nowcast.workers.make_plots --help` """ worker = NowcastWorker(NAME, description=__doc__) worker.init_cli() worker.cli.add_date_option( "--run-date", default=arrow.now().floor("day"), help="Date of the run to make visualization images for.", ) worker.cli.add_argument( "--test-figure", help=""" Identifier for a single figure to do a test on. The identifier is the svg_name of the figure used in make_plots (e.g. day_avg_salinity is the svg_name of figures stored as day_avg_salinity_{yyyymmdd}.svg). The figure will be rendered in /data1/dlatornell/nowcast-sys/figures/test/{yyyymmdd}/ so that it is accessible in a browser at https://gomss.ocean.dal.ca/data1/dlatornell/nowcast-sys/figures/test/{yyyymmdd}/{svg_name}_{yyyymmdd}.svg """, ) worker.run(make_plots, success, failure)
def success(parsed_args): """ :param :py:class:`argparse.Namespace` parsed_args: :return: Nowcast system message type :rtype: str """ logger.info(f'plots for {parsed_args.run_date.format("YYYY-MM-DD")} completed') return "success" def failure(parsed_args): """ :param :py:class:`argparse.Namespace` parsed_args: :return: Nowcast system message type :rtype: str """ logger.critical(f'plots for {parsed_args.run_date.format("YYYY-MM-DD")} failed') return "failure" def make_plots(parsed_args, config, *args): """ :param :py:class:`argparse.Namespace` parsed_args: :param :py:class:`nemo_nowcast.Config` config: :return: Nowcast system checklist items :rtype: dict """ run_date = parsed_args.run_date yyyymmdd = run_date.format("YYYYMMDD") test_figure_id = parsed_args.test_figure results_archive = Path(config["results archive"]) config_name = config["runs"]["config name"] nemo_code = Path(config["runs"]["NEMO code"]) nemo_code_config = nemo_code / "CONFIG" config_dir = nemo_code_config / config_name / "EXP00" bathy_path = (config_dir / config["runs"]["bathymetry"]).resolve() bathy = xarray.open_dataset(bathy_path, mask_and_scale=False) figures = { "day_avg_salinity": { "module": nowcast.figures.day_avg_tracer, "args": [results_archive, run_date, "salt", cmocean.cm.haline, bathy], }, "day_avg_temperature": { "module": nowcast.figures.day_avg_tracer, "args": [results_archive, run_date, "temp", cmocean.cm.thermal, bathy], }, } fig_files = _render_figures(config, yyyymmdd, figures, test_figure_id) checklist = {"files": fig_files, "run date": run_date.format("YYYY-MM-DD")} return checklist def _render_figures(config, yyyymmdd, figures, test_figure_id): """ :param :py:class:`nemo_nowcast.Config` config: :param str yyyymmdd: :param dict figures: :param test_figure_id: :return: list """ fig_files = [] for svg_name, figure in figures.items(): fig_module = figure["module"] args = figure.get("args", []) kwargs = figure.get("kwargs", {}) fig_save_format = figure.get("format", "svg") image_loop_figure = figure.get("image loop", False) if test_figure_id is not None and svg_name != test_figure_id: continue logger.debug(f"starting {fig_module}") fig = fig_module.make_figure(*args, **kwargs) fig_files_dir = ( Path(config["figures"]["test path"], yyyymmdd) if test_figure_id else Path(config["figures"]["storage path"], yyyymmdd) ) fig_files_dir.mkdir(parents=True, exist_ok=True) filename = ( fig_files_dir / f"{svg_name}.{fig_save_format}" if image_loop_figure else fig_files_dir / f"{svg_name}_{yyyymmdd}.{fig_save_format}" ) fig.savefig( os.fspath(filename), facecolor=fig.get_facecolor(), bbox_inches="tight" ) logger.info(f"{filename} saved") if fig_save_format is "svg": _scour_svg_file(filename) fig_files.append(os.fspath(filename)) return fig_files def _scour_svg_file(filename): """ :param :py:class:`pathlib.Path` filename: """ logger.debug(f"starting SVG scouring of {filename}") tmpfilename = filename.with_suffix(".scour") cmd = f"scour {filename} {tmpfilename}" logger.debug(f"running subprocess: {cmd}") try: proc = subprocess.run( shlex.split(cmd), check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) except subprocess.CalledProcessError as e: logger.warning("SVG scouring failed, proceeding with unscoured figure") logger.debug(f"scour return code: {e.returncode}") if e.output: logger.debug(e.output) return logger.debug(proc.stdout) tmpfilename.rename(filename) logger.info(f"{filename} scoured") if __name__ == "__main__": main() # pragma: no cover