# -*- 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
"""
Functions and `dsp` model to model the basic mechanics of the gear box.
"""
import math
import numpy as np
import schedula as sh
from co2mpas.defaults import dfl
import co2mpas.utils as co2_utl
dsp = sh.BlueDispatcher(
name='mechanical model',
description='Models the gear box mechanical.'
)
def _correct_gear_shifts(
times, ratios, gears, velocity_speed_ratios, shift_window=4.0):
import scipy.optimize as sci_opt
from . import calculate_gear_shifts
shifts = calculate_gear_shifts(gears)
vsr = np.vectorize(lambda v: velocity_speed_ratios.get(v, 0))
s = len(gears)
def _err(v, r):
v = int(v) or 1
return np.float32(co2_utl.mae(ratios[slice(v - 1, v + 1, 1)], r))
k = 0
new_gears = np.zeros_like(gears)
dt = shift_window / 2
for i in np.arange(s)[shifts]:
g = gears[slice(i - 1, i + 1, 1)]
j = int(i)
if g[0] != 0 and g[-1] != 0:
t = times[i]
n = max(i - (((t - dt) <= times) & (times <= t)).sum(), min(i, k))
m = min(i + ((t <= times) & (times <= (t + dt))).sum(), s)
if n + 1 > m:
# noinspection PyTypeChecker
j = int(sci_opt.brute(
_err, (slice(n, m, 1),), args=(vsr(g),), finish=None)
)
x = slice(j - 1, j + 1, 1)
new_gears[x] = g
new_gears[k:x.start] = g[0]
k = x.stop
new_gears[k:] = new_gears[k - 1]
return new_gears
dsp.add_data('stop_velocity', dfl.values.stop_velocity)
dsp.add_data('plateau_acceleration', dfl.values.plateau_acceleration)
dsp.add_data('change_gear_window_width', dfl.values.change_gear_window_width)
[docs]@sh.add_function(dsp, outputs=['gears'])
def identify_gears(
times, velocities, accelerations, gear_box_speeds_in,
velocity_speed_ratios, stop_velocity, plateau_acceleration,
change_gear_window_width, idle_engine_speed=(0, 0)):
"""
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 gear_box_speeds_in:
Gear box speed [RPM].
:type gear_box_speeds_in: 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)
:return:
Gear vector identified [-].
:rtype: numpy.array
"""
with np.errstate(divide='ignore', invalid='ignore'):
r = velocities / gear_box_speeds_in
idle = (idle_engine_speed[0] - idle_engine_speed[1],
idle_engine_speed[0] + idle_engine_speed[1])
r[gear_box_speeds_in <= idle[0]] = 0
vsr = velocity_speed_ratios
g, vsr = np.array([(k, v) for k, v in sorted(vsr.items()) if k != 0]).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))
gear = np.where(b, 0, g[i])
b = (velocities > stop_velocity) & (accelerations > 0)
b |= accelerations > plateau_acceleration
gear[(gear == 0) & b] = 1
gear = co2_utl.median_filter(times, gear, change_gear_window_width)
gear = _correct_gear_shifts(times, r, gear, velocity_speed_ratios)
gear = co2_utl.clear_fluctuations(times, gear, change_gear_window_width)
return gear.astype(int)
def _shift(a):
return np.where(np.ediff1d(a.astype(float), [1], [1]) != 0)[0].tolist()
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
[docs]@sh.add_function(dsp, outputs=['gears'], weight=10)
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)
[docs]@sh.add_function(dsp, outputs=['gear_box_speeds_in'])
def calculate_gear_box_speeds_in_v1(
gears, gear_box_speeds_out, gear_box_ratios):
"""
Calculates Gear box speed vector [RPM].
:param gears:
Gear vector [-].
:type gears: numpy.array
:param gear_box_speeds_out:
Wheel speed vector [RPM].
:type gear_box_speeds_out: numpy.array
:param gear_box_ratios:
Gear box ratios [-].
:type gear_box_ratios: dict[int, float | int]
:return:
Gear box speed vector [RPM].
:rtype: numpy.array
"""
d = {0: 0.0}
d.update(gear_box_ratios)
ratios = np.vectorize(lambda k: d[k])(gears)
return gear_box_speeds_out * ratios
[docs]@sh.add_function(dsp, outputs=['gear_box_speeds_in'], weight=25)
def calculate_gear_box_speeds_in(
gears, velocities, velocity_speed_ratios, stop_velocity):
"""
Calculates Gear box speed vector [RPM].
:param gears:
Gear vector [-].
:type gears: numpy.array
:param velocities:
Velocity vector [km/h].
:type velocities: 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
:return:
Gear box speed vector [RPM].
:rtype: numpy.array
"""
speeds = np.array(velocities, dtype=float)
n = velocities >= stop_velocity
b = ~n
for k, r in velocity_speed_ratios.items():
if r:
speeds[n & (gears == k)] /= r
else:
b |= gears == k
speeds[b] = 0.0
return speeds
[docs]@sh.add_function(dsp, outputs=['speed_velocity_ratios'])
def calculate_speed_velocity_ratios(
gear_box_ratios, final_drive_ratios, r_dynamic):
"""
Calculates speed velocity ratios of the gear box [h*RPM/km].
:param gear_box_ratios:
Gear box ratios [-].
:type gear_box_ratios: dict[int, float | int]
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:return:
Speed velocity ratios of the gear box [h*RPM/km].
:rtype: dict
"""
c = 30 / (3.6 * math.pi * r_dynamic)
svr = {k: c * v * final_drive_ratios[k] for k, v in gear_box_ratios.items()}
svr[0] = dfl.INF
return svr
[docs]@sh.add_function(dsp, outputs=['speed_velocity_ratios'], weight=5)
@sh.add_function(
dsp, inputs=['gears', 'velocities', 'engine_speeds_out', 'stop_velocity'],
outputs=['speed_velocity_ratios'], weight=10
)
def identify_speed_velocity_ratios(
gears, velocities, gear_box_speeds_in, stop_velocity):
"""
Identifies speed velocity ratios from gear vector [h*RPM/km].
:param gears:
Gear vector [-].
:type gears: numpy.array
:param velocities:
Velocity vector [km/h].
:type velocities: numpy.array
:param gear_box_speeds_in:
Gear box speed vector [RPM].
:type gear_box_speeds_in: numpy.array
:param stop_velocity:
Maximum velocity to consider the vehicle stopped [km/h].
:type stop_velocity: float
:return:
Speed velocity ratios of the gear box [h*RPM/km].
:rtype: dict
"""
with np.errstate(divide='ignore', invalid='ignore'):
ratios = gear_box_speeds_in / velocities
ratios[velocities < stop_velocity] = 0
svr = {k: co2_utl.reject_outliers(ratios[gears == k])[0]
for k in range(1, int(max(gears)) + 1)
if k in gears}
svr[0] = dfl.INF
return svr
[docs]@sh.add_function(dsp, outputs=['velocity_speed_ratios'])
def calculate_velocity_speed_ratios(speed_velocity_ratios):
"""
Calculates velocity speed (or speed velocity) ratios of the gear box
[km/(h*RPM) or h*RPM/km].
:param speed_velocity_ratios:
Constant speed velocity (or velocity speed) ratios of the gear box
[h*RPM/km or km/(h*RPM)].
:type speed_velocity_ratios: dict[int | float]
:return:
Constant velocity speed (or speed velocity) ratios of the gear box
[km/(h*RPM) or h*RPM/km].
:rtype: dict
"""
def _inverse(v):
if v <= 0:
return dfl.INF
elif v >= dfl.INF:
return 0.0
else:
return 1 / v
return {k: _inverse(v) for k, v in speed_velocity_ratios.items()}
[docs]@sh.add_function(dsp, outputs=['n_gears'])
@sh.add_function(dsp, inputs=['velocity_speed_ratios'], outputs=['n_gears'])
def identify_n_gears(gear_box_ratios):
"""
Identify the number of gears [-].
:param gear_box_ratios:
Gear box ratios [-].
:type gear_box_ratios: dict[int, float | int]
:return:
Number of gears [-].
:rtype: int
"""
return max(gear_box_ratios)
[docs]@sh.add_function(dsp, outputs=['velocity_speed_ratios'], weight=48)
def identify_velocity_speed_ratios(
n_gears, gear_box_speeds_in, velocities, stop_velocity):
"""
Identifies velocity speed ratios from gear box speed vector [km/(h*RPM)].
:param n_gears:
Number of gears [-].
:type n_gears: int
:param gear_box_speeds_in:
Gear box speed [RPM].
:type gear_box_speeds_in: numpy.array
:param velocities:
Velocity vector [km/h].
:type velocities: numpy.array
:param stop_velocity:
Maximum velocity to consider the vehicle stopped [km/h].
:type stop_velocity: float
:return:
Constant velocity speed ratios of the gear box [km/(h*RPM)].
:rtype: dict
"""
return identify_velocity_speed_ratios_v1(
n_gears, gear_box_speeds_in, velocities, (0, 0), stop_velocity
)
[docs]@sh.add_function(dsp, outputs=['velocity_speed_ratios'], weight=49)
def identify_velocity_speed_ratios_v1(
n_gears, engine_speeds_out, velocities, idle_engine_speed,
stop_velocity):
"""
Identifies velocity speed ratios from gear box speed vector [km/(h*RPM)].
:param n_gears:
Number of gears [-].
:type n_gears: int
:param engine_speeds_out:
Engine speed [RPM].
:type engine_speeds_out: numpy.array
:param velocities:
Velocity vector [km/h].
:type velocities: numpy.array
:param idle_engine_speed:
Engine speed idle median and std [RPM].
:type idle_engine_speed: (float, float)
:param stop_velocity:
Maximum velocity to consider the vehicle stopped [km/h].
:type stop_velocity: float
:return:
Constant velocity speed ratios of the gear box [km/(h*RPM)].
:rtype: dict
"""
import sklearn.cluster as sk_clu
idle_speed = idle_engine_speed[0] + idle_engine_speed[1]
b = (engine_speeds_out > idle_speed) & (velocities > stop_velocity)
x = (velocities[b] / engine_speeds_out[b])[:, None]
ms = sk_clu.KMeans(n_clusters=int(n_gears), copy_x=False, random_state=0)
ms.fit(x)
vsr = {k + 1: v for k, v in enumerate(sorted(ms.cluster_centers_[:, 0]))}
vsr[0] = 0.0
return vsr
[docs]@sh.add_function(dsp, outputs=['velocity_speed_ratios'], weight=50)
def identify_velocity_speed_ratios_v2(
gear_box_speeds_in, velocities, stop_velocity):
"""
Identifies velocity speed ratios from gear box speed vector [km/(h*RPM)].
:param gear_box_speeds_in:
Gear box speed [RPM].
:type gear_box_speeds_in: numpy.array
:param velocities:
Velocity vector [km/h].
:type velocities: numpy.array
:param stop_velocity:
Maximum velocity to consider the vehicle stopped [km/h].
:type stop_velocity: float
:return:
Constant velocity speed ratios of the gear box [km/(h*RPM)].
:rtype: dict
"""
return identify_velocity_speed_ratios_v3(
gear_box_speeds_in, velocities, (0, 0), stop_velocity
)
[docs]@sh.add_function(dsp, outputs=['velocity_speed_ratios'], weight=51)
def identify_velocity_speed_ratios_v3(
engine_speeds_out, velocities, idle_engine_speed, stop_velocity):
"""
Identifies velocity speed ratios from gear box speed vector [km/(h*RPM)].
:param engine_speeds_out:
Engine speed [RPM].
:type engine_speeds_out: numpy.array
:param velocities:
Velocity vector [km/h].
:type velocities: numpy.array
:param idle_engine_speed:
Engine speed idle median and std [RPM].
:type idle_engine_speed: (float, float)
:param stop_velocity:
Maximum velocity to consider the vehicle stopped [km/h].
:type stop_velocity: float
:return:
Constant velocity speed ratios of the gear box [km/(h*RPM)].
:rtype: dict
"""
import sklearn.cluster as sk_clu
idle_speed = idle_engine_speed[0] + idle_engine_speed[1]
b = (engine_speeds_out > idle_speed) & (velocities > stop_velocity)
x = (velocities[b] / engine_speeds_out[b])[:, None]
bandwidth = sk_clu.estimate_bandwidth(x, quantile=0.2)
ms = sk_clu.MeanShift(bandwidth=bandwidth, bin_seeding=True)
ms.fit(x)
vsr = {k + 1: v for k, v in enumerate(sorted(ms.cluster_centers_[:, 0]))}
vsr[0] = 0.0
return vsr
[docs]@sh.add_function(dsp, outputs=['gear_box_ratios'])
def calculate_gear_box_ratios(
velocity_speed_ratios, final_drive_ratios, r_dynamic):
"""
Calculates gear box ratios [-].
:param velocity_speed_ratios:
Constant velocity speed ratios of the gear box [km/(h*RPM)].
:type velocity_speed_ratios: dict[int | float]
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:return:
Gear box ratios [-].
:rtype: dict
"""
c = 30 / (3.6 * math.pi * r_dynamic)
r = calculate_velocity_speed_ratios(velocity_speed_ratios)
return {k: v / (c * final_drive_ratios[k]) for k, v in r.items() if k != 0}
[docs]@sh.add_function(dsp, outputs=['max_gear'])
def identify_max_gear(speed_velocity_ratios):
"""
Identifies the maximum gear of the gear box [-].
:param speed_velocity_ratios:
Speed velocity ratios of the gear box [h*RPM/km].
:type speed_velocity_ratios: dict[int | float]
:return:
Maximum gear of the gear box [-].
:rtype: int
"""
return int(max(speed_velocity_ratios))
[docs]@sh.add_function(dsp, outputs=['first_gear_box_ratio'])
def identify_first_gear_box_ratio(gear_box_ratios):
"""
Identify the gear box ratio of first gear.
:param gear_box_ratios:
Gear box ratios [-].
:type gear_box_ratios: dict[int, float | int]
:return:
Gear box ratio of first gear [-].
:return: float
"""
return gear_box_ratios.get(1, sh.NONE)
[docs]@sh.add_function(dsp, outputs=['last_gear_box_ratio'])
def identify_last_gear_box_ratio(gear_box_ratios):
"""
Identify the gear box ratio of last gear.
:param gear_box_ratios:
Gear box ratios [-].
:type gear_box_ratios: dict[int, float | int]
:return:
Gear box ratio of last gear [-].
:return: float
"""
# noinspection PyUnresolvedReferences
return max(gear_box_ratios.items())[1]
[docs]@sh.add_function(dsp, outputs=['engine_speed_at_max_velocity'])
def calculate_engine_speed_at_max_velocity(
r_dynamic, final_drive_ratios, last_gear_box_ratio, maximum_velocity):
"""
Calculates the maximum velocity from full load curve.
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float | numpy.array
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param last_gear_box_ratio:
Gear box ratio of the last gear [-].
:type last_gear_box_ratio: float | numpy.array
:param maximum_velocity:
Maximum velocity [km/h].
:type maximum_velocity: float | numpy.array
:return:
Engine speed at maximum velocity [RPM].
:return: float
"""
speed = last_gear_box_ratio / calculate_last_gear_box_ratio(
r_dynamic, final_drive_ratios, 1, maximum_velocity
)
return speed
[docs]@sh.add_function(dsp, outputs=['last_gear_box_ratio'])
def calculate_last_gear_box_ratio(
r_dynamic, final_drive_ratios, engine_speed_at_max_velocity,
maximum_velocity):
"""
Calculates the gear box ratio of the last gear.
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param engine_speed_at_max_velocity:
Engine speed at maximum velocity [RPM].
:type engine_speed_at_max_velocity: float
:param maximum_velocity:
Maximum velocity [km/h].
:type maximum_velocity: float
:return:
Gear box ratio of the last gear [-].
:return: float
"""
ratio = 3.6 * 2 * np.pi * r_dynamic * engine_speed_at_max_velocity
# noinspection PyUnresolvedReferences
ratio /= 60 * max(final_drive_ratios.items())[1] * maximum_velocity
return ratio
def _calculate_req_power(road_loads, velocity):
return np.polyval(road_loads[::-1], velocity) * velocity / 3600
[docs]@sh.add_function(dsp, outputs=['last_gear_box_ratio'], weight=5)
def calculate_last_gear_box_ratio_v1(
full_load_curve, final_drive_ratios, road_loads, r_dynamic,
maximum_velocity):
"""
Calculates the gear box ratio of the last gear from full load curve.
:param full_load_curve:
Vehicle full load curve.
:type full_load_curve: function
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param road_loads:
Cycle road loads [N, N/(km/h), N/(km/h)^2].
:type road_loads: list, tuple
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:param maximum_velocity:
Maximum velocity [km/h].
:type maximum_velocity: float
:return:
Gear box ratio of the last gear [-].
:return: float
"""
d = dfl.functions.calculate_last_gear_box_ratio_v1
ratio = np.arange(d.MAX_RATIO, d.MIN_RATIO, -d.DELTA_RATIO)
p = full_load_curve(calculate_engine_speed_at_max_velocity(
r_dynamic, final_drive_ratios, ratio, maximum_velocity
))
return ratio[p > _calculate_req_power(road_loads, maximum_velocity)][0]
[docs]@sh.add_function(dsp, outputs=['maximum_velocity', 'gear_at_maximum_velocity'])
def calculate_maximum_velocity(
full_load_curve, road_loads, speed_velocity_ratios):
"""
Calculates the maximum velocity from full load curve.
:param full_load_curve:
Vehicle full load curve.
:type full_load_curve: function
:param road_loads:
Cycle road loads [N, N/(km/h), N/(km/h)^2].
:type road_loads: list, tuple
:param speed_velocity_ratios:
Speed velocity ratios of the gear box [h*RPM/km].
:type speed_velocity_ratios: dict[int | float]
:return:
Maximum velocity and gear at maximum velocity [km/h, -].
:return: float, int
"""
d = dfl.functions.calculate_maximum_velocity
velocity = np.arange(d.MIN_VEL, d.MAX_VEL, d.DELTA_VEL, float)
g_id, svr = zip(*[(k, v) for k, v in sorted(
speed_velocity_ratios.items(), reverse=True
) if k])
p = full_load_curve(np.round(np.multiply(velocity[:, None], svr), 1))
b = (p * d.PREC_FLC) < _calculate_req_power(road_loads, velocity)[:, None]
# noinspection PyUnresolvedReferences
velocity = np.repeat(velocity[:, None], b.shape[1], 1)
velocity[b] = -1
i, j = np.unravel_index(np.argmax(velocity), velocity.shape)
return velocity[i, j], g_id[j]
[docs]@sh.add_function(dsp, outputs=['maximum_velocity'], weight=10)
def calculate_maximum_velocity_v1(
r_dynamic, final_drive_ratios, engine_speed_at_max_velocity,
last_gear_box_ratio):
"""
Calculates the maximum velocity [km/h].
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param engine_speed_at_max_velocity:
Engine speed at maximum velocity [RPM].
:type engine_speed_at_max_velocity: float
:param last_gear_box_ratio:
Gear box ratio of the last gear [-].
:type last_gear_box_ratio: float
:return:
Maximum velocity [km/h].
:return: float
"""
vel = calculate_last_gear_box_ratio(
r_dynamic, final_drive_ratios, engine_speed_at_max_velocity, 1
) / last_gear_box_ratio
return vel
[docs]@sh.add_function(dsp, outputs=['maximum_velocity'], weight=15)
def calculate_maximum_velocity_v2(
full_load_curve, final_drive_ratios, road_loads, r_dynamic,
last_gear_box_ratio):
"""
Calculates the maximum velocity from full load curve.
:param full_load_curve:
Vehicle full load curve.
:type full_load_curve: function
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:param road_loads:
Cycle road loads [N, N/(km/h), N/(km/h)^2].
:type road_loads: list, tuple
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:param last_gear_box_ratio:
Gear box ratio of the last gear [-].
:type last_gear_box_ratio: float
:return:
Maximum velocity [km/h].
:return: float
"""
d = dfl.functions.calculate_maximum_velocity_v2
velocity = np.arange(d.MAX_VEL, d.MIN_VEL, -d.DELTA_VEL)
p = full_load_curve(calculate_engine_speed_at_max_velocity(
r_dynamic, final_drive_ratios, last_gear_box_ratio, velocity
))
return velocity[p > _calculate_req_power(road_loads, velocity)][0]
[docs]@sh.add_function(dsp, outputs=['first_gear_box_ratio'], weight=100)
def calculate_first_gear_box_ratio(
f0, r_dynamic, engine_max_torque, maximum_vehicle_laden_mass,
final_drive_ratios):
"""
Calculates the gear box ratio of the first gear.
:param f0:
Rolling resistance force [N] when angle_slope == 0.
:type f0: float
:param r_dynamic:
Dynamic radius of the wheels [m].
:type r_dynamic: float
:param engine_max_torque:
Engine Max Torque [N*m].
:type engine_max_torque: float
:param maximum_vehicle_laden_mass:
Technically permissible maximum laden mass [kg].
:type maximum_vehicle_laden_mass: float
:param final_drive_ratios:
Final drive ratios [-].
:type final_drive_ratios: dict[int, float | int]
:return:
Gear box ratio of the first gear [-].
:return: float
"""
d = dfl.functions.calculate_first_gear_box_ratio
max_torque = engine_max_torque * d.MAX_TORQUE_PERCENTAGE
slope = np.arctan(d.STARTING_SLOPE)
ratio = f0 * np.cos(slope)
ratio += maximum_vehicle_laden_mass * 9.81 * np.sin(slope)
ratio /= (max_torque * final_drive_ratios[1]) / r_dynamic
return ratio
[docs]@sh.add_function(dsp, outputs=['gear_box_ratios'])
def design_gear_box_ratios(n_gears, first_gear_box_ratio, last_gear_box_ratio):
"""
Designs the gear box ratios [-].
:param n_gears:
Number of gears [-].
:type n_gears: int
:param first_gear_box_ratio:
Gear box ratio of the first gear [-].
:type first_gear_box_ratio: float
:param last_gear_box_ratio:
Gear box ratio of the last gear [-].
:type last_gear_box_ratio: float
:return:
Gear box ratios [-].
:rtype: dict
"""
n_gears = int(n_gears)
d = dfl.functions.design_gear_box_ratios
f_two, f_tuning = np.asarray(d.f_two), np.asarray(d.f_tuning)
ix = np.indices((len(f_two), len(f_tuning))).reshape(2, -1).T
f_two, f_tuning = f_two[ix[:, 0]][:, None], f_tuning[ix[:, 1]][:, None]
ratios = np.zeros((ix.shape[0], n_gears), float)
ratios[:, 0], ratios[:, -1] = first_gear_box_ratio, last_gear_box_ratio
n, fgbr, lgbr = n_gears - 1, first_gear_box_ratio, last_gear_box_ratio
f_one = f_tuning * (fgbr / (f_two ** (n * (n - 1) / 2))) ** (1 / n)
n = n_gears - np.arange(2, n_gears)
ratios[:, 1:-1] = lgbr * (f_one ** n) * (f_two ** (n * (n - 1) / 2))
dr = np.diff(ratios, axis=1)
b = np.all(dr < 0, axis=1)
if b.any():
ratios, dr = ratios[b], dr[b]
b = dr[:, 0] < 2
if b.any():
ratios, dr = ratios[b], dr[b]
b = np.all(np.diff(dr, axis=1) > 0, axis=1)
if b.any():
ratios, dr = ratios[b], dr[b]
res = np.linalg.lstsq(
np.vander(np.arange(n_gears - 1), 3), dr.T, rcond=-1
)[0]
b = res[0] < 0
if b.any():
ratios, dr, res = ratios[b], dr[b], res[:, b]
return dict(enumerate(ratios[np.argmin(res[0])], 1))