Source code for smif.data_layer.validate

# -*- coding: utf-8 -*-
"""Validate the correct format and presence of the config data
for the system-of-systems model
"""

VALIDATION_ERRORS = []


[docs]class ValidationError(Exception): """Custom exception to use for parsing validation. """ pass
[docs]def validate_sos_model_config(data): """Check expected values for data loaded from master config file """ if not isinstance(data, dict): msg = "Main config file should contain setup data, instead found: {}" err = ValidationError(msg.format(data)) VALIDATION_ERRORS.append(err) return # check dependencies if "dependencies" not in data: VALIDATION_ERRORS.append( ValidationError("No 'dependencies' specified in main config file.") ) # check timesteps if "timesteps" not in data: VALIDATION_ERRORS.append( ValidationError("No 'timesteps' file specified in main config file.")) else: validate_path_to_timesteps(data["timesteps"]) # check sector models if "sector_models" not in data: VALIDATION_ERRORS.append( ValidationError("No 'sector_models' specified in main config file.")) else: validate_sector_models_initial_config(data["sector_models"]) # check scenario data if "scenario_data" not in data: VALIDATION_ERRORS.append( ValidationError("No 'scenario_data' specified in main config file.")) else: validate_scenario_data_config(data["scenario_data"]) # check planning if "planning" not in data: VALIDATION_ERRORS.append( ValidationError("No 'planning' mode specified in main config file.")) else: validate_planning_config(data["planning"]) # check region_sets if "region_sets" not in data: VALIDATION_ERRORS.append( ValidationError("No 'region_sets' specified in main config file.")) else: validate_region_sets_config(data["region_sets"]) # check interval_sets if "interval_sets" not in data: VALIDATION_ERRORS.append( ValidationError("No 'interval_sets' specified in main config file.")) else: validate_interval_sets_config(data["interval_sets"])
[docs]def validate_path_to_timesteps(timesteps): """Check timesteps is a path to timesteps file """ if not isinstance(timesteps, str): VALIDATION_ERRORS.append( ValidationError( "Expected 'timesteps' in main config to specify " + "a timesteps file, instead got {}.".format(timesteps)))
[docs]def validate_timesteps(timesteps, file_path): """Check timesteps is a list of integers """ if not isinstance(timesteps, list): msg = "Loading {}: expected a list of timesteps.".format(file_path) VALIDATION_ERRORS.append(ValidationError(msg)) else: msg = "Loading {}: timesteps should be integer years, instead got {}" for timestep in timesteps: if not isinstance(timestep, int): VALIDATION_ERRORS.append(msg.format(file_path, timestep))
[docs]def validate_time_intervals(intervals, file_path): """Check time intervals """ if not isinstance(intervals, list): msg = "Loading {}: expected a list of time intervals.".format(file_path) VALIDATION_ERRORS.append(ValidationError(msg)) else: for interval in intervals: validate_time_interval(interval)
[docs]def validate_time_interval(interval): """Check a single time interval """ if not isinstance(interval, dict): msg = "Expected a time interval, instead got {}.".format(interval) VALIDATION_ERRORS.append(ValidationError(msg)) return required_keys = ["id", "start", "end"] for key in required_keys: if key not in interval: fmt = "Expected a value for '{}' in each " + \ "time interval, only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, interval)))
[docs]def validate_sector_models_initial_config(sector_models): """Check list of sector models initial configuration """ if not isinstance(sector_models, list): fmt = "Expected 'sector_models' in main config to " + \ "specify a list of sector models to run, instead got {}." VALIDATION_ERRORS.append(ValidationError(fmt.format(sector_models))) else: if len(sector_models) == 0: VALIDATION_ERRORS.append( ValidationError("No 'sector_models' specified in main config file.")) # check each sector model for sector_model_config in sector_models: validate_sector_model_initial_config(sector_model_config)
[docs]def validate_sector_model_initial_config(sector_model_config): """Check a single sector model initial configuration """ if not isinstance(sector_model_config, dict): fmt = "Expected a sector model config block, instead got {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(sector_model_config))) return required_keys = ["name", "config_dir", "path", "classname"] for key in required_keys: if key not in sector_model_config: fmt = "Expected a value for '{}' in each " + \ "sector model in main config file, only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, sector_model_config)))
[docs]def validate_dependency_spec(input_spec, model_name): """Check the input specification for a single sector model """ if not isinstance(input_spec, list): fmt = "Expected a list of parameter definitions in '{}' model " + \ "input specification, instead got {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(model_name, input_spec))) return for dep in input_spec: validate_dependency(dep)
[docs]def validate_dependency(dep): """Check a dependency specification """ if not isinstance(dep, dict): fmt = "Expected a dependency specification, instead got {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(dep))) return required_keys = ["name", "spatial_resolution", "temporal_resolution", "units"] for key in required_keys: if key not in dep: fmt = "Expected a value for '{}' in each model dependency, only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, dep)))
[docs]def validate_scenario_data_config(scenario_data): """Check scenario data """ if not isinstance(scenario_data, list): fmt = "Expected a list of scenario datasets in main model config, " + \ "instead got {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(scenario_data))) return for scenario in scenario_data: validate_scenario(scenario)
[docs]def validate_scenario(scenario): """Check a single scenario specification """ if not isinstance(scenario, dict): fmt = "Expected a scenario specification, instead got {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(scenario))) return required_keys = ["parameter", "spatial_resolution", "temporal_resolution", "units", "file"] for key in required_keys: if key not in scenario: fmt = "Expected a value for '{}' in each scenario, only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, scenario)))
[docs]def validate_scenario_data(data, file_path): """Check a list of scenario observations """ if not isinstance(data, list): fmt = "Expected a list of scenario data in {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(file_path))) return for datum in data: validate_scenario_datum(datum, file_path)
[docs]def validate_scenario_datum(datum, file_path): """Check a single scenario datum """ if not isinstance(datum, dict): fmt = "Expected a scenario data point, instead got {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(datum))) return required_keys = ["region", "interval", "year", "value"] for key in required_keys: if key not in datum: fmt = "Expected a value for '{}' in each data point in a scenario, " + \ "only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, datum)))
[docs]def validate_initial_conditions(data, file_path): """Check a list of initial condition observations """ if not isinstance(data, list): fmt = "Expected a list of initial conditions in {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(file_path))) return for datum in data: validate_initial_condition(datum, file_path)
[docs]def validate_initial_condition(datum, file_path): """Check a single initial condition datum """ if not isinstance(datum, dict): fmt = "Expected a initial condition data point, instead got {} from {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(datum, file_path))) return required_keys = ["name", "location", "capital_cost", "operational_lifetime", "economic_lifetime"] for key in required_keys: if key not in datum: fmt = "Expected a value for '{}' in each data point in a initial condition, " + \ "only received {} from {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, datum, file_path)))
[docs]def validate_planning_config(planning): """Check planning options """ required_keys = ["pre_specified", "rule_based", "optimisation"] for key in required_keys: if key not in planning: fmt = "No '{}' settings specified under 'planning' " + \ "in main config file." VALIDATION_ERRORS.append(ValidationError(fmt.format(key))) # check each planning type for key, planning_type in planning.items(): if "use" not in planning_type: fmt = "No 'use' settings specified for '{}' 'planning'" VALIDATION_ERRORS.append(ValidationError(fmt.format(key))) continue if planning_type["use"]: if "files" not in planning_type or \ not isinstance(planning_type["files"], list) or \ len(planning_type["files"]) == 0: fmt = "No 'files' provided for the '{}' " + \ "planning type in main config file." VALIDATION_ERRORS.append(ValidationError(fmt.format(key)))
[docs]def validate_region_sets_config(region_sets): """Check regions sets """ required_keys = ["name", "file"] for key in required_keys: for region_set in region_sets: if key not in region_set: fmt = "Expected a value for '{}' in each " + \ "region set in main config file, only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, region_set)))
[docs]def validate_interval_sets_config(interval_sets): """Check interval sets """ required_keys = ["name", "file"] for key in required_keys: for interval_set in interval_sets: if key not in interval_set: fmt = "Expected a value for '{}' in each " + \ "interval set in main config file, only received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(key, interval_set)))
[docs]def validate_interventions(data, path): """Validate the loaded data as required for model interventions """ # check required keys required_keys = ["name", "location", "capital_cost", "operational_lifetime", "economic_lifetime"] # except for some keys which are allowed simple values, # expect each attribute to be of the form {value: x, units: y} simple_keys = ["name", "sector", "location"] for intervention in data: for key in required_keys: if key not in intervention: fmt = "Loading interventions from {}, required " + \ "a value for '{}' in each intervention, but only " + \ "received {}" VALIDATION_ERRORS.append(ValidationError(fmt.format(path, key, intervention))) for key, value in intervention.items(): if key not in simple_keys and ( not isinstance(value, dict) or "value" not in value or "units" not in value): fmt = "Loading interventions from {3}, {0}.{1} was {2} but " + \ "should have specified units, " + \ "e.g. {{'value': {2}, 'units': 'm'}}" msg = fmt.format(intervention["name"], key, value, path) VALIDATION_ERRORS.append(ValidationError(msg))