Source code for swami

# ---------------------------------------------------------------------
# – Project : SWAMI
# – Customer : N/A
# ---------------------------------------------------------------------
# – Author : Daniel Lubián Arenillas
# – Issue : 1.0
# – Date : 2021-03-31
# – Purpose : Python functions for SWAMI
# - Component : swami python library
# ---------------------------------------------------------------------
# – © Copyright Deimos Space SLU, 2021
# – All rights reserved
# ---------------------------------------------------------------------

import os
import subprocess
import tempfile
from pathlib import Path
from typing import NamedTuple

__version__ = "swami-1.0.rc"

_PWD = Path(__file__).absolute().parent
_NONE = -999999

_PATH_DEFAULT_EXEC = _PWD / "swami.x"
_PATH_DEFAULT_DATA = _PWD / "data"


[docs]class MCMOutput(NamedTuple): # output dens: float temp: float wmm: float d_H: float d_He: float d_O: float d_N2: float d_O2: float d_N: float tinf: float dens_unc: float dens_std: float temp_std: float xwind: float ywind: float xwind_std: float ywind_std: float # input alti: float lati: float longi: float loct: float doy: float f107: float f107m: float kp1: float kp2: float
[docs]class MCM: """MCM Model wrapper. Args: exec_swami (os.PathLike, optional): Path to the executable. Defaults to the one included. path_to_data (os.PathLike, optional): Path to the data. Defaults to the included package. """ path_to_bin = _PATH_DEFAULT_EXEC path_to_data = _PATH_DEFAULT_DATA def __init__(self, exec_swami: os.PathLike = None, path_to_data: os.PathLike = None): """Initialiser Args: exec_swami (os.PathLike, optional): Path to the executable. Defaults to the one included. path_to_data (os.PathLike, optional): Path to the data. Defaults to the included package. """ if exec_swami is not None: self.path_to_bin = exec_swami if path_to_data is not None: self.path_to_data = path_to_data @staticmethod def _generate_nml_from_dict(d: dict, name: str = "input"): """Generate a namelist file from a dictionary Args: d (dict): Dictionary name (str, optional): Name of the namelist. Defaults to "input". """ def logical(b: bool): return ".true." if b else ".false." with tempfile.NamedTemporaryFile(prefix="swami_", delete=False, suffix=".inp", mode="r+") as nml: nml.write(f"&{name}\n") for k, v in d.items(): # Booleans if isinstance(v, bool): nml.write(f"{k} = {logical(v):s}\n") # Strings elif isinstance(v, str): nml.write(f"{k} = '{v:s}'\n") # Floats elif isinstance(v, float): nml.write(f"{k} = {v:23.16e}\n") # Integers elif isinstance(v, int): nml.write(f"{k} = {v:d}\n") # Others else: nml.write(f"{k} = {v}\n") # nml.write("\\") nml.write("&end\n") nml.close() return nml.name @staticmethod def _read_output_file(outfile: os.PathLike): """Read output file from swami.x Args: outfile (os.PathLike): Path to output file Returns: dict: Dictionary with values per model """ with open(outfile, mode="r") as f: res = {} for line in f: var, val = line.split("=") val = float(val) val = None if val == _NONE else val res[var.strip()] = val return MCMOutput(**res)
[docs] def run(self, altitude: float, day_of_year: float, local_time: float, latitude: float, longitude: float, f107: float, f107m: float, kp1: float, kp2: float, get_uncertainty: bool = False, get_winds: bool = False ) -> MCMOutput: """Run the model Returns a MCMOutput object with the results as attributes. Args: altitude (float): Altitude in km day_of_year (float): Day of the year [0-366] local_time (float): Local time, h [0-24] latitude (float): Latitude, deg [-90 to 90] longitude (float): Longitude, deg [0-360] f107 (float): F10.7, instantaneous flux at (t - 24hr) f107m (float): F10.7, average of the last 81 days kp1 (float): Kp, delayed by 3 hours kp2 (float): Kp, mean of previous 24 hours get_uncertainty (bool, optional): Uncertainties will be returned. Defaults to False. get_winds (bool, optional): Winds will be returned. Defaults to False. Returns: MCMOutput: NamedTuple with the results """ # Make temporary output file output_file = tempfile.NamedTemporaryFile( delete=False, suffix=".out", prefix="swami_", mode="r+") # Sanitize paths data_dtm = str(self.path_to_data) data_dtm = data_dtm + "/" if data_dtm[-1] != "/" else data_dtm data_um = str(os.path.join(self.path_to_data, "um")) data_um = data_um + "/" if data_um[-1] != "/" else data_um # Create dictionary with input parameters input_dict = { "altitude": float(altitude), "day_of_year": float(day_of_year), "local_time": float(local_time), "latitude": float(latitude), "longitude": float(longitude), "f107": float(f107), "f107m": float(f107m), "kp1": float(kp1), "kp2": float(kp2), "b_unc_std": bool(get_uncertainty), "b_winds": bool(get_winds), "data_dtm": data_dtm, "data_um": data_um, "output_file": str(output_file.name) } # Generate input file input_file = self._generate_nml_from_dict(input_dict) # Run command cmd = [str(self.path_to_bin), input_file] proc = subprocess.run(cmd, check=True) # Read output file out = self._read_output_file(output_file.name) # Delete temporary files os.unlink(input_file) os.unlink(output_file.name) return out