/**
* methods for calculating miles and fuel/electric usage for journeys
* @module
*/
import { num_cs_mode_distance_miles } from '../../../maths/constants';
import { getBatteryRangeMiles, getIsCSMode } from '../util';
/**
* for PHEVs, if a journey range is not provided then add how far the vehicle
* will travel on Charge Sustaining mode (previously called Condition B)
* @param {struct} skv_config mpg, battery range, optional journey miles
* @returns {number}
*/
const getJourneyMilesForPHEVs = (skv_config) => {
if (getIsCSMode(skv_config)) {
// cs mode formerly called Condition B
return num_cs_mode_distance_miles + getBatteryRangeMiles(skv_config);
}
return skv_config.journey_miles;
};
/**
* work out the electric miles
* @param {number} battery_range_miles
* @param {number} mpg
* @param {number} journey_miles
* @returns {number}
*/
const getElectricMiles = (battery_range_miles, mpg, journey_miles) => {
let electric_miles = battery_range_miles;
// if we have over electric miles the entire journey is carried out on
// electric and no ICE
if (
electric_miles > journey_miles
|| (
mpg === 0
&& battery_range_miles > 0
)
) {
electric_miles = journey_miles;
}
return electric_miles;
};
/**
* work out the ICE miles
* @param {number} electric_miles
* @param {number} journey_miles
* @returns {number}
*/
const getICEMiles = (electric_miles, journey_miles) => journey_miles - electric_miles;
/**
* when user provides a custom mpg then we'll be applying a ratio to the official figures
* @param {number} mpg_official
* @param {number} mpg_user
* @returns {number}
*/
const getMPGofficalRatio = (mpg_official, mpg_user) => mpg_official / mpg_user;
/**
* proportion of journey carried out on fuel or electric miles
* @param {number} travelled_miles fuel or electric miles travelled
* @param {number} journey_miles
* @returns {number}
*/
const getJourneyFuelRatio = (travelled_miles, journey_miles) => travelled_miles / journey_miles;
/**
* number of journeys carried out
* @param {number} journey_miles
* @param {number} total_miles
* @returns {number}
*/
const numberOfJourneys = (journey_miles, total_miles) => total_miles / journey_miles;
/* *********************************
************* COSTS **************
********************************* */
/**
* Calculate fuel or electric portion of journey
* @param {struct} skv_data struct of details for ppm, journey
* @returns {struct}
*/
const journeyCostPortion = (skv_journey, skv_ppm) => {
const skv_return = {
exc_vat: '',
inc_vat: '',
};
skv_return.exc_vat = skv_ppm.exc_vat * skv_journey.miles;
// convert to pounds and round to 2 decimal places
skv_return.exc_vat = parseFloat((skv_return.exc_vat / 100).toFixed(2));
skv_return.inc_vat = skv_ppm.inc_vat * skv_journey.miles;
// convert to pounds and round to 2 decimal places
skv_return.inc_vat = parseFloat((skv_return.inc_vat / 100).toFixed(2));
// if skv_data has exc_reduced_vat then calculate it
if (Object.prototype.hasOwnProperty.call(skv_ppm, 'exc_reduced_vat')) {
skv_return.exc_reduced_vat = skv_ppm.exc_reduced_vat * skv_journey.miles;
// convert to pounds and round to 2 decimal places
skv_return.exc_reduced_vat = parseFloat((skv_return.exc_reduced_vat / 100).toFixed(2));
}
return skv_return;
};
/**
* combine the electric and ice miles and costs
* @param {struct} skv_journey_cost
* @returns {struct}
*/
const journeyCostTotal = (skv_journey_cost) => {
const total_exc_vat = skv_journey_cost.electric.total_pounds.exc_vat + skv_journey_cost.ice.total_pounds.exc_vat;
const total_inc_vat = skv_journey_cost.electric.total_pounds.inc_vat + skv_journey_cost.ice.total_pounds.inc_vat;
const skv_return = {
miles: skv_journey_cost.electric.distance.miles + skv_journey_cost.ice.distance.miles,
total_pounds: {
exc_vat: total_exc_vat,
inc_vat: total_inc_vat,
},
};
const ppm = {
exc_vat: (total_exc_vat / skv_return.miles) * 100,
inc_vat: (total_inc_vat / skv_return.miles) * 100,
};
skv_return.ppm = ppm;
return skv_return;
};
/**
* bring together all the journeys to form the total overall cost
* @param {struct} skv_journey_cost
* @param {number} total_miles
* @returns {struct}
*/
const totalCostOfAllJourneys = (skv_journey_cost, total_miles) => {
const electric_miles = total_miles * skv_journey_cost.electric.distance.ratio;
const ice_miles = total_miles * skv_journey_cost.ice.distance.ratio;
const skv_return = {
electric: {
miles: electric_miles,
cost_pounds: {
exc_vat: skv_journey_cost.electric.ppm.exc_vat * electric_miles,
inc_vat: skv_journey_cost.electric.ppm.inc_vat * electric_miles,
},
},
ice: {
miles: ice_miles,
cost_pounds: {
exc_vat: skv_journey_cost.ice.ppm.exc_vat * ice_miles,
inc_vat: skv_journey_cost.ice.ppm.inc_vat * ice_miles,
},
},
};
skv_return.combined = {
miles: total_miles,
cost_pounds: {
exc_vat: skv_return.electric.cost_pounds.exc_vat + skv_return.ice.cost_pounds.exc_vat,
inc_vat: skv_return.electric.cost_pounds.inc_vat + skv_return.ice.cost_pounds.inc_vat,
},
};
// values are currently in pence so convert to pounds and round to 2 decimal places
Object.keys(skv_return).forEach((key) => {
Object.keys(skv_return[key].cost_pounds).forEach((key2) => {
skv_return[key].cost_pounds[key2] = parseFloat((skv_return[key].cost_pounds[key2] / 100).toFixed(2));
});
});
return skv_return;
};
/** ********************************
********** EMISSIONS *************
********************************* */
/**
* Calculate fuel or electric portion of journey
* @param {struct} skv_data struct of optionally tailpipe and production for ppm, journey
* @returns {struct}
*/
const journeyEmissionsPortion = (skv_journey, skv_co2_g_per_mile) => {
const skv_return = {};
const potential_keys = ['tail_pipe', 'production'];
// loop over potential keys and if they exist then calculate them
potential_keys.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(skv_co2_g_per_mile, key)) {
// multiply the co2 grams per mile by the journey miles
skv_return[key] = skv_co2_g_per_mile[key] * skv_journey.miles;
// round to 2 decimal places
skv_return[key] = parseFloat(skv_return[key].toFixed(2));
}
});
return skv_return;
};
/**
* combine the electric and ice miles and emissions
* @param {struct} skv_journey_emissions
* @returns {struct}
*/
const journeyEmissionsTotal = (skv_journey_emissions) => {
const skv_return = {
miles: skv_journey_emissions.electric.distance.miles + skv_journey_emissions.ice.distance.miles,
total_co2_g: {},
};
const electric_total = skv_journey_emissions.electric.total_co2_g;
const ice_total = skv_journey_emissions.ice.total_co2_g;
const potential_keys = ['tail_pipe', 'production'];
// loop over potential keys and see if they exist in the electric_total struct
potential_keys.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(electric_total, key)) {
skv_return.total_co2_g[key] = electric_total[key];
}
});
// loop over potential keys and see if they exist in the ice_total struct and
// add them to the skv_return struct
potential_keys.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(ice_total, key)) {
if (Object.prototype.hasOwnProperty.call(skv_return.total_co2_g, key)) {
skv_return.total_co2_g[key] += ice_total[key];
} else {
skv_return.total_co2_g[key] = ice_total[key];
}
}
});
return skv_return;
};
/**
* total emissions for all journeys based on total_miles passed in
* @param {struct} skv_journey_emissions
* @param {number} total_miles
* @returns {struct}
*/
const totalEmissionsOfAllJourneys = (skv_journey_emissions, total_miles) => {
const electric_miles = total_miles * skv_journey_emissions.electric.distance.ratio;
const ice_miles = total_miles * skv_journey_emissions.ice.distance.ratio;
const skv_return = {
electric: {
miles: electric_miles,
total_co2_g: {
production: skv_journey_emissions.electric.co2_g_per_mile.production * electric_miles,
},
},
ice: {
miles: ice_miles,
total_co2_g: {
tail_pipe: skv_journey_emissions.ice.co2_g_per_mile.tail_pipe * ice_miles,
production: skv_journey_emissions.ice.co2_g_per_mile.production * ice_miles,
},
},
};
skv_return.combined = {
miles: total_miles,
total_co2_g: {
tail_pipe: skv_return.ice.total_co2_g.tail_pipe,
production: skv_return.ice.total_co2_g.production + skv_return.electric.total_co2_g.production,
},
};
return skv_return;
};
export {
getJourneyMilesForPHEVs,
getElectricMiles,
getICEMiles,
getMPGofficalRatio,
getJourneyFuelRatio,
numberOfJourneys,
// costs
journeyCostPortion,
journeyCostTotal,
totalCostOfAllJourneys,
// emissions
journeyEmissionsPortion,
journeyEmissionsTotal,
totalEmissionsOfAllJourneys,
};