From 129901617d5fc146750d98ff5172a786d5fcc921 Mon Sep 17 00:00:00 2001 From: Maximilian Stiefel Date: Tue, 28 Dec 2021 23:20:28 +0100 Subject: [PATCH] Works nicely. Some magic numbers should be removed. --- ecar.json | 3 +- ecar.py | 122 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 92 insertions(+), 33 deletions(-) diff --git a/ecar.json b/ecar.json index aa234d7..7665627 100644 --- a/ecar.json +++ b/ecar.json @@ -1,4 +1,5 @@ { + "currency": "CHF", "kwh_price_home": 0.2, "kwh_price_commercial": 0.5, "petrol_litre_price": 1.75, @@ -10,7 +11,7 @@ "taxes": 0, "insurance": 354, "kwh_per_kilometer": 0.16, - "maintenance": 200, + "maintenance": 100, "charging_behaviour": { "percent_free_charges": 90, "percent_home_charges": 5, diff --git a/ecar.py b/ecar.py index 06f46cd..332106b 100644 --- a/ecar.py +++ b/ecar.py @@ -17,7 +17,9 @@ class c_settings_extractor: kilometer_price_ecar = ( settings["ecar"]["charging_behaviour"]["percent_home_charges"] * settings["kwh_price_home"] + settings["ecar"]["charging_behaviour"]["percent_commercial_charges"] * settings["kwh_price_commercial"]) / 100.0 self.driving = np.array([kilometer_price_ecar * settings["kilometers_per_year"], kilometer_price_ccar * settings["kilometers_per_year"]]) - self.maintenance = np.array([settings["ecar"]["maintenance"], settings["ecar"]["maintenance"]]) + self.maintenance = np.array([settings["ecar"]["maintenance"], settings["ccar"]["maintenance"]]) + self.kilometers = settings["kilometers_per_year"] + self.currency = settings["currency"] def get_labels(self): return self.labels def get_purchase(self): @@ -30,7 +32,11 @@ class c_settings_extractor: return self.driving def get_maintenance(self): return self.maintenance - + def get_kilometers(self): + return self.kilometers + def get_currency(self): + return self.currency + class c_ecar_comparator: def __init__(self, fname): self.settings_extractor = c_settings_extractor(fname) @@ -50,24 +56,66 @@ class c_ecar_comparator: increment = self.calculate_costs_a_year() / months_a_year months = 0 while total_costs[0] > total_costs[1]: - total_costs += increment + total_costs = total_costs + increment months += 1 - return [months/months_a_year, months%months_a_year] # years, months + kilometers = self.settings_extractor.get_kilometers() * months / months_a_year + return [months//months_a_year, months%months_a_year, kilometers] # years, months + def calculate_amortization_point(self): + y = 0 + m = 1 + months_a_year = 12.0 + costs_a_month = self.calculate_costs(y, m) + savings_a_month = costs_a_month[1]-costs_a_month[0] + months_till_amortized = self.settings_extractor.get_purchase()[0] / savings_a_month + kilometers = months_till_amortized * self.settings_extractor.get_kilometers() / months_a_year + months_till_amortized = np.ceil(months_till_amortized) + return months_till_amortized//months_a_year, months_till_amortized%months_a_year, round(kilometers, ndigits=2) def main(): - parser = argparse.ArgumentParser(description='This script allows to calculate if an electric car makes sense financially for you') - parser.add_argument('-a','--settings', help='Settings file', required=True, metavar=('FILENAME')) - parser.add_argument('-b','--break_even', help='Calculate the break even point. (When does the EV become cheaper)', action='store_true') - parser.add_argument('-c','--savings_per_year', help='Calculate savings per year', action='store_true') - parser.add_argument('-d','--savings_per_month', help='Calculate savings per month', action='store_true') - parser.add_argument('-e','--plot', help='Visualize costs over one or multiple years', type=int, metavar=('YEARS')) + parser = argparse.ArgumentParser(description='This script allows to calculate if an electric car makes sense financially for you.') + parser.add_argument('-a','--settings', help='Settings file.', required=True, metavar=('FILENAME')) + parser.add_argument('-b','--break_even', help='Calculate the break even point (when the EV becomes cheaper).', action='store_true') + parser.add_argument('-c','--amortization', help='Calculate the point in time when the electric vehicle is amortized completely by savings.', action='store_true') + parser.add_argument('-d','--savings_per_month', help='Calculate savings per month.', action='store_true') + parser.add_argument('-e','--savings_per_year', help='Calculate savings per year.', action='store_true') + parser.add_argument('-f','--savings_per_kilometer', help='Calculate savings per 100 kilometers (only driving, no maintenance, taxes or insurance).', action='store_true') + parser.add_argument('-g','--plot', help='Visualize costs over one or multiple years.', type=int, metavar=('YEARS')) args = parser.parse_args() if not args.break_even and not args.savings_per_year and not args.savings_per_month and not args.plot: sys.exit("Please choose one or multiple options") comparator = c_ecar_comparator(args.settings) extractor = c_settings_extractor(args.settings) - if args.plot: + be_years = None + be_months = None + be_kilometers = None + if args.break_even: + be_years, be_months, be_kilometers = comparator.calculate_break_even() + print("Break even after {} years and {} months.".format(be_years, be_months)) + if args.savings_per_month: + years = 0 + months = 1 + savings = comparator.calculate_costs(years, months) + print("Savings per month based on yearly spending: {}.".format(round(savings[1]-savings[0], ndigits=2))) + if args.savings_per_year: + years = 1 + months = 0 + savings = comparator.calculate_costs(years, months) + print("Savings per year: {}.".format(round(savings[1]-savings[0], ndigits=2))) + if args.savings_per_kilometer: + hundred_km = 100.0 + driving = hundred_km * extractor.get_driving() / extractor.get_kilometers() + labels = extractor.get_labels() + print("Costs driving 100 km in the {}: {}. Costs driving 100 km in the {}: {}.".format(labels[0], round(driving[0], ndigits=2), labels[1], round(driving[1]), ndigits=2)) + am_years = None + am_months = None + am_kilometers = None + if args.amortization: + am_years, am_months, am_kilometers = comparator.calculate_amortization_point() + print("The electric vehicle will be amortized by savings after {} years, {} months or exactely at {} kilometres.".format(am_years, am_months, am_kilometers)) + if args.plot != None: width = 0.3 + plt_colors = ["#8ecae6", "#219ebc", "#023047", "#ffb703", "#fb8500"]; + color_ind = 0 labels = extractor.get_labels() purchase = extractor.get_purchase() taxes = extractor.get_taxes() @@ -75,29 +123,39 @@ def main(): driving = extractor.get_driving() maintenance = extractor.get_maintenance() fig, ax = plt.subplots() - ax.bar(labels, purchase, width, label = "Price", color = "gray") + ax.bar(labels, purchase, width, label = "Price", color = plt_colors[0]) current_y = extractor.get_purchase() y = 0 - - for i in range(args.plot): - ax.bar(labels, taxes, width, bottom = current_y, label = "Taxes".format(y), color = "darkgreen") - current_y = current_y + taxes - ax.bar(labels, insurance, width, bottom = current_y, label = "Insurance".format(y), color = "royalblue") - current_y = current_y + insurance - ax.bar(labels, driving, width, bottom = current_y, label = "Driving".format(y), color = "midnightblue") - current_y = current_y + driving - ax.bar(labels, maintenance, width, bottom = current_y, label = "Maintenance".format(y), color = "lavender") - current_y = current_y + maintenance - y += 1 - ecar_top = current_y[0] - #ax.plot(np.linspace(-0.2, 1.2, 10), [ecar_top]*10, "--", color = "firebrick", label = "Break even") - #ax.text(0.3, ecar_top * 0.95, "Break even: {} years, {} kilometers".format(y, y*settings["kilometers_per_year"])) - ax.set_ylabel("CHF") - ax.set_title("Comparision of economics electric vs. combustion car") - ax.legend(["Price", "Taxes", "Insurance", "Driving", "Maintenance"]) - ax.grid(axis = "y") - #print("Break even after {} years and {} kilometers. {}".format(y, y*settings["kilometers_per_year"], current_y[0]-current_y[1])) - plt.show() + for i in range(args.plot): + ax.bar(labels, taxes, width, bottom = current_y, label = "Taxes".format(y), color = plt_colors[1]) + current_y = current_y + taxes + ax.bar(labels, insurance, width, bottom = current_y, label = "Insurance".format(y), color = plt_colors[2]) + current_y = current_y + insurance + ax.bar(labels, driving, width, bottom = current_y, label = "Driving".format(y), color = plt_colors[3]) + current_y = current_y + driving + ax.bar(labels, maintenance, width, bottom = current_y, label = "Maintenance".format(y), color = plt_colors[4]) + current_y = current_y + maintenance + y += 1 + labels = ["Purchase", "Taxes", "Insurance", "Driving", "Maintenance"] + if args.break_even: + months_a_year = 12.0 + be_money = (be_years + be_months/months_a_year) * comparator.calculate_costs_a_year() + be_money = be_money[1] + extractor.get_purchase() + ax.plot(np.linspace(-0.2, 1.2, 10), [be_money[1]]*10, "--", color = plt_colors[2], label = "Break even") + ax.text(0.3, be_money[1] + 100, "Break even: {} years, {} months, {} kilometers".format(be_years, be_months, be_kilometers)) + labels = ["Break even"] + labels + if args.amortization: + months_a_year = 12.0 + am_money = (am_years + am_months/months_a_year) * comparator.calculate_costs_a_year() + am_money = am_money[1] + extractor.get_purchase() + ax.plot(np.linspace(-0.2, 1.2, 10), [am_money[1]]*10, "--", color = plt_colors[2], label = "Amortization") + ax.text(0.3, am_money[1] + 100, "Amortization: {} years, {} months, {} kilometers".format(am_years, am_months, am_kilometers)) + labels = ["Amortization"] + labels + ax.set_ylabel(extractor.get_currency()) + ax.set_title("Comparision of economics electric vs. combustion car") + ax.legend(labels) + ax.grid(axis = "y") + plt.show() if __name__ == "__main__": main()