diff --git a/simulator/src/simulator-ui.ts b/simulator/src/simulator-ui.ts index ba5ebc5..ade3d63 100644 --- a/simulator/src/simulator-ui.ts +++ b/simulator/src/simulator-ui.ts @@ -21,11 +21,6 @@ function runSimulation(parameters: SimulationParameters): Simulator.SimulationRe //console.log(solarIrradiance); //console.log(simulationResult); - //let averageKwhCost = 0.192; // in €/kWh TODO: to verify, this price seems too high - //console.log('Grid recharge cost: ' + (Math.round(simulationResult.gridChargeCount*(vehicle.batteryCapacity/1000)*averageKwhCost*100)/100) + '€'); - - //console.log('Solar energy ratio: ' + Math.round(100*(simulationResult.cumulatedMotorConsumption-(simulationResult.gridChargeCount+1)*vehicle.batteryCapacity)/simulationResult.cumulatedMotorConsumption) + '%'); - return simulationResult; } @@ -76,12 +71,16 @@ document.addEventListener('DOMContentLoaded', function() { let resultsContainer = container.querySelector('.simulation-results'); - let averageKwhCost = 0.192; // in €/kWh TODO: to verify, this price seems too high + let averageKwhCost = 0.1558; // in €/kWh + let totalConsumedGridPower = simulationResult.cumulatedGridRechargeEnergy / simulationResult.vehicle.batteryEfficiency / simulationResult.vehicle.gridTransformerEfficiency; + + let solarRechargeRatio = Math.round(100*(simulationResult.cumulatedSolarRechargeEnergy/(simulationResult.cumulatedSolarRechargeEnergy + simulationResult.cumulatedGridRechargeEnergy))); resultsContainer.querySelector('.result-info').innerHTML = ` -
Il faudra recharger le vhélio sur secteur environ ${simulationResult.gridChargeCount} fois sur l'année
-Cela coûtera ${Math.round(simulationResult.gridChargeCount*(parameters.batteryCapacity/1000)*averageKwhCost*100)/100}€ sur l'année
-La couverture solaire du vhélio est de ${Math.round(100*(simulationResult.cumulatedMotorConsumption-(simulationResult.gridChargeCount+1)*parameters.batteryCapacity)/simulationResult.cumulatedMotorConsumption)}%
+Il faudra recharger le vhélio sur secteur environ ${simulationResult.gridChargeCount} fois sur l'année.
+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.
`; + //${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.
let batteryChargeGraph = new SvgDrawing.SvgElement(resultsContainer.querySelector('.battery-charge-graph svg')); @@ -100,7 +99,7 @@ document.addEventListener('DOMContentLoaded', function() { batteryChargeGraph.viewport.setData({ x: 0, y: 0, width: 365*24, height: parameters.batteryCapacity }); batteryChargeGraph.viewport.setView({ x: marginLeft, y: batteryChargeGraph.height - marginBottom, width: batteryChargeGraph.width - (marginLeft+marginRight), height: -batteryChargeGraph.height+(marginTop+marginBottom) }); - batteryChargeGraph.graph(simulationResult.batteryLevel, simulationResult.batteryLevel.map(x => x == 0 ? 1 : 0), [{className: ''}, {className: 'grid-recharge'}]); + batteryChargeGraph.graph(simulationResult.batteryLevel, simulationResult.batteryLevel.map((x, idx) => x == 0 || idx == simulationResult.batteryLevel.length - 2 ? 1 : 0), [{className: ''}, {className: 'grid-recharge'}]); let months = ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jui', 'Jui', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec']; let monthWidth = 365*24/12 for(let month = 0; month < 12; ++month) { diff --git a/simulator/src/simulator.ts b/simulator/src/simulator.ts index d3f402f..3a0949a 100644 --- a/simulator/src/simulator.ts +++ b/simulator/src/simulator.ts @@ -1,7 +1,8 @@ namespace Simulator { export class Vehicle { batteryCapacity: number; - batteryEfficiency: number = 1.0; // TODO: typical efficiency of a Li-ion battery (round-trip) is 90% + batteryEfficiency: number = 0.9; + gridTransformerEfficiency: number = 0.85; solarPanelEfficiency: number = 0.15; solarPanelArea: number = 1.0; // in square meters @@ -59,19 +60,25 @@ namespace Simulator { } export interface SimulationResult { + vehicle: Vehicle; + batteryLevel: number[]; // Remaining energy in the battery over time (one entry per hour), in Wh gridChargeCount: number; cumulatedGridRechargeEnergy: number; // Cumulated energy added to the battery from the power grid, in Wh of battery charge (actual power grid consumption will be slightly higer due to losses) cumulatedSolarRechargeEnergy: number; // Cumulated energy added to the battery from the solar panel, in Wh of battery charge (actual generated power is slightly higher due to losses) + totalProducedSolarEnergy: number; // Cumulated energy produced (used or unused), before accounting for the battery recharge efficiency. cumulatedMotorConsumption: number; // Cumulated energy consumed by the motor, in Wh. In this simulation, this is equal to the energy drawn from the battery. } export function simulate(vehicle: Vehicle, solarIrradiance: number[], planning: OutingPlanning): SimulationResult { let result: SimulationResult = { + vehicle: vehicle, + batteryLevel: [], gridChargeCount: 0, cumulatedGridRechargeEnergy: 0, cumulatedSolarRechargeEnergy: 0, + totalProducedSolarEnergy: 0, cumulatedMotorConsumption: 0 }; @@ -88,19 +95,20 @@ namespace Simulator { let consumption = vehicle.motorConsumption(outing.distance, outing.ascendingElevation); let production = vehicle.solarPower(solarIrradiance[hourIdx]) * 1.0; // produced energy in Wh is equal to power (W) multiplied by time (h) + result.totalProducedSolarEnergy += production; 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 remainingBatteryCharge += solarCharge - consumption; - let gridRecharge = false; + let fullGridRecharge = false; if(remainingBatteryCharge > vehicle.batteryCapacity) { solarCharge -= remainingBatteryCharge - vehicle.batteryCapacity; remainingBatteryCharge = vehicle.batteryCapacity; } - else if(remainingBatteryCharge <= 0) { + else if(remainingBatteryCharge <= 0 || (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 - gridRecharge = true; + fullGridRecharge = remainingBatteryCharge <= 0; let rechargeEnergy = vehicle.batteryCapacity - remainingBatteryCharge; remainingBatteryCharge += rechargeEnergy; result.cumulatedGridRechargeEnergy += rechargeEnergy; @@ -110,7 +118,7 @@ namespace Simulator { result.cumulatedMotorConsumption += consumption; result.cumulatedSolarRechargeEnergy += solarCharge; - result.batteryLevel[hourIdx] = gridRecharge ? 0 : remainingBatteryCharge; + result.batteryLevel[hourIdx] = fullGridRecharge ? 0 : remainingBatteryCharge; } }