From 72e4f8dd16b46c1fb8a22be395a4126ea43ce27c Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 20 Aug 2021 15:27:58 +0200 Subject: [PATCH 01/13] Added dummy config file --- test/test_data/config.csv | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/test_data/config.csv diff --git a/test/test_data/config.csv b/test/test_data/config.csv new file mode 100644 index 0000000..7761488 --- /dev/null +++ b/test/test_data/config.csv @@ -0,0 +1,36 @@ +Country,Benin +CountryCode,bj +StartYear,2020 +EndYEar,2030 +PopStartYear,12123000 +UrbanRatioStartYear,0.484 +UrbanCutOff, +UrbanRatioModelled, +PopEndYearHigh,15672000 +PopEndYearLow,15672000 +UrbanRatioEndYear,0.541 +NumPeoplePerHHRural,3.6 +NumPeoplePerHHUrban,3.1 +GridCapacityInvestmentCost,1982 +GridLosses,0.143 +DiscountRate,0.08 +BaseToPeak,0.8 +ExistingGridCostRatio,0.1 +MaxGridExtensionDist,50 +NewGridGenerationCapacityAnnualLimitMW,70 +ElecActual,0.42 +Rural_elec_ratio,0.18 +Urban_elec_ratio,0.67 +ElecModelled, +urban_elec_ratio_modelled, +rural_elec_ratio_modelled, +UrbanTargetTier,5 +RuralTargetTier,4 +5YearTarget,0.71 +GridConnectionsLimitThousands,9999 +GridGenerationCost,0.16 +PV_Cost_adjust,1.25 +DieselPrice,9999 +ProductiveDemand,0 +PrioritizationAlgorithm,2 +AutoIntensificationKM,0 From 8309a6bae87fdf9d86c2b680dbb9be674ee76ee8 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 20 Aug 2021 16:07:00 +0200 Subject: [PATCH 02/13] Read in scenario data to a dictionary --- onsset/runner.py | 33 +++++++++++++----- test/test_data/config.csv | 73 ++++++++++++++++++++------------------- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/onsset/runner.py b/onsset/runner.py index 859c7c1..5268963 100644 --- a/onsset/runner.py +++ b/onsset/runner.py @@ -2,10 +2,13 @@ import logging import os +from csv import DictReader import pandas as pd -from onsset import (SET_ELEC_ORDER, SET_LCOE_GRID, SET_MIN_GRID_DIST, SET_GRID_PENALTY, - SET_MV_CONNECT_DIST, SET_WINDCF, SettlementProcessor, Technology) +from onsset import (SET_ELEC_ORDER, SET_GRID_PENALTY, SET_LCOE_GRID, + SET_MIN_GRID_DIST, SET_MV_CONNECT_DIST, SET_WINDCF, + SettlementProcessor, Technology) +from openpyxl import load_workbook try: from onsset.specs import (SPE_COUNTRY, SPE_ELEC, SPE_ELEC_MODELLED, @@ -25,12 +28,26 @@ SPE_NUM_PEOPLE_PER_HH_URBAN, SPE_POP, SPE_POP_FUTURE, SPE_START_YEAR, SPE_URBAN, SPE_URBAN_FUTURE, SPE_URBAN_MODELLED) -from openpyxl import load_workbook logging.basicConfig(format='%(asctime)s\t\t%(message)s', level=logging.DEBUG) -def calibration(specs_path, csv_path, specs_path_calib, calibrated_csv_path): +def read_scenario_data(path_to_config: str) -> pd.DataFrame: + config = {} + with open(path_to_config, 'r') as csvfile: + reader = DictReader(csvfile) + config_file = list(reader) + for row in config_file: + if row['value']: + try: + config[row['parameter']] = float(row['value']) + except ValueError: + config[row['parameter']] = row['value'] + + return config + + +def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path): """ Arguments @@ -40,7 +57,7 @@ def calibration(specs_path, csv_path, specs_path_calib, calibrated_csv_path): specs_path_calib calibrated_csv_path """ - specs_data = pd.read_excel(specs_path, sheet_name='SpecsData') + specs_data = read_scenario_data(specs_path) settlements_in_csv = csv_path settlements_out_csv = calibrated_csv_path @@ -84,7 +101,7 @@ def calibration(specs_path, csv_path, specs_path_calib, calibrated_csv_path): elec_modelled, rural_elec_ratio, urban_elec_ratio = \ onsseter.elec_current_and_future(elec_actual, elec_actual_urban, elec_actual_rural, start_year) - # In case there are limitations in the way grid expansion is moving in a country, + # In case there are limitations in the way grid expansion is moving in a country, # this can be reflected through gridspeed. # In this case the parameter is set to a very high value therefore is not taken into account. @@ -96,7 +113,7 @@ def calibration(specs_path, csv_path, specs_path_calib, calibrated_csv_path): book = load_workbook(specs_path) writer = pd.ExcelWriter(specs_path_calib, engine='openpyxl') writer.book = book - # RUN_PARAM: Here the calibrated "specs" data are copied to a new tab called "SpecsDataCalib". + # RUN_PARAM: Here the calibrated "specs" data are copied to a new tab called "SpecsDataCalib". # This is what will later on be used to feed the model specs_data.to_excel(writer, sheet_name='SpecsDataCalib', index=False) writer.save() @@ -297,7 +314,7 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): grid_connect_limit) onsseter.df[SET_LCOE_GRID + "{}".format(year)], onsseter.df[SET_MIN_GRID_DIST + "{}".format(year)], \ - onsseter.df[SET_ELEC_ORDER + "{}".format(year)], onsseter.df[SET_MV_CONNECT_DIST], grid_investment = \ + onsseter.df[SET_ELEC_ORDER + "{}".format(year)], onsseter.df[SET_MV_CONNECT_DIST], grid_investment = \ onsseter.elec_extension(grid_calc, max_grid_extension_dist, year, diff --git a/test/test_data/config.csv b/test/test_data/config.csv index 7761488..b36d0f1 100644 --- a/test/test_data/config.csv +++ b/test/test_data/config.csv @@ -1,36 +1,37 @@ -Country,Benin -CountryCode,bj -StartYear,2020 -EndYEar,2030 -PopStartYear,12123000 -UrbanRatioStartYear,0.484 -UrbanCutOff, -UrbanRatioModelled, -PopEndYearHigh,15672000 -PopEndYearLow,15672000 -UrbanRatioEndYear,0.541 -NumPeoplePerHHRural,3.6 -NumPeoplePerHHUrban,3.1 -GridCapacityInvestmentCost,1982 -GridLosses,0.143 -DiscountRate,0.08 -BaseToPeak,0.8 -ExistingGridCostRatio,0.1 -MaxGridExtensionDist,50 -NewGridGenerationCapacityAnnualLimitMW,70 -ElecActual,0.42 -Rural_elec_ratio,0.18 -Urban_elec_ratio,0.67 -ElecModelled, -urban_elec_ratio_modelled, -rural_elec_ratio_modelled, -UrbanTargetTier,5 -RuralTargetTier,4 -5YearTarget,0.71 -GridConnectionsLimitThousands,9999 -GridGenerationCost,0.16 -PV_Cost_adjust,1.25 -DieselPrice,9999 -ProductiveDemand,0 -PrioritizationAlgorithm,2 -AutoIntensificationKM,0 +parameter,value,description +Country,Benin, +CountryCode,bj, +StartYear,2020, +EndYEar,2030, +PopStartYear,12123000, +UrbanRatioStartYear,0.484, +UrbanCutOff,, +UrbanRatioModelled,, +PopEndYearHigh,15672000, +PopEndYearLow,15672000, +UrbanRatioEndYear,0.541, +NumPeoplePerHHRural,3.6, +NumPeoplePerHHUrban,3.1, +GridCapacityInvestmentCost,1982, +GridLosses,0.143, +DiscountRate,0.08, +BaseToPeak,0.8, +ExistingGridCostRatio,0.1, +MaxGridExtensionDist,50, +NewGridGenerationCapacityAnnualLimitMW,70, +ElecActual,0.42, +Rural_elec_ratio,0.18, +Urban_elec_ratio,0.67, +ElecModelled,, +urban_elec_ratio_modelled,, +rural_elec_ratio_modelled,, +UrbanTargetTier,5, +RuralTargetTier,4, +5YearTarget,0.71, +GridConnectionsLimitThousands,9999, +GridGenerationCost,0.16, +PV_Cost_adjust,1.25, +DieselPrice,9999, +ProductiveDemand,0, +PrioritizationAlgorithm,2, +AutoIntensificationKM,0, From 73859e10cc793f2e0fc51a14cee00e90d9ca165f Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 20 Aug 2021 16:15:23 +0200 Subject: [PATCH 03/13] Allow csvs in onsset/data and test folders --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c35f777..21d5aa9 100644 --- a/.gitignore +++ b/.gitignore @@ -125,8 +125,8 @@ dmypy.json # Datafiles *.xlsx *.csv - - +!onsset/data/*.csv +!test/*/*.csv # VSCode .vscode From 8e14d4c9b49349323e3963042496349daed1e745 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 20 Aug 2021 16:16:01 +0200 Subject: [PATCH 04/13] Add data subfolder for user config template etc. --- onsset/data/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 onsset/data/.gitkeep diff --git a/onsset/data/.gitkeep b/onsset/data/.gitkeep new file mode 100644 index 0000000..e69de29 From e4712d4ce9ad097c17b1720e4a6846eb9c896ac3 Mon Sep 17 00:00:00 2001 From: Babak Date: Fri, 20 Aug 2021 16:15:43 +0200 Subject: [PATCH 05/13] Create template_config.csv --- test/test_data/template_config.csv | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/test_data/template_config.csv diff --git a/test/test_data/template_config.csv b/test/test_data/template_config.csv new file mode 100644 index 0000000..64a9013 --- /dev/null +++ b/test/test_data/template_config.csv @@ -0,0 +1,37 @@ +parameter;value;description +Country;Benin;Name of country +CountryCode;bj;ISO-2 code of country +StartYear;2020;Start year of analysis +EndYEar;2030;End year of analysis +PopStartYear;12123000;Population in start year +UrbanRatioStartYear;0.484;Urban ratio in start year (0 = all rural, 1 = all urban) +UrbanCutOff;;Cut-off value for urban settlements, all settlements with higher population are urban, else rural. +UrbanRatioModelled;;Urban ratio modelled based on input +PopEndYearHigh;15672000;Population in 2030 (high variant) +PopEndYearLow;15672000;Population in 2030 (low variant) +UrbanRatioEndYear;0.541;Urban ratio in end year (0 = all rural, 1 = all urban) +NumPeoplePerHHRural;3.6;Number of people per housheold in rural setttlements +NumPeoplePerHHUrban;3.1;Number of people per housheold in urban setttlements +GridCapacityInvestmentCost;1982;grid capacity investments costs USD/kW. Weighted average of additions to the grid +GridLosses;0.143;Losses in transmission and distrbution network (%) +DiscountRate;0.08;Discount rate (%) +BaseToPeak;0.8;Base to peak ratio (%) +ExistingGridCostRatio;0.1; +MaxGridExtensionDist;50;Max limit for how far the grid can extend +NewGridGenerationCapacityAnnualLimitMW;70;Annual added capacity limit (MW/year) between start year and the intermediate year (no limit between intermediate and end yaer) +ElecActual;0.42;National electrification rate as reported by country +Rural_elec_ratio;0.18;Rural electrification rate as repoterd by country +Urban_elec_ratio;0.67;Urban electrification rate as repoterd by country +ElecModelled;;National electrification rate modelled by OnSSET +urban_elec_ratio_modelled;;Rural electrification rate modelled by OnSSET +rural_elec_ratio_modelled;;Urban electrification rate modelled by OnSSET +UrbanTargetTier;5;Electricity demand in urban settlements in the end year (1-5 are MTF tiers, 6 is custom demand) +RuralTargetTier;4;Electricity demand in rural settlements in the end year (1-5 are MTF tiers, 6 is custom demand) +5YearTarget;0.71;Electrification rate 5 years from start year of analysis +GridConnectionsLimitThousands;9999;Limit for how many people that can be connected to the grid +GridGenerationCost;0.16;Cost of generating electricity for the centralised grid +PV_Cost_adjust;1.25;Development of PV price +DieselPrice;9999;Diesel price +ProductiveDemand;0; +PrioritizationAlgorithm;2;Determines which settlements to prioritize first (for electrification in the intermediate year). +AutoIntensificationKM;0; From aa90d73404fc9f65a9310b0c3b5fd40a6ecafe85 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 20 Aug 2021 16:38:00 +0200 Subject: [PATCH 06/13] Calibration step reads config data from csv file --- onsset/runner.py | 41 ++++++++++++++++++++++------------------- test/test_runner.py | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/onsset/runner.py b/onsset/runner.py index 5268963..07d80d8 100644 --- a/onsset/runner.py +++ b/onsset/runner.py @@ -3,6 +3,7 @@ import logging import os from csv import DictReader +from typing import Dict import pandas as pd from onsset import (SET_ELEC_ORDER, SET_GRID_PENALTY, SET_LCOE_GRID, @@ -32,7 +33,9 @@ logging.basicConfig(format='%(asctime)s\t\t%(message)s', level=logging.DEBUG) -def read_scenario_data(path_to_config: str) -> pd.DataFrame: +def read_scenario_data(path_to_config: str) -> Dict: + """Reads the scenario data into a dictionary + """ config = {} with open(path_to_config, 'r') as csvfile: reader = DictReader(csvfile) @@ -61,10 +64,8 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path settlements_in_csv = csv_path settlements_out_csv = calibrated_csv_path - onsseter = SettlementProcessor(settlements_in_csv) - - num_people_per_hh_rural = float(specs_data.iloc[0][SPE_NUM_PEOPLE_PER_HH_RURAL]) - num_people_per_hh_urban = float(specs_data.iloc[0][SPE_NUM_PEOPLE_PER_HH_URBAN]) + num_people_per_hh_rural = specs_data['NumPeoplePerHHRural'] + num_people_per_hh_urban = specs_data['NumPeoplePerHHUrban'] # RUN_PARAM: these are the annual household electricity targets tier_1 = 38.7 # 38.7 refers to kWh/household/year. It is the mean value between Tier 1 and Tier 2 @@ -73,6 +74,8 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path tier_4 = 2117 tier_5 = 2993 + onsseter = SettlementProcessor(settlements_in_csv) + onsseter.prepare_wtf_tier_columns(num_people_per_hh_rural, num_people_per_hh_urban, tier_1, tier_2, tier_3, tier_4, tier_5) onsseter.condition_df() @@ -80,18 +83,18 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path onsseter.df[SET_WINDCF] = onsseter.calc_wind_cfs() - pop_actual = specs_data.loc[0, SPE_POP] - pop_future_high = specs_data.loc[0, SPE_POP_FUTURE + 'High'] - pop_future_low = specs_data.loc[0, SPE_POP_FUTURE + 'Low'] - urban_current = specs_data.loc[0, SPE_URBAN] - urban_future = specs_data.loc[0, SPE_URBAN_FUTURE] - start_year = int(specs_data.loc[0, SPE_START_YEAR]) - end_year = int(specs_data.loc[0, SPE_END_YEAR]) + pop_actual = specs_data[SPE_POP] + pop_future_high = specs_data[SPE_POP_FUTURE + 'High'] + pop_future_low = specs_data[SPE_POP_FUTURE + 'Low'] + urban_current = specs_data[SPE_URBAN] + urban_future = specs_data[SPE_URBAN_FUTURE] + start_year = int(specs_data[SPE_START_YEAR]) + end_year = int(specs_data[SPE_END_YEAR]) intermediate_year = 2025 - elec_actual = specs_data.loc[0, SPE_ELEC] - elec_actual_urban = specs_data.loc[0, SPE_ELEC_URBAN] - elec_actual_rural = specs_data.loc[0, SPE_ELEC_RURAL] + elec_actual = specs_data[SPE_ELEC] + elec_actual_urban = specs_data[SPE_ELEC_URBAN] + elec_actual_rural = specs_data[SPE_ELEC_RURAL] pop_modelled, urban_modelled = onsseter.calibrate_current_pop_and_urban(pop_actual, urban_current) @@ -105,10 +108,10 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path # this can be reflected through gridspeed. # In this case the parameter is set to a very high value therefore is not taken into account. - specs_data.loc[0, SPE_URBAN_MODELLED] = urban_modelled - specs_data.loc[0, SPE_ELEC_MODELLED] = elec_modelled - specs_data.loc[0, 'rural_elec_ratio_modelled'] = rural_elec_ratio - specs_data.loc[0, 'urban_elec_ratio_modelled'] = urban_elec_ratio + specs_data[SPE_URBAN_MODELLED] = urban_modelled + specs_data[SPE_ELEC_MODELLED] = elec_modelled + specs_data['rural_elec_ratio_modelled'] = rural_elec_ratio + specs_data['urban_elec_ratio_modelled'] = urban_elec_ratio book = load_workbook(specs_path) writer = pd.ExcelWriter(specs_path_calib, engine='openpyxl') diff --git a/test/test_runner.py b/test/test_runner.py index 41d0da2..6026f07 100644 --- a/test/test_runner.py +++ b/test/test_runner.py @@ -32,7 +32,7 @@ def run_analysis(tmpdir): """ - specs_path = os.path.join('test', 'test_data', 'dj-specs-test.xlsx') + specs_path = os.path.join('test', 'test_data', 'config.csv') csv_path = os.path.join('test', 'test_data', 'dj-test.csv') calibrated_csv_path = os.path.join(tmpdir, 'dj-calibrated.csv') specs_path_calib = os.path.join(tmpdir, 'dj-specs-test-calib.xlsx') From 97f1d5e40602ad1e8ae512b89a728e3fc6fc72c4 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 20 Aug 2021 17:12:50 +0200 Subject: [PATCH 07/13] WIP: config working for calibration, added config test --- onsset/runner.py | 19 ++++++++-------- test/test_config.py | 46 +++++++++++++++++++++++++++++++++++++++ test/test_data/config.csv | 16 +++++++------- test/test_runner.py | 2 +- 4 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 test/test_config.py diff --git a/onsset/runner.py b/onsset/runner.py index 07d80d8..ffba89e 100644 --- a/onsset/runner.py +++ b/onsset/runner.py @@ -2,14 +2,13 @@ import logging import os -from csv import DictReader +from csv import DictReader, DictWriter from typing import Dict import pandas as pd from onsset import (SET_ELEC_ORDER, SET_GRID_PENALTY, SET_LCOE_GRID, SET_MIN_GRID_DIST, SET_MV_CONNECT_DIST, SET_WINDCF, SettlementProcessor, Technology) -from openpyxl import load_workbook try: from onsset.specs import (SPE_COUNTRY, SPE_ELEC, SPE_ELEC_MODELLED, @@ -50,6 +49,13 @@ def read_scenario_data(path_to_config: str) -> Dict: return config +def write_data_to_csv(calibration_data: Dict, calibration_path: str): + with open(calibration_path, 'w') as csv_file: + writer = DictWriter(csv_file, fieldnames=['parameter', 'value']) + for key, value in calibration_data.items(): + writer.writerow({'parameter': key, 'value': value}) + + def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path): """ @@ -113,14 +119,7 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path specs_data['rural_elec_ratio_modelled'] = rural_elec_ratio specs_data['urban_elec_ratio_modelled'] = urban_elec_ratio - book = load_workbook(specs_path) - writer = pd.ExcelWriter(specs_path_calib, engine='openpyxl') - writer.book = book - # RUN_PARAM: Here the calibrated "specs" data are copied to a new tab called "SpecsDataCalib". - # This is what will later on be used to feed the model - specs_data.to_excel(writer, sheet_name='SpecsDataCalib', index=False) - writer.save() - writer.close() + write_data_to_csv(specs_data, specs_path_calib) logging.info('Calibration finished. Results are transferred to the csv file') onsseter.df.to_csv(settlements_out_csv, index=False) diff --git a/test/test_config.py b/test/test_config.py new file mode 100644 index 0000000..7f97db3 --- /dev/null +++ b/test/test_config.py @@ -0,0 +1,46 @@ +import os + +from onsset.runner import read_scenario_data + + +def test_read_scenario_data(): + + path = os.path.join('test', 'test_data', 'config.csv') + + data = [ + ['Country', 'Benin'], + ['CountryCode', 'bj'], + ['StartYear', 2020], + ['EndYEar', 2030], + ['PopStartYear', 12123000], + ['UrbanRatioStartYear', 0.484], + ['PopEndYearHigh', 15672000], + ['PopEndYearLow', 15672000], + ['UrbanRatioEndYear', 0.541], + ['NumPeoplePerHHRural', 3.6], + ['NumPeoplePerHHUrban', 3.1], + ['GridCapacityInvestmentCost', 1982], + ['GridLosses', 0.143], + ['DiscountRate', 0.08], + ['BaseToPeak', 0.8], + ['ExistingGridCostRatio', 0.1], + ['MaxGridExtensionDist', 50], + ['NewGridGenerationCapacityAnnualLimitMW', 70], + ['ElecActual', 0.42], + ['Rural_elec_ratio', 0.18], + ['Urban_elec_ratio', 0.67], + ['UrbanTargetTier', 5], + ['RuralTargetTier', 4], + ['5YearTarget', 0.71], + ['GridConnectionsLimitThousands', 9999], + ['GridGenerationCost', 0.16], + ['PV_Cost_adjust', 1.25], + ['DieselPrice', 9999], + ['ProductiveDemand', 0], + ['PrioritizationAlgorithm', 2], + ['AutoIntensificationKM', 0] + ] + actual = read_scenario_data(path) + expected = dict(data) + + assert actual == expected diff --git a/test/test_data/config.csv b/test/test_data/config.csv index b36d0f1..2ff62e6 100644 --- a/test/test_data/config.csv +++ b/test/test_data/config.csv @@ -3,13 +3,13 @@ Country,Benin, CountryCode,bj, StartYear,2020, EndYEar,2030, -PopStartYear,12123000, -UrbanRatioStartYear,0.484, +PopStartYear,971000, +UrbanRatioStartYear,0.78, UrbanCutOff,, UrbanRatioModelled,, -PopEndYearHigh,15672000, -PopEndYearLow,15672000, -UrbanRatioEndYear,0.541, +PopEndYearHigh,1100000, +PopEndYearLow,1100000, +UrbanRatioEndYear,0.8, NumPeoplePerHHRural,3.6, NumPeoplePerHHUrban,3.1, GridCapacityInvestmentCost,1982, @@ -19,9 +19,9 @@ BaseToPeak,0.8, ExistingGridCostRatio,0.1, MaxGridExtensionDist,50, NewGridGenerationCapacityAnnualLimitMW,70, -ElecActual,0.42, -Rural_elec_ratio,0.18, -Urban_elec_ratio,0.67, +ElecActual,0.6, +Rural_elec_ratio,0.26, +Urban_elec_ratio,0.7, ElecModelled,, urban_elec_ratio_modelled,, rural_elec_ratio_modelled,, diff --git a/test/test_runner.py b/test/test_runner.py index 6026f07..03fe294 100644 --- a/test/test_runner.py +++ b/test/test_runner.py @@ -39,7 +39,7 @@ def run_analysis(tmpdir): calibration(specs_path, csv_path, specs_path_calib, calibrated_csv_path) - scenario(specs_path_calib, calibrated_csv_path, tmpdir, tmpdir) + scenario(specs_path, calibrated_csv_path, tmpdir, tmpdir) actual = os.path.join(tmpdir, 'dj-1-1_1_1_1_0_0_summary.csv') expected = os.path.join('test', 'test_results', 'expected_summary.csv') From f6e15aeb27b579492c63a18986c374506ecb6eab Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 23 Aug 2021 20:25:20 +0200 Subject: [PATCH 08/13] Replace variable parameter names --- onsset/runner.py | 85 ++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/onsset/runner.py b/onsset/runner.py index ffba89e..6f5d5c5 100644 --- a/onsset/runner.py +++ b/onsset/runner.py @@ -9,25 +9,10 @@ from onsset import (SET_ELEC_ORDER, SET_GRID_PENALTY, SET_LCOE_GRID, SET_MIN_GRID_DIST, SET_MV_CONNECT_DIST, SET_WINDCF, SettlementProcessor, Technology) - -try: - from onsset.specs import (SPE_COUNTRY, SPE_ELEC, SPE_ELEC_MODELLED, - SPE_ELEC_RURAL, SPE_ELEC_URBAN, SPE_END_YEAR, - SPE_GRID_CAPACITY_INVESTMENT, SPE_GRID_LOSSES, - SPE_MAX_GRID_EXTENSION_DIST, - SPE_NUM_PEOPLE_PER_HH_RURAL, - SPE_NUM_PEOPLE_PER_HH_URBAN, SPE_POP, SPE_POP_FUTURE, - SPE_START_YEAR, SPE_URBAN, SPE_URBAN_FUTURE, - SPE_URBAN_MODELLED) -except ImportError: - from specs import (SPE_COUNTRY, SPE_ELEC, SPE_ELEC_MODELLED, - SPE_ELEC_RURAL, SPE_ELEC_URBAN, SPE_END_YEAR, - SPE_GRID_CAPACITY_INVESTMENT, SPE_GRID_LOSSES, - SPE_MAX_GRID_EXTENSION_DIST, - SPE_NUM_PEOPLE_PER_HH_RURAL, - SPE_NUM_PEOPLE_PER_HH_URBAN, SPE_POP, SPE_POP_FUTURE, - SPE_START_YEAR, SPE_URBAN, SPE_URBAN_FUTURE, - SPE_URBAN_MODELLED) +from onsset.specs import (SPE_GRID_CAPACITY_INVESTMENT, SPE_GRID_LOSSES, + SPE_MAX_GRID_EXTENSION_DIST, + SPE_NUM_PEOPLE_PER_HH_RURAL, + SPE_NUM_PEOPLE_PER_HH_URBAN) logging.basicConfig(format='%(asctime)s\t\t%(message)s', level=logging.DEBUG) @@ -89,33 +74,34 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path onsseter.df[SET_WINDCF] = onsseter.calc_wind_cfs() - pop_actual = specs_data[SPE_POP] - pop_future_high = specs_data[SPE_POP_FUTURE + 'High'] - pop_future_low = specs_data[SPE_POP_FUTURE + 'Low'] - urban_current = specs_data[SPE_URBAN] - urban_future = specs_data[SPE_URBAN_FUTURE] - start_year = int(specs_data[SPE_START_YEAR]) - end_year = int(specs_data[SPE_END_YEAR]) - intermediate_year = 2025 - elec_actual = specs_data[SPE_ELEC] - elec_actual_urban = specs_data[SPE_ELEC_URBAN] - elec_actual_rural = specs_data[SPE_ELEC_RURAL] + elec_actual = specs_data['ElecActual'] + elec_actual_urban = specs_data['Urban_elec_ratio'] + elec_actual_rural = specs_data['Rural_elec_ratio'] - pop_modelled, urban_modelled = onsseter.calibrate_current_pop_and_urban(pop_actual, urban_current) + pop_modelled, urban_modelled = onsseter.calibrate_current_pop_and_urban(specs_data['PopStartYear'], + specs_data['UrbanRatioStartYear']) - onsseter.project_pop_and_urban(pop_modelled, pop_future_high, pop_future_low, urban_modelled, - urban_future, start_year, end_year, intermediate_year) + onsseter.project_pop_and_urban(pop_modelled, specs_data['PopEndYearHigh'], + specs_data['PopEndYearLow'], + urban_modelled, + specs_data['UrbanRatioEndYear'], + specs_data['StartYear'], + specs_data['EndYear'], + intermediate_year) elec_modelled, rural_elec_ratio, urban_elec_ratio = \ - onsseter.elec_current_and_future(elec_actual, elec_actual_urban, elec_actual_rural, start_year) + onsseter.elec_current_and_future(elec_actual, + elec_actual_urban, + elec_actual_rural, + specs_data['StartYear']) # In case there are limitations in the way grid expansion is moving in a country, # this can be reflected through gridspeed. # In this case the parameter is set to a very high value therefore is not taken into account. - specs_data[SPE_URBAN_MODELLED] = urban_modelled - specs_data[SPE_ELEC_MODELLED] = elec_modelled + specs_data['UrbanRatioModelled'] = urban_modelled + specs_data['ElecModelled'] = elec_modelled specs_data['rural_elec_ratio_modelled'] = rural_elec_ratio specs_data['urban_elec_ratio_modelled'] = urban_elec_ratio @@ -141,7 +127,7 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): scenarios = scenario_info['Scenario'] scenario_parameters = pd.read_excel(specs_path, sheet_name='ScenarioParameters') specs_data = pd.read_excel(specs_path, sheet_name='SpecsDataCalib') - print(specs_data.loc[0, SPE_COUNTRY]) + print(specs_data.loc[0, 'Country']) for scenario in scenarios: print('Scenario: ' + str(scenario + 1)) @@ -169,20 +155,10 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): prioritization = scenario_parameters.iloc[prio_index]['PrioritizationAlgorithm'] auto_intensification = scenario_parameters.iloc[prio_index]['AutoIntensificationKM'] - settlements_in_csv = calibrated_csv_path - settlements_out_csv = os.path.join(results_folder, - '{}-1-{}_{}_{}_{}_{}_{}.csv'.format(country_id, pop_index, tier_index, - five_year_index, grid_index, pv_index, - prio_index)) - summary_csv = os.path.join(summary_folder, - '{}-1-{}_{}_{}_{}_{}_{}_summary.csv'.format(country_id, pop_index, tier_index, - five_year_index, grid_index, pv_index, - prio_index)) - - onsseter = SettlementProcessor(settlements_in_csv) + onsseter = SettlementProcessor(calibrated_csv_path) - start_year = specs_data.iloc[0][SPE_START_YEAR] - end_year = specs_data.iloc[0][SPE_END_YEAR] + start_year = specs_data.iloc[0]['StartYear'] + end_year = specs_data.iloc[0]['EndYear'] num_people_per_hh_rural = float(specs_data.iloc[0][SPE_NUM_PEOPLE_PER_HH_RURAL]) num_people_per_hh_urban = float(specs_data.iloc[0][SPE_NUM_PEOPLE_PER_HH_URBAN]) @@ -348,6 +324,15 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): elif onsseter.df.iloc[:, i].dtype == 'int64': onsseter.df.iloc[:, i] = pd.to_numeric(onsseter.df.iloc[:, i], downcast='signed') + settlements_out_csv = os.path.join(results_folder, + '{}-1-{}_{}_{}_{}_{}_{}.csv'.format(country_id, pop_index, tier_index, + five_year_index, grid_index, pv_index, + prio_index)) + summary_csv = os.path.join(summary_folder, + '{}-1-{}_{}_{}_{}_{}_{}_summary.csv'.format(country_id, pop_index, tier_index, + five_year_index, grid_index, pv_index, + prio_index)) + df_summary.to_csv(summary_csv, index=sumtechs) onsseter.df.to_csv(settlements_out_csv, index=False) From 9f6b3fe950f513ad72956d5063502ab41490e960 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 23 Aug 2021 21:43:17 +0200 Subject: [PATCH 09/13] Used dj data and added datatype column --- test/test_config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_config.py b/test/test_config.py index 7f97db3..77582d2 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -4,6 +4,8 @@ def test_read_scenario_data(): + """Store the configuration data as a dictionary + """ path = os.path.join('test', 'test_data', 'config.csv') From 2c9ce9ac93a54eee68c546920eb930dd5cc2e9bc Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 23 Aug 2021 21:52:44 +0200 Subject: [PATCH 10/13] Interpet datatypes of config file to get code running --- onsset/runner.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/onsset/runner.py b/onsset/runner.py index 6f5d5c5..d54e764 100644 --- a/onsset/runner.py +++ b/onsset/runner.py @@ -3,7 +3,7 @@ import logging import os from csv import DictReader, DictWriter -from typing import Dict +from typing import Any, Dict import pandas as pd from onsset import (SET_ELEC_ORDER, SET_GRID_PENALTY, SET_LCOE_GRID, @@ -17,19 +17,21 @@ logging.basicConfig(format='%(asctime)s\t\t%(message)s', level=logging.DEBUG) -def read_scenario_data(path_to_config: str) -> Dict: +def read_scenario_data(path_to_config: str) -> Dict[str, Any]: """Reads the scenario data into a dictionary """ - config = {} + config = {} # typing: Dict[str, Any] with open(path_to_config, 'r') as csvfile: reader = DictReader(csvfile) config_file = list(reader) for row in config_file: if row['value']: - try: + if row['data_type'] == 'int': + config[row['parameter']] = int(row['value']) + elif row['data_type'] == 'float': config[row['parameter']] = float(row['value']) - except ValueError: - config[row['parameter']] = row['value'] + elif row['data_type'] == 'str': + config[row['parameter']] = str(row['value']) return config From b2ee8b3b310b3fad9995d16766ccf2c8afd2f5c0 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 23 Aug 2021 22:10:45 +0200 Subject: [PATCH 11/13] All tests running --- test/test_config.py | 45 +++++++++++----------------- test/test_data/config.csv | 63 ++++++++++++++++----------------------- test/test_runner.py | 4 +-- 3 files changed, 45 insertions(+), 67 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 77582d2..afe69bf 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -10,37 +10,26 @@ def test_read_scenario_data(): path = os.path.join('test', 'test_data', 'config.csv') data = [ - ['Country', 'Benin'], - ['CountryCode', 'bj'], - ['StartYear', 2020], - ['EndYEar', 2030], - ['PopStartYear', 12123000], - ['UrbanRatioStartYear', 0.484], - ['PopEndYearHigh', 15672000], - ['PopEndYearLow', 15672000], - ['UrbanRatioEndYear', 0.541], - ['NumPeoplePerHHRural', 3.6], - ['NumPeoplePerHHUrban', 3.1], - ['GridCapacityInvestmentCost', 1982], - ['GridLosses', 0.143], - ['DiscountRate', 0.08], + ['Country', 'Djibouti'], + ['CountryCode', 'dj'], + ['StartYear', 2018], + ['EndYear', 2030], + ['PopStartYear', 971000], + ['UrbanRatioStartYear', 0.778], + ['PopEndYearHigh', 1179150], + ['PopEndYearLow', 1132710], + ['UrbanRatioEndYear', 0.8], + ['NumPeoplePerHHRural', 7.7], + ['NumPeoplePerHHUrban', 6.5], + ['GridCapacityInvestmentCost', 4426], + ['GridLosses', 0.083], ['BaseToPeak', 0.8], ['ExistingGridCostRatio', 0.1], ['MaxGridExtensionDist', 50], - ['NewGridGenerationCapacityAnnualLimitMW', 70], - ['ElecActual', 0.42], - ['Rural_elec_ratio', 0.18], - ['Urban_elec_ratio', 0.67], - ['UrbanTargetTier', 5], - ['RuralTargetTier', 4], - ['5YearTarget', 0.71], - ['GridConnectionsLimitThousands', 9999], - ['GridGenerationCost', 0.16], - ['PV_Cost_adjust', 1.25], - ['DieselPrice', 9999], - ['ProductiveDemand', 0], - ['PrioritizationAlgorithm', 2], - ['AutoIntensificationKM', 0] + ['NewGridGenerationCapacityAnnualLimitMW', 19], + ['ElecActual', 0.6], + ['Rural_elec_ratio', 0.26], + ['Urban_elec_ratio', 0.7], ] actual = read_scenario_data(path) expected = dict(data) diff --git a/test/test_data/config.csv b/test/test_data/config.csv index 2ff62e6..8059b3b 100644 --- a/test/test_data/config.csv +++ b/test/test_data/config.csv @@ -1,37 +1,26 @@ -parameter,value,description -Country,Benin, -CountryCode,bj, -StartYear,2020, -EndYEar,2030, -PopStartYear,971000, -UrbanRatioStartYear,0.78, -UrbanCutOff,, -UrbanRatioModelled,, -PopEndYearHigh,1100000, -PopEndYearLow,1100000, -UrbanRatioEndYear,0.8, -NumPeoplePerHHRural,3.6, -NumPeoplePerHHUrban,3.1, -GridCapacityInvestmentCost,1982, -GridLosses,0.143, -DiscountRate,0.08, -BaseToPeak,0.8, -ExistingGridCostRatio,0.1, -MaxGridExtensionDist,50, -NewGridGenerationCapacityAnnualLimitMW,70, -ElecActual,0.6, -Rural_elec_ratio,0.26, -Urban_elec_ratio,0.7, -ElecModelled,, -urban_elec_ratio_modelled,, -rural_elec_ratio_modelled,, -UrbanTargetTier,5, -RuralTargetTier,4, -5YearTarget,0.71, -GridConnectionsLimitThousands,9999, -GridGenerationCost,0.16, -PV_Cost_adjust,1.25, -DieselPrice,9999, -ProductiveDemand,0, -PrioritizationAlgorithm,2, -AutoIntensificationKM,0, +parameter,value,description,data_type +Country,Djibouti,,str +CountryCode,dj,,str +StartYear,2018,,int +EndYear,2030,,int +PopStartYear,971000,,int +UrbanRatioStartYear,0.778,,float +PopEndYearHigh,1179150,,int +PopEndYearLow,1132710,,int +UrbanRatioEndYear,0.8,,float +NumPeoplePerHHRural,7.7,,float +NumPeoplePerHHUrban,6.5,,float +GridCapacityInvestmentCost,4426,,float +GridLosses,0.083,,float +BaseToPeak,0.8,,float +ExistingGridCostRatio,0.1,,float +MaxGridExtensionDist,50,,float +NewGridGenerationCapacityAnnualLimitMW,19,,float +ElecActual,0.6,,float +Rural_elec_ratio,0.26,,float +Urban_elec_ratio,0.7,,float +ElecModelled,,,float +urban_elec_ratio_modelled,,,float +rural_elec_ratio_modelled,,,float +UrbanCutOff,,,float +UrbanRatioModelled,,,float diff --git a/test/test_runner.py b/test/test_runner.py index 03fe294..a41a52a 100644 --- a/test/test_runner.py +++ b/test/test_runner.py @@ -31,15 +31,15 @@ def run_analysis(tmpdir): Returns a tuple of bool for whether the summary or full files match """ - specs_path = os.path.join('test', 'test_data', 'config.csv') + scenario_path = os.path.join('test', 'test_data', 'dj-specs-test.xlsx') csv_path = os.path.join('test', 'test_data', 'dj-test.csv') calibrated_csv_path = os.path.join(tmpdir, 'dj-calibrated.csv') specs_path_calib = os.path.join(tmpdir, 'dj-specs-test-calib.xlsx') calibration(specs_path, csv_path, specs_path_calib, calibrated_csv_path) - scenario(specs_path, calibrated_csv_path, tmpdir, tmpdir) + scenario(specs_path, scenario_path, calibrated_csv_path, tmpdir, tmpdir) actual = os.path.join(tmpdir, 'dj-1-1_1_1_1_0_0_summary.csv') expected = os.path.join('test', 'test_results', 'expected_summary.csv') From 49d6deaf442505c57c7e14dbf55b65273f65e2da Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 23 Aug 2021 22:12:22 +0200 Subject: [PATCH 12/13] Started to add types to a few functions --- onsset/onsset.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/onsset/onsset.py b/onsset/onsset.py index 69a3616..dcfa6aa 100644 --- a/onsset/onsset.py +++ b/onsset/onsset.py @@ -1,10 +1,10 @@ import logging from math import exp, log, pi from typing import Dict -import scipy.spatial import numpy as np import pandas as pd +import scipy.spatial logging.basicConfig(format='%(asctime)s\t\t%(message)s', level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -1566,7 +1566,7 @@ def do_kdtree(combined_x_y_arrays, points): dist, indexes = mytree.query(points) return indexes - def calculate_new_connections(self, year, time_step, start_year): + def calculate_new_connections(self, year: int, time_step: int, start_year: int): """this method defines new connections for grid related purposes Arguments @@ -1719,7 +1719,8 @@ def calculate_total_demand_per_settlement(self, year): self.df.loc[self.df[SET_URBAN] == 2, SET_TOTAL_ENERGY_PER_CELL] = \ self.df[SET_CAPITA_DEMAND] * self.df[SET_POP + "{}".format(year)] - def set_scenario_variables(self, year, num_people_per_hh_rural, num_people_per_hh_urban, time_step, start_year, + def set_scenario_variables(self, year: int, num_people_per_hh_rural, num_people_per_hh_urban, + time_step: int, start_year: int, urban_tier, rural_tier, end_year_pop, productive_demand): """ this method determines some basic parameters required in LCOE calculation @@ -1751,7 +1752,7 @@ def set_scenario_variables(self, year, num_people_per_hh_rural, num_people_per_h self.calculate_total_demand_per_settlement(year) def calculate_off_grid_lcoes(self, mg_hydro_calc, mg_wind_calc, mg_pv_calc, sa_pv_calc, mg_diesel_calc, - sa_diesel_calc, year, end_year, time_step, diesel_techs=0): + sa_diesel_calc, year: int, end_year: int, time_step: int, diesel_techs=0): """ Calculate the LCOEs for all off-grid technologies From 642aa96dcd4676febebe106e2a6cab4ba35df9c3 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 23 Aug 2021 22:13:03 +0200 Subject: [PATCH 13/13] Extracted method for single scenario run --- onsset/runner.py | 365 +++++++++++++++++++++++++---------------------- 1 file changed, 192 insertions(+), 173 deletions(-) diff --git a/onsset/runner.py b/onsset/runner.py index d54e764..63e5776 100644 --- a/onsset/runner.py +++ b/onsset/runner.py @@ -32,6 +32,8 @@ def read_scenario_data(path_to_config: str) -> Dict[str, Any]: config[row['parameter']] = float(row['value']) elif row['data_type'] == 'str': config[row['parameter']] = str(row['value']) + else: + raise ValueError("Config file data type not recognised.") return config @@ -113,7 +115,7 @@ def calibration(specs_path: str, csv_path, specs_path_calib, calibrated_csv_path onsseter.df.to_csv(settlements_out_csv, index=False) -def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): +def scenario(specs_path, scenario_path, calibrated_csv_path, results_folder, summary_folder): """ Arguments @@ -125,15 +127,15 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): """ - scenario_info = pd.read_excel(specs_path, sheet_name='ScenarioInfo') + scenario_info = pd.read_excel(scenario_path, sheet_name='ScenarioInfo') scenarios = scenario_info['Scenario'] - scenario_parameters = pd.read_excel(specs_path, sheet_name='ScenarioParameters') - specs_data = pd.read_excel(specs_path, sheet_name='SpecsDataCalib') - print(specs_data.loc[0, 'Country']) + scenario_parameters = pd.read_excel(scenario_path, sheet_name='ScenarioParameters') + specs_data = read_scenario_data(specs_path) + print(specs_data['Country']) for scenario in scenarios: print('Scenario: ' + str(scenario + 1)) - country_id = specs_data.iloc[0]['CountryCode'] + country_id = specs_data['CountryCode'] pop_index = scenario_info.iloc[scenario]['Population_Growth'] tier_index = scenario_info.iloc[scenario]['Target_electricity_consumption_level'] @@ -157,174 +159,22 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): prioritization = scenario_parameters.iloc[prio_index]['PrioritizationAlgorithm'] auto_intensification = scenario_parameters.iloc[prio_index]['AutoIntensificationKM'] - onsseter = SettlementProcessor(calibrated_csv_path) - - start_year = specs_data.iloc[0]['StartYear'] - end_year = specs_data.iloc[0]['EndYear'] - - num_people_per_hh_rural = float(specs_data.iloc[0][SPE_NUM_PEOPLE_PER_HH_RURAL]) - num_people_per_hh_urban = float(specs_data.iloc[0][SPE_NUM_PEOPLE_PER_HH_URBAN]) - max_grid_extension_dist = float(specs_data.iloc[0][SPE_MAX_GRID_EXTENSION_DIST]) - annual_grid_cap_gen_limit = specs_data.loc[0, 'NewGridGenerationCapacityAnnualLimitMW'] * 1000 - - # RUN_PARAM: Fill in general and technology specific parameters (e.g. discount rate, losses etc.) - Technology.set_default_values(base_year=start_year, - start_year=start_year, - end_year=end_year, - discount_rate=0.08) - - grid_calc = Technology(om_of_td_lines=0.02, - distribution_losses=float(specs_data.iloc[0][SPE_GRID_LOSSES]), - connection_cost_per_hh=125, - base_to_peak_load_ratio=0.8, - capacity_factor=1, - tech_life=30, - grid_capacity_investment=float(specs_data.iloc[0][SPE_GRID_CAPACITY_INVESTMENT]), - grid_penalty_ratio=1, - grid_price=grid_price) - - mg_hydro_calc = Technology(om_of_td_lines=0.02, - distribution_losses=0.05, - connection_cost_per_hh=100, - base_to_peak_load_ratio=0.85, - capacity_factor=0.5, - tech_life=30, - capital_cost={float("inf"): 3000}, - om_costs=0.03, - mini_grid=True) - - mg_wind_calc = Technology(om_of_td_lines=0.02, - distribution_losses=0.05, - connection_cost_per_hh=100, - base_to_peak_load_ratio=0.85, - capital_cost={float("inf"): 3750}, - om_costs=0.02, - tech_life=20, - mini_grid=True) - - mg_pv_calc = Technology(om_of_td_lines=0.02, - distribution_losses=0.05, - connection_cost_per_hh=100, - base_to_peak_load_ratio=0.85, - tech_life=20, - om_costs=0.015, - capital_cost={float("inf"): 2950 * pv_capital_cost_adjust}, - mini_grid=True) + start_year = int(specs_data['StartYear']) + end_year = int(specs_data['EndYear']) - sa_pv_calc = Technology(base_to_peak_load_ratio=0.9, - tech_life=15, - om_costs=0.02, - capital_cost={float("inf"): 6950 * pv_capital_cost_adjust, - 1: 4470 * pv_capital_cost_adjust, - 0.100: 6380 * pv_capital_cost_adjust, - 0.050: 8780 * pv_capital_cost_adjust, - 0.020: 9620 * pv_capital_cost_adjust - }, - standalone=True) + num_people_per_hh_rural = float(specs_data[SPE_NUM_PEOPLE_PER_HH_RURAL]) + num_people_per_hh_urban = float(specs_data[SPE_NUM_PEOPLE_PER_HH_URBAN]) + max_grid_extension_dist = float(specs_data[SPE_MAX_GRID_EXTENSION_DIST]) + annual_grid_cap_gen_limit = specs_data['NewGridGenerationCapacityAnnualLimitMW'] * 1000 - mg_diesel_calc = Technology(om_of_td_lines=0.02, - distribution_losses=0.05, - connection_cost_per_hh=100, - base_to_peak_load_ratio=0.85, - capacity_factor=0.7, - tech_life=15, - om_costs=0.1, - capital_cost={float("inf"): 721}, - mini_grid=True) - - sa_diesel_calc = Technology(base_to_peak_load_ratio=0.9, - capacity_factor=0.5, - tech_life=10, - om_costs=0.1, - capital_cost={float("inf"): 938}, - standalone=True) - - sa_diesel_cost = {'diesel_price': diesel_price, - 'efficiency': 0.28, - 'diesel_truck_consumption': 14, - 'diesel_truck_volume': 300} - - mg_diesel_cost = {'diesel_price': diesel_price, - 'efficiency': 0.33, - 'diesel_truck_consumption': 33.7, - 'diesel_truck_volume': 15000} - - # RUN_PARAM: One shall define here the years of analysis (excluding start year), - # together with access targets per interval and timestep duration - yearsofanalysis = [2025, 2030] - eleclimits = {2025: five_year_target, 2030: 1} - time_steps = {2025: 7, 2030: 5} - - elements = ["1.Population", "2.New_Connections", "3.Capacity", "4.Investment"] - techs = ["Grid", "SA_Diesel", "SA_PV", "MG_Diesel", "MG_PV", "MG_Wind", "MG_Hydro", "MG_Hybrid"] - sumtechs = [] - for element in elements: - for tech in techs: - sumtechs.append(element + "_" + tech) - total_rows = len(sumtechs) - df_summary = pd.DataFrame(columns=yearsofanalysis) - for row in range(0, total_rows): - df_summary.loc[sumtechs[row]] = "Nan" - - onsseter.current_mv_line_dist() - - for year in yearsofanalysis: - eleclimit = eleclimits[year] - time_step = time_steps[year] - - if year - time_step == start_year: - grid_cap_gen_limit = time_step * annual_grid_cap_gen_limit - grid_connect_limit = time_step * annual_new_grid_connections_limit - else: - grid_cap_gen_limit = 9999999999 - grid_connect_limit = 9999999999 - - onsseter.set_scenario_variables(year, num_people_per_hh_rural, num_people_per_hh_urban, time_step, - start_year, urban_tier, rural_tier, end_year_pop, productive_demand) - - onsseter.diesel_cost_columns(sa_diesel_cost, mg_diesel_cost, year) - - sa_diesel_investment, sa_pv_investment, mg_diesel_investment, mg_pv_investment, mg_wind_investment, \ - mg_hydro_investment = onsseter.calculate_off_grid_lcoes(mg_hydro_calc, mg_wind_calc, mg_pv_calc, - sa_pv_calc, mg_diesel_calc, - sa_diesel_calc, year, end_year, time_step) - - grid_investment, grid_cap_gen_limit, grid_connect_limit = \ - onsseter.pre_electrification(grid_price, year, time_step, end_year, grid_calc, grid_cap_gen_limit, - grid_connect_limit) - - onsseter.df[SET_LCOE_GRID + "{}".format(year)], onsseter.df[SET_MIN_GRID_DIST + "{}".format(year)], \ - onsseter.df[SET_ELEC_ORDER + "{}".format(year)], onsseter.df[SET_MV_CONNECT_DIST], grid_investment = \ - onsseter.elec_extension(grid_calc, - max_grid_extension_dist, - year, - start_year, - end_year, - time_step, - grid_cap_gen_limit, - grid_connect_limit, - auto_intensification=auto_intensification, - prioritization=prioritization, - new_investment=grid_investment) - - onsseter.results_columns(year, time_step, prioritization, auto_intensification) - - onsseter.calculate_investments(sa_diesel_investment, sa_pv_investment, mg_diesel_investment, - mg_pv_investment, mg_wind_investment, - mg_hydro_investment, grid_investment, year) - - onsseter.apply_limitations(eleclimit, year, time_step, prioritization, auto_intensification) - - onsseter.calculate_new_capacity(mg_hydro_calc, mg_wind_calc, mg_pv_calc, sa_pv_calc, mg_diesel_calc, - sa_diesel_calc, grid_calc, year) - - onsseter.calc_summaries(df_summary, sumtechs, year) - - for i in range(len(onsseter.df.columns)): - if onsseter.df.iloc[:, i].dtype == 'float64': - onsseter.df.iloc[:, i] = pd.to_numeric(onsseter.df.iloc[:, i], downcast='float') - elif onsseter.df.iloc[:, i].dtype == 'int64': - onsseter.df.iloc[:, i] = pd.to_numeric(onsseter.df.iloc[:, i], downcast='signed') + df_summary, sumtechs, df = perform_run(calibrated_csv_path, start_year, end_year, + specs_data, + grid_price, pv_capital_cost_adjust, diesel_price, + five_year_target, annual_grid_cap_gen_limit, + annual_new_grid_connections_limit, num_people_per_hh_rural, + num_people_per_hh_urban, urban_tier, rural_tier, + end_year_pop, productive_demand, max_grid_extension_dist, + auto_intensification, prioritization) settlements_out_csv = os.path.join(results_folder, '{}-1-{}_{}_{}_{}_{}_{}.csv'.format(country_id, pop_index, tier_index, @@ -336,6 +186,175 @@ def scenario(specs_path, calibrated_csv_path, results_folder, summary_folder): prio_index)) df_summary.to_csv(summary_csv, index=sumtechs) - onsseter.df.to_csv(settlements_out_csv, index=False) + df.to_csv(settlements_out_csv, index=False) logging.info('Finished') + + +def perform_run(calibrated_csv_path, start_year, end_year, specs_data, grid_price, pv_capital_cost_adjust, + diesel_price, five_year_target, annual_grid_cap_gen_limit, annual_new_grid_connections_limit, + num_people_per_hh_rural, num_people_per_hh_urban, urban_tier, rural_tier, end_year_pop, + productive_demand, max_grid_extension_dist, auto_intensification, prioritization): + """Performs an OnSSET run + """ + onsseter = SettlementProcessor(calibrated_csv_path) + # RUN_PARAM: Fill in general and technology specific parameters (e.g. discount rate, losses etc.) + Technology.set_default_values(base_year=start_year, + start_year=start_year, + end_year=end_year, + discount_rate=0.08) + + grid_calc = Technology(om_of_td_lines=0.02, + distribution_losses=float(specs_data[SPE_GRID_LOSSES]), + connection_cost_per_hh=125, + base_to_peak_load_ratio=0.8, + capacity_factor=1, + tech_life=30, + grid_capacity_investment=float(specs_data[SPE_GRID_CAPACITY_INVESTMENT]), + grid_penalty_ratio=1, + grid_price=grid_price) + + mg_hydro_calc = Technology(om_of_td_lines=0.02, + distribution_losses=0.05, + connection_cost_per_hh=100, + base_to_peak_load_ratio=0.85, + capacity_factor=0.5, + tech_life=30, + capital_cost={float("inf"): 3000}, + om_costs=0.03, + mini_grid=True) + + mg_wind_calc = Technology(om_of_td_lines=0.02, + distribution_losses=0.05, + connection_cost_per_hh=100, + base_to_peak_load_ratio=0.85, + capital_cost={float("inf"): 3750}, + om_costs=0.02, + tech_life=20, + mini_grid=True) + + mg_pv_calc = Technology(om_of_td_lines=0.02, + distribution_losses=0.05, + connection_cost_per_hh=100, + base_to_peak_load_ratio=0.85, + tech_life=20, + om_costs=0.015, + capital_cost={float("inf"): 2950 * pv_capital_cost_adjust}, + mini_grid=True) + + sa_pv_calc = Technology(base_to_peak_load_ratio=0.9, + tech_life=15, + om_costs=0.02, + capital_cost={float("inf"): 6950 * pv_capital_cost_adjust, + 1: 4470 * pv_capital_cost_adjust, + 0.100: 6380 * pv_capital_cost_adjust, + 0.050: 8780 * pv_capital_cost_adjust, + 0.020: 9620 * pv_capital_cost_adjust + }, + standalone=True) + + mg_diesel_calc = Technology(om_of_td_lines=0.02, + distribution_losses=0.05, + connection_cost_per_hh=100, + base_to_peak_load_ratio=0.85, + capacity_factor=0.7, + tech_life=15, + om_costs=0.1, + capital_cost={float("inf"): 721}, + mini_grid=True) + + sa_diesel_calc = Technology(base_to_peak_load_ratio=0.9, + capacity_factor=0.5, + tech_life=10, + om_costs=0.1, + capital_cost={float("inf"): 938}, + standalone=True) + + sa_diesel_cost = {'diesel_price': diesel_price, + 'efficiency': 0.28, + 'diesel_truck_consumption': 14, + 'diesel_truck_volume': 300} + + mg_diesel_cost = {'diesel_price': diesel_price, + 'efficiency': 0.33, + 'diesel_truck_consumption': 33.7, + 'diesel_truck_volume': 15000} + + # RUN_PARAM: One shall define here the years of analysis (excluding start year), + # together with access targets per interval and timestep duration + yearsofanalysis = [2025, 2030] + eleclimits = {2025: five_year_target, 2030: 1} + time_steps = {2025: 7, 2030: 5} + + elements = ["1.Population", "2.New_Connections", "3.Capacity", "4.Investment"] + techs = ["Grid", "SA_Diesel", "SA_PV", "MG_Diesel", "MG_PV", "MG_Wind", "MG_Hydro", "MG_Hybrid"] + sumtechs = [] + for element in elements: + for tech in techs: + sumtechs.append(element + "_" + tech) + total_rows = len(sumtechs) + df_summary = pd.DataFrame(columns=yearsofanalysis) + for row in range(0, total_rows): + df_summary.loc[sumtechs[row]] = "Nan" + + onsseter.current_mv_line_dist() + + for year in yearsofanalysis: + eleclimit = eleclimits[year] + time_step = time_steps[year] + + if year - time_step == start_year: + grid_cap_gen_limit = time_step * annual_grid_cap_gen_limit + grid_connect_limit = time_step * annual_new_grid_connections_limit + else: + grid_cap_gen_limit = 9999999999 + grid_connect_limit = 9999999999 + + onsseter.set_scenario_variables(year, num_people_per_hh_rural, num_people_per_hh_urban, time_step, + start_year, urban_tier, rural_tier, end_year_pop, productive_demand) + + onsseter.diesel_cost_columns(sa_diesel_cost, mg_diesel_cost, year) + + sa_diesel_investment, sa_pv_investment, mg_diesel_investment, mg_pv_investment, mg_wind_investment, \ + mg_hydro_investment = onsseter.calculate_off_grid_lcoes(mg_hydro_calc, mg_wind_calc, mg_pv_calc, + sa_pv_calc, mg_diesel_calc, + sa_diesel_calc, year, end_year, time_step) + + grid_investment, grid_cap_gen_limit, grid_connect_limit = \ + onsseter.pre_electrification(grid_price, year, time_step, end_year, grid_calc, grid_cap_gen_limit, + grid_connect_limit) + + onsseter.df[SET_LCOE_GRID + "{}".format(year)], onsseter.df[SET_MIN_GRID_DIST + "{}".format(year)], \ + onsseter.df[SET_ELEC_ORDER + "{}".format(year)], onsseter.df[SET_MV_CONNECT_DIST], grid_investment = \ + onsseter.elec_extension(grid_calc, + max_grid_extension_dist, + year, + start_year, + end_year, + time_step, + grid_cap_gen_limit, + grid_connect_limit, + auto_intensification=auto_intensification, + prioritization=prioritization, + new_investment=grid_investment) + + onsseter.results_columns(year, time_step, prioritization, auto_intensification) + + onsseter.calculate_investments(sa_diesel_investment, sa_pv_investment, mg_diesel_investment, + mg_pv_investment, mg_wind_investment, + mg_hydro_investment, grid_investment, year) + + onsseter.apply_limitations(eleclimit, year, time_step, prioritization, auto_intensification) + + onsseter.calculate_new_capacity(mg_hydro_calc, mg_wind_calc, mg_pv_calc, sa_pv_calc, mg_diesel_calc, + sa_diesel_calc, grid_calc, year) + + onsseter.calc_summaries(df_summary, sumtechs, year) + + for i in range(len(onsseter.df.columns)): + if onsseter.df.iloc[:, i].dtype == 'float64': + onsseter.df.iloc[:, i] = pd.to_numeric(onsseter.df.iloc[:, i], downcast='float') + elif onsseter.df.iloc[:, i].dtype == 'int64': + onsseter.df.iloc[:, i] = pd.to_numeric(onsseter.df.iloc[:, i], downcast='signed') + + return df_summary, sumtechs, onsseter.df