# -*- coding: utf-8 -*-
"""
Main.py contains the functions to calculate the different quantities of materials
in each step of the process. Reffer to the diagram on Package-Overview for the
steps considered.
Support functions include Weibull functions for reliability and failure; also,
functions to modify baseline values and evaluate sensitivity to the parameters.
"""
import numpy as np
import pandas as pd
import datetime
import os
import matplotlib.pyplot as plt
def read_baseline_material(scenario, material='None', file=None):
if file is None:
try:
file = _interactive_load('Select baseline file')
except:
raise Exception('Interactive load failed. Tkinter not supported'+
'on this system. Try installing X-Quartz and reloading')
def _interactive_load(title=None):
# Tkinter file picker
import tkinter
from tkinter import filedialog
root = tkinter.Tk()
root.withdraw() #Start interactive file input
root.attributes("-topmost", True) #Bring window into foreground
return filedialog.askopenfilename(parent=root, title=title) #initialdir = data_dir
def _unitReferences(keyword):
'''
Specify units for variable in scenario or materials
Parameters
----------
keyword : str
String of scenario or material column label
Returns
-------
yunits : str
Unit specific to the keyword provided
'''
moduleDictionary = {'year': {'unit': 'Years', 'source': 'input'},
'new_Installed_Capacity_[MW]': {'unit': 'Power [MW]', 'source':'input'},
'mod_eff': {'unit': 'Efficiency $\eta$ [%]', 'source':'input'},
'mod_reliability_t50': {'unit': 'Years' , 'source':'input'},
'mod_reliability_t90': {'unit': 'Years', 'source':'input'},
'mod_degradation': {'unit': 'Percentage [%]', 'source':'input'},
'mod_lifetime': {'unit': 'Years', 'source':'input'},
'mod_MFG_eff': {'unit': 'Efficiency $\eta$ [%]', 'source':'input'},
'mod_EOL_collection_eff': {'unit': 'Efficiency $\eta$ [%]', 'source':'input'},
'mod_EOL_collected_recycled': {'unit': 'Percentage [%]', 'source':'input'},
'mod_Repowering': {'unit': 'Percentage [%]', 'source':'input'},
'mod_Repairing': {'unit': 'Percentage [%]', 'source':'input'},
'Area': {'unit': 'm$^2$', 'source': 'generated'},
'Cumulative_Area_disposedby_Failure': {'unit': 'm$^2$', 'source': 'generated'},
'Cumulative_Area_disposedby_ProjectLifetime': {'unit': 'm$^2$', 'source': 'generated'},
'Cumulative_Area_disposed': {'unit': 'm$^2$', 'source': 'generated'},
'Cumulative_Active_Area': {'unit': 'm$^2$', 'source': 'generated'},
'Installed_Capacity_[W]': {'unit': 'Power [W]', 'source': 'generated'},
'EOL_on_Year_0': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_1': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_2': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_3': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_4': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_5': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_6': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_7': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_8': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_9': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_10': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_11': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_12': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_13': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_14': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_15': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_16': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_17': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_18': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_19': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_20': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_21': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_22': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_23': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_24': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_25': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_26': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_27': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_28': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_29': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_30': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_31': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_32': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_33': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_34': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_35': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_36': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_37': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_38': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_39': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_40': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_41': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_42': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_43': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_44': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_45': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_46': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_47': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_48': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_49': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_50': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_51': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_52': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_53': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_54': {'unit': 'm$^2$', 'source': 'generated'},
'EOL_on_Year_55': {'unit': 'm$^2$', 'source': 'generated'},
'EoL_Collected': {'unit': 'm$^2$', 'source': 'generated'},
'EoL_NotCollected': {'unit': 'm$^2$', 'source': 'generated'},
'EoL_Recycled': {'unit': 'm$^2$', 'source': 'generated'},
'EoL_NotRecycled_Landfilled': {'unit': 'm$^2$', 'source': 'generated'}
}
materialDictionary={'year': {'unit': 'Years', 'source': 'input'},
'mat_virgin_eff': {'unit': 'Efficiency $\eta$ [%]', 'source': 'input'},
'mat_massperm2': {'unit': 'Mass [g]', 'source': 'input'},
'mat_MFG_eff': {'unit': 'Efficiency $\eta$ [%]', 'source': 'input'},
'mat_MFG_scrap_recycled': {'unit': 'Percentage [%]', 'source': 'input'},
'mat_MFG_scrap_Recycled': {'unit': 'Efficiency $\eta$ [%]', 'source': 'input'},
'mat_MFG_scrap_Recycled_into_HQ': {'unit': 'Percentage [%]', 'source': 'input'},
'mat_MFG_scrap_Recycled_into_HQ_Reused4MFG': {'unit': 'Percentage [%]', 'source': 'input'},
'mat_EOL_collected_Recycled': {'unit': 'Percentage [%]', 'source': 'input'},
'mat_EOL_Recycling_eff': {'unit': 'Efficiency $\eta$ [%]', 'source': 'input'},
'mat_EOL_Recycled_into_HQ': {'unit': 'Percentage [%]', 'source': 'input'},
'mat_EOL_RecycledHQ_Reused4MFG': {'unit': 'Percentage [%]', 'source': 'input'},
'mat_modules_NotRecycled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_modules_NotCollected': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_sento_Recycling': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_NotRecycled_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_Recycled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_Recycled_Losses_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_Recycled_2_HQ': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_Recycled_2_OQ': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EoL_Recycled_HQ_into_MFG': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_EOL_Recycled_HQ_into_OU': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_UsedinManufacturing': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_Manufacturing_Input': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Scrap': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Scrap_Sentto_Recycling': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Scrap_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Scrap_Recycled_Successfully': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Scrap_Recycled_Losses_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Recycled_into_HQ': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Recycled_into_OQ': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Recycled_HQ_into_MFG': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_MFG_Recycled_HQ_into_OU': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_Virgin_Stock': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_Total_EOL_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_Total_MFG_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_Total_Landfilled': {'unit': 'Mass [g]', 'source': 'generated'},
'mat_Total_Recycled_OU': {'unit': 'Mass [g]', 'source': 'generated'}
}
if keyword in moduleDictionary.keys():
yunits = moduleDictionary[keyword]['unit']
elif keyword in materialDictionary.keys():
yunits = materialDictionary[keyword]['unit']
else:
print("Warning: Keyword / Units not Found")
yunits = 'UNITS'
return yunits
def distance(s_lat, s_lng, e_lat, e_lng):
"""
# Haversine formula for numpy arrays
# Author: MalyutinS
# imported from comment on: https://gist.github.com/rochacbruno/2883505
# Example:
# s_lat = 45; s_lng = -110; e_lat=[33, 44]; e_lng = [-115, -140]
# Returns distance from the source point to the two ending points:
# r = distance(s_lat, s_lng, e_lat, e_lng)
# r = array([1402.24996689, 2369.0150434 ])
#
"""
# approximate radius of earth in km
R = 6373.0
# s_lat = s_lat*np.pi/180.0
s_lat = np.deg2rad(s_lat)
s_lng = np.deg2rad(s_lng)
e_lat = np.deg2rad(e_lat)
e_lng = np.deg2rad(e_lng)
d = np.sin((e_lat - s_lat)/2)**2 + np.cos(s_lat)*np.cos(e_lat) * np.sin((e_lng - s_lng)/2)**2
distance = 2 * R * np.arcsin(np.sqrt(d))
return distance
def drivingdistance(origin, destination, APIkey):
"""
Creates call for google-maps api to get driving directions betwen two points.
Input
-----
origin: array
[lat, lon] expected
destination: array
[lat, lon] expected
APYkey: str
String
"""
lat1, lon1 = origin
lat2, lon2 = destination
gm_url = ('https://maps.googleapis.com/maps/api/directions/xml?'+
'origin='+str(lat1)+','+str(lon1)+
'&destination='+str(lat2)+','+str(lon2)+
'&key='+APIkey)
return gm_url
[docs]class Simulation:
"""
The ScenarioObj top level class is used to work on Circular Economy scenario objects,
keep track of filenames, data for module and materials, operations modifying
the baselines, etc.
Parameters
----------
name : text to append to output files
nowstr : current date/time string
path : working directory with circular economy results
Methods
-------
__init__ : initialize the object
_setPath : change the working directory
"""
def __init__(self, name=None, path=None):
'''
initialize ScenarioObj with path of Scenario's baseline of module and materials
as well as a basename to append to
Parameters
----------
name: string, append temporary and output files with this value
path: location of Radiance materials and objects
Returns
-------
none
'''
self.path = "" # path of working directory
self.name = "" # basename to append
now = datetime.datetime.now()
self.nowstr = str(now.date())+'_'+str(now.hour)+str(now.minute)+str(now.second)
if path is None:
self._setPath(os.getcwd())
else:
self._setPath(path)
if name is None:
self.name = self.nowstr # set default filename for output files
else:
self.name = name
self.scenario={}
def _setPath(self, path):
"""
setPath - move path and working directory
"""
self.path = os.path.abspath(path)
print('path = '+ path)
try:
os.chdir(self.path)
except OSError as exc:
LOGGER.error('Path doesn''t exist: %s' % (path))
LOGGER.exception(exc)
raise(exc)
# check for path in the new Radiance directory:
def _checkPath(path): # create the file structure if it doesn't exist
if not os.path.exists(path):
os.makedirs(path)
print('Making path: '+path)
def createScenario(self, name, file=None):
self.scenario[name] = Scenario(name, file)
def calculateMassFlow(self, weibullInputParams = None, debugflag=False):
'''
Function takes as input a baseline dataframe already imported,
with the right number of columns and content.
It returns the dataframe with all the added calculation columns.
Parameters
------------
weibullInputParams : None
Dictionary with 'alpha' and 'beta' value for shaping the weibull
curve. beta is sometimes exchanged with lifetime, for example on
Irena 2016 values beta = 30. If weibullInputParams = None,
alfa and beta are calcualted from the t50 and t90 columns on the
module baseline.
Returns
--------
df: dataframe
input dataframe with addeds columns for the calculations of recycled,
collected, waste, installed area, etc.
'''
for scen in self.scenario:
print("Working on Scenario: ", scen)
print("********************")
df = self.scenario[scen].data
# Constant
irradiance_stc = 1000 # W/m^2
# Renaming and re-scaling
df['new_Installed_Capacity_[W]'] = df['new_Installed_Capacity_[MW]']*1e6
df['t50'] = df['mod_reliability_t50']
df['t90'] = df['mod_reliability_t90']
# Calculating Area and Mass
df['Area'] = df['new_Installed_Capacity_[W]']/(df['mod_eff']*0.01)/irradiance_stc # m^2
df['Area'] = df['Area'].fillna(0) # Chagne na's to 0s.
# Calculating Wast by Generation by Year, and Cumulative Waste by Year.
Generation_Disposed_byYear = []
Generation_Active_byYear= []
Generation_Power_byYear = []
weibullParamList = []
df['Cumulative_Area_disposedby_Failure'] = 0
df['Cumulative_Area_disposedby_ProjectLifetime'] = 0
df['Cumulative_Area_disposed'] = 0
df['Cumulative_Active_Area'] = 0
df['Installed_Capacity_[W]'] = 0
for generation, row in df.iterrows():
#generation is an int 0,1,2,.... etc.
#generation=4
#row=df.iloc[generation]
t50, t90 = row['t50'], row['t90']
if not weibullInputParams:
weibullIParams = weibull_params({t50: 0.50, t90: 0.90})
else:
weibullIParams = weibullInputParams
f = weibull_cdf(weibullIParams['alpha'], weibullIParams['beta'])
weibullParamList.append(weibullIParams)
x = np.clip(df.index - generation, 0, np.inf)
cdf = list(map(f, x))
# pdf = [0] + [j - i for i, j in zip(cdf[: -1], cdf[1 :])]
activearea = row['Area']
if np.isnan(activearea):
activearea=0
activeareacount = []
areadisposed_failure = []
areadisposed_projectlifetime = []
areapowergen = []
active=-1
disposed_projectlifetime=0
for age in range(len(cdf)):
disposed_projectlifetime=0
if cdf[age] == 0.0:
activeareacount.append(0)
areadisposed_failure.append(0)
areadisposed_projectlifetime.append(0)
areapowergen.append(0)
else:
active += 1
activeareaprev = activearea
activearea = activearea*(1-cdf[age]*(1-df.iloc[age]['mod_Repairing']*0.01))
areadisposed_failure.append(activeareaprev-activearea)
if age == int(row['mod_lifetime']+generation):
activearea_temp = activearea
activearea = 0+activearea*(df.iloc[age]['mod_Repowering']*0.01)
disposed_projectlifetime = activearea_temp-activearea
areadisposed_projectlifetime.append(disposed_projectlifetime)
activeareacount.append(activearea)
areapowergen.append(activearea*row['mod_eff']*0.01*irradiance_stc*(1-row['mod_degradation']*0.01)**active)
try:
# becuase the clip starts with 0 for the installation year, identifying installation year
# and adding initial area
fixinitialareacount = next((i for i, e in enumerate(x) if e), None) - 1
activeareacount[fixinitialareacount] = activeareacount[fixinitialareacount]+row['Area']
areapowergen[fixinitialareacount] = (activeareacount[fixinitialareacount] +
row['Area'] * row['mod_eff'] *0.01 * irradiance_stc)
except:
# Last value does not have a xclip value of nonzero so it goes
# to except. But it also means the loop finished for the calculations
# of Lifetime.
fixinitialareacount = len(cdf)-1
activeareacount[fixinitialareacount] = activeareacount[fixinitialareacount]+row['Area']
areapowergen[fixinitialareacount] = (activeareacount[fixinitialareacount] +
row['Area'] * row['mod_eff'] *0.01 * irradiance_stc)
print("Finished Area+Power Generation Calculations")
# area_disposed_of_generation_by_year = [element*row['Area'] for element in pdf]
df['Cumulative_Area_disposedby_Failure'] += areadisposed_failure
df['Cumulative_Area_disposedby_ProjectLifetime'] += areadisposed_projectlifetime
df['Cumulative_Area_disposed'] += areadisposed_failure
df['Cumulative_Area_disposed'] += areadisposed_projectlifetime
df['Cumulative_Active_Area'] += activeareacount
df['Installed_Capacity_[W]'] += areapowergen
Generation_Disposed_byYear.append([x + y for x, y in zip(areadisposed_failure, areadisposed_projectlifetime)])
Generation_Active_byYear.append(activeareacount)
Generation_Power_byYear.append(areapowergen)
df['WeibullParams'] = weibullParamList
MatrixDisposalbyYear = pd.DataFrame(Generation_Disposed_byYear, columns = df.index, index = df.index)
MatrixDisposalbyYear = MatrixDisposalbyYear.add_prefix("EOL_on_Year_")
try:
df = df[df.columns.drop(list(df.filter(regex='EOL_on_Year_')))]
except:
print("Warning: Issue dropping EOL columns generated by " \
"calculateMFC routine to overwrite")
df = df.join(MatrixDisposalbyYear)
## Start to do EOL Processes
############################
filter_col = [col for col in df if col.startswith('EOL_on_Year_')]
EOL = df[filter_col]
# This Multiplication pattern goes through Module and then material.
# It is for processes that depend on each year as they improve, i.e.
# Collection Efficiency,
#
# [ G1_1 G1_2 G1_3 G2_4 ...] [N1
# [ 0 G2_1 G2_2 G2_3 ...] X N2
# [ 0 0 G3_1 G3_2 ...] N3
# N4]
#
# EQUAL
# EOL_Collected =
# [ G1_1*N1 G1_2 *N2 G1_3 *N3 G2_4 *N4 ...]
# [ 0 G2_1 *N2 G2_2 *N3 G2_3 *N4 ...]
# [ 0 0 G3_1 *N3 G3_2 *N4 ...]
#
EOL_Collected = EOL.mul(df['mod_EOL_collection_eff'].values*0.01)
df['EoL_Collected'] = list(EOL_Collected.sum())
landfill_Collection = EOL.mul(1-(df['mod_EOL_collection_eff'].values*0.01))
df['EoL_NotCollected'] = list(landfill_Collection.sum())
EOL_Recycled = EOL_Collected.mul(df['mod_EOL_collected_recycled'].values*0.01)
df['EoL_Recycled'] = list(EOL_Recycled.sum())
EOL_NotRecycled_Landfilled = EOL_Collected.mul((1-df['mod_EOL_collected_recycled'].values*0.01))
df['EoL_NotRecycled_Landfilled'] = list(EOL_NotRecycled_Landfilled.sum())
# Cleanup of internal renaming and internal use columns
df.drop(['new_Installed_Capacity_[W]', 't50', 't90'], axis = 1, inplace=True)
self.scenario[scen].data = df
# collection losses here
# Recyle % here
################
# Material Loop#
################
for mat in self.scenario[scen].material:
print("==> Working on Material : ", mat)
dm = self.scenario[scen].material[mat].materialdata
# SWITCH TO MASS UNITS FOR THE MATERILA NOW:
# THIS IS DIFFERENT MULTIPLICATION THAN THE REST
# BECAUSE IT DEPENDS TO THE ORIGINAL MASS OF EACH MODULE WHEN INSTALLED
# [M1 * [ G1_1 G1_2 G1_3 G2_4 ...]
# M2 [ 0 G2_1 G2_2 G2_3 ...]
# M3] [ 0 0 G3_1 G3_2 ...]
#
# EQUAL
# mat_EOL_sentoRecycling =
# [ G1_1*M1 G1_2*M1 G1_3*M1 G2_4*M1 ...]
# [ 0 G2_1*M2 G2_2*M2 G2_3*M2 ...]
# [ 0 0 G3_1*M3 G3_2*M3 ...]
#
mat_modules_EOL_sentoRecycling = EOL_Recycled.multiply(dm['mat_massperm2'], axis=0)
dm['mat_modules_NotRecycled'] = list(EOL_NotRecycled_Landfilled.multiply(dm['mat_massperm2'], axis=0).sum())
dm['mat_modules_NotCollected'] = list(landfill_Collection.multiply(dm['mat_massperm2'], axis=0).sum())
# mat_EOL_collected_Recycled CHANGE NAME
# chnge also landfill_material_EOL_NotRecycled_Landfilled
mat_EOL_sento_Recycling = mat_modules_EOL_sentoRecycling.mul(dm['mat_EOL_collected_Recycled'].values*0.01)
dm['mat_EOL_sento_Recycling'] = list(mat_EOL_sento_Recycling.sum())
landfill_material_EOL_NotRecycled_Landfilled = mat_modules_EOL_sentoRecycling.mul(1-(dm['mat_EOL_collected_Recycled'].values*0.01))
dm['mat_EOL_NotRecycled_Landfilled'] = list(landfill_material_EOL_NotRecycled_Landfilled.sum())
mat_EOL_Recycled_Succesfully = mat_EOL_sento_Recycling.mul(dm['mat_EOL_Recycling_eff'].values*0.01)
dm['mat_EOL_Recycled'] = list(mat_EOL_Recycled_Succesfully.sum())
landfill_material_EOL_Recyled_Losses_Landfilled = mat_EOL_sento_Recycling.mul(1-(dm['mat_EOL_Recycling_eff'].values*0.01))
dm['mat_EOL_Recycled_Losses_Landfilled'] = list(landfill_material_EOL_Recyled_Losses_Landfilled.sum())
mat_EOL_Recycled_HQ = mat_EOL_Recycled_Succesfully.mul(dm['mat_EOL_Recycled_into_HQ'].values*0.01)
dm['mat_EOL_Recycled_2_HQ'] = list(mat_EOL_Recycled_HQ.sum())
mat_EOL_Recycled_OQ = mat_EOL_Recycled_Succesfully.mul(1-(dm['mat_EOL_Recycled_into_HQ'].values*0.01))
dm['mat_EOL_Recycled_2_OQ'] = list(mat_EOL_Recycled_OQ.sum())
mat_EOL_Recycled_HQ_into_MFG = mat_EOL_Recycled_HQ.mul(dm['mat_EOL_RecycledHQ_Reused4MFG'].values*0.01)
dm['mat_EoL_Recycled_HQ_into_MFG'] = list(mat_EOL_Recycled_HQ_into_MFG.sum())
mat_EOL_Recycled_HQ_into_OU = mat_EOL_Recycled_HQ.mul(1-(dm['mat_EOL_RecycledHQ_Reused4MFG'].values*0.01))
dm['mat_EOL_Recycled_HQ_into_OU'] = list(mat_EOL_Recycled_HQ_into_OU.sum())
# BULK Calculations Now
dm['mat_UsedSuccessfullyinModuleManufacturing'] = (df['Area'] * dm['mat_massperm2'])
dm['mat_EnteringModuleManufacturing'] = (df['Area'] * dm['mat_massperm2']*100/df['mod_MFG_eff'])
dm['mat_LostinModuleManufacturing'] = dm['mat_EnteringModuleManufacturing'] - dm['mat_UsedSuccessfullyinModuleManufacturing']
dm['mat_Manufacturing_Input'] = dm['mat_EnteringModuleManufacturing'] / (dm['mat_MFG_eff'] * 0.01)
# Scrap = Lost to Material manufacturing losses + Module manufacturing losses
dm['mat_MFG_Scrap'] = (dm['mat_Manufacturing_Input'] - dm['mat_EnteringModuleManufacturing'] +
dm['mat_LostinModuleManufacturing'])
dm['mat_MFG_Scrap_Sentto_Recycling'] = dm['mat_MFG_Scrap'] * dm['mat_MFG_scrap_Recycled'] * 0.01
dm['mat_MFG_Scrap_Landfilled'] = dm['mat_MFG_Scrap'] - dm['mat_MFG_Scrap_Sentto_Recycling']
dm['mat_MFG_Scrap_Recycled_Successfully'] = (dm['mat_MFG_Scrap_Sentto_Recycling'] *
dm['mat_MFG_scrap_Recycling_eff'] * 0.01)
dm['mat_MFG_Scrap_Recycled_Losses_Landfilled'] = (dm['mat_MFG_Scrap_Sentto_Recycling'] -
dm['mat_MFG_Scrap_Recycled_Successfully'])
dm['mat_MFG_Recycled_into_HQ'] = (dm['mat_MFG_Scrap_Recycled_Successfully'] *
dm['mat_MFG_scrap_Recycled_into_HQ'] * 0.01)
dm['mat_MFG_Recycled_into_OQ'] = dm['mat_MFG_Scrap_Recycled_Successfully'] - dm['mat_MFG_Recycled_into_HQ']
dm['mat_MFG_Recycled_HQ_into_MFG'] = (dm['mat_MFG_Recycled_into_HQ'] *
dm['mat_MFG_scrap_Recycled_into_HQ_Reused4MFG'] * 0.01)
dm['mat_MFG_Recycled_HQ_into_OU'] = dm['mat_MFG_Recycled_into_HQ'] - dm['mat_MFG_Recycled_HQ_into_MFG']
dm['mat_Virgin_Stock'] = dm['mat_Manufacturing_Input'] - dm['mat_EoL_Recycled_HQ_into_MFG'] - dm['mat_MFG_Recycled_HQ_into_MFG']
# Calculate raw virgin needs before mining and refining efficiency losses
dm['mat_Virgin_Stock_Raw'] = (dm['mat_Virgin_Stock'] * 100 / dm['mat_virgin_eff'])
# Add Wastes
dm['mat_Total_EOL_Landfilled'] = (dm['mat_modules_NotCollected'] +
dm['mat_modules_NotRecycled'] +
dm['mat_EOL_NotRecycled_Landfilled'] +
dm['mat_EOL_Recycled_Losses_Landfilled'])
dm['mat_Total_MFG_Landfilled'] = (dm['mat_MFG_Scrap_Landfilled'] +
dm['mat_MFG_Scrap_Recycled_Losses_Landfilled'])
dm['mat_Total_Landfilled'] = (dm['mat_Total_EOL_Landfilled'] +
dm['mat_Total_MFG_Landfilled'])
dm['mat_Total_Recycled_OU'] = (dm['mat_EOL_Recycled_2_OQ'] +
dm['mat_EOL_Recycled_HQ_into_OU'] +
dm['mat_MFG_Recycled_into_OQ'] +
dm['mat_MFG_Recycled_HQ_into_OU'])
self.scenario[scen].material[mat].materialdata = dm
def plotScenariosComparison(self, keyword=None):
if keyword is None:
scens = list(self.scenario.keys())[0]
print("Choose one of the keywords: ", list(self.scenario[scens].data.keys()))
return
yunits = _unitReferences(keyword)
plt.figure()
for scen in self.scenario:
plt.plot(self.scenario[scen].data['year'],self.scenario[scen].data[keyword], label=scen)
plt.legend()
plt.xlabel('Year')
plt.title(keyword.replace('_', " "))
plt.ylabel(yunits)
def plotMaterialComparisonAcrossScenarios(self, material = None, keyword=None):
if keyword is None:
scens = list(self.scenario.keys())[0]
mats = list(self.scenario[scens].material.keys())[0]
print("Choose one of the keywords: ", list(self.scenario[scens].material[mats].materialdata.keys()))
return
if material is None:
scens = list(self.scenario.keys())[0]
mats = list(self.scenario[scens].material.keys())
print("Choose one of the Materials: ", mats)
return
yunits = _unitReferences(keyword)
plt.figure()
for scen in self.scenario:
plt.plot(self.scenario[scen].data['year'], self.scenario[scen].material[material].materialdata[keyword], label=scen)
plt.legend()
plt.xlabel('Year')
plt.title((material + ' ' + keyword.replace('_', " ")))
plt.ylabel(yunits)
[docs]class Scenario(Simulation):
def __init__(self, name, file=None):
self.name = name
self.material = {}
if file is None:
try:
file = _interactive_load('Select module baseline file')
except:
raise Exception('Interactive load failed. Tkinter not supported'+
'on this system. Try installing X-Quartz and reloading')
csvdata = open(str(file), 'r', encoding="UTF-8")
csvdata = open(str(file), 'r', encoding="UTF-8-sig")
firstline = csvdata.readline()
secondline = csvdata.readline()
head = firstline.rstrip('\n').split(",")
meta = dict(zip(head, secondline.rstrip('\n').split(",")))
data = pd.read_csv(csvdata, names=head)
data.loc[:, data.columns != 'year'] = data.loc[:, data.columns != 'year'].astype(float)
self.baselinefile = file
self.metdata = meta,
self.data = data
def addMaterial(self, materialname, file=None):
self.material[materialname] = Material(materialname, file)
def __getitem__(self, key):
return getattr(self, key)
def __setitem__(self, key):
return setattr(self, key)
[docs]class Material:
def __init__(self, materialname, file):
self.materialname = materialname
if file is None:
try:
file = _interactive_load('Select material baseline file')
except:
raise Exception('Interactive load failed. Tkinter not supported'+
'on this system. Try installing X-Quartz and reloading')
csvdata = open(str(file), 'r', encoding="UTF-8")
csvdata = open(str(file), 'r', encoding="UTF-8-sig")
firstline = csvdata.readline()
secondline = csvdata.readline()
head = firstline.rstrip('\n').split(",")
meta = dict(zip(head, secondline.rstrip('\n').split(",")))
data = pd.read_csv(csvdata, names=head)
data.loc[:, data.columns != 'year'] = data.loc[:, data.columns != 'year'].astype(float)
self.materialfile = file
self.materialmetdata = meta
self.materialdata = data
[docs]def weibull_params(keypoints):
r'''Returns shape parameter `alpha` and scale parameter `beta`
for a Weibull distribution whose CDF passes through the
two time: value pairs in `keypoints`
Parameters
----------
keypoints : list
Two lists of t50 and 590 values, where t50 is the year since deployment
that the cohort has lost 50% of originally installed modules, and t90
is the year since deployment that the cohort has lost 90% of the originally
installed modules. These values are used to calcualte the shape and scale
parameters for the weibull distribution.
Returns
-------
alpha : float
Shape parameter `alpha` for weibull distribution.
beta : float
Scale parameter `beta` for weibull distribution. Often exchanged with ``lifetime``
like in Irena 2016, beta = 30.
'''
t1, t2 = tuple(keypoints.keys())
cdf1, cdf2 = tuple(keypoints.values())
alpha = np.ndarray.item(np.real_if_close(
(np.log(np.log(1 - cdf1)+0j) - np.log(np.log(1 - cdf2)+0j))/(np.log(t1) - np.log(t2))
))
beta = np.abs(np.exp(
(
np.log(t2)*((0+1j)*np.pi + np.log(np.log(1 - cdf1)+0j))
+ np.log(t1)*(((0-1j))*np.pi - np.log(np.log(1 - cdf2)+0j))
)/(
np.log(np.log(1 - cdf1)+0j) - np.log(np.log(1 - cdf2)+0j)
)
))
return {'alpha': alpha, 'beta': beta}
[docs]def weibull_cdf(alpha, beta):
'''Return the CDF for a Weibull distribution having:
shape parameter `alpha`
scale parameter `beta`
Parameters
----------
alpha : float
Shape parameter `alpha` for weibull distribution.
beta : float
Scale parameter `beta` for weibull distribution. Often exchanged with ``lifetime``
like in Irena 2016, beta = 30.
'''
def cdf(x):
return 1 - np.exp(-(np.array(x)/beta)**alpha)
return cdf
def weibull_pdf(alpha, beta):
r'''Return the PDF for a Weibull distribution having:
shape parameter `alpha`
scale parameter `beta`
Parameters
----------
alpha : float
Shape parameter `alpha` for weibull distribution.
beta : float
Scale parameter `beta` for weibull distribution. Often exchanged with ``lifetime``
like in Irena 2016, beta = 30.
'''
def pdf(x):
return (alpha/np.array(x)) * ((np.array(x)/beta)**alpha) * (np.exp(-(np.array(x)/beta)**alpha))
return pdf
def weibull_cdf_vis(alpha, beta, xlim=56):
r''' Returns the CDF for a weibull distribution of 1 generation
so it can be plotted.
Parameters
----------
alpha : float
Shape parameter `alpha` for weibull distribution.
beta : float
Scale parameter `beta` for weibull distribution. Often exchanged with ``lifetime``
like in Irena 2016, beta = 30.
xlim : int
Number of years to calculate the distribution for. i.e. x-axis limit.
Returns
-------
idf : list
List of weibull cumulative distribution values for year 0 until xlim.
'''
dfindex = pd.RangeIndex(0,xlim,1)
x = np.clip(dfindex - 0, 0, np.inf)
if alpha and beta:
i = weibull_cdf(alpha, beta)
idf = list(map(i, x))
return idf
[docs]def sens_StageImprovement(df, stage, improvement=1.3, start_year=None):
'''
Modifies baseline scenario for evaluating sensitivity of lifetime parameter.
t50 and t90 reliability years get incresed by `improvement` parameter
starting the `year_increase` year specified.
Parameters
----------
df : dataframe
dataframe to be modified
stage : str
Stage that wants to be modified. This can be any of the module or
material specified values, for example:'MFG_Material_eff',
'mat_MFG_scrap_recycled', 'mat_MFG_scrap_Recycled',
'mat_MFG_scrap_Recycled_into_HQ', 'mat_MFG_scrap_Recycled_into_HQ_Reused4MFG'
'mod_EOL_collection_losses', 'mod_EOL_collected_recycled',
'mat_EOL_Recycling_eff', 'mat_EOL_Recycled_into_HQ',
'mat_EOL_RecycledHQ_Reused4MFG', 'mod_repowering', 'mod_eff', etc.
improvement : decimal
Percent increase in decimal (i.e. "1.3" for 30% increase in value)
or percent decrease (i.e. "0.3") relative to values in df.
start_year :
the year at which the improvement occurs
Returns
--------
df : dataframe
dataframe of expected module lifetime increased or decreased at specified year
'''
if start_year is None:
start_year = int(datetime.datetime.now().year)
#df[df.index > 2000]['mod_reliability_t50'].apply(lambda x: x*1.3)
df[stage] = df[stage].astype(float)
df.loc[df.index > start_year, stage] = df[df.index > start_year][stage].apply(lambda x: x*improvement)
return df
[docs]def sens_StageEfficiency(df, stage, target_eff = 95.0, start_year = None,
goal_year = 2030, plotflag = False):
'''
Modifies baseline scenario for evaluating sensitivity to increasing a stage in the
lifetime of the module's efficiency. It either increases or decreases from the
start year until the goal year the value to the target efficiency by interpolation.
Parameters
----------
df : dataframe
dataframe to be modified
stage : str
Stage that wants to be modified. This can be any of the module or
material specified efficiencies, for example:'MFG_Material_eff',
'mat_MFG_scrap_recycled', 'mat_MFG_scrap_Recycled',
'mat_MFG_scrap_Recycled_into_HQ', 'mat_MFG_scrap_Recycled_into_HQ_Reused4MFG'
'mod_EOL_collection_losses', 'mod_EOL_collected_recycled',
'mat_EOL_Recycling_eff', 'mat_EOL_Recycled_into_HQ',
'mat_EOL_RecycledHQ_Reused4MFG', 'mod_repowering', 'mod_eff', etc.
start_year: int
Year to start modifying the value. This specifies the initial efficiency
value that is going to be modified. If None is passed, current year is used.
target_eff: flat
target eff value in percentage to be reached. i.e. 95.0 %.
goal_year : int
year by which target efficiency will be reached. i.e. 2030. Must be higher than current year.
Returns
-------
df : dataframe
modified dataframe
'''
if start_year is None:
start_year = int(datetime.datetime.now().year)
if start_year > goal_year:
print("Error. Goal Year is before start year")
return
if 0 < abs(target_eff) < 1: # checking it is not 0.95 but 95% i.e.
print("Warning: target_eff value is between 0 and 1; it has been"
"multiplied by 100% assuming it was a percentage in decimal form.")
target_eff = target_eff*100
if target_eff > 100 or target_eff < 0:
print("Warning: target_eff is out of range. Input value between"
"0 and 100")
return
if stage in df.columns:
df2 = df.copy()
df2[stage]=df2[stage].astype(float)
df2.loc[(df2.index < goal_year) & (df2.index > start_year), stage] = np.nan
df2.loc[df2.index >= goal_year , stage] = target_eff
df2[stage] = df2[stage].interpolate()
if plotflag:
plt.plot(df[stage], label='Original')
plt.plot(df2[stage], label='Modified')
plt.title('Updated values for '+stage)
plt.legend()
return df2
else:
print("Stage name incorrect.")
def _modDict(originaldict, moddict):
'''
Compares keys in originaldict with moddict and updates values of
originaldict to moddict if existing.
Parameters
----------
originaldict : dictionary
Original dictionary calculated, for example frontscan or backscan dictionaries.
moddict : dictionary
Modified dictinoary, for example modscan['x'] = 0 to change position of x.
Returns
-------
originaldict : dictionary
Updated original dictionary with values from moddict.
'''
for key in moddict:
try:
originaldict[key] = moddict[key]
except:
print("Wrong key in modified dictionary")
return originaldict
[docs]def calculateLCA(PVarea, modified_impacts=None, printflag = False):
'''
'''
if printflag:
print("Doing calculations of LCA analysis for Silicon Photovoltaic Panels")
impacts = {'Acidification':{'UUID': '75d0c8a2-e466-3bd7-813b-5beef2209330',
'Result': 1.29374135667815,
'Unit': 'kg SO2' },
'Carcinogenics':{'UUID': 'a6e5e5d8-a1e5-3c77-8170-586c4fe37514',
'Result': 0.0000231966690476102,
'Unit': 'CTUh' },
'Ecotoxicity':{'UUID': '338e9370-ceb0-3d18-9d87-5f91feb7829c',
'Result': 5933.77859696668,
'Unit': 'CTUe' },
'Eutrophication':{'UUID': '45b8cd56-498a-3c6f-9488-134e951d8c02',
'Result': 1.34026194777363,
'Unit': 'kg N eq' },
'Fossil fuel depletion':{'UUID': '0e45786f-67fa-3b8a-b8a3-73a7c316434c',
'Result': 249.642261689385,
'Unit': 'MJ surplus' },
'Global warming':{'UUID': '31967441-d687-313d-9910-13da3a584ab7',
'Result': 268.548841324818,
'Unit': 'kg CO2 eq' },
'Non carcinogenics':{'UUID': 'd4827ae3-c873-3ea4-85fb-860b7f3f2dee',
'Result': 0.000135331806321799,
'Unit': 'CTUh' },
'Ozone depletion':{'UUID': '6c05dad1-6661-35f2-82aa-6e8e6a498aec',
'Result': 0.0000310937628622019,
'Unit': 'kg CFC-11 eq' },
'Respiratory effects':{'UUID': 'e0916d62-7fbd-3d0a-a4a5-52659b0ac9c1',
'Result': 0.373415542664206,
'Unit': 'kg PM2.5 eq' },
'Smog':{'UUID': '7a149078-e2fd-3e07-a5a3-79035c60e7c3',
'Result': 15.35483065,
'Unit': 'kg O3 eq' },
}
if modified_impacts is not None:
impacts = _modDict(impacts, modified_impacts)
if printflag:
print("Following Modified impacts provided instead of TRACI 2.1 default")
print(impacts)
print("")
else:
if printflag:
print("Following TRACI 2.1")
acidification = impacts['Acidification']['Result']*PVarea
carcinogenics = impacts['Carcinogenics']['Result']*PVarea
ecotoxicity = impacts['Ecotoxicity']['Result']*PVarea
eutrophication = impacts['Eutrophication']['Result']*PVarea
fossil_fuel_depletion = impacts['Fossil fuel depletion']['Result']*PVarea
global_warming = impacts['Global warming']['Result']*PVarea
non_carcinogenics = impacts['Non carcinogenics']['Result']*PVarea
ozone_depletion = impacts['Ozone depletion']['Result']*PVarea
respiratory_effects = impacts['Respiratory effects']['Result']*PVarea
smog = impacts['Smog']['Result']*PVarea
if printflag:
print("RESULTS FOR PV AREA ", PVarea, " m2 ")
print("****************************************")
print('Acidification: ', round(impacts['Acidification']['Result']*PVarea, 2), ' ', impacts['Acidification']['Unit'])
print('Carcinogenics: ', round(impacts['Carcinogenics']['Result']*PVarea, 2), ' ', impacts['Carcinogenics']['Unit'])
print('Ecotoxicity: ', round(impacts['Ecotoxicity']['Result']*PVarea, 2), ' ', impacts['Ecotoxicity']['Unit'])
print('Eutrophication: ', round(impacts['Eutrophication']['Result']*PVarea, 2), ' ', impacts['Eutrophication']['Unit'])
print('Fossil fuel depletion: ', round(impacts['Fossil fuel depletion']['Result']*PVarea, 2), ' ', impacts['Fossil fuel depletion']['Unit'])
print('Global warming: ', round(impacts['Global warming']['Result']*PVarea, 2), ' ', impacts['Global warming']['Unit'])
print('Non carcinogenics: ', round(impacts['Non carcinogenics']['Result']*PVarea, 2), ' ', impacts['Non carcinogenics']['Unit'])
print('Ozone depletion: ', round(impacts['Ozone depletion']['Result']*PVarea, 2), ' ', impacts['Ozone depletion']['Unit'])
print('Respiratory effects: ', round(impacts['Respiratory effects']['Result']*PVarea, 2), ' ', impacts['Respiratory effects']['Unit'])
print('Smog: ', round(impacts['Smog']['Result']*PVarea, 2), ' ', impacts['Smog']['Unit'])
return (acidification, carcinogenics, ecotoxicity, eutrophication,
fossil_fuel_depletion, global_warming,
non_carcinogenics, ozone_depletion, respiratory_effects, smog)