# 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 to download Mercator Ocean PSY4 model fields.
A slice of the Mercator Ocean PSY4V1R2 daily average model product fields
are downloaded for use in the preparation of boundary condition files containing
u & v currents, sea surface height, salinity, and temperature.
"""
import logging
import os
import shlex
import subprocess
import arrow
from nemo_nowcast import NowcastWorker, WorkerError
NAME = "download_psy4"
logger = logging.getLogger(NAME)
[docs]def main():
"""Set up and run the worker.
For command-line usage see:
:command:`python -m nowcast.workers.download_psy4 --help`
"""
worker = NowcastWorker(NAME, description=__doc__)
worker.init_cli()
worker.cli.add_date_option(
"--psy4-date",
default=arrow.now().floor("day"),
help="Date for which to download the psy4 fields.",
)
worker.run(download_psy4, success, failure)
def success(parsed_args):
ymd = parsed_args.psy4_date.format("YYYY-MM-DD")
logger.info(
f"{ymd} psy4 daily average file download complete", extra={"psy4_date": ymd}
)
msg_type = "success"
return msg_type
def failure(parsed_args):
ymd = parsed_args.psy4_date.format("YYYY-MM-DD")
logger.critical(
f"{ymd} psy4 daily average file download failed", extra={"psy4_date": ymd}
)
msg_type = "failure"
return msg_type
def download_psy4(parsed_args, config, *args):
## TODO: matlab script that makes boundary conditions files requires 1d
## per file, so download_psy4 needs to change to get n files instead of
## n days in one file if we want to run for more than 1d
ymd = parsed_args.psy4_date.format("YYYY-MM-DD")
logger.info(
f"downloading psy4v1r2 daily average fields file for {ymd} run",
extra={"psy4_date": ymd},
)
download_config = config["open boundaries"]["download"]
user, password = os.environ["MOTU_CREDENTIALS"].split(":")
min_lon, max_lon = download_config["lon range"]
min_lat, max_lat = download_config["lat range"]
min_depth, max_depth = download_config["depth range"]
forecast_days = download_config["forecast days"]
start_ymdhms = parsed_args.psy4_date.format("YYYY-MM-DD 00:00:00")
end_ymdhms = parsed_args.psy4_date.shift(days=+forecast_days).format("YYYY-MM-DD 00:00:00")
cmd = (
f'{download_config["motu client python"]} -m motu-client '
f"-u {user} -p {password} "
f'-m {download_config["motu server"]} '
f'-s {download_config["motu service id"]} '
f'-d {download_config["dataset"]} '
f"-x {min_lon} -X {max_lon} -y {min_lat} -Y {max_lat} -z {min_depth} "
f'-Z {max_depth} -t "{start_ymdhms}" -T "{end_ymdhms}"'
)
cmd = " -v ".join((cmd, *(var for var in download_config["variables"])))
filename = download_config["file template"].format(
yyyymmdd=parsed_args.psy4_date.format("YYYYMMDD")
)
cmd = f'{cmd} -o {download_config["dest dir"]} -f {filename}'
sanitized_cmd = cmd.replace(f"-u {user}", "-u ***").replace(
f"-p {password}", "-p ***"
)
logger.debug(f"running command in subprocess: {sanitized_cmd}")
proc = subprocess.run(
shlex.split(cmd),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
)
try:
proc.check_returncode()
except subprocess.CalledProcessError:
logger.error(f"motu-client failed: {proc.stdout}")
raise WorkerError
for line in proc.stdout.splitlines():
# Exclude download progress
if " - " not in line and not line.endswith("%)"):
# Strip leading "HH:MM:SS [ INFO]"
logger.debug(line[17:])
checklist = {"psy4 date": ymd}
return checklist
if __name__ == "__main__":
main() # pragma: no cover