EcoTracker-II / backend /src /services /simulationService.js
Akash-Dragon's picture
refactor: restructure project as a monorepo and implement backend API
7e49c4d
Raw
History Blame Contribute Delete
3.84 kB
import { calculateAllEmissions } from './carbonCalculator.js';
/**
* Simulation Service
* Projects future emissions based on behavior change scenarios
*/
/**
* Apply a simulation scenario to the original assessment data and calculate new emissions
* @param {Object} assessmentData - original user inputs
* @param {Object} scenarioParams - changes to apply
* @param {string} scenarioName
* @returns {Object} simulation result
*/
export function runSimulation(assessmentData, scenarioParams, scenarioName) {
// Build modified data by applying scenario params
const modifiedData = applyScenario(assessmentData, scenarioParams);
// Calculate original and projected emissions
const original = calculateAllEmissions(assessmentData);
const projected = calculateAllEmissions(modifiedData);
const originalTotal = original.totalEmission;
const projectedTotal = projected.totalEmission;
const savings = originalTotal - projectedTotal;
const reductionPercentage =
originalTotal > 0 ? parseFloat(((savings / originalTotal) * 100).toFixed(1)) : 0;
return {
scenarioName,
scenarioParams,
originalEmission: originalTotal,
projectedEmission: projectedTotal,
reductionPercentage,
annualSavingsKg: Math.round(savings),
categoryBreakdown: {
original: {
transport: original.transportEmission,
energy: original.energyEmission,
food: original.foodEmission,
shopping: original.shoppingEmission,
},
projected: {
transport: projected.transportEmission,
energy: projected.energyEmission,
food: projected.foodEmission,
shopping: projected.shoppingEmission,
},
},
};
}
/**
* Apply scenario parameters to original assessment data
* @param {Object} original
* @param {Object} params
* @returns {Object} modified assessment data
*/
function applyScenario(original, params) {
const modified = { ...original };
// Direct field overrides
if (params.carFuelType !== undefined) modified.carFuelType = params.carFuelType;
if (params.dietType !== undefined) modified.dietType = params.dietType;
if (params.renewablePercentage !== undefined)
modified.renewablePercentage = params.renewablePercentage;
// Reduction factors
if (params.cyclingReductionFactor !== undefined) {
// Reduce daily car km by the cycling reduction factor
modified.dailyCarKm = original.dailyCarKm * (1 - params.cyclingReductionFactor);
modified.cyclingKmPerWeek =
(original.cyclingKmPerWeek || 0) + original.dailyCarKm * params.cyclingReductionFactor * 5;
}
if (params.flightReductionFactor !== undefined) {
modified.shortFlightsPerYear = Math.round(
original.shortFlightsPerYear * (1 - params.flightReductionFactor)
);
modified.longFlightsPerYear = Math.round(
original.longFlightsPerYear * (1 - params.flightReductionFactor)
);
}
if (params.electricityReductionFactor !== undefined) {
modified.monthlyElectricityKwh =
original.monthlyElectricityKwh * (1 - params.electricityReductionFactor);
}
if (params.shoppingReductionFactor !== undefined) {
modified.clothingItemsPerYear = Math.round(
original.clothingItemsPerYear * (1 - params.shoppingReductionFactor)
);
modified.electronicsItemsPerYear = Math.round(
original.electronicsItemsPerYear * (1 - params.shoppingReductionFactor)
);
}
return modified;
}
/**
* Run multiple simulations and return ranked results
* @param {Object} assessmentData
* @param {Array} scenarios
* @returns {Array} sorted simulation results
*/
export function runMultipleSimulations(assessmentData, scenarios) {
const results = scenarios.map(({ name, params }) => runSimulation(assessmentData, params, name));
return results.sort((a, b) => b.annualSavingsKg - a.annualSavingsKg);
}