def forcing_init(self, sink_start, forcing_start, ghg_start, partition_interval, forcing_p1, forcing_p2, forcing_p3, absorbtion_p1, absorbtion_p2, lsc_p1, lsc_p2): """Initialize Forcing object and cum_forcings used in calculating the mitigation up to a node. Args: **kwargs: Arguments to initialize Forcing object, see Forcing class for more info. """ bau_emission = self.bau.ghg_end - self.bau.ghg_start self.emit_pct = 1.0 - (self.ghg_levels-self.bau.ghg_start) / bau_emission self.cum_forcings = np.zeros((self.tree.num_periods, self.dnum)) self.forcing = Forcing(self.tree, self.bau, sink_start, forcing_start, ghg_start, partition_interval, forcing_p1, forcing_p2, forcing_p3, absorbtion_p1, absorbtion_p2, lsc_p1, lsc_p2) mitigation = np.ones((self.dnum, self.tree.num_decision_nodes)) * self.emit_pct[:, np.newaxis] path_ghg_levels = np.zeros((self.dnum, self.tree.num_periods+1)) path_ghg_levels[0,:] = self.bau.ghg_start for i in range(0, self.dnum): for n in range(1, self.tree.num_periods+1): node = self.tree.get_node(n, 0) self.cum_forcings[n-1, i] = self.forcing.forcing_at_node(mitigation[i], node, i)
def _forcing_init(self): """Initialize `Forcing` object and cum_forcings used in calculating the force mitigation up to a node.""" if self.emit_pct is None: bau_emission = self.bau.ghg_end - self.bau.ghg_start self.emit_pct = 1.0 - (self.ghg_levels - self.bau.ghg_start) / bau_emission self.cum_forcings = np.zeros((self.tree.num_periods, self.dnum)) mitigation = np.ones( (self.dnum, self.tree.num_decision_nodes)) * self.emit_pct[:, np.newaxis] for i in range(0, self.dnum): for n in range(1, self.tree.num_periods + 1): node = self.tree.get_node(n, 0) self.cum_forcings[n - 1, i] = Forcing.forcing_at_node( mitigation[i], node, self.tree, self.bau, self.subinterval_len)
def _damage_function_node(self, m, node): """Calculate the damage at any given node, based on mitigation actions in `m`.""" if self.damage_coefs is None: self._damage_interpolation() if self.cum_forcings is None: self._forcing_init() if node == 0: return 0.0 period = self.tree.get_period(node) forcing = Forcing.forcing_at_node(m, node, self.tree, self.bau, self.subinterval_len) force_mitigation = self._forcing_based_mitigation(forcing, period) worst_end_state, best_end_state = self.tree.reachable_end_states( node, period=period) probs = self.tree.final_states_prob[worst_end_state:best_end_state + 1] if force_mitigation < self.emit_pct[1]: damage = (probs *(self.damage_coefs[worst_end_state:best_end_state+1, period-1, 1, 1] * force_mitigation \ + self.damage_coefs[worst_end_state:best_end_state+1, period-1, 1, 2])).sum() elif force_mitigation < self.emit_pct[0]: damage = (probs * (self.damage_coefs[worst_end_state:best_end_state+1, period-1, 0, 0] * force_mitigation**2 \ + self.damage_coefs[worst_end_state:best_end_state+1, period-1, 0, 1] * force_mitigation \ + self.damage_coefs[worst_end_state:best_end_state+1, period-1, 0, 2])).sum() else: damage = 0.0 i = 0 for state in range(worst_end_state, best_end_state + 1): if self.d[0, state, period - 1] > 1e-5: deriv = 2.0 * self.damage_coefs[state, period-1, 0, 0]*self.emit_pct[0] \ + self.damage_coefs[state, period-1, 0, 1] decay_scale = deriv / (self.d[0, state, period - 1] * np.log(0.5)) dist = force_mitigation - self.emit_pct[0] + np.log(self.d[0, state, period-1]) \ / (np.log(0.5) * decay_scale) damage += probs[i] * (0.5**(decay_scale * dist) * np.exp( -np.square(force_mitigation - self.emit_pct[0]) / 60.0) ) i += 1 return damage / probs.sum()
def init_input_obj(self): """Section 4 - Create UWG objects from input parameters self.simTime # simulation time parameter obj self.weather # weather obj for simulation time period self.forcIP # Forcing obj self.forc # Empty forcing obj self.geoParam # geographic parameters obj self.RSM # Rural site & vertical diffusion model obj self.USM # Urban site & vertical diffusion model obj self.UCM # Urban canopy model obj self.UBL # Urban boundary layer model self.road # urban road element self.rural # rural road element self.soilindex1 # soil index for urban rsoad depth self.soilindex2 # soil index for rural road depth self.BEM # list of BEMDef objects self.Sch # list of Schedule objects """ climate_file_path = os.path.join(self.epwDir, self.epwFileName) self.simTime = SimParam(self.dtSim, self.dtWeather, self.Month, self.Day, self.nDay) # simulation time parametrs self.weather = Weather( climate_file_path, self.simTime.timeInitial, self.simTime.timeFinal ) # weather file data for simulation time period self.forcIP = Forcing(self.weather.staTemp, self.weather) # initialized Forcing class self.forc = Forcing() # empty forcing class # Initialize geographic Param and Urban Boundary Layer Objects nightStart = 18. # arbitrary values for begin/end hour for night setpoint nightEnd = 8. maxdx = 250. # max dx (m) self.geoParam = Param(self.h_ubl1,self.h_ubl2,self.h_ref,self.h_temp,self.h_wind,self.c_circ,\ self.maxDay,self.maxNight,self.latTree,self.latGrss,self.albVeg,self.vegStart,self.vegEnd,\ nightStart,nightEnd,self.windMin,self.WGMAX,self.c_exch,maxdx,self.G,self.CP,self.VK,self.R,\ self.RV,self.LV,math.pi,self.SIGMA,self.WATERDENS,self.LVTT,self.TT,self.ESTT,self.CL,\ self.CPV,self.B, self.CM, self.COLBURN) self.UBL = UBLDef('C', self.charLength, self.weather.staTemp[0], maxdx, self.geoParam.dayBLHeight, self.geoParam.nightBLHeight) # Defining road emis = 0.93 asphalt = Material(self.kRoad, self.cRoad, 'asphalt') road_T_init = 293. road_horizontal = 1 road_veg_coverage = min(self.vegCover / (1 - self.bldDensity), 1.) # fraction of surface vegetation coverage # define road layers road_layer_num = int(math.ceil(self.d_road / 0.05)) thickness_vector = map(lambda r: 0.05, range( road_layer_num)) # 0.5/0.05 ~ 10 x 1 matrix of 0.05 thickness material_vector = map(lambda n: asphalt, range(road_layer_num)) self.road = Element(self.alb_road,emis,thickness_vector,material_vector,road_veg_coverage,\ road_T_init,road_horizontal,name="urban_road") self.rural = copy.deepcopy(self.road) self.rural.vegCoverage = self.rurVegCover self.rural._name = "rural_road" # Define BEM for each DOE type (read the fraction) if not os.path.exists(self.readDOE_file_path): raise Exception("readDOE.pkl file: '{}' does not exist.".format( readDOE_file_path)) readDOE_file = open(self.readDOE_file_path, 'rb') # open pickle file in binary form refDOE = cPickle.load(readDOE_file) refBEM = cPickle.load(readDOE_file) refSchedule = cPickle.load(readDOE_file) readDOE_file.close() # Define building energy models k = 0 r_glaze = 0 # Glazing ratio for total building stock SHGC = 0 # SHGC addition for total building stock alb_wall = 0 # albedo wall addition for total building stock h_floor = self.flr_h or 3.05 # average floor height total_urban_bld_area = math.pow( self.charLength, 2 ) * self.bldDensity * self.bldHeight / h_floor # total building floor area area_matrix = utilities.zeros(16, 3) self.BEM = [] # list of BEMDef objects self.Sch = [] # list of Schedule objects for i in xrange(16): # 16 building types for j in xrange(3): # 3 built eras if self.bld[i][j] > 0.: # Add to BEM list self.BEM.append(refBEM[i][j][self.zone]) self.BEM[k].frac = self.bld[i][j] self.BEM[k].fl_area = self.bld[i][j] * total_urban_bld_area # Overwrite with optional parameters if provided if self.glzR: self.BEM[k].building.glazingRatio = self.glzR if self.albRoof: self.BEM[k].roof.albedo = self.albRoof if self.vegRoof: self.BEM[k].roof.vegCoverage = self.vegRoof if self.SHGC: self.BEM[k].building.shgc = self.SHGC if self.albWall: self.BEM[k].wall.albedo = self.albWall # Keep track of total urban r_glaze, SHGC, and alb_wall for UCM model r_glaze = r_glaze + self.BEM[k].frac * self.BEM[ k].building.glazingRatio ## SHGC = SHGC + self.BEM[k].frac * self.BEM[k].building.shgc alb_wall = alb_wall + self.BEM[k].frac * self.BEM[ k].wall.albedo # Add to schedule list self.Sch.append(refSchedule[i][j][self.zone]) k += 1 # Reference site class (also include VDM) self.RSM = RSMDef(self.lat, self.lon, self.GMT, self.h_obs, self.weather.staTemp[0], self.weather.staPres[0], self.geoParam, self.z_meso_dir_path) self.USM = RSMDef(self.lat, self.lon, self.GMT, self.bldHeight / 10., self.weather.staTemp[0], self.weather.staPres[0], self.geoParam, self.z_meso_dir_path) T_init = self.weather.staTemp[0] H_init = self.weather.staHum[0] self.UCM = UCMDef(self.bldHeight,self.bldDensity,self.verToHor,self.treeCoverage,self.sensAnth,self.latAnth,T_init,H_init,\ self.weather.staUmod[0],self.geoParam,r_glaze,SHGC,alb_wall,self.road) self.UCM.h_mix = self.h_mix # Define Road Element & buffer to match ground temperature depth roadMat, newthickness = procMat(self.road, self.MAXTHICKNESS, self.MINTHICKNESS) for i in xrange(self.nSoil): # if soil depth is greater then the thickness of the road # we add new slices of soil at max thickness until road is greater or equal is_soildepth_equal = self.is_near_zero( self.depth_soil[i][0] - sum(newthickness), 1e-15) if is_soildepth_equal or (self.depth_soil[i][0] > sum(newthickness)): while self.depth_soil[i][0] > sum(newthickness): newthickness.append(self.MAXTHICKNESS) roadMat.append(self.SOIL) self.soilindex1 = i break self.road = Element(self.road.albedo, self.road.emissivity, newthickness, roadMat,\ self.road.vegCoverage, self.road.layerTemp[0], self.road.horizontal, self.road._name) # Define Rural Element ruralMat, newthickness = procMat(self.rural, self.MAXTHICKNESS, self.MINTHICKNESS) for i in xrange(self.nSoil): # if soil depth is greater then the thickness of the road # we add new slices of soil at max thickness until road is greater or equal is_soildepth_equal = self.is_near_zero( self.depth_soil[i][0] - sum(newthickness), 1e-15) if is_soildepth_equal or (self.depth_soil[i][0] > sum(newthickness)): while self.depth_soil[i][0] > sum(newthickness): newthickness.append(self.MAXTHICKNESS) ruralMat.append(self.SOIL) self.soilindex2 = i break self.rural = Element(self.rural.albedo, self.rural.emissivity, newthickness,\ ruralMat,self.rural.vegCoverage,self.rural.layerTemp[0],self.rural.horizontal, self.rural._name)
def _ghg_level_node(self, m, node): return Forcing.ghg_level_at_node(m, node, self.tree, self.bau, self.subinterval_len)
def init_input_obj(self): """Section 4 - Create uwg objects from input parameters self.simTime # simulation time parameter obj self.weather # weather obj for simulation time period self.forcIP # Forcing obj self.forc # Empty forcing obj self.geoParam # geographic parameters obj self.RSM # Rural site & vertical diffusion model obj self.USM # Urban site & vertical diffusion model obj self.UCM # Urban canopy model obj self.UBL # Urban boundary layer model self.road # urban road element self.rural # rural road element self.soilindex1 # soil index for urban rsoad depth self.soilindex2 # soil index for rural road depth self.Sch # list of Schedule objects """ climate_file_path = os.path.join(self.epwDir, self.epwFileName) self.simTime = SimParam(self.dtSim, self.dtWeather, self.Month, self.Day, self.nDay) # simulation time parametrs # weather file data for simulation time period self.weather = Weather(climate_file_path, self.simTime.timeInitial, self.simTime.timeFinal) self.forcIP = Forcing(self.weather.staTemp, self.weather) # initialized Forcing class self.forc = Forcing() # empty forcing class # Initialize geographic Param and Urban Boundary Layer Objects nightStart = 18. # arbitrary values for begin/end hour for night setpoint nightEnd = 8. maxdx = 250. # max dx (m) self.geoParam = Param(self.h_ubl1, self.h_ubl2, self.h_ref, self.h_temp, self.h_wind, self.c_circ, self.maxDay, self.maxNight, self.latTree, self.latGrss, self.albVeg, self.vegStart, self.vegEnd, nightStart, nightEnd, self.windMin, self.WGMAX, self.c_exch, maxdx, self.G, self.CP, self.VK, self.R, self.RV, self.LV, math.pi, self.SIGMA, self.WATERDENS, self.LVTT, self.TT, self.ESTT, self.CL, self.CPV, self.B, self.CM, self.COLBURN) self.UBL = UBLDef( 'C', self.charLength, self.weather.staTemp[0], maxdx, self.geoParam.dayBLHeight, self.geoParam.nightBLHeight) # Defining road emis = 0.93 asphalt = Material(self.kRoad, self.cRoad, 'asphalt') road_T_init = 293. road_horizontal = 1 # fraction of surface vegetation coverage road_veg_coverage = min(self.vegCover/(1-self.bldDensity), 1.) # define road layers road_layer_num = int(math.ceil(self.d_road/0.05)) # 0.5/0.05 ~ 10 x 1 matrix of 0.05 thickness thickness_vector = [0.05 for r in xrange(road_layer_num)] material_vector = [asphalt for r in xrange(road_layer_num)] self.road = Element(self.alb_road, emis, thickness_vector, material_vector, road_veg_coverage, road_T_init, road_horizontal, name="urban_road") self.rural = copy.deepcopy(self.road) self.rural.vegCoverage = self.rurVegCover self.rural._name = "rural_road" # Reference site class (also include VDM) self.RSM = RSMDef(self.lat, self.lon, self.GMT, self.h_obs, self.weather.staTemp[0], self.weather.staPres[0], self.geoParam, self.z_meso_dir_path) self.USM = RSMDef(self.lat, self.lon, self.GMT, self.bldHeight/10., self.weather.staTemp[0], self.weather.staPres[0], self.geoParam, self.z_meso_dir_path) T_init = self.weather.staTemp[0] H_init = self.weather.staHum[0] self.UCM = UCMDef(self.bldHeight, self.bldDensity, self.verToHor, self.treeCoverage, self.sensAnth, self.latAnth, T_init, H_init, self.weather.staUmod[0], self.geoParam, self.r_glaze_total, self.SHGC_total, self.alb_wall_total, self.road) self.UCM.h_mix = self.h_mix # Define Road Element & buffer to match ground temperature depth roadMat, newthickness = procMat(self.road, self.MAXTHICKNESS, self.MINTHICKNESS) for i in xrange(self.nSoil): # if soil depth is greater then the thickness of the road # we add new slices of soil at max thickness until road is greater or equal is_soildepth_equal = self.is_near_zero(self.depth_soil[i][0] - sum(newthickness), 1e-15) if is_soildepth_equal or (self.depth_soil[i][0] > sum(newthickness)): while self.depth_soil[i][0] > sum(newthickness): newthickness.append(self.MAXTHICKNESS) roadMat.append(self.SOIL) self.soilindex1 = i break self.road = Element(self.road.albedo, self.road.emissivity, newthickness, roadMat, self.road.vegCoverage, self.road.layerTemp[0], self.road.horizontal, self.road._name) # Define Rural Element ruralMat, newthickness = procMat(self.rural, self.MAXTHICKNESS, self.MINTHICKNESS) for i in xrange(self.nSoil): # if soil depth is greater then the thickness of the road # we add new slices of soil at max thickness until road is greater or equal is_soildepth_equal = self.is_near_zero(self.depth_soil[i][0] - sum(newthickness), 1e-15) if is_soildepth_equal or (self.depth_soil[i][0] > sum(newthickness)): while self.depth_soil[i][0] > sum(newthickness): newthickness.append(self.MAXTHICKNESS) ruralMat.append(self.SOIL) self.soilindex2 = i break self.rural = Element(self.rural.albedo, self.rural.emissivity, newthickness, ruralMat, self.rural.vegCoverage, self.rural.layerTemp[0], self.rural.horizontal, self.rural._name)
class DLWDamage(Damage): """Damage class for the DLW-model. Provides the damages from emissions and mitigation outcomes. Parameters: tree (obj: 'TreeModel'): Provides the tree structure used. bau (obj: 'BusinessAsUsual'): Business-as-usual scenario of emissions. cons_growth (float): Constant consumption growth rate. ghg_levels (ndarray or list): End GHG levels for each end scenario. TODO: * re-write the _recombine_nodes """ def __init__(self, tree, bau, cons_growth, ghg_levels): super(DLWDamage, self).__init__(tree, bau) self.ghg_levels = ghg_levels if isinstance(self.ghg_levels, list): self.ghg_levels = np.array(self.ghg_levels) self.cons_growth = cons_growth self.dnum = len(ghg_levels) self.cum_forcings = None self.d = None self.forcing = None self.damage_coefs = None def _recombine_nodes(self): nperiods = self.tree.num_periods sum_class = np.zeros(nperiods, dtype=int) new_state = np.zeros([nperiods, self.tree.num_final_states], dtype=int) temp_prob = self.tree.final_states_prob.copy() for old_state in range(self.tree.num_final_states): temp = old_state n = nperiods-2 d_class = 0 while n >= 0: if temp >= 2**n: temp -= 2**n d_class += 1 n -= 1 sum_class[d_class] += 1 new_state[d_class, sum_class[d_class]-1] = old_state sum_nodes = np.append(0, sum_class.cumsum()) prob_sum = np.array([self.tree.final_states_prob[sum_nodes[i]:sum_nodes[i+1]].sum() for i in range(len(sum_nodes)-1)]) for period in range(nperiods): for k in range(self.dnum): d_sum = np.zeros(nperiods) old_state = 0 for d_class in range(nperiods): for i in range(sum_class[d_class]): d_sum[d_class] += self.tree.final_states_prob[old_state] * self.d[k, old_state, period] old_state += 1 for d_class in range(nperiods): for i in range(sum_class[d_class]): self.d[k, new_state[d_class, i], period] = d_sum[d_class] / prob_sum[d_class] old_state = 0 for d_class in range(nperiods): for i in range(sum_class[d_class]): self.tree.final_states_prob[new_state[d_class, i]] = temp_prob[old_state] self.tree.node_prob[-len(self.tree.final_states_prob):] = self.tree.final_states_prob for p in range(1,nperiods-1): nodes = self.tree.get_nodes_in_period(p) for node in range(nodes[0], nodes[1]+1): worst_end_state, best_end_state = self.tree.reachable_end_states(node, period=p) self.tree.node_prob[node] = self.tree.final_states_prob[worst_end_state:best_end_state+1].sum() def _damage_interpolation(self): """Create the interpolation coeffiecients used to calculate damages. """ if self.d is None: print("Importing stored damage simulation") self.import_damages() self._recombine_nodes() self.damage_coefs = np.zeros((self.tree.num_final_states, self.tree.num_periods, self.dnum-1, self.dnum)) amat = np.ones((self.tree.num_periods, self.dnum, self.dnum)) bmat = np.ones((self.tree.num_periods, self.dnum)) self.damage_coefs[:, :, -1, -1] = self.d[-1, :, :] self.damage_coefs[:, :, -1, -2] = (self.d[-2, :, :] - self.d[-1, :, :]) / self.emit_pct[-2] amat[:, 0, 0] = 2.0 * self.emit_pct[-2] amat[:, 1:, 0] = self.emit_pct[:-1]**2 amat[:, 1:, 1] = self.emit_pct[:-1] amat[:, 0, -1] = 0.0 for state in range(0, self.tree.num_final_states): bmat[:, 0] = self.damage_coefs[state, :, -1, -2] * self.emit_pct[-2] bmat[:, 1:] = self.d[:-1, state, :].T self.damage_coefs[state, :, 0] = np.linalg.solve(amat, bmat) def import_damages(self, loc="data/simulated_damages.csv"): try: with open(loc, 'r') as f: d = np.loadtxt(f, delimiter=";", comments="#") except IOError as e: import sys print("Could not import simulated damages:\n\t{}".format(e)) sys.exit(0) n = self.tree.num_final_states self.d = np.array([d[n*i:n*(i+1)] for i in range(0, self.dnum)]) def damage_simulation(self, draws, peak_temp, disaster_tail, tip_on, temp_map, temp_dist_params, maxh, cons_growth, save_simulation=True): """Initializion of simulation of damages. Either import stored simulation of damages or simulate new values. Args: import_damages (bool): If program should import already stored values. Default is True. **kwargs: Arguments to initialize DamageSimulation class, in the case of import_damages = False. See DamageSimulation class for more info. """ ds = DamageSimulation(tree=self.tree, ghg_levels=self.ghg_levels, peak_temp=peak_temp, disaster_tail=disaster_tail, tip_on=tip_on, temp_map=temp_map, temp_dist_params=temp_dist_params, maxh=maxh, cons_growth=cons_growth) self.d = ds.simulate(draws) return self.d def _forcing_based_mitigation(self, forcing, period): """Calculation of mitigation based on forcing up to period. Args: forcing (float): Cumulative forcing up to node. period (int): Period of node. Returns: float: Mitigation. """ p = period - 1 if forcing > self.cum_forcings[p][1]: weight_on_sim2 = (self.cum_forcings[p][2] - forcing) / (self.cum_forcings[p][2] - self.cum_forcings[p][1]) weight_on_sim3 = 0 elif forcing > self.cum_forcings[p][0]: weight_on_sim2 = (forcing - self.cum_forcings[p][0]) / (self.cum_forcings[p][1] - self.cum_forcings[p][0]) weight_on_sim3 = (self.cum_forcings[p][1] - forcing) / (self.cum_forcings[p][1] - self.cum_forcings[p][0]) else: weight_on_sim2 = 0 weight_on_sim3 = 1.0 + (self.cum_forcings[p][0] - forcing) / self.cum_forcings[p][0] return weight_on_sim2 * self.emit_pct[1] + weight_on_sim3*self.emit_pct[0] def forcing_init(self, sink_start, forcing_start, ghg_start, partition_interval, forcing_p1, forcing_p2, forcing_p3, absorbtion_p1, absorbtion_p2, lsc_p1, lsc_p2): """Initialize Forcing object and cum_forcings used in calculating the mitigation up to a node. Args: **kwargs: Arguments to initialize Forcing object, see Forcing class for more info. """ bau_emission = self.bau.ghg_end - self.bau.ghg_start self.emit_pct = 1.0 - (self.ghg_levels-self.bau.ghg_start) / bau_emission self.cum_forcings = np.zeros((self.tree.num_periods, self.dnum)) self.forcing = Forcing(self.tree, self.bau, sink_start, forcing_start, ghg_start, partition_interval, forcing_p1, forcing_p2, forcing_p3, absorbtion_p1, absorbtion_p2, lsc_p1, lsc_p2) mitigation = np.ones((self.dnum, self.tree.num_decision_nodes)) * self.emit_pct[:, np.newaxis] path_ghg_levels = np.zeros((self.dnum, self.tree.num_periods+1)) path_ghg_levels[0,:] = self.bau.ghg_start for i in range(0, self.dnum): for n in range(1, self.tree.num_periods+1): node = self.tree.get_node(n, 0) self.cum_forcings[n-1, i] = self.forcing.forcing_at_node(mitigation[i], node, i) def average_mitigation_node(self, m, node, period): """Calculate the average mitigation until node. Args: m (ndarray): Array of mitigation. node (int): The node for which average mitigation is to be calculated for. Returns: float: Average mitigation. """ if period == 0: return 0 period = self.tree.get_period(node) state = self.tree.get_state(node, period) path = self.tree.get_path(node, period) new_m = np.zeros(len(path)-1) for i in range(len(new_m)): new_m[i] = m[path[i]] period_len = self.tree.decision_times[1:period+1] - self.tree.decision_times[:period] bau_emissions = self.bau.emission_by_decisions[:period] total_emission = np.dot(bau_emissions, period_len) ave_mitigation = np.dot(new_m, bau_emissions*period_len) return ave_mitigation / total_emission def average_mitigation(self, m, period): nodes = self.tree.get_num_nodes_period(period) ave_mitigation = np.zeros(nodes) for i in range(nodes): node = self.tree.get_node(period, i) ave_mitigation[i] = self.average_mitigation_node(m, node, period) return ave_mitigation def _damage_function_node(self, m, node): """Calculate the damage at any given node, based on mitigation actions. Args: m (ndarray): Array of mitigation. node (int): The node for which damage is to be calculated for. Returns: float: damage at node. """ if self.damage_coefs is None: self._damage_interpolation() if node == 0: return 0.0 period = self.tree.get_period(node) forcing = self.forcing.forcing_at_node(m, node) force_mitigation = self._forcing_based_mitigation(forcing, period) worst_end_state, best_end_state = self.tree.reachable_end_states(node, period=period) probs = self.tree.final_states_prob[worst_end_state:best_end_state+1] if force_mitigation < self.emit_pct[1]: damage = (probs *(self.damage_coefs[worst_end_state:best_end_state+1, period-1, 1, 1] * force_mitigation \ + self.damage_coefs[worst_end_state:best_end_state+1, period-1, 1, 2])).sum() elif force_mitigation < self.emit_pct[0]: #do dot product instead? damage = (probs * (self.damage_coefs[worst_end_state:best_end_state+1, period-1, 0, 0] * force_mitigation**2 \ + self.damage_coefs[worst_end_state:best_end_state+1, period-1, 0, 1] * force_mitigation \ + self.damage_coefs[worst_end_state:best_end_state+1, period-1, 0, 2])).sum() ######### what's happening here? ############## else: damage = 0.0 i = 0 for state in range(worst_end_state, best_end_state+1): if self.d[0, state, period-1] > 1e-5: deriv = 2.0 * self.damage_coefs[state, period-1, 0, 0]*self.emit_pct[0] \ + self.damage_coefs[state, period-1, 0, 1] decay_scale = deriv / (self.d[0, state, period-1]*np.log(0.5)) dist = force_mitigation - self.emit_pct[0] + np.log(self.d[0, state, period-1]) \ / (np.log(0.5) * decay_scale) damage += probs[i] * (0.5**(decay_scale*dist) * np.exp(-np.square(force_mitigation-self.emit_pct[0])/60.0)) i += 1 return damage / probs.sum() def damage_function(self, m, period): """Calculate the damage for every node in a period, based on mitigation actions. Args: m (ndarray): Array of mitigation. period (int): The period for which damage is to be calculated. Returns: ndarray: Array of damages. """ nodes = self.tree.get_num_nodes_period(period) damages = np.zeros(nodes) for i in range(nodes): node = self.tree.get_node(period, i) damages[i] = self._damage_function_node(m, node) return damages