Source code for program_files.preprocessing.pre_model_analysis

"""
    Christian Klemm - christian.klemm@fh-muenster.de
"""
import pandas
import logging


[docs]def filter_result_component_types(components: pandas.DataFrame, component_type: str) -> pandas.DataFrame: """ returns dataframe containing only one specific component type :param components: pandas DataFrame containing the \ components.csv content :type components: pandas.DataFrame :param component_type: str defining which component type will \ be searched for :type component_type: str :return: - **-** (pandas.DataFrame) - return the filtered \ pandas.DataFrame """ # search for entries of the components.csv with a given type return components[(components.type == component_type)]
[docs]def update_component_investment_decisions( components: pandas.DataFrame, model_definition_path: str, model_definition_type_name: str, result_type_name: str, investment_boundary_factor: int, investment_boundaries=True ) -> (pandas.DataFrame, list): """ Adapts investment decision depending on the results of a \ pre-model and returns new dataset plus list of deactivated \ components. :param components: DataFrame holding the pre-model result \ data's components.csv content :type components: pandas.DataFrame :param model_definition_path: file path of the \ pre-model-definition-file which shall be adapted :type model_definition_path: str :param model_definition_type_name: string which defines the \ model definition's component type (used to import the \ correct spreadsheet) :type model_definition_type_name: str :param result_type_name: string which defines the \ result's component type (used to filter the \ components.csv file) :type result_type_name: str :param investment_boundary_factor: the investment boundaries \ will be tightened to the respective investment decision of\ the pre-run multiplied by this factor. :type investment_boundary_factor: int :param investment_boundaries: decision whether tightening of \ the investment boundaries should be carried out :type investment_boundaries: bool :return: - **components_xlsx** (pandas.DataFrame) - updated \ DataFrame after the deactivation and \ investment tightening process - **list_of_deactivated_components** (list) - list \ holding the deactivated components """ # return components type results result_components = filter_result_component_types( components=components, component_type=result_type_name) # read the component type model definition sheet components_xlsx = pandas.read_excel( io=model_definition_path, sheet_name=model_definition_type_name) # drop first (nan) column components_xlsx = components_xlsx.iloc[1:, :] # reset of index required, so that it is uniform to the result-dataframe components_xlsx = components_xlsx.reset_index() component_type_switch_dict = { "district heating": dh_technical_pre_selection, "buses": bus_technical_pre_selection, "insulation": insulation_technical_pre_selection } try: # run the component specific method if applicable in the # component_type_switch_dict component_type_switch_dict.get(model_definition_type_name)( components_xlsx=components_xlsx, result_components=result_components) # return the updated components_xlsx file as well as an empty # list since no components were deactivated return components_xlsx, [] except TypeError: # run the general technical pre selection if the component_type # is not applicable within the component_type_switch_dict list_of_deactivated_components = technical_pre_selection( components_xlsx=components_xlsx, result_components=result_components) if investment_boundaries: tightening_investment_boundaries( components_xlsx=components_xlsx, result_components=result_components, investment_boundary_factor=investment_boundary_factor) return components_xlsx, list_of_deactivated_components
[docs]def technical_pre_selection(components_xlsx: pandas.DataFrame, result_components: pandas.DataFrame) -> list: """ deactivates investment-components for which no investments has \ been carried out and additionally returns a list of deactivated\ components :param components_xlsx: DataFrame holding the currently \ considered sheet of the model definition file :type components_xlsx: pandas.DataFrame :param result_components: DataFrame holding the currently \ considered components of the result data components.csv file :type result_components: pandas.DataFrame :return: - **list_of_deactivated_components** (list) - list \ containing the components which were deactivated within \ this method """ # create an empty list to collect the deactivated components list_of_deactivated_components = [] # reset the index of the component.csv to the ID column result_components.set_index('ID', inplace=True) # iterate threw all components stored with in the model definition for num, component in components_xlsx.iterrows(): # extract the current component label label = str(component["label"]) # check rather the current considered component is in the # results components.csv file if label in result_components.index.values: # check rather an investment was made "investment/kW" and # if an investment was possible max. invest if not # deactivate the current considered component and append # it's label on the list of deactivated components if str(result_components.loc[label]['investment/kW']) == '0.0' and\ float(result_components.loc[label]['max. invest./kW']) > 0: components_xlsx.at[num, 'active'] = 0 list_of_deactivated_components.append(label) # return the list of deactivated components return list_of_deactivated_components
[docs]def tightening_investment_boundaries(components_xlsx: pandas.DataFrame, result_components: pandas.DataFrame, investment_boundary_factor: int) -> None: """ tightens investment boundaries :param components_xlsx: DataFrame holding the currently \ considered sheet of the model definition file :type components_xlsx: pandas.DataFrame :param result_components: DataFrame holding the currently \ considered components of the result data components.csv file :type result_components: pandas.DataFrame :param investment_boundary_factor: the investment boundaries \ will be tightened to the respective investment decision of\ the pre-run multiplied by this factor. :type investment_boundary_factor: int """ # iterate threw all components stored with in the model definition for num, component in components_xlsx.iterrows(): # extract the current component label label = str(component["label"]) # check whether an investment on the currently considered # component was possible and if the component is part of the # result data's components.csv if label in result_components.index.values and \ float(result_components.loc[label]['max. invest./kW']) > 0: # calculate the investment boundary which is defined as # solvers investment decision multiplied by the investment # boundary factor invest_boundary = ( float(result_components.loc[label]['investment/kW']) * investment_boundary_factor) # if the invest boundary is lower than the max invest value # of the pre model it has to be adapted if invest_boundary \ <= float(result_components.loc[label]['max. invest./kW']): # adapt the max investment capacity of the currently # considered component components_xlsx.at[num, 'max. investment capacity'] = \ invest_boundary
[docs]def update_component_model_definition_sheet( updated_data: pandas.DataFrame, model_definition_sheet_name: str, updated_model_definition_path: str) -> None: """ updates the original data within the updated model definition sheet :param updated_data: DataFrame holding the updated DataFrame \ resulting from the pre-model algorithm :type updated_data: pandas.DataFrame :param model_definition_sheet_name: String holding the sheet \ name to be stored using the pandas ExcelWriter :type model_definition_sheet_name: str :param updated_model_definition_path: path where the update \ Excel file will be stored :type updated_model_definition_path: str """ sheets = ['buses', 'district heating', 'sources', 'transformers', 'storages', 'links'] if model_definition_sheet_name in sheets: # adding an empty row at the top of the dataframe (replacing the # unit row in the original model definition file) updated_data = pandas.DataFrame( [[0 for x in range(len(updated_data.columns))]], columns=updated_data.columns).append(updated_data) writer = pandas.ExcelWriter(updated_model_definition_path, engine="openpyxl", mode="a", if_sheet_exists="replace") with writer: updated_data.to_excel(writer, model_definition_sheet_name, index=False)
[docs]def deactivate_respective_competition_constraints( model_definition_path: str, list_of_deactivated_components: list) -> pandas.DataFrame: """ identifies which competition constraints contains deactivated \ components. The respective competition constraints are \ deactivated in an updated dataframe :param model_definition_path: file path of the \ pre-model-definition-file which shall be adapted :type model_definition_path: str :param list_of_deactivated_components: list holding the \ deactivated components :type list_of_deactivated_components: list :return: - **competition_constraints_xlsx** (pandas.DataFrame) \ - DataFrame holding the updated competition \ constraints sheet """ competition_constraints_xlsx = pandas.read_excel( model_definition_path, sheet_name='competition constraints') for i, constraint in competition_constraints_xlsx.iterrows(): if constraint['component 1'] in list_of_deactivated_components: competition_constraints_xlsx.at[i, 'active'] = 0 if constraint['component 2'] in list_of_deactivated_components: competition_constraints_xlsx.at[i, 'active'] = 0 return competition_constraints_xlsx
[docs]def dh_technical_pre_selection(components_xlsx: pandas.DataFrame, result_components: pandas.DataFrame) -> None: """ deactivates district heating investment decisions for which no investments has been carried out :param components_xlsx: DataFrame holding the currently \ considered sheet of the model definition file :type components_xlsx: pandas.DataFrame :param result_components: DataFrame holding the currently \ considered components of the result data components.csv file :type result_components: pandas.DataFrame """ # create list of street-sections for which an investment has been # carried out dh_investment_list = [] # reduce the result_components Data Frame on entries with an investment result_components = result_components[result_components["investment/kW"]] # iterate threw the reduced result_components data frame for num, dh_section in result_components.iterrows(): # if the ID does not contain 'dh_heat_house_station' it must be # a pipe of the considered thermal network if 'dh_heat_house_station' not in dh_section['ID']: no_invest_list = ['0.0', '0', '0.00', '---', '-0', '-0.0', '-0.00'] # if the investment is in the no invest list the heat # network section will be appended on the section list if str(dh_section['investment/kW']) not in no_invest_list: section_name = dh_section['ID'].split('_Diameter') dh_investment_list.append(section_name[0]) # since the Diameter str was part of the heat network consideration # the user needs to be informed that if one of his components # contains this str the algorithm does not work logging.info("\t WARNING: IF THE ORIGINAL SECTION NAME CONTAINED THE " "STRING '_Diameter_' THIS ANALYSIS IS NOT VALID!") # deactivate those street section for which no investment has been # carried out for num, dh_section in components_xlsx.iterrows(): # check whether the heat network section is within the # dh_investment_list if not deactivate the section if str(dh_section['label']) not in dh_investment_list: components_xlsx.at[num, 'active'] = 0
[docs]def bus_technical_pre_selection(components_xlsx: pandas.DataFrame, result_components: pandas.DataFrame) -> None: """ deactivates the district heating connection for those busses for which no connection has been carried out during optimization :param components_xlsx: DataFrame holding the currently \ considered sheet of the model definition file :type components_xlsx: pandas.DataFrame :param result_components: DataFrame holding the currently \ considered components of the result data components.csv file :type result_components: pandas.DataFrame """ bus_xlsx = components_xlsx no_invest_list = ['0.0', '0', '0.00', '---', '-0', '-0.0', '-0.00'] # creates list of heating buses for which an investment has been # carried out dh_investment_list = [] for i, dh_section in result_components.iterrows(): if str(dh_section['investment/kW']) not in no_invest_list: if 'dh_heat_house_station' in dh_section['ID']: section_name = dh_section['ID'].split('dh_heat_house_station_') dh_investment_list.append(section_name[1]) elif str(dh_section['capacity/kW']) not in no_invest_list: if 'dh_source_link' in dh_section['ID']: section_name = dh_section['ID'].split('_dh_source_link_')[0] dh_investment_list.append(section_name) logging.info("WARNING: IF THE ORIGINAL BUS NAME CONTAINED THE STRING " "'dh_heat_house_station' THIS ANALYSIS IS NOT VALID!") logging.info("WARNING: IF THE ORIGINAL BUS NAME CONTAINED THE STRING " "'dh_source_link' THIS ANALYSIS IS NOT VALID!") logging.info("WARNING: IF THE ORIGINAL BUS NAME ARE DUPLICATES BEFORE " "USING '_' ANALYSIS IS NOT VALID!") logging.info("WARNING: IF THE ORIGINAL BUS NAME CONTAINED THE STRING " "'_Diameter_' THIS ANALYSIS IS NOT VALID!") # deactivate those bus connections for which no investment has been # carried out for num, dh_bus in bus_xlsx.iterrows(): label = str(dh_bus['label']) if str(dh_bus['district heating conn.']) not in no_invest_list: if label not in dh_investment_list \ and label[0:9] not in dh_investment_list \ and label.split('_')[0] not in dh_investment_list: #if dh_bus['district heating conn.'] == "dh-system": # bus_xlsx.at[num, 'active'] = 0 bus_xlsx.at[num, 'district heating conn.'] = 0 elif len(dh_investment_list) < 2: bus_xlsx.at[num, 'district heating conn.'] = 0
[docs]def insulation_technical_pre_selection(components_xlsx: pandas.DataFrame, result_components: pandas.DataFrame ) -> None: """ deactivates district heating investment decisions for which no investments has been carried out :param components_xlsx: DataFrame holding the currently \ considered sheet of the model definition file :type components_xlsx: pandas.DataFrame :param result_components: DataFrame holding the currently \ considered components of the result data components.csv file :type result_components: pandas.DataFrame """ # create list of insulation measures for which an investment has # been carried out insulation_investment_list = [] for i, insulation in result_components.iterrows(): if insulation['investment/kW']: if str(insulation['investment/kW']) != '0.0': insulation_investment_list.append(insulation['ID']) # deactivate those street section for which no investment has been # carried out for i, insulation in components_xlsx.iterrows(): if str(insulation['label'] + "-insulation") \ not in insulation_investment_list and insulation['existing'] != 1: components_xlsx.at[i, 'active'] = 0
[docs]def update_model_according_pre_model_results( model_definition_path: str, results_components_path: str, updated_model_definition_path: str, investment_boundary_factor: int, investment_boundaries: bool) -> None: """ Carries out technical pre-selection and tightens investment boundaries for a model definition, based on a previously performed pre-model. :param model_definition_path: file path of the \ pre-model-definition-file which shall be adapted :type model_definition_path: str :param results_components_path: folder path of the \ pre-model-results on which base the model definition shall \ be adapted :type results_components_path: str :param updated_model_definition_path: file path, where the adapted model definition shall be saved :type updated_model_definition_path: str :param investment_boundary_factor: the investment boundaries \ will be tightened to the respective investment decision of \ the pre-run multiplied by this factor. :type investment_boundary_factor: int :param investment_boundaries: decision whether tightening of \ the investment boundaries should be carried out :type investment_boundaries: bool """ # import de model definition file model_definition_xlsx = pandas.read_excel( io=model_definition_path, sheet_name=None) # import the components.csv of the pre-model's result data components = pandas.read_csv(filepath_or_buffer=results_components_path) # Copy original model definition sheet to new file with pandas.ExcelWriter(updated_model_definition_path) as writer: for sheet in model_definition_xlsx: model_definition_xlsx[sheet].to_excel(writer, sheet_name=sheet) # Create List required for adaption of competition constraints complete_list_of_deactivated_components = [] # list of lists of component types. the first value of the sub-lists # represent the name of the component type in the model definition # sheet, the second values the component name in the result sheets component_types = [['buses', 'transformer'], ['transformers', 'transformer'], ['sources', 'source'], ['storages', 'storage'], ['links', 'link'], ['insulation', 'insulation']] # iterate threw the list of list for sub_list in component_types: # represents the model component type model_definition_type_name = sub_list[0] # represents the components.csv component type result_type_name = sub_list[1] # technical pre-selection and tightening of investment boundaries updated_components, list_of_deactivated_components = \ update_component_investment_decisions( components=components, model_definition_path=model_definition_path, model_definition_type_name=model_definition_type_name, result_type_name=result_type_name, investment_boundary_factor=investment_boundary_factor, investment_boundaries=investment_boundaries) # add the newly deactivated components to the list of \ # deactivated components complete_list_of_deactivated_components \ += list_of_deactivated_components # save updated data update_component_model_definition_sheet( updated_data=updated_components, model_definition_sheet_name=model_definition_type_name, updated_model_definition_path=updated_model_definition_path) # deactivate the competition constraint of components that are not # longer part of the energy system updated_constraints = deactivate_respective_competition_constraints( model_definition_path=model_definition_path, list_of_deactivated_components=complete_list_of_deactivated_components) # save the changes done in the competition constraints sheet update_component_model_definition_sheet( updated_model_definition_path=updated_model_definition_path, updated_data=updated_constraints, model_definition_sheet_name='competition constraints') logging.info('\t Model definition updated according to the results of the ' 'pre-model.')