Source code for co2mpas.core.load.validate.hard

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Copyright 2015-2019 European Commission (JRC);
# Licensed under the EUPL (the 'Licence');
# You may not use this work except in compliance with the Licence.
# You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
"""
It provides functions to perform the hard validation.
"""
import logging
import functools
import numpy as np
import schedula as sh
from co2mpas.defaults import dfl

log = logging.getLogger(__name__)


[docs]def check_sign_currents(battery_currents, alternator_currents): """ Checks if battery currents and alternator currents have the right signs. :param battery_currents: Low voltage battery current vector [A]. :type battery_currents: numpy.array :param alternator_currents: Alternator currents [A]. :type alternator_currents: numpy.array :return: If battery and alternator currents have the right signs. :rtype: (bool, bool) """ #: Maximum allowed positive current for the alternator currents check [A]. import co2mpas.utils as co2_utl max_pos_curr = 1.0 b_c, a_c = battery_currents, alternator_currents a = co2_utl.reject_outliers(a_c, med=np.mean)[0] a = a <= max_pos_curr c = np.cov(a_c, b_c)[0][1] if c < 0: x = (a, a) elif c == 0: if any(b_c): x = (co2_utl.reject_outliers(b_c, med=np.mean)[0] <= 0, a) else: x = (True, a) else: x = (not a, a) return x
# noinspection PyUnusedLocal def _check_sign_currents(data, *args): c = ('service_battery_currents', 'alternator_currents') try: a = sh.selector(c, data, output_type='list') s = check_sign_currents(*a) if not all(s): s = ' and '.join([k for k, v in zip(c, s) if not v]) msg = "Probably '{}' have the wrong sign!".format(s) return c, msg except KeyError: # `c` is not in `data`. pass # noinspection PyUnusedLocal def _check_acr(data, *args): s = ('active_cylinder_ratios', 'engine_has_cylinder_deactivation') acr = data.get(s[0], (1,)) has_acr = data.get(s[1], dfl.values.engine_has_cylinder_deactivation) if has_acr and len(acr) <= 1: msg = "Please since `engine_has_cylinder_deactivation` is True set " \ "at least two `active_cylinder_ratios` or set False!" return s, msg elif not has_acr and len(acr) > 1: msg = "Please since there are %d `active_cylinder_ratios` set " \ "`engine_has_cylinder_deactivation = True` " \ "or remove the extra ratios!" % len(acr) return s, msg # noinspection PyUnusedLocal def _check_ki_factor(data, *args): s = 'has_periodically_regenerating_systems', 'ki_multiplicative', \ 'ki_additive' has_prs = data.get(s[0], dfl.values.has_periodically_regenerating_systems) if data.get(s[1], 1) > 1 and data.get(s[2], 0) > 0: msg = "Please since `ki_multiplicative` is > 1 and `ki_additive` " \ "is > 0 set `ki_multiplicative = 1` or set `ki_additive = 0`!" return s[1:], msg elif not has_prs: if data.get(s[1], 1) > 1: msg = "Please since `ki_multiplicative` is > 1 set " \ "`has_periodically_regenerating_systems = True` or set " \ "`ki_multiplicative = 1`!" return s, msg elif data.get(s[2], 0) > 0: msg = "Please since `ki_additive` is > 0 set " \ "`has_periodically_regenerating_systems = True` or set " \ "`ki_additive = 0`!" return s, msg # noinspection PyUnusedLocal def _check_prediction_gears_not_mt(data, usage, stage, cycle, *args): s = ('gear_box_type', 'gears') gear_box_type = data.get(s[0], 'manual') if stage == 'prediction' and s[1] in data and gear_box_type != 'manual': msg = "`gears` cannot be provided when `gear_box_type` is '%s'." \ " Hence, remove the `gears` or set `gear_box_type` to manual!" return s, msg % gear_box_type @functools.lru_cache(None) def _get_engine_model(outputs): from ...model.physical.engine import dsp return dsp.register(memo={}).shrink_dsp(outputs=outputs) # noinspection PyUnusedLocal def _check_lean_burn_tech(data, *args): s = ('has_lean_burn', 'ignition_type') it = _get_engine_model(s[1:])(data, outputs=s[1:]).get(s[1], None) has_lb = data.get(s[0], dfl.values.has_lean_burn) if has_lb and it not in ('positive', None): msg = "`has_lean_burn` cannot be enable with `ignition_type = '%s'`." \ "Hence, set `has_lean_burn = False` or " \ "set `ignition_type = 'positive'`!" % it return s, msg # noinspection PyUnusedLocal def _check_full_load(data, *args): s = ('idle_engine_speed_median', 'full_load_speeds') try: r = sh.selector(s, _get_engine_model(s)(data, s), output_type='list') except KeyError: return if r[0] < r[1][0]: msg = "You have not provided Full Load Curve values below %f RPMs. \n" \ "This may cause issues in the simulation. Please start from " \ "idle RPM (%f) or correct the " \ "`idle_engine_speed_median = full_load_speeds[0]`!" return s, msg % (r[1][0], r[0]) # noinspection PyUnusedLocal def _warn_vva(data, *args): s = ('engine_has_variable_valve_actuation', 'ignition_type') it = _get_engine_model(s[1:]).dispatch(data, outputs=s[1:]).get(s[1], None) has_vva = data.get(s[0], dfl.values.engine_has_variable_valve_actuation) if has_vva and it not in ('positive', None): msg = "Please, ensure that the input combination " \ "`engine_has_variable_valve_actuation = True` and " \ "`ignition_type = '%s'` is correct. If it is intentionally " \ "added, you can neglect this warning. Otherwise, please " \ "correct the input setting " \ "`engine_has_variable_valve_actuation = False` or setting " \ "`ignition_type = 'positive'`!" % it log.warning(msg) # noinspection PyUnusedLocal def _check_scr(data, *args): s = ('has_selective_catalytic_reduction', 'ignition_type') out = _get_engine_model(s[1:]).dispatch(data, outputs=s[1:]) it = out.get(s[1], None) has_scr = data.get(s[0], dfl.values.has_selective_catalytic_reduction) if has_scr and it == 'positive': msg = "`has_selective_catalytic_reduction` cannot be enable with " \ "`ignition_type = '%s'`." \ "Hence, set `has_selective_catalytic_reduction = False` or " \ "set `ignition_type = 'compression'`!" % it return s, msg # noinspection PyUnusedLocal def _check_has_torque_converter(data, *args): s = ('gear_box_type', 'has_torque_converter') if data.get(s[1], False) and data.get(s[0], '') == 'manual': msg = "`has_torque_converter` cannot be 'True' when " \ "`gear_box_type` is 'manual'." \ "Hence, set `has_torque_converter = False` or " \ "set `gear_box_type != 'manual'`!" return s, msg # noinspection PyUnusedLocal def _check_relative_electric_energy_change(data, *args): c = ('relative_electric_energy_change', 'transition_cycle_index') try: reec, n = sh.selector(c, data, output_type='list') b = np.array(reec) < .04 if not (b.shape[0] and b.sum() == 1 and b[-1]): msg = '{} must have only the last value <.04!'.format(c[0]) return c[:-1], msg if len(reec) != n + 1: msg = '`len({}) != {} + 1`!'.format(*c) return c, msg except KeyError: # `c` is not in `data`. pass # noinspection PyUnusedLocal def _check_gear_box(data, *args): c = ('gear_box_type', 'is_hybrid') try: gear_box_type, is_hybrid = sh.selector(c, data, output_type='list') if gear_box_type == 'planetary' and not is_hybrid: msg = "`gear_box_type` cannot be 'planetary' when " \ "`is_hybrid = False`." \ "Hence, set `gear_box_type != 'planetary'` or " \ "set `is_hybrid = True`!" return c, msg except KeyError: # `c` is not in `data`. pass def _hard_validation(data, usage, stage=None, cycle=None, *args): if usage in ('input', 'target', 'meta'): checks = ( _check_sign_currents, _check_acr, _check_ki_factor, _check_prediction_gears_not_mt, _check_lean_burn_tech, _check_full_load, _warn_vva, _check_scr, _check_has_torque_converter, _check_relative_electric_energy_change, _check_gear_box ) for check in checks: c = check(data, usage, stage, cycle, *args) if c: yield c
[docs]def hard_validation(inputs, errors): """ Parse input data for the hard validation. :param inputs: Input data. :type inputs: dict :param errors: Errors container. :type errors: dict :return: Parsed input data and errors container. :rtype: dict, dict """ from schema import SchemaError for k, v in sh.stack_nested_keys(inputs, depth=3): for c, msg in _hard_validation(v, *k): sh.get_nested_dicts(errors, *k)[c] = SchemaError([], [msg]) return inputs, errors