Source code for suboptimumg.compsim.utils

"""
Utility functions for competition scoring and energy calculations.
"""

import numpy as np
import numpy.typing as npt

from ..constants import joule_to_kwh
from .models import *


[docs] def compute_event_points(scoring_config: EventScoring, t_your: float) -> float: """ Compute competition points for an event based on your time. Parameters ---------- scoring_config : EventScoring Configuration for event scoring t_your : float Your time for the event Returns ------- float Competition points earned """ best_time = min(t_your, scoring_config.event_best_time) worst_time = best_time * scoring_config.worst_time_ratio your_ratio_to_worst = (worst_time / t_your) ** scoring_config.scale_time_ratio - 1 best_ratio_to_worst = ( worst_time / best_time ) ** scoring_config.scale_time_ratio - 1 return scoring_config.min_points + scoring_config.max_point_gain * ( your_ratio_to_worst / best_ratio_to_worst )
[docs] def compute_efficiency_points( scoring_config: EfficiencyScoring, consumed_energy: float, endurance_runtime: float ) -> float: """ Compute efficiency points based on energy consumption and endurance runtime. Parameters ---------- scoring_config : EfficiencyScoring Configuration for efficiency scoring consumed_energy : float Energy consumed per lap (kWh) endurance_runtime : float Total endurance runtime (seconds) Returns ------- float Efficiency points earned """ # Calculate our car's CO2 emissions (Assumes power is constant across each lap) total_energy = consumed_energy * scoring_config.num_endurance_laps co2_your = scoring_config.co2_scaling * total_energy # Calculate our average laptime laptime_your = endurance_runtime / scoring_config.num_endurance_laps # Calculate our efficiency factor t_ratio = scoring_config.fastest_lap_time / laptime_your co2_ratio = scoring_config.co2_min / co2_your eff_your = t_ratio * co2_ratio # Calculate the rules-defined minimum efficiency, a constant used to scale points eff_min_t_ratio = 1.0 / scoring_config.max_time_scale eff_min_co2_ratio = scoring_config.co2_min / scoring_config.eff_min_co2_your eff_min = eff_min_t_ratio * eff_min_co2_ratio # Convert efficiency factor to competition points eff_points = ( scoring_config.max_eff_points * (eff_your - eff_min) / (scoring_config.eff_max - eff_min) ) return min(eff_points, scoring_config.max_eff_points)
[docs] def energy_data( time_list: npt.NDArray[np.float64], power_list: npt.NDArray[np.float64] ) -> tuple[float, float, float]: """ Manually integrates power to determine energy consumption across a lap. Uses trapezoidal integration Parameters ---------- time_list: npt.ndarray[np.float64] Cumulative timestamps corresponding to power values (seconds) power_list : npt.ndarray[np.float64] List of power values (Watts) Returns ------- float Positive energy consumption in kWh float Negative energy consumption in kWh float Net energy consumption in kWh """ t = np.asarray(time_list, dtype=float) p = np.asarray(power_list, dtype=float) dt = np.diff(t) # n - 1 intervals interval_energy = ( 0.5 * (p[:-1] + p[1:]) ) * dt # trapezoidal rule over n - 1 intervals pos_mask = (p[:-1] > 0) & (p[1:] > 0) neg_mask = (p[:-1] < 0) & (p[1:] < 0) pos_joules = interval_energy[pos_mask].sum() neg_joules = interval_energy[neg_mask].sum() total_joules = interval_energy.sum() return ( joule_to_kwh(pos_joules), joule_to_kwh(neg_joules), joule_to_kwh(total_joules), )
[docs] def backsolve_energy_from_efficiency( scoring_config: EfficiencyScoring, total_endurance_time: float, efficiency_score: float, ) -> float: """ Calculates consumed energy in kWh given competition configuration, total endurance event time, and efficiency score. Parameters ---------- scoring_config : EfficiencyScoring total_endurance_time : float Total endurance event time in seconds (tyour) efficiency_score : float Efficiency points score Returns ------- float Consumed energy in kWh """ # Calculate eff_min (efficiency factor minimum) # From: compute_efficiency_factor(max_time_scale * fastest_lap_time, eff_min_co2_your) eff_min = (1 / scoring_config.max_time_scale) * ( scoring_config.co2_min / scoring_config.eff_min_co2_your ) # Solve for eff_your from efficiency_score # eff_points = max_eff_points * (eff_your - eff_min) / (eff_max - eff_min) eff_your = ( efficiency_score * (scoring_config.eff_max - eff_min) / scoring_config.max_eff_points + eff_min ) # Calculate average laptime laptime_your = total_endurance_time / scoring_config.num_endurance_laps # Solve for co2_your from eff_your # eff_your = (fastest_lap_time / laptime_your) * (co2_min / co2_your) co2_your = ( (scoring_config.fastest_lap_time / laptime_your) * scoring_config.co2_min / eff_your ) # Solve for consumed_energy from co2_your # co2_your = co2_scaling * consumed_energy_total consumed_energy_kwh = co2_your / scoring_config.co2_scaling return consumed_energy_kwh
[docs] def pretty_print_results( comp_results: CompetitionResults, ): """ Print formatted competition results. Parameters ---------- comp_results : CompetitionResults """ accel_time = round(comp_results.accel.tyour, 3) accel_pts = round(comp_results.accel.points, 1) print(f"{'Accel time:':<12} {accel_time:<8} {'Accel points:':<14} {accel_pts}") skidpad_time = round(comp_results.skidpad.tyour, 3) skidpad_pts = round(comp_results.skidpad.points, 1) print(f"{'SkidP time:':<12} {skidpad_time:<8} {'SkidP points:':<14} {skidpad_pts}") autox_time = round(comp_results.autoX.tyour, 3) autox_pts = round(comp_results.autoX.points, 1) print(f"{'AutoX time:':<12} {autox_time:<8} {'AutoX points:':<14} {autox_pts}") endurance_time = round(comp_results.endurance.tyour, 1) endurance_pts = round(comp_results.endurance.points, 1) efficiency_pts = round(comp_results.efficiency_points, 1) print( f"{'Endur time:':<12} {endurance_time:<8} {'Endur points:':<14} {endurance_pts}" ) print(f"{'':<21} {'Effcy points:':<14} {efficiency_pts}") total_pts = round(comp_results.total_points, 1) print(f"{'':<21} {'Total points:':<14} {total_pts}")