def _calibrate_gsm( velocity_speed_ratios, on_engine, anomalies, gear, velocities, stop_velocity, idle_engine_speed): # noinspection PyProtectedMember from .at_gear.cmv import CMV, _filter_gear_shifting_velocity as filter_gs idle = idle_engine_speed[0] - idle_engine_speed[1] _vsr = sh.combine_dicts(velocity_speed_ratios, base={0: 0}) limits = { 0: {False: [0]}, 1: {True: [stop_velocity]}, max(_vsr): {True: [dfl.INF]} } shifts = np.unique(sum(map(_shift, (on_engine, anomalies)), [])) for i, j in sh.pairwise(shifts): if on_engine[i:j].all() and not anomalies[i:j].any(): for v in np.array(list(sh.pairwise(_shift(gear[i:j])))) + i: if j != v[1]: v, (g, ng) = velocities[slice(*v)], gear[[v[1] - 1, v[1]]] up = g < ng sh.get_nested_dicts(limits, g, up, default=list).append( v.max() if up else v.min() ) for k, v in list(limits.items()): limits[k] = v.get(False, [_vsr[k] * idle] * 2), v.get(True, []) d = {j: i for i, j in enumerate(sorted(limits))} gsm = CMV(filter_gs(sh.map_dict(d, limits), stop_velocity)) gsm.velocity_speed_ratios = sh.selector(gsm, sh.map_dict(d, _vsr)) gsm.convert(_vsr) return gsm
def fit(self, gears, velocities, motive_powers, velocity_speed_ratios, stop_velocity): self.clear() self.velocity_speed_ratios = velocity_speed_ratios it = zip(velocities, motive_powers, sh.pairwise(gears)) for v, p, (g0, g1) in it: if v > stop_velocity and g0 != g1: x = self.get(g0, [[], [[], []]]) if g0 < g1 and p >= 0: x[1][0].append(p) x[1][1].append(v) elif g0 > g1 and p <= 0: x[0].append(v) else: continue self[g0] = x self[0] = [[0.0], [[0.0], [stop_velocity]]] self[max(self)][1] = [[0, 1], [dfl.INF] * 2] self.cloud = {k: copy.deepcopy(v) for k, v in self.items()} self._fit_cloud() return self
def _identify_gear_shifting_velocity_limits(gears, velocities, stop_velocity): """ Identifies gear shifting velocity matrix. :param gears: Gear vector [-]. :type gears: numpy.array :param velocities: Vehicle velocity [km/h]. :type velocities: numpy.array :param stop_velocity: Maximum velocity to consider the vehicle stopped [km/h]. :type stop_velocity: float :return: Gear shifting velocity matrix. :rtype: dict """ limits = {} for v, (g0, g1) in zip(velocities, sh.pairwise(gears)): if v >= stop_velocity and g0 != g1: limits[g0] = limits.get(g0, [[], []]) limits[g0][int(g0 < g1)].append(v) return _filter_gear_shifting_velocity(limits, stop_velocity)
def get_start_stop(vehicle_max_speed, speed_per_gear, poly_spline): """ Calculate Speed boundaries for each gear. :param vehicle_max_speed: Vehicle maximum speed. :type vehicle_max_speed: int :param speed_per_gear: Speed per gear. :type speed_per_gear: numpy.array :param poly_spline: Poly spline. :type poly_spline: list :return: Start and Stop for each gear. :rtype: numpy.array, numpy.array """ v = np.asarray(speed_per_gear) # Ensure that a higher gear starts from higher speed. assert (v[:-1, 0] < v[1:, 0]).all(), "Incoherent shifting point (vi < vi1)!" start, stop = v[:, 0].copy(), np.minimum(v[:, -1], vehicle_max_speed) start[0], index = 0, np.maximum(0, np.arange(v.shape[1]) - 1) # Find where the curve of each gear cuts the next one. b, _min = v[:-1] > v[1:, 0, None], lambda *a: np.min(a) for i, (_v, (ps0, ps1)) in enumerate(zip(v[:-1], sh.pairwise(poly_spline))): stop[i] = _min(stop[i], *_v[index[b[i] & (ps1(_v) > ps0(_v))]]) return start, stop
def select_phases_integration_times(cycle_type): """ Selects the cycle phases integration times [s]. :param cycle_type: Cycle type (WLTP or NEDC). :type cycle_type: str :return: Cycle phases integration times [s]. :rtype: tuple """ from co2mpas.defaults import dfl v = dfl.functions.select_phases_integration_times.INTEGRATION_TIMES return tuple(sh.pairwise(v[cycle_type.upper()]))
def _correct_gsv(gsv, stop_velocity): """ Corrects gear shifting velocity matrix from unreliable limits. :param gsv: Gear shifting velocity matrix. :type gsv: dict :param stop_velocity: Maximum velocity to consider the vehicle stopped [km/h]. :type stop_velocity: float :return: Gear shifting velocity matrix corrected from unreliable limits. :rtype: dict """ gsv[0] = [0, (stop_velocity, (dfl.INF, 0))] # noinspection PyMissingOrEmptyDocstring def func(x): return not x and float('inf') or 1 / x for v0, v1 in sh.pairwise(gsv.values()): up0, s0, down1, s1 = v0[1][0], v0[1][1][1], v1[0][0], v1[0][1][1] if down1 + s1 <= v0[0]: v0[1], v1[0] = up0 + s0, up0 - s0 elif up0 >= down1: v0[1], v1[0] = up0 + s0, down1 - s1 continue elif (v0[1][1][0], func(s0)) >= (v1[0][1][0], func(s1)): v0[1], v1[0] = up0 + s0, up0 - s0 else: v0[1], v1[0] = down1 + s1, down1 - s1 v0[1] += stop_velocity gsv[max(gsv)][1] = dfl.INF return gsv
def identify_on_idle( times, velocities, engine_speeds_out_hot, gear_box_speeds_in, gears, stop_velocity, min_engine_on_speed, on_engine, idle_engine_speed): """ Identifies when the engine is on idle [-]. :param times: Time vector [s]. :type times: numpy.array :param velocities: Velocity vector [km/h]. :type velocities: numpy.array :param engine_speeds_out_hot: Engine speed at hot condition [RPM]. :type engine_speeds_out_hot: numpy.array :param gear_box_speeds_in: Gear box speed [RPM]. :type gear_box_speeds_in: numpy.array :param gears: Gear vector [-]. :type gears: numpy.array :param stop_velocity: Maximum velocity to consider the vehicle stopped [km/h]. :type stop_velocity: float :param min_engine_on_speed: Minimum engine speed to consider the engine to be on [RPM]. :type min_engine_on_speed: float :param on_engine: If the engine is on [-]. :type on_engine: numpy.array :param idle_engine_speed: Engine speed idle median and std [RPM]. :type idle_engine_speed: (float, float) :return: If the engine is on idle [-]. :rtype: numpy.array """ # noinspection PyProtectedMember from .gear_box.mechanical import _shift b = engine_speeds_out_hot > min_engine_on_speed b &= (gears == 0) | (velocities <= stop_velocity) on_idle = np.zeros_like(times, int) i = np.where(on_engine)[0] ds = np.abs(gear_box_speeds_in[i] - engine_speeds_out_hot[i]) on_idle[i[ds > idle_engine_speed[1]]] = 1 on_idle = co2_utl.median_filter(times, on_idle, 4) on_idle[b] = 1 for i, j in sh.pairwise(_shift(on_idle)): if not on_idle[i] and times[j - 1] - times[i] <= 2: on_idle[i:j] = 1 return co2_utl.clear_fluctuations(times, on_idle, 4).astype(bool)
def identify_gears_v1( times, velocities, accelerations, motive_powers, on_engine, engine_speeds_out, velocity_speed_ratios, stop_velocity, plateau_acceleration, change_gear_window_width, idle_engine_speed, correct_gear): """ Identifies gear time series [-]. :param times: Time vector [s]. :type times: numpy.array :param velocities: Velocity vector [km/h]. :type velocities: numpy.array :param accelerations: Acceleration vector [m/s2]. :type accelerations: numpy.array :param motive_powers: Motive power [kW]. :type motive_powers: numpy.array :param on_engine: If the engine is on [-]. :type on_engine: numpy.array :param engine_speeds_out: Engine speed [RPM]. :type engine_speeds_out: numpy.array :param velocity_speed_ratios: Constant velocity speed ratios of the gear box [km/(h*RPM)]. :type velocity_speed_ratios: dict[int | float] :param stop_velocity: Maximum velocity to consider the vehicle stopped [km/h]. :type stop_velocity: float :param plateau_acceleration: Maximum acceleration to be at constant velocity [m/s2]. :type plateau_acceleration: float :param change_gear_window_width: Time window used to apply gear change filters [s]. :type change_gear_window_width: float :param idle_engine_speed: Engine speed idle median and std [RPM]. :type idle_engine_speed: (float, float) :param correct_gear: A function to correct the gear predicted. :type correct_gear: callable :return: Gear vector identified [-]. :rtype: numpy.array """ n = [k for k in velocity_speed_ratios if k != 0] if len(n) == 1: gears = np.ones_like(times, int) * n[0] gears[velocities <= stop_velocity] = 0 return gears with np.errstate(divide='ignore', invalid='ignore'): r = velocities / engine_speeds_out idle = (idle_engine_speed[0] - idle_engine_speed[1], idle_engine_speed[0] + idle_engine_speed[1]) r[engine_speeds_out <= idle[0]] = 0 _vsr = sh.combine_dicts(velocity_speed_ratios, base={0: 0}) g, vsr = np.array([(k, v) for k, v in sorted(_vsr.items())]).T dr = np.abs(vsr[:, None] - r) i, j = np.argmin(dr, 0), np.arange(times.shape[0]) b = velocities <= vsr[i] * idle[0] if idle[1]: b |= np.abs(velocities / idle[1] - r) < dr[i, j] b = (velocities <= stop_velocity) | (b & (accelerations < 0)) gears = np.where(b, 0, g[i]).astype(int) gears = co2_utl.median_filter(times, gears, change_gear_window_width) gears = _correct_gear_shifts(times, r, gears, velocity_speed_ratios) gears = co2_utl.clear_fluctuations(times, gears, change_gear_window_width) anomalies = velocities > stop_velocity anomalies &= (accelerations > 0) | ~on_engine anomalies |= accelerations > plateau_acceleration anomalies &= gears == 0 from ..control.conventional import calculate_engine_speeds_out_hot ds = calculate_engine_speeds_out_hot(calculate_gear_box_speeds_in( gears, velocities, velocity_speed_ratios, stop_velocity ), on_engine, idle_engine_speed) - engine_speeds_out i = np.where(on_engine & ~anomalies)[0] med = np.nanmedian(ds[i]) std = 3 * max(50, co2_utl.mad(ds[i], med=med)) anomalies[i] |= np.abs(ds[i] - med) >= std b = (gears == 0) & (velocities <= stop_velocity) anomalies[b] = False anomalies = co2_utl.clear_fluctuations( times, anomalies.astype(int), change_gear_window_width ) for i, j in sh.pairwise(_shift(gears)): anomalies[i:j] = anomalies[i:j].mean() > .3 gsm = _calibrate_gsm( velocity_speed_ratios, on_engine, anomalies, gears, velocities, stop_velocity, idle_engine_speed ).init_gear( gears, times, velocities, accelerations, motive_powers, correct_gear=correct_gear ) for i, v in enumerate(anomalies): if v: gears[i] = gsm(i) gears = co2_utl.median_filter(times, gears, change_gear_window_width) gears = co2_utl.clear_fluctuations(times, gears, change_gear_window_width) return gears.astype(int)
def _is_sorted(iterable, key=lambda a, b: a <= b): return all(key(a, b) for a, b in sh.pairwise(iterable))
def test_pairwise(self): self.assertEqual(list(sh.pairwise([1, 2, 3])), [(1, 2), (2, 3)]) sh.pairwise([1, 2, 3, 4]) self.assertEqual(list(sh.pairwise([1])), [])