Improved battery recharge logic
Added warning if battery capacity is not sufficient
This commit is contained in:
parent
ed3e9f7dc4
commit
1b14a62f6e
@ -6,6 +6,10 @@ input[type=number] {
|
|||||||
flex-grow: 2.5;
|
flex-grow: 2.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.simulation-warning {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown.is-fullwidth {
|
.dropdown.is-fullwidth {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
@ -95,8 +95,9 @@ function initializeSimulator(container: HTMLElement) {
|
|||||||
<p>Cela coûtera ${Math.round(totalConsumedGridPower/1000*averageKwhCost*100)/100}€ sur l'année. Le vhélio sera rechargé à ${solarRechargeRatio}% par le soleil, ${100-solarRechargeRatio}% sur secteur.</p>
|
<p>Cela coûtera ${Math.round(totalConsumedGridPower/1000*averageKwhCost*100)/100}€ sur l'année. Le vhélio sera rechargé à ${solarRechargeRatio}% par le soleil, ${100-solarRechargeRatio}% sur secteur.</p>
|
||||||
<p><br/></p>
|
<p><br/></p>
|
||||||
<p>Vitesse moyenne : ${Math.round(simulationResult.averageSpeed*10.0)/10.0} km/h (${Math.round(simulationResult.flatTerrainSpeed*10.0)/10.0} km/h sur plat, ${Math.round(simulationResult.uphillSpeed*10.0)/10.0} km/h en côte à ${Math.round((parameters.dailyAscendingElevation/1000.0)/(parameters.dailyDistance*(1.0-parameters.flatTerrainRatio)*0.5)*100.0)}%, ${Math.round(simulationResult.downhillSpeed*10.0)/10.0} km/h en descente)</p>
|
<p>Vitesse moyenne : ${Math.round(simulationResult.averageSpeed*10.0)/10.0} km/h (${Math.round(simulationResult.flatTerrainSpeed*10.0)/10.0} km/h sur plat, ${Math.round(simulationResult.uphillSpeed*10.0)/10.0} km/h en côte à ${Math.round((parameters.dailyAscendingElevation/1000.0)/(parameters.dailyDistance*(1.0-parameters.flatTerrainRatio)*0.5)*100.0)}%, ${Math.round(simulationResult.downhillSpeed*10.0)/10.0} km/h en descente)</p>
|
||||||
<p>Durée de trajet quotidien : ${dailyDurationHours}h ${dailyDurationMinutes}min. Distance annuelle : ${Math.round(simulationResult.cumulatedDistance)} km.</p>
|
<p>Durée de trajet quotidienne : ${dailyDurationHours}h ${dailyDurationMinutes}min. Distance annuelle : ${Math.round(simulationResult.cumulatedDistance)} km.</p>
|
||||||
`;
|
`
|
||||||
|
+ (simulationResult.outOfBatteryDistance >= 1 ? `<p><br/></p><p class="simulation-warning">/!\\ En raison d'une capacité de batterie insuffisante, il faudra faire ${Math.round(simulationResult.outOfBatteryDistance)}km/an sans assistance électrique</p>` : "");
|
||||||
//<p>${Math.round(100*(simulationResult.cumulatedSolarRechargeEnergy/simulationResult.vehicle.batteryEfficiency) / simulationResult.totalProducedSolarEnergy)}% de l'énergie produite par le panneau photovoltaïque sera utilisée pour recharger le vhélio.</p>
|
//<p>${Math.round(100*(simulationResult.cumulatedSolarRechargeEnergy/simulationResult.vehicle.batteryEfficiency) / simulationResult.totalProducedSolarEnergy)}% de l'énergie produite par le panneau photovoltaïque sera utilisée pour recharger le vhélio.</p>
|
||||||
|
|
||||||
let batteryChargeGraph = new SvgDrawing.SvgElement(resultsContainer.querySelector('.battery-charge-graph svg'));
|
let batteryChargeGraph = new SvgDrawing.SvgElement(resultsContainer.querySelector('.battery-charge-graph svg'));
|
||||||
|
@ -9,6 +9,7 @@ namespace Simulator {
|
|||||||
batteryCapacity: number;
|
batteryCapacity: number;
|
||||||
batteryEfficiency: number = 0.9;
|
batteryEfficiency: number = 0.9;
|
||||||
gridTransformerEfficiency: number = 0.85;
|
gridTransformerEfficiency: number = 0.85;
|
||||||
|
rechargeMargin: number = 0.15; // We recharge the battery before an outing if it would otherwise go below this charge ratio
|
||||||
|
|
||||||
solarPanelEfficiency: number = 0.15;
|
solarPanelEfficiency: number = 0.15;
|
||||||
solarPanelArea: number = 1.0; // in square meters
|
solarPanelArea: number = 1.0; // in square meters
|
||||||
@ -117,6 +118,8 @@ namespace Simulator {
|
|||||||
uphillSpeed: number;
|
uphillSpeed: number;
|
||||||
downhillSpeed: number;
|
downhillSpeed: number;
|
||||||
averageSpeed: number;
|
averageSpeed: number;
|
||||||
|
|
||||||
|
outOfBatteryDistance: number; // distance, in km, that the driver had to pedal without assistance, because the battery was empty
|
||||||
}
|
}
|
||||||
|
|
||||||
export function simulate(vehicle: Vehicle, solarIrradiance: number[], planning: OutingPlanning): SimulationResult {
|
export function simulate(vehicle: Vehicle, solarIrradiance: number[], planning: OutingPlanning): SimulationResult {
|
||||||
@ -136,7 +139,9 @@ namespace Simulator {
|
|||||||
flatTerrainSpeed: 0,
|
flatTerrainSpeed: 0,
|
||||||
uphillSpeed: 0,
|
uphillSpeed: 0,
|
||||||
downhillSpeed: 0,
|
downhillSpeed: 0,
|
||||||
averageSpeed: 0
|
averageSpeed: 0,
|
||||||
|
|
||||||
|
outOfBatteryDistance: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
let remainingBatteryCharge = vehicle.batteryCapacity;
|
let remainingBatteryCharge = vehicle.batteryCapacity;
|
||||||
@ -144,7 +149,7 @@ namespace Simulator {
|
|||||||
let outing: Outing = { distance: 0, ascendingElevation: 0 };
|
let outing: Outing = { distance: 0, ascendingElevation: 0 };
|
||||||
let consumption: ConsumptionData = { motorEnergy: 0, humanEnergy: 0, averageSpeed: 0 };
|
let consumption: ConsumptionData = { motorEnergy: 0, humanEnergy: 0, averageSpeed: 0 };
|
||||||
let resetConsumption = function(outConsumption: ConsumptionData) {
|
let resetConsumption = function(outConsumption: ConsumptionData) {
|
||||||
consumption.motorEnergy = 0; consumption.humanEnergy = 0; consumption.averageSpeed = 0;
|
outConsumption.motorEnergy = 0; outConsumption.humanEnergy = 0; outConsumption.averageSpeed = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
let flatTerrainRatio = MathUtils.clamp(planning.flatTerrainRatio, 0.0, 1.0);
|
let flatTerrainRatio = MathUtils.clamp(planning.flatTerrainRatio, 0.0, 1.0);
|
||||||
@ -188,27 +193,44 @@ namespace Simulator {
|
|||||||
result.totalProducedSolarEnergy += production;
|
result.totalProducedSolarEnergy += production;
|
||||||
let solarCharge = production * vehicle.batteryEfficiency;
|
let solarCharge = production * vehicle.batteryEfficiency;
|
||||||
|
|
||||||
// TODO: we should keep a margin because real users will recharge before they reach the bare minimum required for an outing
|
let remainingBatteryChargeBeforeOuting = remainingBatteryCharge;
|
||||||
remainingBatteryCharge += solarCharge - consumption.motorEnergy;
|
remainingBatteryCharge += solarCharge - consumption.motorEnergy;
|
||||||
|
|
||||||
let fullGridRecharge = false;
|
let gridRechargeFrom = -1;
|
||||||
|
let lowBatteryThreshold = vehicle.rechargeMargin * vehicle.batteryCapacity;
|
||||||
if(remainingBatteryCharge > vehicle.batteryCapacity) {
|
if(remainingBatteryCharge > vehicle.batteryCapacity) {
|
||||||
solarCharge -= remainingBatteryCharge - vehicle.batteryCapacity;
|
solarCharge -= remainingBatteryCharge - vehicle.batteryCapacity;
|
||||||
remainingBatteryCharge = vehicle.batteryCapacity;
|
remainingBatteryCharge = vehicle.batteryCapacity;
|
||||||
}
|
}
|
||||||
else if(remainingBatteryCharge <= 0 || (day==364 && hour==23)) {
|
else if(remainingBatteryCharge <= lowBatteryThreshold || (day==364 && hour==23)) {
|
||||||
// TODO: detect if battery capacity is too low for a single outing, abort simulation and display an explanation for the user
|
gridRechargeFrom = remainingBatteryChargeBeforeOuting;
|
||||||
fullGridRecharge = remainingBatteryCharge <= 0;
|
|
||||||
let rechargeEnergy = vehicle.batteryCapacity - remainingBatteryCharge;
|
let rechargeEnergy = vehicle.batteryCapacity - remainingBatteryChargeBeforeOuting;
|
||||||
remainingBatteryCharge += rechargeEnergy;
|
remainingBatteryCharge += rechargeEnergy;
|
||||||
result.cumulatedGridRechargeEnergy += rechargeEnergy;
|
result.cumulatedGridRechargeEnergy += rechargeEnergy;
|
||||||
result.gridChargeCount += 1;
|
result.gridChargeCount += 1;
|
||||||
|
|
||||||
|
if(remainingBatteryCharge < 0)
|
||||||
|
{
|
||||||
|
// battery was exhausted during outing
|
||||||
|
let missingEnergy = -remainingBatteryCharge;
|
||||||
|
result.outOfBatteryDistance += outing.distance * missingEnergy / consumption.motorEnergy;
|
||||||
|
consumption.motorEnergy -= missingEnergy;
|
||||||
|
consumption.humanEnergy += missingEnergy;
|
||||||
|
|
||||||
|
// charge battery again after outing
|
||||||
|
let secondRechargeEnergy = vehicle.batteryCapacity;
|
||||||
|
remainingBatteryCharge = vehicle.batteryCapacity;
|
||||||
|
result.cumulatedGridRechargeEnergy += secondRechargeEnergy;
|
||||||
|
result.gridChargeCount += 1;
|
||||||
|
gridRechargeFrom = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.cumulatedMotorConsumption += consumption.motorEnergy;
|
result.cumulatedMotorConsumption += consumption.motorEnergy;
|
||||||
result.cumulatedSolarRechargeEnergy += solarCharge;
|
result.cumulatedSolarRechargeEnergy += solarCharge;
|
||||||
|
|
||||||
result.batteryLevel[hourIdx] = fullGridRecharge ? 0 : remainingBatteryCharge;
|
result.batteryLevel[hourIdx] = gridRechargeFrom >= 0 ? gridRechargeFrom : remainingBatteryCharge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ let css = ['../.intermediate/simulator.css'];
|
|||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
output: '../.intermediate/simulator.css',
|
output: '../.intermediate/simulator.css',
|
||||||
whitelist: ['is-multiple', 'is-loading', 'is-narrow', 'is-active', 'climate-zone', 'grid-recharge', 'is-max-desktop', 'is-max-widescreen', 'line', 'page'],
|
whitelist: ['is-multiple', 'is-loading', 'is-narrow', 'is-active', 'climate-zone', 'grid-recharge', 'is-max-desktop', 'is-max-widescreen', 'line', 'page', 'simulation-warning'],
|
||||||
minify: false,
|
minify: false,
|
||||||
info: false
|
info: false
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user