From 571c986e604df411081075cb9b8a6222bf736e3e Mon Sep 17 00:00:00 2001 From: "Ghosh, Tapajyoti" Date: Fri, 17 Jan 2025 09:41:35 -0700 Subject: [PATCH 1/9] Updating the pylca_celavi code --- celavi/pylca_celavi/liaison/lci_calculator.py | 24 ++++++------ celavi/pylca_celavi/liaison/liaison_model.py | 20 +++++----- .../liaison/search_activity_ecoinvent.py | 37 ++++++++++++++++--- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/celavi/pylca_celavi/liaison/lci_calculator.py b/celavi/pylca_celavi/liaison/lci_calculator.py index 9eb0609..a1cc816 100644 --- a/celavi/pylca_celavi/liaison/lci_calculator.py +++ b/celavi/pylca_celavi/liaison/lci_calculator.py @@ -39,7 +39,7 @@ def search_index_creator(ei_cf_36_db): dic[i['name']+'@'+i['location']+'@'+i['unit']][i['code']] = i - dic2[i['name']+'@'+i['location']+'@'+i['unit']] = i + dic2[i['code']] = i @@ -75,10 +75,12 @@ def search_index_reader(p_name,p_loc,p_unit,data_dict): for key in activity_dict.keys(): return activity_dict[key] else: - print('\nWarning --- Issue with process dictionary length when '+dic_key+' is chosen',flush=True) + + print('\nINFO: Process dictionary length when '+dic_key+' is chosen is '+str(len(activity_dict)),flush=True) for key in activity_dict.keys(): - print('Warning --- Multiple activities found ---- ',activity_dict[key],key,flush=True) + print('INFO Multiple activities found ---- ',activity_dict[key],key,flush=True) print('\n') + return activity_dict[key] @@ -158,8 +160,8 @@ def search_dictionary(db,bw): ei_cf_36_db = bw.Database(db) - database_dict,process_database_dict = search_index_creator(ei_cf_36_db) - return database_dict,process_database_dict + database_dict,database_dict_secondary = search_index_creator(ei_cf_36_db) + return database_dict def liaison_calc(db,run_filename,bw): @@ -185,7 +187,7 @@ def liaison_calc(db,run_filename,bw): """ ei_cf_36_db = bw.Database(db) print('creating inventory withing the database---',db,flush=True) - database_dict,process_database_dict = search_index_creator(ei_cf_36_db) + database_dict,database_dict_secondary = search_index_creator(ei_cf_36_db) inventory = run_filename.sort_values(by=['process','process_location']) @@ -265,7 +267,7 @@ def liaison_calc(db,run_filename,bw): process_dict[key].save() #Recreate the database dictionary so that the new created processes are listed in the inventory - database_dict,process_database_dict = search_index_creator(ei_cf_36_db) + database_dict,database_dict_secondary = search_index_creator(ei_cf_36_db) # Step 3 is to define the flows that are inputs to the datasets # Only technosphere can be inputs @@ -290,8 +292,8 @@ def liaison_calc(db,run_filename,bw): activity = None # Then the UUID has been supplied and we can try to find using UUID try: - activity = process_dict[str(row['code'])] - print('Complete Success - Provided location '+ row['supplying_location']+' for '+ row['flow'] +' was found. Chosen location was '+activity['location'] + ' . Chosen process was ' + activity['name'] ,flush = True) + activity = database_dict_secondary(row['code']) + print('UUID matched - Provided location '+ row['supplying_location']+' for '+ row['flow'] +' was found. Chosen location was '+activity['location'] + ' . Chosen process was ' + activity['name'] ,flush = True) print_flag = True except: # This exception is to make sure that if flows are not found for the user provided location, other locations are searched for and linked automatically. @@ -393,8 +395,8 @@ def liaison_calc(db,run_filename,bw): print('') - database_dict,process_database_dict = search_index_creator(ei_cf_36_db) - return process_database_dict + database_dict,database_dict_secondary = search_index_creator(ei_cf_36_db) + return database_dict def lcia_traci_run(db,primary_process,functional_unit,mc_foreground_flag,mc_runs,bw): diff --git a/celavi/pylca_celavi/liaison/liaison_model.py b/celavi/pylca_celavi/liaison/liaison_model.py index dcc39cc..c7e56ba 100644 --- a/celavi/pylca_celavi/liaison/liaison_model.py +++ b/celavi/pylca_celavi/liaison/liaison_model.py @@ -118,21 +118,16 @@ def lca_runner(db,r,mc_runs,mc_foreground_flag,lca_flag,functional_unit,run_file """ # This function creates a dictionary from ecoinvent for searching for activities. - dictionary,process_dictionary = search_dictionary(db,bw) + process_dictionary = search_dictionary(db,bw) # This function searches for the primary process under study in ecoinvent. If found we extract it. # dictionary or string - searched_item = search_activity_in_ecoinvent(dictionary,process_under_study,location_under_study,unit_under_study,run_filename,data_dir) + searched_item = search_activity_in_ecoinvent(process_dictionary,process_under_study,location_under_study,unit_under_study,run_filename,data_dir) - #if the search fails only string name of activity is returned if type(searched_item) == str: # Reading from the inventory csv files print('Using the provided inventory files',flush = True) print('Reading from ' + run_filename,flush = True) - try: - inventory = pd.read_csv(run_filename) #dataframe - except: - print("#### Error: Additional foreground inventory file missing. Please check ####",flush=True) - sys.exit(0) + inventory = pd.read_csv(run_filename) #dataframe #inventory is a dataframe process_dictionary = liaison_calc(db,inventory,bw) @@ -142,14 +137,17 @@ def lca_runner(db,r,mc_runs,mc_foreground_flag,lca_flag,functional_unit,run_file if edit_ecoinvent_user_controlled == True: #inventory here has to be a dictionary. So if we read inventory from csv file we cannot edit it. - run_filename = user_controlled_editing_ecoinvent_activity(inventory,year_of_study,location_under_study,output_dir) + run_filename = user_controlled_editing_ecoinvent_activity(inventory,year_of_study,data_dir) print('Activity edited according to user prereferences and saved success',flush=True) #run_filename is a dataframe. process_dictionary = liaison_calc(db,run_filename,bw) if lca_flag: - result_dir1,n_lcias1 = lcia_traci_run(db,process_dictionary[process_under_study+'@'+location_under_study+'@'+unit_under_study],functional_unit,mc_foreground_flag,mc_runs,bw) - result_dir2,n_lcias2 = lcia_recipe_run(db,process_dictionary[process_under_study+'@'+location_under_study+'@'+unit_under_study],functional_unit,mc_foreground_flag,mc_runs,bw) + + + activity_lca = search_index_reader(process_under_study,location_under_study,unit_under_study,process_dictionary) + result_dir1,n_lcias1 = lcia_traci_run(db,activity_lca,functional_unit,mc_foreground_flag,mc_runs,bw) + result_dir2,n_lcias2 = lcia_recipe_run(db,activity_lca,functional_unit,mc_foreground_flag,mc_runs,bw) #result_dir3,n_lcias3 = lcia_premise_gwp_run(db,dictionary[process_under_study],1,mc_foreground_flag,mc_runs,bw) result_dir3 = {} n_lcias3 = 0 diff --git a/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py b/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py index d6070b3..5a543f5 100644 --- a/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py +++ b/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py @@ -1,7 +1,29 @@ import sys import pandas as pd +def extract_process(dic_key,data_dict): + """ + This functions extracts the process from the dictionary + Parameters + --------- + + data_dic: dictionary + Returns + -------- + extracted_dic: dictionary + """ + activity_dict = data_dict[dic_key] + if len(activity_dict) == 1: + for key in activity_dict.keys(): + return activity_dict[key] + + else: + print('\nINFO: Process dictionary length when '+dic_key+' is chosen is '+str(len(activity_dict)),flush=True) + for key in activity_dict.keys(): + print('INFO Multiple activities found ---- ',activity_dict[key],key,flush=True) + print('\n') + return activity_dict[key] def search_activity_in_ecoinvent(dictionary,process_under_study,location_under_study,unit_under_study,run_filename,data_dir): """ @@ -20,31 +42,34 @@ def search_activity_in_ecoinvent(dictionary,process_under_study,location_under_s default_unit = unit_under_study state_location = location_under_study - print('Searching for activity to extract from Ecoinvent',flush=True) + print('Searching for activity to extract from Ecoinvent and changing location and processes',flush=True) + print('Editing activities within ecoinvent to US location and US state wise grid mix',flush=True) print(process_under_study+'@'+location_under_study+'@'+default_unit, 'to be searched') + process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) try: - process_selected_as_foreground = dictionary[process_under_study+'@'+location_under_study+'@'+default_unit] + process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) print('Complete success: Process found in ecoinvent in chosen location',flush=True) except: try: location_under_study = 'US' - process_selected_as_foreground = dictionary[process_under_study+'@'+location_under_study+'@'+default_unit] + process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) print('Minor success: Process found in ecoinvent in US',flush=True) except: try: location_under_study = 'RoW' - process_selected_as_foreground = dictionary[process_under_study+'@'+location_under_study+'@'+default_unit] + process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) print('Minor success: Process found in ecoinvent in RoW',flush=True) except: try: location_under_study = 'GLO' - process_selected_as_foreground = dictionary[process_under_study+'@'+location_under_study+'@'+default_unit] + process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) print('Minor success: Process found in ecoinvent in GLO',flush=True) + except: try: location_under_study = 'RER' - process_selected_as_foreground = dictionary[process_under_study+'@'+location_under_study+'@'+default_unit] + process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) print('Minor success: Process found in ecoinvent in GLO',flush=True) except: print('****Failed -- Did not find this process in Ecoinvent. Please check process****',flush=True) From d2aeca998aea84899207a4d4b29774d666686a72 Mon Sep 17 00:00:00 2001 From: "Ghosh, Tapajyoti" Date: Fri, 17 Jan 2025 10:14:49 -0700 Subject: [PATCH 2/9] Corrections to edit activity code --- celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py | 7 +++---- celavi/pylca_celavi/liaison/liaison_model.py | 5 ++--- celavi/pylca_celavi/liaison/search_activity_ecoinvent.py | 2 -- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py b/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py index c1b812b..59f2f07 100644 --- a/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py +++ b/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py @@ -64,9 +64,8 @@ def user_controlled_editing_ecoinvent_activity(process_selected_as_foreground,ye #Extracting ecoinvent database for activity and flows and creating a LiAISON friendly dataframe - for key in process_selected_as_foreground.keys(): - for exch in process_selected_as_foreground[key].exchanges(): - process.append(process_selected_as_foreground[key]['name']) + for exch in process_selected_as_foreground.exchanges(): + process.append(process_selected_as_foreground['name']) value.append(exch['amount']) unit.append(exch['unit']) @@ -128,7 +127,7 @@ def user_controlled_editing_ecoinvent_activity(process_selected_as_foreground,ye example['code'] = flow_code #Sanity check to write the dataframe. Can be deleted later - example.to_csv(data_dir+process_selected_as_foreground+str(year_of_study)+location_under_study+'.csv',index=False) + example.to_csv(data_dir+process_selected_as_foreground['name']+str(year_of_study)+location_under_study+'.csv',index=False) run_filename = example diff --git a/celavi/pylca_celavi/liaison/liaison_model.py b/celavi/pylca_celavi/liaison/liaison_model.py index c7e56ba..a9f54a2 100644 --- a/celavi/pylca_celavi/liaison/liaison_model.py +++ b/celavi/pylca_celavi/liaison/liaison_model.py @@ -9,7 +9,7 @@ import os import time from celavi.pylca_celavi.liaison.montecarloforeground import mc_foreground -from celavi.pylca_celavi.liaison.lci_calculator import liaison_calc,search_dictionary,lcia_traci_run,lcia_recipe_run, lcia_premise_gwp_run +from celavi.pylca_celavi.liaison.lci_calculator import liaison_calc,search_dictionary,search_index_reader,lcia_traci_run,lcia_recipe_run, lcia_premise_gwp_run from celavi.pylca_celavi.liaison.search_activity_ecoinvent import search_activity_in_ecoinvent from celavi.pylca_celavi.liaison.edit_activity_ecoinvent import user_controlled_editing_ecoinvent_activity @@ -137,14 +137,13 @@ def lca_runner(db,r,mc_runs,mc_foreground_flag,lca_flag,functional_unit,run_file if edit_ecoinvent_user_controlled == True: #inventory here has to be a dictionary. So if we read inventory from csv file we cannot edit it. - run_filename = user_controlled_editing_ecoinvent_activity(inventory,year_of_study,data_dir) + run_filename = user_controlled_editing_ecoinvent_activity(inventory,year_of_study,location_under_study,output_dir) print('Activity edited according to user prereferences and saved success',flush=True) #run_filename is a dataframe. process_dictionary = liaison_calc(db,run_filename,bw) if lca_flag: - activity_lca = search_index_reader(process_under_study,location_under_study,unit_under_study,process_dictionary) result_dir1,n_lcias1 = lcia_traci_run(db,activity_lca,functional_unit,mc_foreground_flag,mc_runs,bw) result_dir2,n_lcias2 = lcia_recipe_run(db,activity_lca,functional_unit,mc_foreground_flag,mc_runs,bw) diff --git a/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py b/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py index 5a543f5..75de973 100644 --- a/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py +++ b/celavi/pylca_celavi/liaison/search_activity_ecoinvent.py @@ -43,9 +43,7 @@ def search_activity_in_ecoinvent(dictionary,process_under_study,location_under_s default_unit = unit_under_study state_location = location_under_study print('Searching for activity to extract from Ecoinvent and changing location and processes',flush=True) - print('Editing activities within ecoinvent to US location and US state wise grid mix',flush=True) print(process_under_study+'@'+location_under_study+'@'+default_unit, 'to be searched') - process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) try: process_selected_as_foreground = extract_process(process_under_study+'@'+location_under_study+'@'+default_unit,dictionary) print('Complete success: Process found in ecoinvent in chosen location',flush=True) From 5785762ba4ecc955435cf650dfa1b53cfc11d05b Mon Sep 17 00:00:00 2001 From: rhanes Date: Fri, 17 Jan 2025 11:41:21 -0600 Subject: [PATCH 3/9] removes loss dict from glass wool and scm manufacturing; losses are zero for these steps --- celavi/costmethods.py | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/celavi/costmethods.py b/celavi/costmethods.py index 543a126..093c2c9 100644 --- a/celavi/costmethods.py +++ b/celavi/costmethods.py @@ -622,10 +622,7 @@ def glass_wool_manufacturing(self, path_dict): _learn_dict['learn rate'], self.run ) - _loss = apply_array_uncertainty( - path_dict['path_split']['glass wool manufacturing']['fraction'], - self.run - ) + _loss = 0.0 _initial_cost = apply_array_uncertainty( path_dict['cost uncertainty']['glass wool manufacturing']['initial cost'], self.run @@ -637,10 +634,7 @@ def glass_wool_manufacturing(self, path_dict): elif path_dict['cost uncertainty']['glass wool manufacturing']['uncertainty'] == 'stochastic': if path_dict['year'] == self.start_year: - _loss = apply_stoch_uncertainty( - path_dict['path_split']['glass wool manufacturing']['fraction'], - seed=self.seed - ) + _loss = 0.0 _learn_rate = -1.0 * apply_stoch_uncertainty( _learn_dict['learn rate'], seed=self.seed @@ -653,8 +647,6 @@ def glass_wool_manufacturing(self, path_dict): path_dict['cost uncertainty']['glass wool manufacturing']['revenue'], seed=self.seed ) - if isinstance(path_dict['path_split']['glass wool manufacturing']['fraction'],dict): - path_dict['path_split']['glass wool manufacturing']['fraction']['value'] = _loss if isinstance(_learn_dict['learn rate'], dict): _learn_dict['learn rate']['value'] = _learn_rate if isinstance(path_dict['cost uncertainty']['glass wool manufacturing']['initial cost'],dict): @@ -662,17 +654,14 @@ def glass_wool_manufacturing(self, path_dict): if isinstance(path_dict['cost uncertainty']['glass wool manufacturing']['revenue'], dict): path_dict['cost uncertainty']['glass wool manufacturing']['revenue']['value'] = _revenue else: - _loss = path_dict['path_split']['glass wool manufacturing']['fraction']['value'] + _loss = 0.0 _learn_rate = _learn_dict['learn rate']['value'] _initial_cost = path_dict['cost uncertainty']['glass wool manufacturing']['initial cost']['value'] _revenue = path_dict['cost uncertainty']['glass wool manufacturing']['revenue']['value'] else: # No uncertainty _learn_rate = apply_array_uncertainty(_learn_dict['learn rate'], self.run) - _loss = apply_array_uncertainty( - path_dict['path_split']['glass wool manufacturing']['fraction'], - self.run - ) + _loss = 0.0 _initial_cost = path_dict['cost uncertainty']['glass wool manufacturing']['initial cost'] _revenue = path_dict['cost uncertainty']['glass wool manufacturing']['revenue'] @@ -732,10 +721,7 @@ def scm_manufacturing(self, path_dict): _learn_dict['learn rate'], self.run ) - _loss = apply_array_uncertainty( - path_dict['path_split']['scm manufacturing']['fraction'], - self.run - ) + _loss = 0.0 _initial_cost = apply_array_uncertainty( path_dict['cost uncertainty']['scm manufacturing']['initial cost'], self.run @@ -747,10 +733,7 @@ def scm_manufacturing(self, path_dict): elif path_dict['cost uncertainty']['scm manufacturing']['uncertainty'] == 'stochastic': if path_dict['year'] == self.start_year: - _loss = apply_stoch_uncertainty( - path_dict['path_split']['scm manufacturing']['fraction'], - seed=self.seed - ) + _loss = 0.0 _learn_rate = -1.0 * apply_stoch_uncertainty( _learn_dict['learn rate'], seed=self.seed @@ -763,8 +746,6 @@ def scm_manufacturing(self, path_dict): path_dict['cost uncertainty']['scm manufacturing']['revenue'], seed=self.seed ) - if isinstance(path_dict['path_split']['scm manufacturing']['fraction'],dict): - path_dict['path_split']['scm manufacturing']['fraction']['value'] = _loss if isinstance(_learn_dict['learn rate'], dict): _learn_dict['learn rate']['value'] = _learn_rate if isinstance(path_dict['cost uncertainty']['scm manufacturing']['initial cost'],dict): @@ -772,17 +753,14 @@ def scm_manufacturing(self, path_dict): if isinstance(path_dict['cost uncertainty']['scm manufacturing']['revenue'], dict): path_dict['cost uncertainty']['scm manufacturing']['revenue']['value'] = _revenue else: - _loss = path_dict['path_split']['scm manufacturing']['fraction']['value'] + _loss = 0.0 _learn_rate = _learn_dict['learn rate']['value'] _initial_cost = path_dict['cost uncertainty']['scm manufacturing']['initial cost']['value'] _revenue = path_dict['cost uncertainty']['scm manufacturing']['revenue']['value'] else: # No uncertainty _learn_rate = apply_array_uncertainty(_learn_dict['learn rate'], self.run) - _loss = apply_array_uncertainty( - path_dict['path_split']['scm manufacturing']['fraction'], - self.run - ) + _loss = 0.0 _initial_cost = path_dict['cost uncertainty']['scm manufacturing']['initial cost'] _revenue = path_dict['cost uncertainty']['scm manufacturing']['revenue'] From 10e67037485284ff663e7b704393020d2b0e8180 Mon Sep 17 00:00:00 2001 From: rhanes Date: Fri, 17 Jan 2025 11:42:09 -0600 Subject: [PATCH 4/9] continues replacing node ids with node names --- celavi/component.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/celavi/component.py b/celavi/component.py index fc766a0..6786e16 100644 --- a/celavi/component.py +++ b/celavi/component.py @@ -95,7 +95,7 @@ def create_pathway_queue(self, from_facility_id: int): from_facility_id: int The starting location of the the component. """ - path_choices = self.context.cost_graph.choose_paths(source=self.in_use_facility) + path_choices = self.context.cost_graph.choose_paths(source_node=self.in_use_facility) path_choices_dict = { path_choice["source"]: path_choice for path_choice in path_choices } @@ -192,7 +192,7 @@ def bol_process(self, env): yield env.timeout(self.initial_lifespan_timesteps) # Component's next steps are determined and stored in self.pathway - self.create_pathway_queue(self.in_use_facility_id) + self.create_pathway_queue(self.in_use_facility) # Component is decremented from in use inventories count_inventory.increment_quantity(self.kind, -1, env.now) From 8b2c7157fd6cfd465818973a60a05c624848e1b7 Mon Sep 17 00:00:00 2001 From: rhanes Date: Fri, 17 Jan 2025 11:43:00 -0600 Subject: [PATCH 5/9] refactors to use source_node instead of source --- celavi/costgraph.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/celavi/costgraph.py b/celavi/costgraph.py index 619e0ce..a70ccd0 100644 --- a/celavi/costgraph.py +++ b/celavi/costgraph.py @@ -240,7 +240,7 @@ def list_of_tuples( else: return list(map(lambda x, y: (x, y), list1, list2)) - def find_nearest(self, source: str, crit: str): + def find_nearest(self, source_node: str, crit: str): """ Method that finds the nearest nodes to source and returns that node name, the path length to the nearest node, and the path to the nearest node as @@ -251,7 +251,7 @@ def find_nearest(self, source: str, crit: str): Parameters ---------- - source + source_node Name of node where this path begins. crit Criteria to calculate path "length". May be cost or dict. @@ -267,10 +267,10 @@ def find_nearest(self, source: str, crit: str): # Calculate the length of paths from fromnode to all other nodes lengths = nx.single_source_bellman_ford_path_length( - self.supply_chain, source, weight=crit + self.supply_chain, source_node, weight=crit ) - short_paths = nx.single_source_bellman_ford_path(self.supply_chain, source) + short_paths = nx.single_source_bellman_ford_path(self.supply_chain, source_node) # We are only interested in a particular type(s) of node targets = list( @@ -282,7 +282,10 @@ def find_nearest(self, source: str, crit: str): # return the smallest of all lengths to get to typeofnode if subdict: # dict of shortest paths to all targets - nearest = min(subdict, key=subdict.get) + try: + nearest = min(subdict, key=subdict.get) + except NameError: + pdb.set_trace() timeout = nx.get_node_attributes(self.supply_chain, "timeout") timeout_list = [ value for key, value in timeout.items() if key in short_paths[nearest] @@ -303,13 +306,12 @@ def find_nearest(self, source: str, crit: str): # create dictionary for this preferred pathway cost and decision # criterion and append to the pathway_crit_history - _fac_id = self.supply_chain.nodes[source]["facility_id"] + _fac_id = self.supply_chain.nodes[source_node]["facility_id"] _loc_line = self.loc_df[self.loc_df.facility_id == _fac_id] _bol_crit = nx.shortest_path_length( self.supply_chain, - source="manufacturing_" - + str(self.find_upstream_neighbor(node_id=_fac_id, crit="cost")), - target=str(source), + source=self.find_upstream_neighbor(node_id=_fac_id, crit="cost"), + target=source_node, weight=crit, method="bellman-ford", ) @@ -739,7 +741,7 @@ def build_supplychain_graph(self): flush=True, ) - def choose_paths(self, source: str = None, crit: str = "cost"): + def choose_paths(self, source_node: str = None, crit: str = "cost"): """ Calculate total pathway costs (sum of all node and edge costs) over all possible pathways between source and target nodes. Other "costs" @@ -748,7 +750,7 @@ def choose_paths(self, source: str = None, crit: str = "cost"): Parameters ---------- - source : str + source_node : str Node name in the format "facilitytype_facilityid". crit : str Criterion on which "shortest" path is defined. Defaults to cost. @@ -762,17 +764,17 @@ def choose_paths(self, source: str = None, crit: str = "cost"): # Since all edges now contain both processing costs (for the u node) # as well as transport costs (including distances), all we need to do # is get the shortest path using the 'cost' attribute as the edge weight - if source is None: + if source_node is None: raise ValueError(f"CostGraph.choose_paths: source node cannot be None") else: - if source not in self.supply_chain.nodes(): - raise ValueError(f"CostGraph.choose_paths: {source} not in CostGraph") + if source_node not in self.supply_chain.nodes(): + raise ValueError(f"CostGraph.choose_paths: {source_node} not in CostGraph") else: _paths = [] - _chosen_path = self.find_nearest(source=source, crit=crit) + _chosen_path = self.find_nearest(source_node=source_node, crit=crit) _paths.append( { - "source": source, + "source": source_node, "target": _chosen_path[0], "path": _chosen_path[2], "cost": _chosen_path[1], From 20ee557080aadd5d541516c80425417190547d6f Mon Sep 17 00:00:00 2001 From: "Ghosh, Tapajyoti" Date: Fri, 17 Jan 2025 19:03:14 -0700 Subject: [PATCH 6/9] Removing extra variables from scenario and des interface and some minor corrections to LiAISON code. --- celavi/__init__.py | 0 celavi/pylca_celavi/des_interface.py | 43 -------------------- celavi/pylca_celavi/liaison/liaison_model.py | 2 +- celavi/pylca_celavi/pylca_liaison.py | 4 +- celavi/scenario.py | 16 -------- celavi/tests/__init__.py | 0 celavi/tests/unit/__init__.py | 0 7 files changed, 3 insertions(+), 62 deletions(-) mode change 100755 => 100644 celavi/__init__.py mode change 100755 => 100644 celavi/tests/__init__.py mode change 100755 => 100644 celavi/tests/unit/__init__.py diff --git a/celavi/__init__.py b/celavi/__init__.py old mode 100755 new mode 100644 diff --git a/celavi/pylca_celavi/des_interface.py b/celavi/pylca_celavi/des_interface.py index 4b9acde..21d00d4 100644 --- a/celavi/pylca_celavi/des_interface.py +++ b/celavi/pylca_celavi/des_interface.py @@ -17,19 +17,8 @@ def __init__( self, lcia_des_filename, shortcutlca_filename, - intermediate_demand_filename, - dynamic_lci_filename, - electricity_grid_spatial_level, - static_lci_filename, - uslci_tech_filename, - uslci_emission_filename, - uslci_process_filename, - stock_filename, - emissions_lci_filename, - traci_lci_filename, use_shortcut_lca_calculations, verbose, - substitution_rate, run=0, ): """ @@ -43,52 +32,20 @@ def __init__( shortcutlca_filename: str Path to file where previously calculated impacts are stored. This file can be used instead of re-calculating impacts from the inventory. - intermediate_demand_filename: str - Path to file that stores the final demand vector every time the LCIA - calculations are run. For debugging purposes only. - dynamic_lci_filename: str - Path to the LCI dataset which changes with time. - electricity_grid_spatial_level: str - Specification of grid spatial level used for lca calculations. Must be - "state" or "national". - static_lci_filename: str - Path to the LCI dataset which does not change with time. - uslci_filename: str - Path to the U.S. LCI dataset pickle file. - stock_filename: str - Filename for storage pickle variable. - emissions_lci_filename: str - Filename for emissions inventory. - traci_lci_filename: str - Filename for TRACI 2.0 characterization factor dataset. use_shortcut_lca_calculations: Boolean Boolean flag for using previously calculating impact data or running the optimization code to re-calculate impacts. verbose: int 0 to suppress detailed print statements 1 to allow print statements - substitution_rate: Dict - Dictionary of material name: substitution rates for materials displaced by the - circular component. run: int Model run. Defaults to zero. """ # filepaths for files used in the pylca calculations self.lcia_des_filename = lcia_des_filename self.shortcutlca_filename = shortcutlca_filename - self.intermediate_demand_filename = intermediate_demand_filename - self.dynamic_lci_filename = dynamic_lci_filename - self.electricity_grid_spatial_level = electricity_grid_spatial_level - self.static_lci_filename = static_lci_filename - self.uslci_tech_filename = uslci_tech_filename - self.uslci_emission_filename = uslci_emission_filename - self.uslci_process_filename = uslci_process_filename - self.stock_filename = stock_filename - self.emissions_lci_filename = emissions_lci_filename - self.traci_lci_filename = traci_lci_filename self.use_shortcut_lca_calculations = use_shortcut_lca_calculations self.verbose = verbose - self.substitution_rate = substitution_rate self.run = run # The results file should be removed if present. The LCA results are appended to the results file. diff --git a/celavi/pylca_celavi/liaison/liaison_model.py b/celavi/pylca_celavi/liaison/liaison_model.py index a9f54a2..52cda55 100644 --- a/celavi/pylca_celavi/liaison/liaison_model.py +++ b/celavi/pylca_celavi/liaison/liaison_model.py @@ -137,7 +137,7 @@ def lca_runner(db,r,mc_runs,mc_foreground_flag,lca_flag,functional_unit,run_file if edit_ecoinvent_user_controlled == True: #inventory here has to be a dictionary. So if we read inventory from csv file we cannot edit it. - run_filename = user_controlled_editing_ecoinvent_activity(inventory,year_of_study,location_under_study,output_dir) + run_filename = user_controlled_editing_ecoinvent_activity(inventory,year_of_study,location_under_study,data_dir) print('Activity edited according to user prereferences and saved success',flush=True) #run_filename is a dataframe. process_dictionary = liaison_calc(db,run_filename,bw) diff --git a/celavi/pylca_celavi/pylca_liaison.py b/celavi/pylca_celavi/pylca_liaison.py index b31aa07..5d0d927 100644 --- a/celavi/pylca_celavi/pylca_liaison.py +++ b/celavi/pylca_celavi/pylca_liaison.py @@ -98,8 +98,8 @@ def liaison_lci( # Defining a list of processes for which LCA needs to be done. # This is not required and will be deleted after integration with the glass recycling model of CELAVI - processes_list = ["treatment of waste glass from unsorted public collection, sorting","photovoltaic plant construction, 570kWp, multi-Si, on open ground","treatment of waste glass sheet, sorting plant","glass wool mat production","cement production, Portland","treatment of waste glass, unsanitary landfill, wet infiltration class (500mm)","flat glass production, uncoated"] - random_number = random.randint(0,6) + processes_list = ["treatment of waste glass from unsorted public collection, sorting","treatment of waste glass sheet, sorting plant","glass wool mat production","cement production, Portland","treatment of waste glass, unsanitary landfill, wet infiltration class (500mm)","flat glass production, uncoated"] + random_number = random.randint(0,5) process_in_ecoinvent_for_lca_from_celavi = processes_list[random_number] unit_under_study = "kilogram" diff --git a/celavi/scenario.py b/celavi/scenario.py index 8248fa4..3a9d9cb 100644 --- a/celavi/scenario.py +++ b/celavi/scenario.py @@ -317,26 +317,10 @@ def setup(self): self.lca = PylcaCelavi( lcia_des_filename=self.files["lcia_to_des"], shortcutlca_filename=self.files["lcia_shortcut_db"], - intermediate_demand_filename=self.files["intermediate_demand"], - dynamic_lci_filename=dynamic_lci_filename, - electricity_grid_spatial_level=electricity_grid_spatial_level, - static_lci_filename=self.files["static_lci"], - uslci_tech_filename=self.files["uslci_tech"], - uslci_emission_filename=self.files["uslci_emission"], - uslci_process_filename=self.files["uslci_process_adder"], - stock_filename=self.files["stock_filename"], - emissions_lci_filename=self.files["emissions_lci"], - traci_lci_filename=self.files["traci_lci"], use_shortcut_lca_calculations=self.scen["flags"].get( "use_lcia_shortcut", True ), verbose = self.case["model_run"].get("lcia_verbose"), - substitution_rate={ - mat: apply_array_uncertainty(rate, self.run) - for mat, rate in self.scen["technology_components"] - .get("substitution_rates") - .items() - }, run=self.run, ) diff --git a/celavi/tests/__init__.py b/celavi/tests/__init__.py old mode 100755 new mode 100644 diff --git a/celavi/tests/unit/__init__.py b/celavi/tests/unit/__init__.py old mode 100755 new mode 100644 From f5b766b7058aa97f9fe4f792c041a9d22da23279 Mon Sep 17 00:00:00 2001 From: "Ghosh, Tapajyoti" Date: Fri, 17 Jan 2025 19:12:37 -0700 Subject: [PATCH 7/9] Update scenario.py removing electricity grid related code lines from old pylcia code. --- celavi/scenario.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/celavi/scenario.py b/celavi/scenario.py index 3a9d9cb..b67ffc7 100644 --- a/celavi/scenario.py +++ b/celavi/scenario.py @@ -289,26 +289,6 @@ def setup(self): self.netw = pickle.load(open(self.files["costgraph_pickle"], "rb")) print(f"CostGraph read in at {self.simtime(self.start)}", flush=True) - # Electricity spatial mix level. Defaults to 'state' when not provided. - electricity_grid_spatial_level = self.scen["scenario"].get( - "electricity_mix_level", "state" - ) - - if electricity_grid_spatial_level == "state": - reeds_importer = ReedsImporter( - reeds_imported_filename=self.files["state_reeds_grid_mix"], - reeds_output_filename=self.files["state_electricity_lci"], - ) - reeds_importer.state_level_reeds_importer() - dynamic_lci_filename = self.files["state_electricity_lci"] - else: - reeds_importer = ReedsImporter( - reeds_imported_filename=self.files["national_reeds_grid_mix"], - reeds_output_filename=self.files["national_electricity_lci"], - ) - reeds_importer.national_level_reeds_importer() - dynamic_lci_filename = self.files["national_electricity_lci"] - # Prepare LCIA code #verbose = 0 means no print statements. #verbose = 1 prints detailed LCA calculation steps From 3e3574b518fccbe7740032dec08fc75140ecbd82 Mon Sep 17 00:00:00 2001 From: "Ghosh, Tapajyoti" Date: Fri, 17 Jan 2025 21:29:51 -0700 Subject: [PATCH 8/9] Corrections to state names and addition of US-WECC as default alternative region choice. Also replaced all electricity flows with market group for electricity, high voltage for proper connections to ReEDS State Grid mix --- celavi/pylca_celavi/des_interface.py | 4 ++-- celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py | 7 +++---- celavi/pylca_celavi/liaison/lci_calculator.py | 9 ++++++--- celavi/pylca_celavi/liaison/liaison_model.py | 2 +- celavi/pylca_celavi/pylca_liaison.py | 9 +++++---- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/celavi/pylca_celavi/des_interface.py b/celavi/pylca_celavi/des_interface.py index 21d00d4..ee97e36 100644 --- a/celavi/pylca_celavi/des_interface.py +++ b/celavi/pylca_celavi/des_interface.py @@ -240,7 +240,7 @@ def pylca_run_main(self, df, verbose=0): index=False, header=False, ) - res.to_csv('results.csv',mode='a', index=False) + res.to_csv('results_checked_to_be_deleted.csv',mode='a', index=False) res_calculated = res @@ -269,7 +269,7 @@ def pylca_run_main(self, df, verbose=0): res_df['impacts'] = res_df['lcia'] res_df['impact'] = res_df['value'] res_df2 = res_df[['year','facility_id','material','route_id','state','stage','impacts','impact','run']] - res_df2.to_csv(self.lcia_des_filename, mode='a', header=False, index=False) + res_df2.to_csv(self.lcia_des_filename, mode='a', header=True, index=False) # This is the result that needs to be analyzed every timestep. return res_df2 diff --git a/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py b/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py index 59f2f07..beca1bd 100644 --- a/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py +++ b/celavi/pylca_celavi/liaison/edit_activity_ecoinvent.py @@ -17,7 +17,7 @@ def electricity_correction(exchange_ob): """ if 'electricity' in exchange_ob['name']: - name_of_flow = 'ReEDS_State_Grid_Mix' + name_of_flow = 'market group for electricity, high voltage' else: name_of_flow = exchange_ob['name'] @@ -39,7 +39,7 @@ def user_controlled_editing_ecoinvent_activity(process_selected_as_foreground,ye ------- """ print('Editing activities within ecoinvent to US location and US state wise grid mix',flush=True) - new_location = location_under_study + new_location = str(location_under_study) # These variables are used to create inventory dataframe process = [] @@ -71,8 +71,7 @@ def user_controlled_editing_ecoinvent_activity(process_selected_as_foreground,ye #Changing name of electricity flow #Changing name of electricity flow - #name_of_flow = electricity_correction(exch) - name_of_flow = exch['name'] + name_of_flow = electricity_correction(exch) flow.append(name_of_flow) if exch['type'] == 'production': diff --git a/celavi/pylca_celavi/liaison/lci_calculator.py b/celavi/pylca_celavi/liaison/lci_calculator.py index a1cc816..610a66a 100644 --- a/celavi/pylca_celavi/liaison/lci_calculator.py +++ b/celavi/pylca_celavi/liaison/lci_calculator.py @@ -320,9 +320,12 @@ def liaison_calc(db,run_filename,bw): try: activity = search_index_reader(row['flow'],'RER',row['unit'],database_dict) except: - print('Warning --- Failed - Not found '+row['flow'] + ' ' + row['supplying_location'] + ' ',flush = True) - print_flag = True - not_found = True + try: + activity = search_index_reader(row['flow'],'US-WECC',row['unit'],database_dict) + except: + print('Warning --- Failed - Not found '+row['flow'] + ' ' + row['supplying_location'] + ' ',flush = True) + print_flag = True + not_found = True if print_flag == False: print('Minor Success - Provided location '+ row['supplying_location']+' for '+ row['flow'] +' was not found. Shifting to ' + activity['name']+' ' + activity['location'],flush = True) diff --git a/celavi/pylca_celavi/liaison/liaison_model.py b/celavi/pylca_celavi/liaison/liaison_model.py index 52cda55..f173d2c 100644 --- a/celavi/pylca_celavi/liaison/liaison_model.py +++ b/celavi/pylca_celavi/liaison/liaison_model.py @@ -234,7 +234,7 @@ def lca_runner(db,r,mc_runs,mc_foreground_flag,lca_flag,functional_unit,run_file 'method': method }) - lcia_df.to_csv(output_dir+results_filename+str(r)+db+primary_process+'.csv',index = False) + #lcia_df.to_csv(output_dir+results_filename+str(r)+db+primary_process+'.csv',index = False) print("LCA performed succesfully", flush= True) save_project = False diff --git a/celavi/pylca_celavi/pylca_liaison.py b/celavi/pylca_celavi/pylca_liaison.py index 5d0d927..bf876e0 100644 --- a/celavi/pylca_celavi/pylca_liaison.py +++ b/celavi/pylca_celavi/pylca_liaison.py @@ -78,10 +78,10 @@ def liaison_lci( year_from_celavi = 2024 else: print('year to change',yr) - if yr%4 == 0: + if yr%2 == 0: year_from_celavi = yr else: - year_from_celavi = yr-yr%4 + year_from_celavi = yr-yr%2 print('year changed from ',yr,year_from_celavi) # Sanity Check to make sure LCA dataframe from CELAVI only contains one row. @@ -94,6 +94,7 @@ def liaison_lci( # Not sure why I changed state to a list and then loop through the list. Can be changed to directly storing the variable. + state = "US-"+state state_from_celavi = [state] # Defining a list of processes for which LCA needs to be done. @@ -118,9 +119,9 @@ def liaison_lci( inventory_to_be_built_from_celavi = "additional_inventories"+".csv" # These project names match to the project names of HIPSTER and need to be added to the yaml file - updated_project_name='USMid_Case'+str(year_from_celavi) + updated_project_name='Mid_Case'+str(year_from_celavi) updated_database='premise_base' - lca_project='USMid_Case_celavi'+str(year_from_celavi) + lca_project='Mid_Case_celavi'+str(year_from_celavi) def correct_natural_land_transformation(bw): """ From 3840eca7b0e8e77b5b8281d904673232124fe02a Mon Sep 17 00:00:00 2001 From: rhanes Date: Tue, 21 Jan 2025 10:37:49 -0600 Subject: [PATCH 9/9] adds new parameters to PylcaCelavi class definition and instantiation statement in scenario.py #220 --- celavi/pylca_celavi/des_interface.py | 15 +++++++++++++++ celavi/scenario.py | 2 ++ 2 files changed, 17 insertions(+) diff --git a/celavi/pylca_celavi/des_interface.py b/celavi/pylca_celavi/des_interface.py index ee97e36..8eaceef 100644 --- a/celavi/pylca_celavi/des_interface.py +++ b/celavi/pylca_celavi/des_interface.py @@ -15,6 +15,8 @@ class PylcaCelavi: def __init__( self, + data_dir, + liaison_params, lcia_des_filename, shortcutlca_filename, use_shortcut_lca_calculations, @@ -29,6 +31,10 @@ def __init__( lcia_des_filename: str Path to file that stores calculated impacts for passing back to the discrete event simulation. + data_dir: str + Path to outer directory of data repository. + liaison_params: Dict + Dictionary of liaison-specific parameters shortcutlca_filename: str Path to file where previously calculated impacts are stored. This file can be used instead of re-calculating impacts from the inventory. @@ -42,6 +48,15 @@ def __init__( Model run. Defaults to zero. """ # filepaths for files used in the pylca calculations + self.generated_dir = os.path.join(data_dir, 'generated','liaison') + self.inputs_dir = os.path.join(data_dir, 'inputs','liaison') + # create liaison-specific input directories if they don't exist + for _dir in [self.generated_dir, self.inputs_dir]: + if not os.path.isdir(_dir): + os.makedirs( + _dir + ) + self.liaison_params = liaison_params self.lcia_des_filename = lcia_des_filename self.shortcutlca_filename = shortcutlca_filename self.use_shortcut_lca_calculations = use_shortcut_lca_calculations diff --git a/celavi/scenario.py b/celavi/scenario.py index b67ffc7..570a534 100644 --- a/celavi/scenario.py +++ b/celavi/scenario.py @@ -295,6 +295,8 @@ def setup(self): if self.case["model_run"].get("warning_verbose") == 0: warnings.filterwarnings('ignore') self.lca = PylcaCelavi( + data_dir=self.args.data, + liaison_params=self.case['liaison'], lcia_des_filename=self.files["lcia_to_des"], shortcutlca_filename=self.files["lcia_shortcut_db"], use_shortcut_lca_calculations=self.scen["flags"].get(