From 30487ca32266e965d365aadcc4e94995e01f6d4a Mon Sep 17 00:00:00 2001 From: Michi Date: Thu, 24 Apr 2014 16:51:26 +0200 Subject: [PATCH 1/7] i try to rebase the dp to the master --- .../data/tardis_default_config_definition.yml | 362 +++++ tardis/io/default_config_parser.py | 1187 +++++++++++++++++ .../tardis_configv1_artis_density_v_slice.yml | 2 +- .../data/tardis_configv1_ascii_density.yml | 2 +- ...tardis_configv1_ascii_density_uniabund.yml | 6 +- 5 files changed, 1554 insertions(+), 5 deletions(-) create mode 100644 tardis/data/tardis_default_config_definition.yml create mode 100644 tardis/io/default_config_parser.py diff --git a/tardis/data/tardis_default_config_definition.yml b/tardis/data/tardis_default_config_definition.yml new file mode 100644 index 00000000000..cbff78d9186 --- /dev/null +++ b/tardis/data/tardis_default_config_definition.yml @@ -0,0 +1,362 @@ +tardis_config_version: + property_type: string + default: None + mandatory: True + help: Version of the configuration file + + +supernova: + luminosity_requested: + property_type: quantity + mandatory: True + default: 1 solLum + help: requested output luminosity for simulation + + time_explosion: + property_type: quantity + mandatory: True + default: None + help: time since explosion + + distance: + property_type: quantity + mandatory: False + default: None + help: distance to supernova + + + +atom_data: + property_type: string + mandatory: True + help: path or filename to the Atomic Data HDF5 file + +plasma: + initial_t_inner: + property_type: quantity + mandatory: False + default: -1 K + help: > + initial temperature of the inner boundary black body. If set to -1 K + will result in automatic calculation of boundary + + + initial_t_rad: + property_type: quantity + mandatory: False + default: 10000 K + help: initial radiative temperature in all cells (if set + + disable_electron_scattering: + property_type: bool + mandatory: False + default: False + help: > + disable electron scattering process in montecarlo part - non-physical only + for tests + + ionization: + property_type: string + mandatory: True + default: None + allowed_value: nebular lte + help: ionization treatment mode + + + excitation: + property_type: string + mandatory: True + default: None + allowed_value: lte dilute-lte + help: excitation treatment mode + + radiative_rates_type: + property_type: string + mandatory: True + default: None + allowed_value: dilute-blackbody detailed + help: radiative rates treatment mode + + line_interaction_type: + property_type: string + mandatory: True + default: None + allowed_value: scatter downbranch macroatom + help: line interaction mode + + w_epsilon: + property_type: float + mandatory: False + default: 1e-10 + help: w to use when j_blues get numerically 0. - avoids numerical complications + + + nlte: + species: + property_type: list + mandatory: False + default: [] + help: > + Species that are requested to be NLTE treated in the format + ['Si 2', 'Ca 1', etc.] + + coronal_approximation: + property_type: bool + default: False + mandatory: False + help: set all jblues=0.0 + + classical_nebular: + property_type: bool + default: False + mandatory: False + help: sets all beta_sobolevs to 1 + + + +model: + structure: + property_type : container-property + type: + property_type: container-declaration + containers: ['file', 'specific'] + _file: ['filename','filetype'] + +file: ['v_inner_boundary','v_outer_boundary'] + _specific: ['specific-property'] + + + filename: + property_type: string + default: None + mandatory: True + help: file name (with path) to structure model + + filetype: + property_type: string + default: None + mandatory: True + help: file type + #### there are only a handful of types available how do we validate that #### + + + + v_inner_boundary: + property_type: quantity + default: 0 km/s + mandatory: False + help: location of the inner boundary chosen from the model + + v_outer_boundary: + property_type: quantity + default: inf km/s + mandatory: False + help: location of the inner boundary chosen from the model + + specific_property: + velocity: + property_type: quantity_range + default: None + mandatory: True + help: location of boundaries in the velocity field. There will be n-1 cells as this specifis both the inner and outer velocity components + + + density: + property_type: container-property + type: + property_type: container-declaration + containers: ['branch85_w7'] + + _branch85_w7: ['branch85_w7-property'] # list + + branch85_w7-property: + time_0: + property_type: quantity + default: 19.9999584 s + mandatory: False + help: This needs no change - DO NOT TOUCH + + density_coefficient: + property_type: float + default: 3e29 + mandatory: False + help: This needs no change - DO NOT TOUCH + + abundances: + property_type: container-property + type: + property_type: container-declaration + containers: ['file','uniform'] + _uniform: [] + _file: ['filetype','filename'] + + filename: + property_type: string + default: None + mandatory: True + help: filename + + filetype: + property_type: string + default: None + mandatory: False + help: type of abundance file to read in + + + + + + + +montecarlo: + seed: + property_type: int + default: 23111963 + mandatory: False + help: Seed for the random number generator + + no_of_packets: + property_type: int + default: None + mandatory: True + help: Seed for the random number generator + + iterations: + property_type: int + default: None + mandatory: True + help: Number of maximum iterations + + black_body_sampling: + property_type: quantity_range_sampled + default: [1 angstrom, 1e6 angstrom, 1e6 angstrom] + mandatory: False + help: Sampling of the black-body for energy packet creation (giving maximum and minimum packet frequency) + + last_no_of_packets: + property_type: int + default: 100 + mandatory: False + help: This can set the number of packets for the last run. If set to None it will remain the same as all other runs. + + no_of_virtual_packets: + property_type: int + default: 0 + mandatory: False + help: Setting the number of virtual packets for the last iteration. + + enable_reflective_inner_boundary: + property_type: bool + default: False + mandatory: False + help: > + experimental feature to enable a reflective boundary. + + inner_boundary_albedo: + property_type: float + default: 0.0 + mandatory: False + help: albedo of the reflective boundary + + convergence_strategy: + property_type : container-property + type: + property_type: container-declaration + containers: ['damped', 'specific'] + _damped: ['damping_constant'] + +damped: ['t_inner', 't_rad', 'w'] + _specific: ['damping_constant', 'threshold', 'fraction', 'hold-iterations'] + +specific: ['t_inner', 't_rad', 'w'] + + t_inner_update_exponent: + property_type: float + default: -0.5 + mandatory: False + help: L=4*pi*r**2*T^y + + lock_t_inner_cycles: + property_type: int + mandatory: False + default: 1 + help: > + The number of cycles to lock the update of the inner boundary temperature. + This process helps with convergence. The default is to switch it off (1 cycle) + hold-iterations: + property_type: int + default: 3 + mandatory: True + help: > + the number of iterations that the convergence criteria need to be + fulfilled before TARDIS accepts the simulation as converged + + fraction: + property_type: float + default: 0.8 + mandatory: True + help: > + the fraction of shells that have to converge to the given + convergence threshold. For example, 0.8 means that 80% of shells + have to converge to the threshold that convergence is established + + damping_constant: + property_type: float + mandatory: False + default: 0.5 + help: damping constant + + threshold: + property_type: float + mandatory: True + help: > + specifies the threshold that is taken as convergence + (i.e. 0.05 means that the value does not change more than 5%) + + + t_inner: + damping_constant: + property_type: float + mandatory: False + default: 0.5 + help: damping constant + + threshold: + property_type: float + mandatory: False + help: > + specifies the threshold that is taken as convergence + (i.e. 0.05 means that the value does not change more than 5%) + + t_rad: + damping_constant: + property_type: float + mandatory: False + default: 0.5 + help: damping constant + + threshold: + property_type: float + mandatory: True + help: > + specifies the threshold that is taken as convergence + (i.e. 0.05 means that the value does not change more than 5%) + + + w: + damping_constant: + property_type: float + mandatory: False + default: 0.5 + help: damping constant + + threshold: + property_type: float + mandatory: True + help: > + specifies the threshold that is taken as convergence + (i.e. 0.05 means that the value does not change more than 5%) + +spectrum: + property_type: quantity_range_sampled + default: None + mandatory: True + help: Final spectrum sampling + diff --git a/tardis/io/default_config_parser.py b/tardis/io/default_config_parser.py new file mode 100644 index 00000000000..c6c0d664982 --- /dev/null +++ b/tardis/io/default_config_parser.py @@ -0,0 +1,1187 @@ +# coding=utf-8 + +import re +import logging +from astropy import units +from tardis.util import parse_quantity +from astropy.units.core import UnitsException +from tardis.atomic import symbol2atomic_number +import yaml +import pprint + +logger = logging.getLogger(__name__) + + + +class Error(Exception): + """Base class for exceptions in the config parser.""" + pass + + +class ConfigTypeError(Error, ValueError): + """ + Exception raised if the type of the configured value mismatches the type + specified in the default configuration. + """ + + def __init__(self, value, expected_type, help): + self.value = value + self.expected_type = expected_type + self.help = help + + def __str__(self): + return "Expected type %s but found %s.\nHelp:%s " % \ + (repr(self.expected_type), repr(type(self.value)), help) + + +class ConfigError(Error): + """ + Exception raised if something is wrong in the configuration. + """ + + def __init__(self, path): + self.path = path + + def __str__(self): + return "Error in the configuration at %s " % ("->".join(self.path)) + +class ConfigValueError(ConfigError, ValueError): + """ + Exception raised if the given value does not match the allowed constraints. + """ + + default_msg = "Given value (%s) not allowed in constraint (%s). [%s]" + def __init__(self, config_value, allowed_constraint, path, msg=None): + self.config_value = config_value + self.allowed_constraint = allowed_constraint + self.path = path + if msg is None: + self.msg = self.default_msg + else: + self.msg = msg + + def __str__(self): + return self.msg % (str(self.config_value), str(self.allowed_constraint), self.path) + + + + + + + + +class DefaultConfigError(ConfigError): + """ + Exception raised if something is wrong in the default configuration. + """ + + def __str__(self): + return "Error in the default configuration at %s " % \ + ("->".join(self.path)) + + +class PropertyType(object): + def __init__(self): + self._default = None + self._allowed_value = None + self._allowed_type = None + self._help = None + self._mandatory = False + self._lower = None + self._upper =None + pass + + @property + def default(self): + return self._default + + @default.setter + def default(self, value): + self._default = self.to_type(value) + + @property + def allowed_value(self): + return self._allowed_value + + @allowed_value.setter + def allowed_value(self, value): + if isinstance(value, basestring): + self._allowed_value = set(self.__list_dtype(value.split())) + elif isinstance(value, list) or isinstance(value, set): + self._allowed_value = set(self.__list_dtype(value)) + elif isinstance(value, float) or isinstance(value, int): + self._allowed_type = set([value]) + else: + raise ValueError("Can not set allowed value.") + + + + @property + def allowed_type(self): + return self._allowed_value + + @allowed_type.setter + def allowed_type(self, value): + self._allowed_type = value + if '_parse_allowed_type' in (set(dir(self.__class__)) - set(dir(PropertyType))) and value != None: + self._lower, self._upper = self._parse_allowed_type(value) + + @property + def help(self): + return self._help + + @help.setter + def help(self, value): + self._help = value + + @property + def mandatory(self): + return self._mandatory + + @mandatory.setter + def mandatory(self, value): + self._mandatory = value + + def check_type(self, value): + return True + + def to_type(self, value): + return value + + def __list_dtype(self, mixed_list): + try: + tmp = [str(a) for a in mixed_list] + except ValueError: + try: + tmp = [float(a) for a in mixed_list] + except ValueError: + try: + tmp = [int(a) for a in mixed_list] + except: + raise ValueError("Forbidden type in allowed_type") + return tmp + + + def _check_allowed_value(self, _value): + """ + Returns True if the value is allowed or no allowed value is given. + """ + if self._allowed_value != None: + atype = type(iter(self.allowed_value).next()) + value = atype(_value) + if value in self.allowed_value: + return True + else: + return False + else: + return True + + def __repr__(self): + if hasattr(self, "_allowed_type"): + return "Type %s; Allowed type: %s" %(self.__class__.__name__, self._allowed_type) + else: + return "Type %s; " %(self.__class__.__name__) + + +class PropertyTypeContainer(PropertyType): + + def check_type(self): + pass + +class PropertyTypeBool(PropertyType): + + def check_type(self, value): + try: + foo = bool(value) + return True + except: + return False + + def to_type(self, value): + return bool(value) + + + +class PropertyTypeInt(PropertyType): + + def check_type(self, value): + try: + int(value) + if float.is_integer(float(value)): + if self._check_allowed_value(value) and self._check_allowed_type(value): + return True + else: + return False + except ValueError: + return False + + def to_type(self, value): + return int(value) + #ToDo: use this if allowed type is specified + def _parse_allowed_type(self, allowed_type): + string = allowed_type.strip() + upper = None + lower = None + if string.find("<") or string.find(">"): + #like x < a + match = re.compile('[<][\s]*[0-9.+^*eE]*$').findall(string) + if match: + value = re.compile('[0-9.+^*eE]+').findall(string)[0] + upper = float(value) + #like a > x" + match = re.compile('^[\s0-9.+^*eE]*[\s]*[<]$').findall(string) + if match: + value = re.compile('[0-9.+^*eE]+').findall(string)[0] + upper = float(value) + #like x > a + match = re.compile('[>][\s]*[0-9.+^*eE]*$').findall(string) + if match: + value = re.compile('[0-9.+^*eE]+').findall(string)[0] + lower = float(value) + #like a < x + match = re.compile('^[\s0-9.+^*eE]*[\s]*[<]$').findall(string) + if match: + value = re.compile('[0-9.+^*eE]+').findall(string)[0] + lower = float(value) + return lower, upper + + def __check_type(self, value, lower_lim, upper_lim): + upper, lower = True, True + if upper_lim != None: + upper = value < upper_lim + if lower_lim != None: + lower = value > lower_lim + return upper and lower + + + def _check_allowed_type(self, value): + if self._allowed_type != None: + if self.__check_type(value, self._lower, self._upper): + return True + else: + return False + else: + return True + + def _is_valid(self, value): + if not self.check_type(value): + return False + if self.allowed_value != None: + return False + if not self.__check_type(value, self._lower, self._upper): + return False + return True + + + + + + +class PropertyTypeFloat(PropertyTypeInt): + + def check_type(self, value): + try: + float(value) + if self._check_allowed_value(value) and self._check_allowed_type(value): + return True + except ValueError: + return False + + def to_type(self, value): + return float(value) + + +class PropertyTypeQuantity(PropertyType): + + def check_type(self, value): + try: + quantity_split = value.strip().split() + quantity_value = quantity_split[0] + quantity_unit = ' '.join(quantity_split[1:]) + + print('--->>-') + print(self._default) + print(value) + print('----') + if self._default is not None : + + #d_quantity_split = self._default.strip().split() + self._default.to( quantity_unit ) + float(quantity_value) + units.Unit(quantity_unit) + return True + except ValueError: + return False + + def to_type(self, value): + quantity_split = value.strip().split() + quantity_value = quantity_split[0] + quantity_unit = ' '.join(quantity_split[1:]) + return float(quantity_value) * units.Unit(quantity_unit) + +class PropertyTypeQuantityRange(PropertyTypeQuantity): + + def _to_units(self, los): + if len(los) > 2: + loq = [(lambda x: (units.Quantity(float(x[0]),x[1])))(x.split()) for x in los[:-1]] + else: + loq = [(lambda x: (units.Quantity(float(x[0]),x[1])))(x.split()) for x in los] + try: + _ = reduce((lambda a, b: a.to(b.unit)), loq) + loq = [a.to(loq[0].unit) for a in loq] + return loq + except UnitsException as e: + msg = "Incompatible units in %s"%str(los) + str(e) + raise ValueError(msg) + + def check_type(self, value): + if isinstance(value, dict): + if reduce((lambda a, b: a and b in value), [True, 'start', 'end']): + los = [value['start'], value['end']] + loq = self._to_units(los) + if abs(loq[0].value - loq[1].value) > 0: + return True + elif isinstance(value, list): + if len(value) == 2: + loq = self._to_units(value) + if abs(loq[0].value - loq[1].value) > 0: + return True + return False + + def to_type(self, value): + if isinstance(value, list): + return self._to_units(value[:2]) + elif isinstance(value, dict): + los = [value['start'], value['end']] + return self._to_units(los) + + +class PropertyTypeQuantityRangeSampled(PropertyTypeQuantityRange): + + def check_type(self, value): + if isinstance(value, dict): + if reduce((lambda a, b: a and b in value), [True, 'start', 'stop', 'num']): + los = [value['start'], value['stop']] + loq = self._to_units(los) + if abs(loq[0].value - loq[1].value) > 0: + return True + elif isinstance(value, list): + if len(value) == 3: + loq = self._to_units(value) + if abs(loq[0].value - loq[1].value) > 0: + return True + return False + + def to_type(self, value): + if isinstance(value, list): + _tmp = self._to_units(value[:2]) + _tmp.append(value[2]) + return _tmp + elif isinstance(value, dict): + los = [value['start'], value['stop']] + _tmp = self._to_units(los) + _tmp.append(value['num']) + return _tmp + + +class PropertyTypeString(PropertyType): + + def check_type(self, value): + try: + str(value) + if self._check_allowed_value(value): + return True + except ValueError: + return False + + def to_type(self, value): + return str(value) + +class PropertyTypeStringList(PropertyTypeString): + + def check_type(self, value): + try: + str(value) + except ValueError: + return False + if value in self.allowed_value: + return True + else: + return False + + def to_type(self, value): + return str(value) + + pass + + +class PropertyTypeList(PropertyType): + + def check_type(self, value): + try: + return isinstance(value, list) + except ValueError: + return False + + def to_type(self, value): + if isinstance(value, list): + return value + elif isinstance(value, basestring): + return value.split() + else: + return [] + + +class PropertyTypeRange(PropertyType): + + def check_type(self, value): + if isinstance(value, dict): + if reduce((lambda a, b: a in value), [True, 'start', 'stop']): + if abs(value['start'] - value['stop']) > 0: + return True + elif isinstance(value, list): + if len(value) == 2: + if abs(value[0] - value[1]) > 0: + return True + return False + + def to_type(self, value): + if isinstance(value, list): + return value + elif isinstance(value, dict): + return [value['start'], value['stop']] + +class PropertyTypeRangeSampled(PropertyTypeRange): + + def check_type(self, value): + if isinstance(value, dict): + if reduce((lambda a, b: a in value),\ + [True, 'start', 'stop', 'num']): + if abs(value['start'] - value['stop']) > 0: + return True + elif isinstance(value, list): + if len(value) == 3: + if abs(value[0] - value[1]) > 0: + return True + return False + + def to_type(self, value): + if isinstance(value, list): + return value + elif isinstance(value, dict): + return [value['start'], value['stop'], value['num']] + +class PropertyTypeAbundances(PropertyType): + + elements = { 'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, 'ds':110, 'rg':111, 'cn':112 } + + def check_type(self, _value): + value = dict((k.lower(), v) for k,v in _value.items()) + if set(value).issubset(set(self.elements)): + return True + else: + return False + + def to_type(self, _value): + if isinstance(_value, dict): + value = dict((k.lower(), v) for k,v in _value.items()) + abundances = dict.fromkeys(self.elements.copy(), 0.0) + for k in value: + abundances[k] = value[k] + abundances = {k: v for k, v in abundances.items() if not (v == 0.)} + return abundances + else: + raise ConfigError + +class PropertyTypeLegacyAbundances(PropertyType): + + elements = { 'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, 'ds':110, 'rg':111, 'cn':112 } + types = ['uniform'] + + def check_type(self, _value): + value = dict((k.lower(), v) for k,v in _value.items()) + if 'type' in value: + if value['type'] in self.types: + print('type is ok') + tmp = value.copy() + tmp.pop('type', None) + if set(tmp).issubset(set(self.elements)): + return True + else: + return False + return False + + def to_type(self, _value): + if isinstance(_value, dict): + value = dict((k.lower(), v) for k,v in _value.items()) + abundances = dict.fromkeys(self.elements.copy(), 0.0) + for k in value: + abundances[k] = value[k] + abundances['type'] = value['type'] + return abundances + else: + raise ConfigError + + + + + +class DefaultParser(object): + """Not invented here syndrome""" + + __check = {} + __convert = {} + __list_of_leaf_types = [] + __types = {} + + def __init__(self, default_dict, item_path=None): + """Creates a new property object for the given config level + :param default_dict: default configuration + :return: + """ + + self.__item_path = item_path + #create property type dict + self.__types['arbitrary'] = PropertyType + + self.__types['int'] = PropertyTypeInt + self.__register_leaf('int') + + self.__types['float'] = PropertyTypeFloat + self.__register_leaf('float') + + self.__types['quantity'] = PropertyTypeQuantity + self.__register_leaf('quantity') + + self.__types['quantity_range'] = PropertyTypeQuantityRange + self.__register_leaf('quantity_range') + + self.__types['quantity_range_sampled'] = PropertyTypeQuantityRangeSampled + self.__register_leaf('quantity_range_sampled') + + self.__types['string'] = PropertyTypeString + self.__register_leaf('string') + + + self.__types['range'] = PropertyTypeRange + self.__register_leaf('range') + + self.__types['range_sampled'] = PropertyTypeRangeSampled + self.__register_leaf('range_sampled') + + self.__types['list'] = PropertyTypeList + self.__register_leaf('list') + + self.__types['container-declaration'] = PropertyTypeContainer + self.__register_leaf('container-declaration') + + self.__types['container-property'] = PropertyTypeContainer + self.__register_leaf('container-property') + + self.__types['abundance_set'] = PropertyTypeAbundances + self.__register_leaf('abundance_set') + + self.__types['legacy-abundances'] =PropertyTypeLegacyAbundances + self.__register_leaf('legacy-abundances') + + self.__types['bool'] = PropertyTypeBool + self.__register_leaf('bool') + + self.__mandatory = False + self.__default_value = None + + self.__allowed_value = None + self.__allowed_type = None + self.__config_value = None + self.__path = None + + self.__default_dict = default_dict + + if not 'property_type' in default_dict: + self.__property_type = 'arbitrary' + else: + self.__property_type = default_dict['property_type'] + #print(self.__property_type) + #print(self.__types.keys()) + if not self.__property_type in self.__types.keys(): + raise ValueError + self.__type = self.__types[self.__property_type]() + + if 'allowed_value' in default_dict: + self.__type.allowed_value = default_dict['allowed_value'] + + if 'allowed_type' in default_dict: + self.__type.allowed_type = default_dict['allowed_type'] + +#ToDo: move all to classes + if 'default' in default_dict: + if default_dict['default'] != None and not default_dict['default'] in ['None','']: + self.__type.default = default_dict['default'] + + if 'mandatory' in default_dict: + self.__type.mandatory = default_dict['mandatory'] + + self.is_leaf = self.__is_leaf(self.__property_type) + + def get_default(self): + """Returns the default value of this property, if specified. + :return: default value + """ + return self.__type.default + + def set_default(self, value): + """ + Set a new default value. + :param value: new default value + """ + if value is not None: + if self.__type.check_type(value): + self.__type.default = value + else: + raise ConfigValueError(value, self.__type.allowed_value, self.get_path_in_dict(), msg='Default value (%s) violates property constraint (%s). [%s]') + else: + self.__type.default = None + + def is_mandatory(self): + """ + Returns True if this property is a mandatory. + :return: mandatory + """ + return self.__type.mandatory + + def has_default(self): + """ + Returns True if this property has a default value + :return: has a default value + """ + try: + if self.__type.default != None: + return True + else: + return False + except NameError: + pass + + def set_path_in_dic(self, path): + """ + Set the path to this property in the config + :param path: path(chain of keys) + :return: + """ + self.__path = path + + def get_path_in_dict(self): + """ + Returns the path of this property in the config + :return: path + """ + return self.__path + + def set_config_value(self, value): + """ + Set a new value + :param value: + :return: + """ + self.__config_value = value + + def get_value(self): + """ + Returns the configuration value from the configuration. + If the value specified in the configuration is invalid + the default value is returned + :return: value + """ + if (self.__config_value is not None): + if self.__type.check_type(self.__config_value): + return self.__type.to_type(self.__config_value) + else: + raise ConfigValueError(self.__config_value, self.__type.allowed_value, self.get_path_in_dict()) + else: + if self.has_default(): + logger.info("Value <%s> specified in the configuration violates a constraint\ + given in the default configuration. Expected type: %s. Using the default value."%(str(self.__config_value),str(self.__property_type))) + return self.__type.default + else: + if self.is_mandatory(): + raise ValueError('Value is mandatory, but no value was given in default configuration. [%s]' %str(self.get_path_in_dict())) + else: + logger.info("Value is not mandatory and is not specified in the configuration. [%s]" %(str(self.get_path_in_dict()))) + return None + + + def is_container(self): + """ + Returns True if this property is of type container. + :return: + """ + return self.__is_container() + + def get_container_dic(self): + """ + If this property is a container it returns the corresponding + container dictionary + :return: container dictionary + """ + if self.__is_container(): + return self.__container_dic + + @classmethod + def update_container_dic(cls, container_dic, current_entry_name): + if reduce(lambda a, b: a or b,\ + [container_dic.has_key(i) for i in ['and', 'or']], True): + if 'or' in container_dic: + if current_entry_name in container_dic['or']: + container_dic['or'] = [] + return container_dic + if 'and' in container_dic: + if current_entry_name in container_dic['and']: + current_entry_name['and'].remove(current_entry_name) + return container_dic + + + def is_valid(self, value): + if not self.__check[self.__property_type](self, value): + return False + if self.__allowed_value: + if not self.__is_allowed_value(value, self.__allowed_value): + return False + if self.__allowed_type: + if not self.__check_value(value, self.__lower, self.__upper): + return False + return True + + def __register_leaf(self, type_name): + #print(type_name) + if not type_name in self.__list_of_leaf_types: + self.__list_of_leaf_types.append(type_name) + + def __is_leaf(self, type_name): + return type_name in self.__list_of_leaf_types + + def __is_container(self): + if self.__property_type == 'container-property': + try: + self.__container_dic = self.__default_dict['type']['containers'] + return True + except KeyError: + return False + else: + return False + +# __check['container-property'] = __is_container + + def __is_container_declaration(self, value): + pass + + +class Container(DefaultParser): + def __init__(self, container_default_dict, container_dict, container_path=None): + """Creates a new container object. + :param container_default_dict: Dictionary containing the default properties of the container. + :param container_dict: Dictionary containing the configuration of the container. + """ + + #self.__register_leaf('list') + #self.__register_leaf('int') + #self.__register_leaf('float') + #self.__register_leaf('quantity') + #self.__register_leaf('string') + + self.__container_path = container_path + self.__type = None + self.__allowed_value = None + self.__allowed_type = None + self.__config_value = None + self.__path = None + self.__paper_abundances = False + self.__has_additional_items = False + + self.__property_type = 'container-property' + + self.__default_container = {} + self.__config_container = {} + + #check if it is a valid default container + if not 'type' in container_default_dict: + raise ValueError('The given default container is no valid') + + #set allowed containers + try: + self.__allowed_container = container_default_dict['type']['containers'] + except KeyError: + raise ValueError('No container names specified') + + #check if the specified container in the config is allowed + try: + #print(container_dict) + if not container_dict['type'] in self.__allowed_container: + + raise ValueError('Wrong container type') + else: + type_dict = container_dict['type'] + self.__type = container_dict['type'] + except KeyError: + raise ValueError('No container type specified') + + #get selected container from conf + try: + self.__selected_container = container_dict['type'] + except KeyError: + self.__selected_container = None + raise ValueError('No container type specified in config') + + ####This is for the uniform abundances section in the paper. + if self.__type == 'uniform' and self.__container_path[-1] == 'abundances': + self.__paper_abundances = True + cabundances_section = PropertyTypeAbundances() + tmp_container_dict = dict(container_dict) + tmp_container_dict.pop('type', None) + cabundances_section.check_type(tmp_container_dict) + tmp = cabundances_section.to_type(tmp_container_dict) + self.__default_container, self.__config_container = tmp, tmp + #### + else: + #look for necessary items + entry_name = '_' + self.__selected_container + try: + necessary_items = container_default_dict['type'][entry_name] + except KeyError: + raise ValueError('Container insufficient specified') + + #look for additional items + entry_name = '+' + self.__selected_container + try: + additional_items = container_default_dict['type'][entry_name] + self.__has_additional_items = True + except KeyError: + self.__has_additional_items = False + pass + + + def parse_container_items(top_default, top_config, level_name, full_path): + """Recursive parser for the container default dictionary and the container configuration dictionary. + + :param top_default: container default dictionary of the upper recursion level + :param top_config: container configuration dictionary of the upper recursion level + :param level_name: name(key) of the of the upper recursion level + :param path: path in the nested container dictionary from the main level to the current level + :return: If the current recursion level is not a leaf, the function returns a dictionary with itself for + each branch. If the current recursion level is a leaf the configured value and a configuration object is + returned + """ + path = reduce_list(list(full_path), self.__container_path + [item]) + print('--C') + print(item) + print(full_path) + print(path) + print('--') + tmp_conf_ob = {} + tmp_conf_val = {} + #pdb.set_trace() + if isinstance(top_default, dict): + default_property = DefaultParser(top_default) + if default_property.is_container(): + container_conf = get_value_by_path(top_config, path) + ccontainer = Container(top_default, container_conf) + return ccontainer.get_container_ob(), ccontainer.get_container_conf() + elif not default_property.is_leaf: + # print(top_default.items()) + for k, v in top_default.items(): + print('>>><<<') + print(k) + tmp_conf_ob[k], tmp_conf_val[k] = parse_container_items(v, top_config, k, full_path + [k]) + return tmp_conf_ob, tmp_conf_val + else: + default_property.set_path_in_dic(path) + try: + conf_value = get_value_by_path(top_config, path) + except KeyError: + conf_value = None + + if conf_value is not None: + default_property.set_config_value(conf_value) + + return default_property, default_property.get_value() + + def reduce_list(a,b): + """ + removes items from list a which are in b + """ + for k in b: + a.remove(k) + return a + + + def get_value_by_path(dict, path): + """ + Value from a nested dictionary specified by its path. + :param dict: nested source dictionary + :param path: path (composed of keys) in the dictionary + :return: + """ + for key in path: + dict = dict[key] + return dict + + if not self.__paper_abundances: + for item in necessary_items: + if not item in container_dict.keys(): + raise ValueError('Entry %s is missing in container [%s]' % (str(item), self.__container_path)) + else: + self.__default_container[item], self.__config_container[item] = parse_container_items(container_default_dict[item], + container_dict[item], item, self.__container_path + [item]) + if self.__has_additional_items: + for item in additional_items: + try: + self.__default_container[item], self.__config_container[item] = parse_container_items(container_default_dict[item], + container_dict[item], item, self.__container_path + [item]) + except KeyError: + pass + + #go through all items and create an conf object thereby check the conf + self.__container_ob = self.__default_container + if isinstance(self.__config_container, dict): + self.__conf = self.__config_container + else: + pdb.set_trace() + self.__conf = {"No Name":self.__config_container} + + + def get_container_ob(self): + """ + Return the container configuration object + :return: + """ + return self.__container_ob + + def get_container_conf(self): + """ + Return the configuration + :return: + """ + self.__conf['type'] = self.__type + return self.__conf + + +class Config(object): + """ + An configuration object represents the parsed configuration. + """ + + + def __init__(self, default_configuration, input_configuration): + """Creates the configuration object. + :param default_configuration: Default configuration dictionary + :param input_configuration: Configuration dictionary + """ + self.__conf_o = None + self.__conf_v = None + self.mandatories = {} + self.fulfilled = {} + self.__create_default_conf(default_configuration) + self.__parse_config(default_configuration, input_configuration) + self.__help_string = '' + + @classmethod + def from_yaml(cls, fname_config, fname_default): + with open(fname_config) as f: + conff = f.read() + conf = yaml.safe_load(conff) + with open(fname_default) as f: + defaf = f.read() + defa = yaml.safe_load(defaf) + return cls(defa, conf) + + + def __mandatory_key(self, path): + """Return the key string for dictionary of mandatory entries + :param path: path (composed of keys) in the dictionary + :return: corresponding key + """ + return ':'.join(path) + + + + def register_mandatory(self, name, path): + """Register a mandatory entry + :param name: name of the mandatory entry to be registered + :param path: path (composed of keys) in the dictionary + """ + self.mandatories[self.__mandatory_key(path)] = name + + def deregister_mandatory(self, name, path): + """Register a deregistered mandatory entry + :param name: name of the mandatory entry to be deregistered + :param path: path (composed of keys) in the dictionary + """ + self.fulfilled[self.__mandatory_key(path)] = name + + def is_mandatory_fulfilled(self): + """ + Check if all mandatory entries are deregistered. + """ + if len(set(self.mandatories.keys()) - set(self.fulfilled.keys())) <= 0: + return True + else: + return False + + def __parse_config(self, default_configuration, configuration): + """Parser for the default dictionary and the configuration dictionary. + :param default_configuration: Default configuration dictionary + :param configuration: Configuration dictionary + """ + + def find_item(dict, key): + """ + Returns the value for a specific key in a nested dictionary + :param dict: nested dictionary + :param key: + """ + if key in dict: return dict[key] + for k, v in dict.items(): + if isinstance(v, dict): + item = find_item(v, key) + if item is not None: + return item + + + def get_property_by_path(d, path): + """ Returns the value for a specific path(chain of keys) in a nested dictionary + :param dict: nested dictionary + :param path: chain of keys as list + """ + if len(path)<=0: + return d + else: + try: + v = d + for k in path: + v = v[k] + return v + except KeyError: + return None + + def recursive_parser(top_default, configuration, path): + """ + Recursive parser for the default dictionary. + :param top_default: container default dictionary of the upper recursion level + :param configuration: configuration dictionary + :param path: path in the nested container dictionary from the main level to the current level + :return: If the current recursion level is not a leaf, the function returns a dictionary with itself for + each branch. If the current recursion level is a leaf, the configuration value and object are + returned + """ + tmp_conf_ob = {} + tmp_conf_val = {} + if isinstance(top_default, dict): + default_property = DefaultParser(top_default, item_path=path) + if default_property.is_mandatory(): + self.register_mandatory(self, path) + self.deregister_mandatory(self, path) + + if default_property.is_container(): + container_conf = get_property_by_path(configuration, path) + try: + ccontainer = Container(top_default, container_conf, container_path=path) + return ccontainer.get_container_ob(), ccontainer.get_container_conf() + except: + return None, None + elif not default_property.is_leaf: + no_default = self.__check_items_in_conf(get_property_by_path(configuration, path), top_default) + if len(no_default) > 0: + logger.warning('The items %s from the configuration are not specified in the default configuration'%str(no_default)) + for k, v in top_default.items(): + #print('>---<') + #print(k) + tmp_conf_ob[k], tmp_conf_val[k] = recursive_parser(v, configuration, path + [k]) + #print(tmp_conf_val[k]) + return tmp_conf_ob, tmp_conf_val + else: + default_property.set_path_in_dic(path) + try: +# print('get_property_by_path') + #print(path) + conf_value = get_property_by_path(configuration, path) +# print(conf_value) +# print('End:get_property_by_path') + except KeyError: + conf_value = None + + if conf_value is not None: + default_property.set_config_value(conf_value) + return default_property, default_property.get_value() + + + self.__conf_o, self.__conf_v = recursive_parser(default_configuration, configuration, []) + # print('|\|\|\|') + # print(self.__conf_v) + + def __check_items_in_conf(self, config_dict, default_dict): + if isinstance(config_dict, dict) and len(config_dict) > 0: + return list(set(config_dict.keys()) - set(default_dict.keys())) + else: + return list(default_dict.keys()) + + + + def __create_default_conf(self, default_conf): + """Returns the default configuration values as dictionary. + :param default_conf: default configuration dictionary + :return: default configuration values + """ + + def recursive_default_parser(top_default, path): + """Recursive parser for the default dictionary. + :param top_default: container default dictionary of the upper recursion level + :param path: path in the nested container dictionary from the main level to the current level + :return: If the current recursion level is not a leaf, the function returns a dictionary with itself for + each branch. If the current recursion level is a leaf, the default configuration value is + returned + """ + tmp_default = {} + if isinstance(top_default, dict): + default_property = DefaultParser(top_default) + if not default_property.is_container(): + if not default_property.is_leaf: + for k, v in top_default.items(): + tmp_default[k] = recursive_default_parser(v, path + [k]) + return tmp_default + else: + default_property.set_path_in_dic(path) + if default_property.has_default(): + return default_property.get_default() + else: + return None + + self.__default_config = recursive_default_parser(default_conf, []) + + + + def get_config(self): + """Returns the parsed configuration as dictionary. + :return: configuration values as dictionary + """ + print(self.__conf_v) + return self.__conf_v + + def get_default_config(self): + """Returns the default configuration values as dictionary + :return: default configuration values as dictionary + """ + return self.__default_config + + def get_config_object(self): + """Returns the default configuration objects as dictionary + :return: default configuration objects as dictionary + """ + return self.__conf_o + + def get_help(self): + return(pprint.pformat(self.get_default_config())) + + + def __repr__(self): + return(str(pprint.pformat(self.get_config()))) + + diff --git a/tardis/io/tests/data/tardis_configv1_artis_density_v_slice.yml b/tardis/io/tests/data/tardis_configv1_artis_density_v_slice.yml index 528bd380098..53dfab507d9 100644 --- a/tardis/io/tests/data/tardis_configv1_artis_density_v_slice.yml +++ b/tardis/io/tests/data/tardis_configv1_artis_density_v_slice.yml @@ -1,7 +1,7 @@ tardis_config_version: v1.0 supernova: - luminosity_requested: 9.44 log_lsun + luminosity_requested: 9.44 solLum time_explosion: 13 day atom_data: kurucz_atom_pure_simple.h5 diff --git a/tardis/io/tests/data/tardis_configv1_ascii_density.yml b/tardis/io/tests/data/tardis_configv1_ascii_density.yml index 87ba8bfcfce..88bae0e81c1 100644 --- a/tardis/io/tests/data/tardis_configv1_ascii_density.yml +++ b/tardis/io/tests/data/tardis_configv1_ascii_density.yml @@ -1,7 +1,7 @@ tardis_config_version: v1.0 supernova: - luminosity_requested: 9.44 log_lsun + luminosity_requested: 9.44 solLum time_explosion: 13 day atom_data: kurucz_atom_pure_simple.h5 diff --git a/tardis/io/tests/data/tardis_configv1_ascii_density_uniabund.yml b/tardis/io/tests/data/tardis_configv1_ascii_density_uniabund.yml index f1d596f89b3..7c0a8b9e61d 100644 --- a/tardis/io/tests/data/tardis_configv1_ascii_density_uniabund.yml +++ b/tardis/io/tests/data/tardis_configv1_ascii_density_uniabund.yml @@ -5,7 +5,7 @@ #Currently only simple1d is allowed tardis_config_version: v1.0 supernova: - luminosity_requested: 9.44 log_lsun + luminosity_requested: 9.44 solLum time_explosion: 13 day atom_data: kurucz_atom_pure_simple.h5 @@ -47,12 +47,12 @@ montecarlo: last_no_of_packets: 1.e+5 no_of_virtual_packets: 5 - convergence_criteria: + convergence_strategy: type: specific damping_constant: 1.0 threshold: 0.05 fraction: 0.8 - hold: 3 + hold-iterations: 3 t_inner: damping_constant: 1.0 From 9ec0953e1004b577af177db91f9433c54fcc2bec Mon Sep 17 00:00:00 2001 From: Michi Date: Thu, 24 Apr 2014 16:56:55 +0200 Subject: [PATCH 2/7] add new test to the default parser --- tardis/io/tests/test_default_config_parser.py | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 tardis/io/tests/test_default_config_parser.py diff --git a/tardis/io/tests/test_default_config_parser.py b/tardis/io/tests/test_default_config_parser.py new file mode 100644 index 00000000000..588170cbfbc --- /dev/null +++ b/tardis/io/tests/test_default_config_parser.py @@ -0,0 +1,160 @@ +import tardis +from tardis.io.default_config_parser import DefaultParser, Config + +import os +import yaml +import pytest +from glob import glob + +existing_configs = glob(os.path.join('tardis', 'docs', 'examples', '*.yml')) +config_definition = os.path.join('tardis', 'data', 'tardis_default_config_definition.yml') + +@pytest.mark.parametrize(("config_filename",), existing_configs) +def test_configread(config_filename): + config = Config.from_yaml(config_filename, config_definition) + + +#test the whole thing +def test_default_config_parser(): + test_config_fname = os.path.join(tardis.__path__[0], 'data', 'conf_tes.yml') + test_default_fname = os.path.join(tardis.__path__[0], 'data', 'conf_def.yml') + + with open(test_config_fname) as f: + test_config = yaml.SafeLoader(f) + + with open(test_default_fname) as f: + test_default = yaml.SafeLoader(f) + + + test_conf_ob = Config(test_default, test_config) + + +def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, mandatory): + test_ob = DefaultParser(test_dic) + + if not default == None: + dhelper = True + else: + dhelper = False + + assert test_ob.has_default() == dhelper + assert test_ob.get_default() == default + assert test_ob.is_leaf + assert test_ob.is_container() == container + assert test_ob.is_mandatory() == mandatory + + #set good default + test_ob.set_default(default) + + assert test_ob.get_default() == default + #set bad default + if not wdefault == None: + with pytest.raises(ValueError): + test_ob.set_default(wdefault) + + assert test_ob.get_value() == default + + #set good value + test_ob.set_config_value(value) + + assert test_ob.get_value() == value + + #set bad value + if not wvalue == None: + test_ob.set_config_value(wvalue) + assert test_ob.get_value() == default + + return 0 + + + +def test_default_parser_float(): + example_dic = {'default': 99.99, + 'help': 'float value for testing', + 'mandatory': True, + 'property_type': 'float'} + default = 99.99 + wdefault = "xx" + value = 11.12 + wvalue = "yy" + container = False + mandatory = True + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + + +def test_default_parser_integer(): + example_dic = {'default': 99, + 'help': 'integer value for testing', + 'mandatory': True, + 'property_type': 'int'} + + default = 99 + wdefault = 9.15 + value = 11 + wvalue = 9.22 + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + +def test_default_parser_quantity(): + example_dic = {'default': '99.99 cm', + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'quantity'} + + default = "99.99 cm" + wdefault = "kl" + value = "11.12 m" + wvalue = "yy" + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + + +def test_default_parser_range(): + example_dic = {'default': [0, 10], + 'help': 'range for testing', + 'mandatory': False, + 'property_type': 'range'} + + default = [0,10] + wdefault = 1 + value = [7,8] + wvalue = 2 + container = False + mandatory = False + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + +def test_default_parser_range_sampled(): + example_dic = {'default': [0, 10, 1], + 'help': 'range for testing', + 'mandatory': False, + 'property_type': 'range_sampled'} + + default = [0,10,1] + wdefault = [1,3] + value = [1,5,1] + wvalue = [1,1] + container = False + mandatory = False + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + +def test_default_parser_string(): + example_dic = {'default': 'DEFAULT', + 'help': 'string for testing', + 'mandatory': True, + 'property_type': 'string'} + + default = "DEFAULT" + wdefault = None + value = "blub" + wvalue = None + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + From b4d48b84a5209c8944c3861161bcaf4cf3ff8c8a Mon Sep 17 00:00:00 2001 From: Michi Date: Thu, 24 Apr 2014 16:59:28 +0200 Subject: [PATCH 3/7] added documentation to branch --- docs/default_config_parser.rst | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/default_config_parser.rst diff --git a/docs/default_config_parser.rst b/docs/default_config_parser.rst new file mode 100644 index 00000000000..48ce4881b51 --- /dev/null +++ b/docs/default_config_parser.rst @@ -0,0 +1,72 @@ + +Default Configuration Parser +============================ + +The default config parser takes a user configuration and a default configuration and creates a consistent and valid configuration for tardis based on the constraints given in the default configuration. Both input data are normally given as a yaml dictionary with a consistent hierarchical structure i.e. for every item in the user configuration there has to be a declaration in the default configuration at the same hierarchical level. This declaration can be either an unspecific empty level declaration like: +- Main_level: + - Second_level: + - Third_level: + … +Or a declaration of a configuration item like: +- item: + - property_type: int + - default: 1 + - mandatory: True + - help: ‘This is a doc string.' + +This contains always the keywords help, default, mandatory, and property_type. The keyword help is a doc-string which describes the corresponding item. Default specifies the default value which is used in case that no value for this item is specified in the corresponding user configuration item. If the keyword mandatory is True, the item has to be specified in the user configuration. The keyword property_type is used to specify the type of the item. At the moment, the config parser knows the following types: +Int: The property type int is for integer like config items. +Float: The property type float is for float like config items. +String: The property type string is for string like config items. +Quantity: The property type quantity is for physical quantities with units given as string. The string contains value and unit separated by a whitespace E.g. 2 cm. +Range: The property type range specifies a range via start and end. Note: abs(start - end ) > 0 +Quantity_range: Like property type range but with quantities as start and stop. The consistency of the units is checked. +Additionally to the four standard keywords the types integer, float, and quantity can have the keywords allowed_value and allowed_type. allowed_value specifies the allowed values in a list, whereas allowed_type specifies a range of allowed values like “x>10”. + +Container +--------- + +For more complex configurations with dependencies, you can use the containers which allow branching in the configuration. A container is declared in the default configuration file by setting the property_type to container property and specifying the properties of the container with keyword type. The property_type of this section is container-declaration which allows you to specify the possible container items with the keyword container. For every specified container item, the code expects the declaration of all sub items. The keywords for this are “_“ + “name of the container item”. +If the type declaration for this container is finished you can specify all container items like normal items. Here is an example for a container configuration with two branches +- container_example: + - property_type: container-property + - type: + - property_type: container-declaration + - containers: ['one', 'two', 'three'] + - _one: ['one_one', 'one_two'] + - _two: ['two_one'] + + - one_one: + - property_type: string + - default: 'This is a container item' + - mandatory: False + - help: This is a container item from the container one. + + - one_two: + - sub_one_two_one: + - property_type: string + - default: 'This is a container item' + - mandatory: False + - help: This is a container item from the container one. + - sub_one_two_two: + - property_type: string + - default: 'This is a container item' + - mandatory: False + - help: This is a container item from the container one. + + - two_one: + - quantity_range: + - property_type: quantity_range + - default: [1 m,10 cm] #[Start,End] + - mandatory: False + - help: Like property type range but with quantities as start and stop. The consistency of the units is checked. + +How to use +---------- + +To use the default parser create a new config object form the class Config by either from a dictionaries or from yaml files. +- My_config = Config(default configuration dictionary, user configuration dictionary) +or +- My_config = Config.from_yaml(default configuration file, user configuration file) +To access the configuration for tardis use the method get_config + From 6150d67b0bb22202a1c25e95f32dece009f4eb34 Mon Sep 17 00:00:00 2001 From: Michi Date: Thu, 24 Apr 2014 23:48:28 +0200 Subject: [PATCH 4/7] today i fixed the test for the default config parser --- tardis/io/default_config_parser.py | 60 +++++++++++++++++-- tardis/io/tests/test_default_config_parser.py | 35 ++++------- 2 files changed, 66 insertions(+), 29 deletions(-) diff --git a/tardis/io/default_config_parser.py b/tardis/io/default_config_parser.py index c6c0d664982..fb7a908c4dd 100644 --- a/tardis/io/default_config_parser.py +++ b/tardis/io/default_config_parser.py @@ -8,6 +8,7 @@ from tardis.atomic import symbol2atomic_number import yaml import pprint +import ast logger = logging.getLogger(__name__) @@ -298,6 +299,11 @@ def check_type(self, value): quantity_split = value.strip().split() quantity_value = quantity_split[0] quantity_unit = ' '.join(quantity_split[1:]) + try: + float(quantity_value) + units.Unit(quantity_unit) + except ValueError: + return False print('--->>-') print(self._default) @@ -310,7 +316,7 @@ def check_type(self, value): float(quantity_value) units.Unit(quantity_unit) return True - except ValueError: + except (ValueError,AttributeError): return False def to_type(self, value): @@ -418,16 +424,29 @@ def to_type(self, value): class PropertyTypeList(PropertyType): def check_type(self, value): - try: - return isinstance(value, list) - except ValueError: - return False + if isinstance(value, list): + return True + elif isinstance(value, basestring): + try: + ast.literal_eval(value) + return True + except SyntaxError: + try: + value.split() + return True + except: + return False + return False + def to_type(self, value): if isinstance(value, list): return value elif isinstance(value, basestring): - return value.split() + try: + return ast.literal_eval(value) + except SyntaxError: + return value.split() else: return [] @@ -443,6 +462,15 @@ def check_type(self, value): if len(value) == 2: if abs(value[0] - value[1]) > 0: return True + elif isinstance(value, basestring): + try: + clist = ast.literal_eval(value) + if abs(clist[0] - clist[1]) > 0: + return True + except SyntaxError: + clist = value.split() + if abs(clist[0] - clist[1]) > 0: + return True return False def to_type(self, value): @@ -450,6 +478,12 @@ def to_type(self, value): return value elif isinstance(value, dict): return [value['start'], value['stop']] + elif isinstance(value, basestring): + try: + return ast.literal_eval(value) + except SyntaxError: + return value.split() + class PropertyTypeRangeSampled(PropertyTypeRange): @@ -463,6 +497,15 @@ def check_type(self, value): if len(value) == 3: if abs(value[0] - value[1]) > 0: return True + elif isinstance(value, basestring): + try: + clist = ast.literal_eval(value) + if abs(clist[0] - clist[1]) > 0: + return True + except SyntaxError: + clist = value.split() + if abs(clist[0] - clist[1]) > 0: + return True return False def to_type(self, value): @@ -470,6 +513,11 @@ def to_type(self, value): return value elif isinstance(value, dict): return [value['start'], value['stop'], value['num']] + elif isinstance(value, basestring): + try: + return ast.literal_eval(value) + except SyntaxError: + return value.split() class PropertyTypeAbundances(PropertyType): diff --git a/tardis/io/tests/test_default_config_parser.py b/tardis/io/tests/test_default_config_parser.py index 588170cbfbc..732c9a964af 100644 --- a/tardis/io/tests/test_default_config_parser.py +++ b/tardis/io/tests/test_default_config_parser.py @@ -1,6 +1,7 @@ import tardis -from tardis.io.default_config_parser import DefaultParser, Config +from tardis.io.default_config_parser import DefaultParser, Config, ConfigValueError +from astropy import units as u import os import yaml import pytest @@ -14,20 +15,6 @@ def test_configread(config_filename): config = Config.from_yaml(config_filename, config_definition) -#test the whole thing -def test_default_config_parser(): - test_config_fname = os.path.join(tardis.__path__[0], 'data', 'conf_tes.yml') - test_default_fname = os.path.join(tardis.__path__[0], 'data', 'conf_def.yml') - - with open(test_config_fname) as f: - test_config = yaml.SafeLoader(f) - - with open(test_default_fname) as f: - test_default = yaml.SafeLoader(f) - - - test_conf_ob = Config(test_default, test_config) - def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, mandatory): test_ob = DefaultParser(test_dic) @@ -37,6 +24,7 @@ def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, else: dhelper = False + assert test_ob.has_default() == dhelper assert test_ob.get_default() == default assert test_ob.is_leaf @@ -44,25 +32,26 @@ def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, assert test_ob.is_mandatory() == mandatory #set good default - test_ob.set_default(default) + test_ob.set_default(str(default)) assert test_ob.get_default() == default #set bad default - if not wdefault == None: + if wdefault is not None: with pytest.raises(ValueError): test_ob.set_default(wdefault) assert test_ob.get_value() == default #set good value - test_ob.set_config_value(value) + test_ob.set_config_value(str(value)) assert test_ob.get_value() == value #set bad value - if not wvalue == None: - test_ob.set_config_value(wvalue) - assert test_ob.get_value() == default + if wvalue is not None: + with pytest.raises(ConfigValueError): + test_ob.set_config_value(wvalue) + test_ob.get_value() return 0 @@ -103,9 +92,9 @@ def test_default_parser_quantity(): 'mandatory': True, 'property_type': 'quantity'} - default = "99.99 cm" + default = 99.99 * u.cm wdefault = "kl" - value = "11.12 m" + value = 11.12 * u. m wvalue = "yy" container = False mandatory = True From 2d6660ccb3a90334e2934179d7fbff7b483f5321 Mon Sep 17 00:00:00 2001 From: Michi Date: Fri, 25 Apr 2014 13:49:12 +0200 Subject: [PATCH 5/7] code cleanup the first act --- tardis/io/default_config_parser.py | 406 +++++++++--------- .../data/tardis_configv1_artis_density.yml | 2 +- tardis/io/tests/test_default_config_parser.py | 122 +++--- 3 files changed, 265 insertions(+), 265 deletions(-) diff --git a/tardis/io/default_config_parser.py b/tardis/io/default_config_parser.py index fb7a908c4dd..dd0a227d986 100644 --- a/tardis/io/default_config_parser.py +++ b/tardis/io/default_config_parser.py @@ -2,16 +2,15 @@ import re import logging +import pprint +import ast + from astropy import units -from tardis.util import parse_quantity from astropy.units.core import UnitsException -from tardis.atomic import symbol2atomic_number import yaml -import pprint -import ast -logger = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class Error(Exception): @@ -32,7 +31,7 @@ def __init__(self, value, expected_type, help): def __str__(self): return "Expected type %s but found %s.\nHelp:%s " % \ - (repr(self.expected_type), repr(type(self.value)), help) + (repr(self.expected_type), repr(type(self.value)), help) class ConfigError(Error): @@ -45,13 +44,15 @@ def __init__(self, path): def __str__(self): return "Error in the configuration at %s " % ("->".join(self.path)) - + + class ConfigValueError(ConfigError, ValueError): """ Exception raised if the given value does not match the allowed constraints. """ - + default_msg = "Given value (%s) not allowed in constraint (%s). [%s]" + def __init__(self, config_value, allowed_constraint, path, msg=None): self.config_value = config_value self.allowed_constraint = allowed_constraint @@ -60,15 +61,9 @@ def __init__(self, config_value, allowed_constraint, path, msg=None): self.msg = self.default_msg else: self.msg = msg - + def __str__(self): return self.msg % (str(self.config_value), str(self.allowed_constraint), self.path) - - - - - - class DefaultConfigError(ConfigError): @@ -78,9 +73,9 @@ class DefaultConfigError(ConfigError): def __str__(self): return "Error in the default configuration at %s " % \ - ("->".join(self.path)) - - + ("->".join(self.path)) + + class PropertyType(object): def __init__(self): self._default = None @@ -89,21 +84,21 @@ def __init__(self): self._help = None self._mandatory = False self._lower = None - self._upper =None + self._upper = None pass - + @property def default(self): return self._default - + @default.setter def default(self, value): self._default = self.to_type(value) - + @property def allowed_value(self): return self._allowed_value - + @allowed_value.setter def allowed_value(self, value): if isinstance(value, basestring): @@ -114,41 +109,40 @@ def allowed_value(self, value): self._allowed_type = set([value]) else: raise ValueError("Can not set allowed value.") - - - + + @property def allowed_type(self): return self._allowed_value - + @allowed_type.setter def allowed_type(self, value): self._allowed_type = value if '_parse_allowed_type' in (set(dir(self.__class__)) - set(dir(PropertyType))) and value != None: self._lower, self._upper = self._parse_allowed_type(value) - + @property def help(self): return self._help - + @help.setter def help(self, value): self._help = value - + @property def mandatory(self): return self._mandatory - + @mandatory.setter def mandatory(self, value): self._mandatory = value - + def check_type(self, value): return True - + def to_type(self, value): return value - + def __list_dtype(self, mixed_list): try: tmp = [str(a) for a in mixed_list] @@ -161,8 +155,8 @@ def __list_dtype(self, mixed_list): except: raise ValueError("Forbidden type in allowed_type") return tmp - - + + def _check_allowed_value(self, _value): """ Returns True if the value is allowed or no allowed value is given. @@ -176,35 +170,32 @@ def _check_allowed_value(self, _value): return False else: return True - + def __repr__(self): if hasattr(self, "_allowed_type"): - return "Type %s; Allowed type: %s" %(self.__class__.__name__, self._allowed_type) + return "Type %s; Allowed type: %s" % (self.__class__.__name__, self._allowed_type) else: - return "Type %s; " %(self.__class__.__name__) - - + return "Type %s; " % (self.__class__.__name__) + + class PropertyTypeContainer(PropertyType): - def check_type(self): pass - + + class PropertyTypeBool(PropertyType): - def check_type(self, value): try: foo = bool(value) return True except: return False - - def to_type(self, value): + + def to_type(self, value): return bool(value) - - - + + class PropertyTypeInt(PropertyType): - def check_type(self, value): try: int(value) @@ -215,10 +206,11 @@ def check_type(self, value): return False except ValueError: return False - + def to_type(self, value): return int(value) - #ToDo: use this if allowed type is specified + #ToDo: use this if allowed type is specified + def _parse_allowed_type(self, allowed_type): string = allowed_type.strip() upper = None @@ -245,7 +237,7 @@ def _parse_allowed_type(self, allowed_type): value = re.compile('[0-9.+^*eE]+').findall(string)[0] lower = float(value) return lower, upper - + def __check_type(self, value, lower_lim, upper_lim): upper, lower = True, True if upper_lim != None: @@ -253,47 +245,41 @@ def __check_type(self, value, lower_lim, upper_lim): if lower_lim != None: lower = value > lower_lim return upper and lower - - + + def _check_allowed_type(self, value): if self._allowed_type != None: - if self.__check_type(value, self._lower, self._upper): + if self.__check_type(value, self._lower, self._upper): return True else: return False else: return True - + def _is_valid(self, value): if not self.check_type(value): return False if self.allowed_value != None: return False if not self.__check_type(value, self._lower, self._upper): - return False + return False return True - - - - - - + + class PropertyTypeFloat(PropertyTypeInt): - def check_type(self, value): try: float(value) if self._check_allowed_value(value) and self._check_allowed_type(value): - return True + return True except ValueError: return False - + def to_type(self, value): return float(value) - - + + class PropertyTypeQuantity(PropertyType): - def check_type(self, value): try: quantity_split = value.strip().split() @@ -304,42 +290,41 @@ def check_type(self, value): units.Unit(quantity_unit) except ValueError: return False - + print('--->>-') print(self._default) print(value) print('----') - if self._default is not None : - + if self._default is not None: #d_quantity_split = self._default.strip().split() - self._default.to( quantity_unit ) + self._default.to(quantity_unit) float(quantity_value) units.Unit(quantity_unit) return True - except (ValueError,AttributeError): + except (ValueError, AttributeError): return False - + def to_type(self, value): quantity_split = value.strip().split() quantity_value = quantity_split[0] quantity_unit = ' '.join(quantity_split[1:]) return float(quantity_value) * units.Unit(quantity_unit) - + + class PropertyTypeQuantityRange(PropertyTypeQuantity): - def _to_units(self, los): if len(los) > 2: - loq = [(lambda x: (units.Quantity(float(x[0]),x[1])))(x.split()) for x in los[:-1]] + loq = [(lambda x: (units.Quantity(float(x[0]), x[1])))(x.split()) for x in los[:-1]] else: - loq = [(lambda x: (units.Quantity(float(x[0]),x[1])))(x.split()) for x in los] + loq = [(lambda x: (units.Quantity(float(x[0]), x[1])))(x.split()) for x in los] try: - _ = reduce((lambda a, b: a.to(b.unit)), loq) + _ = reduce((lambda a, b: a.to(b.unit)), loq) loq = [a.to(loq[0].unit) for a in loq] return loq except UnitsException as e: - msg = "Incompatible units in %s"%str(los) + str(e) + msg = "Incompatible units in %s" % str(los) + str(e) raise ValueError(msg) - + def check_type(self, value): if isinstance(value, dict): if reduce((lambda a, b: a and b in value), [True, 'start', 'end']): @@ -353,17 +338,16 @@ def check_type(self, value): if abs(loq[0].value - loq[1].value) > 0: return True return False - + def to_type(self, value): if isinstance(value, list): return self._to_units(value[:2]) elif isinstance(value, dict): los = [value['start'], value['end']] return self._to_units(los) - - + + class PropertyTypeQuantityRangeSampled(PropertyTypeQuantityRange): - def check_type(self, value): if isinstance(value, dict): if reduce((lambda a, b: a and b in value), [True, 'start', 'stop', 'num']): @@ -377,7 +361,7 @@ def check_type(self, value): if abs(loq[0].value - loq[1].value) > 0: return True return False - + def to_type(self, value): if isinstance(value, list): _tmp = self._to_units(value[:2]) @@ -388,10 +372,9 @@ def to_type(self, value): _tmp = self._to_units(los) _tmp.append(value['num']) return _tmp - - + + class PropertyTypeString(PropertyType): - def check_type(self, value): try: str(value) @@ -399,12 +382,12 @@ def check_type(self, value): return True except ValueError: return False - + def to_type(self, value): return str(value) - + + class PropertyTypeStringList(PropertyTypeString): - def check_type(self, value): try: str(value) @@ -414,15 +397,14 @@ def check_type(self, value): return True else: return False - + def to_type(self, value): return str(value) - + pass - - + + class PropertyTypeList(PropertyType): - def check_type(self, value): if isinstance(value, list): return True @@ -437,22 +419,21 @@ def check_type(self, value): except: return False return False - - + + def to_type(self, value): if isinstance(value, list): return value elif isinstance(value, basestring): try: return ast.literal_eval(value) - except SyntaxError: + except SyntaxError: return value.split() else: return [] - - + + class PropertyTypeRange(PropertyType): - def check_type(self, value): if isinstance(value, dict): if reduce((lambda a, b: a in value), [True, 'start', 'stop']): @@ -472,7 +453,7 @@ def check_type(self, value): if abs(clist[0] - clist[1]) > 0: return True return False - + def to_type(self, value): if isinstance(value, list): return value @@ -484,13 +465,12 @@ def to_type(self, value): except SyntaxError: return value.split() - + class PropertyTypeRangeSampled(PropertyTypeRange): - def check_type(self, value): if isinstance(value, dict): - if reduce((lambda a, b: a in value),\ - [True, 'start', 'stop', 'num']): + if reduce((lambda a, b: a in value), \ + [True, 'start', 'stop', 'num']): if abs(value['start'] - value['stop']) > 0: return True elif isinstance(value, list): @@ -507,7 +487,7 @@ def check_type(self, value): if abs(clist[0] - clist[1]) > 0: return True return False - + def to_type(self, value): if isinstance(value, list): return value @@ -518,21 +498,32 @@ def to_type(self, value): return ast.literal_eval(value) except SyntaxError: return value.split() - + + class PropertyTypeAbundances(PropertyType): - - elements = { 'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, 'ds':110, 'rg':111, 'cn':112 } + elements = {'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, + 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, + 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, + 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, + 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, + 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, + 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, + 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, + 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, + 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, + 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, + 'ds': 110, 'rg': 111, 'cn': 112} def check_type(self, _value): - value = dict((k.lower(), v) for k,v in _value.items()) + value = dict((k.lower(), v) for k, v in _value.items()) if set(value).issubset(set(self.elements)): return True else: return False - + def to_type(self, _value): if isinstance(_value, dict): - value = dict((k.lower(), v) for k,v in _value.items()) + value = dict((k.lower(), v) for k, v in _value.items()) abundances = dict.fromkeys(self.elements.copy(), 0.0) for k in value: abundances[k] = value[k] @@ -540,14 +531,25 @@ def to_type(self, _value): return abundances else: raise ConfigError - + + class PropertyTypeLegacyAbundances(PropertyType): - - elements = { 'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, 'ds':110, 'rg':111, 'cn':112 } + elements = {'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, + 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, + 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, + 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, + 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, + 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, + 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, + 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, + 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, + 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, + 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, + 'ds': 110, 'rg': 111, 'cn': 112} types = ['uniform'] def check_type(self, _value): - value = dict((k.lower(), v) for k,v in _value.items()) + value = dict((k.lower(), v) for k, v in _value.items()) if 'type' in value: if value['type'] in self.types: print('type is ok') @@ -558,10 +560,10 @@ def check_type(self, _value): else: return False return False - + def to_type(self, _value): if isinstance(_value, dict): - value = dict((k.lower(), v) for k,v in _value.items()) + value = dict((k.lower(), v) for k, v in _value.items()) abundances = dict.fromkeys(self.elements.copy(), 0.0) for k in value: abundances[k] = value[k] @@ -569,9 +571,6 @@ def to_type(self, _value): return abundances else: raise ConfigError - - - class DefaultParser(object): @@ -587,55 +586,55 @@ def __init__(self, default_dict, item_path=None): :param default_dict: default configuration :return: """ - + self.__item_path = item_path #create property type dict self.__types['arbitrary'] = PropertyType - + self.__types['int'] = PropertyTypeInt self.__register_leaf('int') - + self.__types['float'] = PropertyTypeFloat self.__register_leaf('float') - + self.__types['quantity'] = PropertyTypeQuantity self.__register_leaf('quantity') - + self.__types['quantity_range'] = PropertyTypeQuantityRange self.__register_leaf('quantity_range') - + self.__types['quantity_range_sampled'] = PropertyTypeQuantityRangeSampled self.__register_leaf('quantity_range_sampled') - + self.__types['string'] = PropertyTypeString self.__register_leaf('string') - - + self.__types['range'] = PropertyTypeRange self.__register_leaf('range') - + self.__types['range_sampled'] = PropertyTypeRangeSampled self.__register_leaf('range_sampled') - + self.__types['list'] = PropertyTypeList self.__register_leaf('list') - + self.__types['container-declaration'] = PropertyTypeContainer self.__register_leaf('container-declaration') - + self.__types['container-property'] = PropertyTypeContainer self.__register_leaf('container-property') - + self.__types['abundance_set'] = PropertyTypeAbundances self.__register_leaf('abundance_set') - - self.__types['legacy-abundances'] =PropertyTypeLegacyAbundances + + self.__types['legacy-abundances'] = PropertyTypeLegacyAbundances self.__register_leaf('legacy-abundances') - + self.__types['bool'] = PropertyTypeBool self.__register_leaf('bool') - + self.__mandatory = False + self.__default_value = None self.__allowed_value = None @@ -653,17 +652,16 @@ def __init__(self, default_dict, item_path=None): #print(self.__types.keys()) if not self.__property_type in self.__types.keys(): raise ValueError - self.__type = self.__types[self.__property_type]() + self.__type = self.__types[self.__property_type]() if 'allowed_value' in default_dict: self.__type.allowed_value = default_dict['allowed_value'] if 'allowed_type' in default_dict: self.__type.allowed_type = default_dict['allowed_type'] - -#ToDo: move all to classes + if 'default' in default_dict: - if default_dict['default'] != None and not default_dict['default'] in ['None','']: + if default_dict['default'] != None and not default_dict['default'] in ['None', '']: self.__type.default = default_dict['default'] if 'mandatory' in default_dict: @@ -686,10 +684,12 @@ def set_default(self, value): if self.__type.check_type(value): self.__type.default = value else: - raise ConfigValueError(value, self.__type.allowed_value, self.get_path_in_dict(), msg='Default value (%s) violates property constraint (%s). [%s]') + raise ConfigValueError(value, self.__type.allowed_value, self.get_path_in_dict(), + msg='Default value (%s) violates property constraint (%s). [%s]') else: self.__type.default = None + @property def is_mandatory(self): """ Returns True if this property is a mandatory. @@ -697,6 +697,7 @@ def is_mandatory(self): """ return self.__type.mandatory + @property def has_default(self): """ Returns True if this property has a default value @@ -744,19 +745,22 @@ def get_value(self): if self.__type.check_type(self.__config_value): return self.__type.to_type(self.__config_value) else: - raise ConfigValueError(self.__config_value, self.__type.allowed_value, self.get_path_in_dict()) + raise ConfigValueError(self.__config_value, self.__type.allowed_value, self.get_path_in_dict()) else: - if self.has_default(): + if self.has_default: logger.info("Value <%s> specified in the configuration violates a constraint\ - given in the default configuration. Expected type: %s. Using the default value."%(str(self.__config_value),str(self.__property_type))) + given in the default configuration. Expected type: %s. Using the default value." % ( + str(self.__config_value), str(self.__property_type))) return self.__type.default else: - if self.is_mandatory(): - raise ValueError('Value is mandatory, but no value was given in default configuration. [%s]' %str(self.get_path_in_dict())) + if self.is_mandatory: + raise ValueError('Value is mandatory, but no value was given in default configuration. [%s]' % str( + self.get_path_in_dict())) else: - logger.info("Value is not mandatory and is not specified in the configuration. [%s]" %(str(self.get_path_in_dict()))) + logger.info("Value is not mandatory and is not specified in the configuration. [%s]" % ( + str(self.get_path_in_dict()))) return None - + def is_container(self): """ @@ -776,7 +780,7 @@ def get_container_dic(self): @classmethod def update_container_dic(cls, container_dic, current_entry_name): - if reduce(lambda a, b: a or b,\ + if reduce(lambda a, b: a or b, \ [container_dic.has_key(i) for i in ['and', 'or']], True): if 'or' in container_dic: if current_entry_name in container_dic['or']: @@ -817,7 +821,7 @@ def __is_container(self): else: return False -# __check['container-property'] = __is_container + # __check['container-property'] = __is_container def __is_container_declaration(self, value): pass @@ -835,7 +839,7 @@ def __init__(self, container_default_dict, container_dict, container_path=None): #self.__register_leaf('float') #self.__register_leaf('quantity') #self.__register_leaf('string') - + self.__container_path = container_path self.__type = None self.__allowed_value = None @@ -890,14 +894,14 @@ def __init__(self, container_default_dict, container_dict, container_path=None): self.__default_container, self.__config_container = tmp, tmp #### else: - #look for necessary items + #look for necessary items entry_name = '_' + self.__selected_container try: necessary_items = container_default_dict['type'][entry_name] except KeyError: raise ValueError('Container insufficient specified') - - #look for additional items + + #look for additional items entry_name = '+' + self.__selected_container try: additional_items = container_default_dict['type'][entry_name] @@ -905,7 +909,7 @@ def __init__(self, container_default_dict, container_dict, container_path=None): except KeyError: self.__has_additional_items = False pass - + def parse_container_items(top_default, top_config, level_name, full_path): """Recursive parser for the container default dictionary and the container configuration dictionary. @@ -934,7 +938,7 @@ def parse_container_items(top_default, top_config, level_name, full_path): ccontainer = Container(top_default, container_conf) return ccontainer.get_container_ob(), ccontainer.get_container_conf() elif not default_property.is_leaf: - # print(top_default.items()) + # print(top_default.items()) for k, v in top_default.items(): print('>>><<<') print(k) @@ -951,15 +955,15 @@ def parse_container_items(top_default, top_config, level_name, full_path): default_property.set_config_value(conf_value) return default_property, default_property.get_value() - - def reduce_list(a,b): + + def reduce_list(a, b): """ removes items from list a which are in b """ for k in b: a.remove(k) - return a - + return a + def get_value_by_path(dict, path): """ @@ -977,24 +981,26 @@ def get_value_by_path(dict, path): if not item in container_dict.keys(): raise ValueError('Entry %s is missing in container [%s]' % (str(item), self.__container_path)) else: - self.__default_container[item], self.__config_container[item] = parse_container_items(container_default_dict[item], - container_dict[item], item, self.__container_path + [item]) + self.__default_container[item], self.__config_container[item] = parse_container_items( + container_default_dict[item], + container_dict[item], item, self.__container_path + [item]) if self.__has_additional_items: for item in additional_items: try: - self.__default_container[item], self.__config_container[item] = parse_container_items(container_default_dict[item], - container_dict[item], item, self.__container_path + [item]) + self.__default_container[item], self.__config_container[item] = parse_container_items( + container_default_dict[item], + container_dict[item], item, self.__container_path + [item]) except KeyError: pass - - #go through all items and create an conf object thereby check the conf + + #go through all items and create an conf object thereby check the conf self.__container_ob = self.__default_container if isinstance(self.__config_container, dict): self.__conf = self.__config_container else: pdb.set_trace() - self.__conf = {"No Name":self.__config_container} - + self.__conf = {"No Name": self.__config_container} + def get_container_ob(self): """ @@ -1040,7 +1046,7 @@ def from_yaml(cls, fname_config, fname_default): defaf = f.read() defa = yaml.safe_load(defaf) return cls(defa, conf) - + def __mandatory_key(self, path): """Return the key string for dictionary of mandatory entries @@ -1048,8 +1054,7 @@ def __mandatory_key(self, path): :return: corresponding key """ return ':'.join(path) - - + def register_mandatory(self, name, path): """Register a mandatory entry @@ -1099,7 +1104,7 @@ def get_property_by_path(d, path): :param dict: nested dictionary :param path: chain of keys as list """ - if len(path)<=0: + if len(path) <= 0: return d else: try: @@ -1124,35 +1129,34 @@ def recursive_parser(top_default, configuration, path): tmp_conf_val = {} if isinstance(top_default, dict): default_property = DefaultParser(top_default, item_path=path) - if default_property.is_mandatory(): + if default_property.is_mandatory: self.register_mandatory(self, path) self.deregister_mandatory(self, path) if default_property.is_container(): container_conf = get_property_by_path(configuration, path) try: - ccontainer = Container(top_default, container_conf, container_path=path) + ccontainer = Container(top_default, container_conf, container_path=path) return ccontainer.get_container_ob(), ccontainer.get_container_conf() + except: + logger.warning('Container specified in default_configuration, but not used in the current\ + configuration file. [%s]' % str(path)) return None, None + elif not default_property.is_leaf: no_default = self.__check_items_in_conf(get_property_by_path(configuration, path), top_default) if len(no_default) > 0: - logger.warning('The items %s from the configuration are not specified in the default configuration'%str(no_default)) + logger.warning('The items %s from the configuration are not specified in the default\ + configuration' % str(no_default)) for k, v in top_default.items(): - #print('>---<') - #print(k) tmp_conf_ob[k], tmp_conf_val[k] = recursive_parser(v, configuration, path + [k]) - #print(tmp_conf_val[k]) return tmp_conf_ob, tmp_conf_val + else: default_property.set_path_in_dic(path) try: -# print('get_property_by_path') - #print(path) conf_value = get_property_by_path(configuration, path) -# print(conf_value) -# print('End:get_property_by_path') except KeyError: conf_value = None @@ -1162,16 +1166,13 @@ def recursive_parser(top_default, configuration, path): self.__conf_o, self.__conf_v = recursive_parser(default_configuration, configuration, []) - # print('|\|\|\|') - # print(self.__conf_v) - + def __check_items_in_conf(self, config_dict, default_dict): if isinstance(config_dict, dict) and len(config_dict) > 0: return list(set(config_dict.keys()) - set(default_dict.keys())) else: - return list(default_dict.keys()) - - + return list(default_dict.keys()) + def __create_default_conf(self, default_conf): """Returns the default configuration values as dictionary. @@ -1197,14 +1198,13 @@ def recursive_default_parser(top_default, path): return tmp_default else: default_property.set_path_in_dic(path) - if default_property.has_default(): + if default_property.has_default: return default_property.get_default() else: return None self.__default_config = recursive_default_parser(default_conf, []) - def get_config(self): """Returns the parsed configuration as dictionary. @@ -1224,12 +1224,12 @@ def get_config_object(self): :return: default configuration objects as dictionary """ return self.__conf_o - + def get_help(self): - return(pprint.pformat(self.get_default_config())) - - + return (pprint.pformat(self.get_default_config())) + + def __repr__(self): - return(str(pprint.pformat(self.get_config()))) + return (str(pprint.pformat(self.get_config()))) diff --git a/tardis/io/tests/data/tardis_configv1_artis_density.yml b/tardis/io/tests/data/tardis_configv1_artis_density.yml index 28c6fdc814d..3974024df5f 100644 --- a/tardis/io/tests/data/tardis_configv1_artis_density.yml +++ b/tardis/io/tests/data/tardis_configv1_artis_density.yml @@ -1,7 +1,7 @@ tardis_config_version: v1.0 supernova: - luminosity_requested: 9.44 log_lsun + luminosity_requested: 9.44 solLum time_explosion: 13 day atom_data: kurucz_atom_pure_simple.h5 diff --git a/tardis/io/tests/test_default_config_parser.py b/tardis/io/tests/test_default_config_parser.py index 732c9a964af..967856da766 100644 --- a/tardis/io/tests/test_default_config_parser.py +++ b/tardis/io/tests/test_default_config_parser.py @@ -1,67 +1,64 @@ -import tardis -from tardis.io.default_config_parser import DefaultParser, Config, ConfigValueError +import os +from glob import glob from astropy import units as u -import os -import yaml import pytest -from glob import glob + +from tardis.io.default_config_parser import DefaultParser, Config, ConfigValueError existing_configs = glob(os.path.join('tardis', 'docs', 'examples', '*.yml')) config_definition = os.path.join('tardis', 'data', 'tardis_default_config_definition.yml') + @pytest.mark.parametrize(("config_filename",), existing_configs) def test_configread(config_filename): config = Config.from_yaml(config_filename, config_definition) - def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, mandatory): test_ob = DefaultParser(test_dic) if not default == None: - dhelper = True + dhelper = True else: dhelper = False - - assert test_ob.has_default() == dhelper + assert test_ob.has_default == dhelper assert test_ob.get_default() == default assert test_ob.is_leaf assert test_ob.is_container() == container - assert test_ob.is_mandatory() == mandatory - + assert test_ob.is_mandatory == mandatory + #set good default test_ob.set_default(str(default)) - + assert test_ob.get_default() == default #set bad default if wdefault is not None: with pytest.raises(ValueError): test_ob.set_default(wdefault) - + assert test_ob.get_value() == default - + #set good value test_ob.set_config_value(str(value)) - + assert test_ob.get_value() == value - + #set bad value if wvalue is not None: with pytest.raises(ConfigValueError): test_ob.set_config_value(wvalue) test_ob.get_value() - + return 0 - - - + + def test_default_parser_float(): example_dic = {'default': 99.99, - 'help': 'float value for testing', - 'mandatory': True, - 'property_type': 'float'} + 'help': 'float value for testing', + 'mandatory': True, + 'property_type': 'float'} default = 99.99 wdefault = "xx" value = 11.12 @@ -69,81 +66,84 @@ def test_default_parser_float(): container = False mandatory = True ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) - - + + def test_default_parser_integer(): example_dic = {'default': 99, - 'help': 'integer value for testing', - 'mandatory': True, - 'property_type': 'int'} - + 'help': 'integer value for testing', + 'mandatory': True, + 'property_type': 'int'} + default = 99 wdefault = 9.15 value = 11 wvalue = 9.22 container = False mandatory = True - + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) - + + def test_default_parser_quantity(): example_dic = {'default': '99.99 cm', - 'help': 'quantity for testing', - 'mandatory': True, - 'property_type': 'quantity'} - + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'quantity'} + default = 99.99 * u.cm wdefault = "kl" - value = 11.12 * u. m + value = 11.12 * u.m wvalue = "yy" container = False mandatory = True - + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) - - + + def test_default_parser_range(): example_dic = {'default': [0, 10], - 'help': 'range for testing', - 'mandatory': False, - 'property_type': 'range'} - - default = [0,10] + 'help': 'range for testing', + 'mandatory': False, + 'property_type': 'range'} + + default = [0, 10] wdefault = 1 - value = [7,8] + value = [7, 8] wvalue = 2 container = False mandatory = False - + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + def test_default_parser_range_sampled(): example_dic = {'default': [0, 10, 1], - 'help': 'range for testing', - 'mandatory': False, - 'property_type': 'range_sampled'} - - default = [0,10,1] - wdefault = [1,3] - value = [1,5,1] - wvalue = [1,1] + 'help': 'range for testing', + 'mandatory': False, + 'property_type': 'range_sampled'} + + default = [0, 10, 1] + wdefault = [1, 3] + value = [1, 5, 1] + wvalue = [1, 1] container = False mandatory = False - + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) - + + def test_default_parser_string(): example_dic = {'default': 'DEFAULT', - 'help': 'string for testing', - 'mandatory': True, - 'property_type': 'string'} - + 'help': 'string for testing', + 'mandatory': True, + 'property_type': 'string'} + default = "DEFAULT" wdefault = None value = "blub" wvalue = None container = False mandatory = True - + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) From 3dd129fb3b608d06e184beac934f3be7f805832d Mon Sep 17 00:00:00 2001 From: Michi Date: Tue, 29 Apr 2014 00:46:18 +0200 Subject: [PATCH 6/7] dear git log tonight i removed the elements from the df file and used the element data from atomic. --- tardis/io/default_config_parser.py | 143 ++++++++---------- tardis/io/tests/test_default_config_parser.py | 14 ++ 2 files changed, 75 insertions(+), 82 deletions(-) diff --git a/tardis/io/default_config_parser.py b/tardis/io/default_config_parser.py index dd0a227d986..6fee440c5b5 100644 --- a/tardis/io/default_config_parser.py +++ b/tardis/io/default_config_parser.py @@ -5,6 +5,7 @@ import pprint import ast +from tardis.atomic import atomic_symbols_data from astropy import units from astropy.units.core import UnitsException import yaml @@ -291,10 +292,6 @@ def check_type(self, value): except ValueError: return False - print('--->>-') - print(self._default) - print(value) - print('----') if self._default is not None: #d_quantity_split = self._default.strip().split() self._default.to(quantity_unit) @@ -337,6 +334,29 @@ def check_type(self, value): loq = self._to_units(value) if abs(loq[0].value - loq[1].value) > 0: return True + else: + return False + elif isinstance(value, basestring): + try: + clist = ast.literal_eval(value) + if len(clist) == 2: + loq = self._to_units(value) + if abs(loq[0].value - loq[1].value) > 0: + return True + else: + return False + else: + return False + except SyntaxError: + clist = value.split() + if len(clist) == 2: + loq = self._to_units(value) + if abs(loq[0].value - loq[1].value) > 0: + return True + else: + return False + else: + return False return False def to_type(self, value): @@ -501,18 +521,7 @@ def to_type(self, value): class PropertyTypeAbundances(PropertyType): - elements = {'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, - 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, - 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, - 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, - 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, - 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, - 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, - 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, - 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, - 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, - 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, - 'ds': 110, 'rg': 111, 'cn': 112} + elements = dict([(x,y.lower()) for (x,y) in atomic_symbols_data]) def check_type(self, _value): value = dict((k.lower(), v) for k, v in _value.items()) @@ -532,27 +541,14 @@ def to_type(self, _value): else: raise ConfigError - class PropertyTypeLegacyAbundances(PropertyType): - elements = {'neut': 0, 'h': 1, 'he': 2, 'li': 3, 'be': 4, 'b': 5, 'c': 6, 'n': 7, 'o': 8, 'f': 9, 'ne': 10, - 'na': 11, 'mg': 12, 'al': 13, 'si': 14, 'p': 15, 's': 16, 'cl': 17, 'ar': 18, 'k': 19, 'ca': 20, - 'sc': 21, 'ti': 22, 'v': 23, 'cr': 24, 'mn': 25, 'fe': 26, 'co': 27, 'ni': 28, 'cu': 29, 'zn': 30, - 'ga': 31, 'ge': 32, 'as': 33, 'se': 34, 'br': 35, 'kr': 36, 'rb': 37, 'sr': 38, 'y': 39, 'zr': 40, - 'nb': 41, 'mo': 42, 'tc': 43, 'ru': 44, 'rh': 45, 'pd': 46, 'ag': 47, 'cd': 48, 'in': 49, 'sn': 50, - 'sb': 51, 'te': 52, 'i': 53, 'xe': 54, 'cs': 55, 'ba': 56, 'la': 57, 'ce': 58, 'pr': 59, 'nd': 60, - 'pm': 61, 'sm': 62, 'eu': 63, 'gd': 64, 'tb': 65, 'dy': 66, 'ho': 67, 'er': 68, 'tm': 69, 'yb': 70, - 'lu': 71, 'hf': 72, 'ta': 73, 'w': 74, 're': 75, 'os': 76, 'ir': 77, 'pt': 78, 'au': 79, 'hg': 80, - 'tl': 81, 'pb': 82, 'bi': 83, 'po': 84, 'at': 85, 'rn': 86, 'fr': 87, 'ra': 88, 'ac': 89, 'th': 90, - 'pa': 91, 'u': 92, 'np': 93, 'pu': 94, 'am': 95, 'cm': 96, 'bk': 97, 'cf': 98, 'es': 99, 'fm': 100, - 'md': 101, 'no': 102, 'lr': 103, 'rf': 104, 'db': 105, 'sg': 106, 'bh': 107, 'hs': 108, 'mt': 109, - 'ds': 110, 'rg': 111, 'cn': 112} + elements = dict([(x,y.lower()) for (x,y) in atomic_symbols_data]) types = ['uniform'] def check_type(self, _value): value = dict((k.lower(), v) for k, v in _value.items()) if 'type' in value: if value['type'] in self.types: - print('type is ok') tmp = value.copy() tmp.pop('type', None) if set(tmp).issubset(set(self.elements)): @@ -648,8 +644,6 @@ def __init__(self, default_dict, item_path=None): self.__property_type = 'arbitrary' else: self.__property_type = default_dict['property_type'] - #print(self.__property_type) - #print(self.__types.keys()) if not self.__property_type in self.__types.keys(): raise ValueError self.__type = self.__types[self.__property_type]() @@ -792,19 +786,18 @@ def update_container_dic(cls, container_dic, current_entry_name): return container_dic - def is_valid(self, value): - if not self.__check[self.__property_type](self, value): - return False - if self.__allowed_value: - if not self.__is_allowed_value(value, self.__allowed_value): - return False - if self.__allowed_type: - if not self.__check_value(value, self.__lower, self.__upper): - return False - return True + # def is_valid(self, value): + # if not self.__check[self.__property_type](self, value): + # return False + # if self.__allowed_value: + # if not self.__is_allowed_value(value, self.__allowed_value): + # return False + # if self.__allowed_type: + # if not self.__check_value(value, self.__lower, self.__upper): + # return False + # return True def __register_leaf(self, type_name): - #print(type_name) if not type_name in self.__list_of_leaf_types: self.__list_of_leaf_types.append(type_name) @@ -834,11 +827,6 @@ def __init__(self, container_default_dict, container_dict, container_path=None): :param container_dict: Dictionary containing the configuration of the container. """ - #self.__register_leaf('list') - #self.__register_leaf('int') - #self.__register_leaf('float') - #self.__register_leaf('quantity') - #self.__register_leaf('string') self.__container_path = container_path self.__type = None @@ -866,7 +854,6 @@ def __init__(self, container_default_dict, container_dict, container_path=None): #check if the specified container in the config is allowed try: - #print(container_dict) if not container_dict['type'] in self.__allowed_container: raise ValueError('Wrong container type') @@ -910,6 +897,31 @@ def __init__(self, container_default_dict, container_dict, container_path=None): self.__has_additional_items = False pass + if not self.__paper_abundances: + for item in necessary_items: + if not item in container_dict.keys(): + raise ValueError('Entry %s is missing in container [%s]' % (str(item), self.__container_path)) + else: + self.__default_container[item], self.__config_container[item] = parse_container_items( + container_default_dict[item], + container_dict[item], item, self.__container_path + [item]) + if self.__has_additional_items: + for item in additional_items: + try: + self.__default_container[item], self.__config_container[item] = parse_container_items( + container_default_dict[item], + container_dict[item], item, self.__container_path + [item]) + except KeyError: + pass + + #go through all items and create an conf object thereby check the conf + self.__container_ob = self.__default_container + if isinstance(self.__config_container, dict): + self.__conf = self.__config_container + else: + pdb.set_trace() + self.__conf = {"No Name": self.__config_container} + def parse_container_items(top_default, top_config, level_name, full_path): """Recursive parser for the container default dictionary and the container configuration dictionary. @@ -923,11 +935,6 @@ def parse_container_items(top_default, top_config, level_name, full_path): returned """ path = reduce_list(list(full_path), self.__container_path + [item]) - print('--C') - print(item) - print(full_path) - print(path) - print('--') tmp_conf_ob = {} tmp_conf_val = {} #pdb.set_trace() @@ -938,10 +945,7 @@ def parse_container_items(top_default, top_config, level_name, full_path): ccontainer = Container(top_default, container_conf) return ccontainer.get_container_ob(), ccontainer.get_container_conf() elif not default_property.is_leaf: - # print(top_default.items()) for k, v in top_default.items(): - print('>>><<<') - print(k) tmp_conf_ob[k], tmp_conf_val[k] = parse_container_items(v, top_config, k, full_path + [k]) return tmp_conf_ob, tmp_conf_val else: @@ -976,30 +980,6 @@ def get_value_by_path(dict, path): dict = dict[key] return dict - if not self.__paper_abundances: - for item in necessary_items: - if not item in container_dict.keys(): - raise ValueError('Entry %s is missing in container [%s]' % (str(item), self.__container_path)) - else: - self.__default_container[item], self.__config_container[item] = parse_container_items( - container_default_dict[item], - container_dict[item], item, self.__container_path + [item]) - if self.__has_additional_items: - for item in additional_items: - try: - self.__default_container[item], self.__config_container[item] = parse_container_items( - container_default_dict[item], - container_dict[item], item, self.__container_path + [item]) - except KeyError: - pass - - #go through all items and create an conf object thereby check the conf - self.__container_ob = self.__default_container - if isinstance(self.__config_container, dict): - self.__conf = self.__config_container - else: - pdb.set_trace() - self.__conf = {"No Name": self.__config_container} def get_container_ob(self): @@ -1210,7 +1190,6 @@ def get_config(self): """Returns the parsed configuration as dictionary. :return: configuration values as dictionary """ - print(self.__conf_v) return self.__conf_v def get_default_config(self): diff --git a/tardis/io/tests/test_default_config_parser.py b/tardis/io/tests/test_default_config_parser.py index 967856da766..50443fd341d 100644 --- a/tardis/io/tests/test_default_config_parser.py +++ b/tardis/io/tests/test_default_config_parser.py @@ -99,6 +99,20 @@ def test_default_parser_quantity(): ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) +# def test_default_parser_quantity_range(): +# example_dic = {'default': ['1 cm', '5 cm'], +# 'help': 'quantity for testing', +# 'mandatory': True, +# 'property_type': 'quantity_range'} +# +# default = [1.0 * u.cm, 5 * u.cm] +# wdefault = "kl" +# value = [10 * u.m, 50 * u.cm] +# wvalue = "yy" +# container = False +# mandatory = True +# +# ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) def test_default_parser_range(): example_dic = {'default': [0, 10], From 7157505e6ae154af603bd2eb2f085d996c528b04 Mon Sep 17 00:00:00 2001 From: Michi Date: Tue, 29 Apr 2014 17:54:24 +0200 Subject: [PATCH 7/7] new test for the config parser --- tardis/io/default_config_parser.py | 15 +- tardis/io/tests/test_default_config_parser.py | 163 +++++++++++++++--- 2 files changed, 149 insertions(+), 29 deletions(-) diff --git a/tardis/io/default_config_parser.py b/tardis/io/default_config_parser.py index 6fee440c5b5..6e005aa8ace 100644 --- a/tardis/io/default_config_parser.py +++ b/tardis/io/default_config_parser.py @@ -347,7 +347,7 @@ def check_type(self, value): return False else: return False - except SyntaxError: + except (SyntaxError): clist = value.split() if len(clist) == 2: loq = self._to_units(value) @@ -357,6 +357,8 @@ def check_type(self, value): return False else: return False + except ValueError: + return False return False def to_type(self, value): @@ -524,9 +526,12 @@ class PropertyTypeAbundances(PropertyType): elements = dict([(x,y.lower()) for (x,y) in atomic_symbols_data]) def check_type(self, _value): - value = dict((k.lower(), v) for k, v in _value.items()) - if set(value).issubset(set(self.elements)): - return True + if isinstance(_value,dict): + value = dict((k.lower(), v) for k, v in _value.items()) + if set(value).issubset(set(self.elements.values())): + return True + else: + return False else: return False @@ -551,7 +556,7 @@ def check_type(self, _value): if value['type'] in self.types: tmp = value.copy() tmp.pop('type', None) - if set(tmp).issubset(set(self.elements)): + if set(tmp).issubset(set(self.elements.values())): return True else: return False diff --git a/tardis/io/tests/test_default_config_parser.py b/tardis/io/tests/test_default_config_parser.py index 50443fd341d..33d7fa03d0b 100644 --- a/tardis/io/tests/test_default_config_parser.py +++ b/tardis/io/tests/test_default_config_parser.py @@ -3,6 +3,7 @@ from astropy import units as u import pytest +import ast from tardis.io.default_config_parser import DefaultParser, Config, ConfigValueError @@ -15,35 +16,42 @@ def test_configread(config_filename): config = Config.from_yaml(config_filename, config_definition) -def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, mandatory): +def default_parser_helper(test_dic, default, wdefault, value, wvalue, container, mandatory, return_default=None, + return_value=None, value_as_string=True): test_ob = DefaultParser(test_dic) + if return_value is None: + return_value = value + + if return_default is None: + return_default = default + if not default == None: dhelper = True else: dhelper = False assert test_ob.has_default == dhelper - assert test_ob.get_default() == default + assert test_ob.get_default() == return_default assert test_ob.is_leaf assert test_ob.is_container() == container assert test_ob.is_mandatory == mandatory #set good default - test_ob.set_default(str(default)) + test_ob.set_default(default) - assert test_ob.get_default() == default + assert test_ob.get_default() == return_default #set bad default if wdefault is not None: with pytest.raises(ValueError): test_ob.set_default(wdefault) - assert test_ob.get_value() == default + assert test_ob.get_value() == return_default #set good value - test_ob.set_config_value(str(value)) + test_ob.set_config_value(value) - assert test_ob.get_value() == value + assert test_ob.get_value() == return_value #set bad value if wvalue is not None: @@ -90,29 +98,89 @@ def test_default_parser_quantity(): 'mandatory': True, 'property_type': 'quantity'} - default = 99.99 * u.cm + default = "99.99 cm" + return_default = 99.99 * u.cm wdefault = "kl" - value = 11.12 * u.m + value = "11.12 m" + return_value = 11.12 * u.m wvalue = "yy" container = False mandatory = True - ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory, + return_default=return_default,return_value=return_value ) + +def test_default_parser_quantity_range(): + example_dic = {'default': ['1 cm', '5 cm'], + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'quantity_range'} + + default = ['1.0 cm', '5 cm'] + return_default = [1.0 * u.cm, 5 * u.cm] + wdefault = "kl" + value = ['10 m', '50 cm'] + return_value = [10 * u.m, 50 * u.cm] + wvalue = "yy" + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory, + return_default=return_default, return_value=return_value) + +def test_default_parser_quantity_range_old(): + example_dic = {'default':{'start':'1 cm', 'end':'5 cm'}, + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'quantity_range'} + + default = ['1.0 cm', '5 cm'] + return_default = [1.0 * u.cm, 5 * u.cm] + wdefault = "kl" + value = ['10 m', '50 cm'] + return_value = [10 * u.m, 50 * u.cm] + wvalue = "yy" + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory, + return_default=return_default, return_value=return_value) + +def test_default_parser_quantity_range_sampeled(): + example_dic = {'default': ['1 cm', '5 cm', 10], + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'quantity_range_sampled'} + + default = ['1.0 cm', '5 cm',10] + return_default = [1.0 * u.cm, 5 * u.cm, 10] + wdefault = "kl" + value = ['10 m', '50 cm', 10] + return_value = [10 * u.m, 50 * u.cm, 10] + wvalue = "yy" + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory, + return_default=return_default, return_value=return_value) + +def test_default_parser_quantity_range_sampeled_old(): + example_dic = {'default': {'start':'1 cm', 'stop':'5 cm', 'num':10}, + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'quantity_range_sampled'} + + default = ['1.0 cm', '5 cm',10] + return_default = [1.0 * u.cm, 5 * u.cm, 10] + wdefault = "kl" + value = ['10 m', '50 cm', 10] + return_value = [10 * u.m, 50 * u.cm, 10] + wvalue = "yy" + container = False + mandatory = True -# def test_default_parser_quantity_range(): -# example_dic = {'default': ['1 cm', '5 cm'], -# 'help': 'quantity for testing', -# 'mandatory': True, -# 'property_type': 'quantity_range'} -# -# default = [1.0 * u.cm, 5 * u.cm] -# wdefault = "kl" -# value = [10 * u.m, 50 * u.cm] -# wvalue = "yy" -# container = False -# mandatory = True -# -# ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory, + return_default=return_default, return_value=return_value) def test_default_parser_range(): example_dic = {'default': [0, 10], @@ -129,6 +197,20 @@ def test_default_parser_range(): ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) +def test_default_parser_range_old(): + example_dic = {'default': {'start':0,'stop':10}, + 'help': 'range for testing', + 'mandatory': False, + 'property_type': 'range'} + + default = [0, 10] + wdefault = 1 + value = [7, 8] + wvalue = 2 + container = False + mandatory = False + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) def test_default_parser_range_sampled(): example_dic = {'default': [0, 10, 1], @@ -145,6 +227,20 @@ def test_default_parser_range_sampled(): ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) +def test_default_parser_range_sampled(): + example_dic = {'default': {'start':0,'stop':10,'num':1}, + 'help': 'range for testing', + 'mandatory': False, + 'property_type': 'range_sampled'} + + default = [0, 10, 1] + wdefault = [1, 3] + value = [1, 5, 1] + wvalue = [1, 1] + container = False + mandatory = False + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) def test_default_parser_string(): example_dic = {'default': 'DEFAULT', @@ -160,4 +256,23 @@ def test_default_parser_string(): mandatory = True ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory) + +def test_property_type_bundances(): + example_dic = {'default': {'He':0.4,'Mg':0.1,'Pb':0.5}, + 'help': 'quantity for testing', + 'mandatory': True, + 'property_type': 'abundance_set'} + + default = {'He':0.4,'Mg':0.1,'Pb':0.5} + return_default = {'he':0.4,'mg':0.1,'pb':0.5} + wdefault = "kl" + value = {'He':0.4,'Mg':0.6} + return_value = {'he':0.4,'mg':0.6} + wvalue = "yy" + container = False + mandatory = True + + ex = default_parser_helper(example_dic, default, wdefault, value, wvalue, container, mandatory, + return_default=return_default, return_value=return_value) +