210 lines
7.9 KiB
Python
210 lines
7.9 KiB
Python
from typing import Union
|
|
import re
|
|
import xml.etree.ElementTree as eT
|
|
import astropy.units as u
|
|
from ..lib.logger import logger
|
|
import difflib
|
|
import os
|
|
import numpy as np
|
|
|
|
|
|
class Entry(object):
|
|
"""
|
|
A class used to represent a configuration entry.
|
|
Taken from ExoSim (https://github.com/ExoSim/ExoSimPublic)
|
|
"""
|
|
val: Union[str, bool, u.Quantity]
|
|
|
|
def __init__(self, **kwargs):
|
|
for key, value in kwargs.items():
|
|
self.__setattr__(key, value)
|
|
|
|
def __call__(self):
|
|
return self.val if hasattr(self, "val") else None
|
|
|
|
def parse(self, xml: eT.Element):
|
|
"""
|
|
Parse attributes of a XML element
|
|
|
|
Parameters
|
|
----------
|
|
xml : xml.etree.ElementTree.Element
|
|
XML element to parse the attributes from
|
|
"""
|
|
# Copy the XML attributes to object attributes
|
|
for attrib in xml.attrib.keys():
|
|
setattr(self, attrib, xml.attrib[attrib])
|
|
# parse units
|
|
attribs = list(xml.attrib.keys())
|
|
units = list(filter(re.compile(".*_unit$").match, attribs))
|
|
for unit in units:
|
|
# enable imperial units
|
|
u.imperial.enable()
|
|
var = unit.replace("_unit", "")
|
|
if hasattr(self, var):
|
|
try:
|
|
val = u.Quantity(list(map(float, getattr(self, var).split(','))), getattr(self, unit))
|
|
if len(val) == 1:
|
|
val = val[0]
|
|
setattr(self, var, val)
|
|
except (ValueError, LookupError):
|
|
logger.error("unable to convert units in entry '" + xml.tag + "': " + getattr(self, var) + " " +
|
|
getattr(self, unit), exit_=False)
|
|
# Convert boolean values
|
|
if hasattr(self, "val") and type(self.val) == str and self.val.lower() in ["false", "true"]:
|
|
self.val = (self.val.lower() == "true")
|
|
|
|
def check_quantity(self, name: str, unit: u.Unit, use_default: bool = True) -> Union[None, str]:
|
|
"""
|
|
Check a parameter as type quantity
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the parameter to be checked.
|
|
unit : Quantity
|
|
The default quantity to be used for conversion and equality checking.
|
|
use_default : bool
|
|
Use the given unit as default unit and try to convert strings to Quantities with this unit.
|
|
|
|
Returns
|
|
-------
|
|
mes : Union[None, str]
|
|
The error message of the check. This will be None if the check was successful.
|
|
"""
|
|
u.imperial.enable()
|
|
if not hasattr(self, name):
|
|
return "Parameter '" + name + "' not found."
|
|
attr = getattr(self, name)
|
|
if type(attr) != u.Quantity:
|
|
if unit == u.dimensionless_unscaled or use_default:
|
|
try:
|
|
self.__setattr__(name, float(attr) * unit)
|
|
except ValueError:
|
|
try:
|
|
val = list(map(u.Quantity, attr.split(',')))
|
|
if len(val) == 1:
|
|
val = val[0]
|
|
else:
|
|
val = np.array(val[:(len(val)-1)] + [val[-1].value]) << val[-1].unit
|
|
self.__setattr__(name, val)
|
|
mes = self.check_quantity(name, unit, use_default)
|
|
if mes is not None:
|
|
return mes
|
|
except (ValueError, TypeError):
|
|
return "Expected parameter '" + name + "' with unit '" + unit.to_string() + \
|
|
"' but got no unit and cannot convert '" + attr + "' to a numeric value."
|
|
else:
|
|
return "Expected parameter '" + name + "' with unit '" + unit.to_string() + "' but got no unit."
|
|
elif not attr.unit.is_equivalent(unit):
|
|
if unit == u.K and attr.unit == u.Celsius:
|
|
setattr(self, name, attr.to(unit, equivalencies=u.temperature()))
|
|
setattr(self, name + '_unit', unit.to_string())
|
|
elif unit.is_equivalent(u.m) and attr.unit.is_equivalent(u.Hz):
|
|
setattr(self, name, attr.to(unit, equivalencies=u.spectral()))
|
|
setattr(self, name + '_unit', unit.to_string())
|
|
else:
|
|
return "Expected parameter '" + name + "' with unit equivalent to '" + unit.to_string() + \
|
|
"' but got unit '" + attr.unit.to_string() + "'."
|
|
return None
|
|
|
|
def check_selection(self, name: str, choices: list) -> Union[None, str]:
|
|
"""
|
|
Check a parameter against a list of possible choices. In case of a mismatch, a recommendation will be given.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the parameter to be checked.
|
|
choices : list
|
|
List of choices to be used for checking.
|
|
|
|
Returns
|
|
-------
|
|
mes : Union[None, str]
|
|
The error message of the check. This will be None if the check was successful.
|
|
"""
|
|
if not hasattr(self, name):
|
|
return "Parameter '" + name + "' not found."
|
|
attr = getattr(self, name)
|
|
if type(attr) != str:
|
|
return "Expected parameter '" + name + "' to be of type string."
|
|
if attr not in choices:
|
|
match = difflib.get_close_matches(attr, choices, 1)
|
|
if len(match) > 0:
|
|
# noinspection PyTypeChecker
|
|
return "Value '" + attr + "' not allowed for parameter '" + name + "'. Did you mean '" + \
|
|
match[0] + "'?"
|
|
else:
|
|
return "Value '" + attr + "' not allowed for parameter '" + name + "'."
|
|
return None
|
|
|
|
def check_file(self, name) -> Union[None, str]:
|
|
"""
|
|
Check a parameter to be a valid path to a file.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the parameter to be checked.
|
|
|
|
Returns
|
|
-------
|
|
mes : Union[None, str]
|
|
The error message of the check. This will be None if the check was successful.
|
|
"""
|
|
if not hasattr(self, name):
|
|
return "Parameter '" + name + "' not found."
|
|
if not os.path.isfile(getattr(self, name)):
|
|
return "File '" + getattr(self, name) + "' does not exist."
|
|
|
|
def check_path(self, name) -> Union[None, str]:
|
|
"""
|
|
Check a parameter to be a valid path to a file.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the parameter to be checked.
|
|
|
|
Returns
|
|
-------
|
|
mes : Union[None, str]
|
|
The error message of the check. This will be None if the check was successful.
|
|
"""
|
|
if not hasattr(self, name):
|
|
return "Parameter '" + name + "' not found."
|
|
if not os.path.isdir(getattr(self, name)):
|
|
return "Path '" + getattr(self, name) + "' does not exist."
|
|
|
|
def check_float(self, name) -> Union[None, str]:
|
|
"""
|
|
Check a parameter to be a floating point value
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the parameter to be checked.
|
|
|
|
Returns
|
|
-------
|
|
mes : Union[None, str]
|
|
The error message of the check. This will be None if the check was successful.
|
|
"""
|
|
if not hasattr(self, name):
|
|
return "Parameter '" + name + "' not found."
|
|
attr = getattr(self, name)
|
|
if type(attr) == float:
|
|
return None
|
|
elif type(attr) == u.Quantity:
|
|
setattr(self, name, attr.value)
|
|
elif type(attr) == str:
|
|
try:
|
|
setattr(self, name, float(attr))
|
|
except ValueError:
|
|
return "Cannot convert parameter '" + name + "' with value '" + attr + "' to a numeric value."
|
|
elif type(attr) == int:
|
|
setattr(self, name, float(attr))
|
|
else:
|
|
return "Expected parameter '" + name + "' to be numeric but got '" + type(attr) + "' instead."
|