Beispiel #1
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)
Beispiel #2
0
    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)
Beispiel #3
0
    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()
Beispiel #4
0
    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)
Beispiel #5
0
 def _ghg_level_node(self, m, node):
     return Forcing.ghg_level_at_node(m, node, self.tree, self.bau,
                                      self.subinterval_len)
Beispiel #6
0
    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)
Beispiel #7
0
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