Example #1
0
    def find_term_structure(self, price, *var_args):
        '''    
          Function called by a zero root finder which is used
          to find the price of a bond that creates equal utility at time 0 as adding .01 to the value of consumption in the final period
          the purpose of this function is to find the interest rate embedded in the EZ Utility model
    
          first calculate the utility with a final payment
        '''
        my_tree = var_args[0]
        my_damage_model = var_args[1]
        my_optimization = var_args[2]
        my_cost_model = var_args[3]
        time_period = var_args[4]
        payment = 0.01

        #        self.my_tree.period_consumption_epsilon[time_period] = 0.
        #        utility_with_payment = fm.utility_function( my_optimization.guess, my_tree, my_damage_model, my_cost_model )
        self.my_tree.period_consumption_epsilon[time_period] = payment
        utility_with_payment = fm.utility_function(my_optimization.guess,
                                                   my_tree, my_damage_model,
                                                   my_cost_model)
        self.my_tree.period_consumption_epsilon[time_period] = 0.
        #        utility_without_payment = fm.utility_function( my_optimization.guess, my_tree, my_damage_model, my_cost_model )
        '''
          then calculate the utility with an initial payment equal to the final payment discounted to today at the target interest_rate
        '''
        self.my_tree.first_period_epsilon = payment * price
        utility_with_initial_payment = fm.utility_function(
            my_optimization.guess, my_tree, my_damage_model, my_cost_model)
        self.my_tree.first_period_epsilon = 0.0
        distance = (utility_with_payment - utility_with_initial_payment)

        return (distance)
Example #2
0
    def find_bec(self, delta_con, *var_args):
        '''    
          Function called by a zero root finder which is used
          to find a value for consumption that equalizes utility at time 0 in two different solutions
    
          first calculate the utility with a base case
        '''
        my_tree = var_args[0]
        my_damage_model = var_args[1]
        my_optimization = var_args[2]
        my_cost_model = var_args[3]
        base_case = my_optimization.guess
        '''    
        base_case_utility = fm.utility_function( base_case, my_tree, my_damage_model, my_cost_model )
          then calculate the utility with the alternative case
          
        alternative_case_utility = fm.utility_function( alternative_case, my_tree, my_damage_model, my_cost_model )

        distance = (alternative_case_utility - base_case_utility)
        '''
        my_tree.first_period_epsilon = 0.0

        base_utility = fm.utility_function(base_case, my_tree, my_damage_model,
                                           my_cost_model)

        my_tree.first_period_epsilon = delta_con

        new_utility = fm.utility_function(base_case, my_tree, my_damage_model,
                                          my_cost_model)

        my_tree.first_period_epsilon = 0.0

        distance = (new_utility -
                    base_utility) - my_optimization.constraint_cost

        return (distance)
Example #3
0
def run_model(tp1=30, tree_analysis=4, tree_final_states=32, damage_peak_temp=11.0, damage_disaster_tail=18.0, draws=50):
    print('These arguments set in batch mode')
    #print('growth rate = ', sys.argv[1]
    # Original 1st parm: print('period_1_years =', sys.argv[1])
    #print('analysis =', sys.argv[2]
    #print('force_simul =', sys.argv[3]
    
    #scenario_df = pd.read_excel('./scenarios.xls')
    #tp1 = scenario_df.parm_value[(scenario_df.parm_name == 'tp1')].values[0]
    '''
      initialize the tree class
    '''
    #self.update_state(state='PROGRESS',
    #                      meta={'current': 10, 'total': 98,
    #                            'status': 'Starting model tree'})    
    # TREE MODEL
    #Orig: my_tree = tree_model(tp1=int(sys.argv[1]))
    my_tree = tree_model(tp1=tp1, analysis=tree_analysis, final_states=tree_final_states)
    #my_tree = tree_model()
    print('tree nodes', my_tree.decision_period_pointer)
    print('horizon times', my_tree.decision_times)
    print('utility periods', my_tree.utility_nperiods)
    print('utility full_tree', my_tree.utility_full_tree)
    print('u_times', my_tree.utility_times)
    print('decision_period?', my_tree.decision_period)
    print('branch_period?', my_tree.information_period)
    print('u_first_node', my_tree.utility_period_pointer)
    print('u_nodes', my_tree.utility_period_nodes)
    print('u_tree_period', my_tree.utility_decision_period)
    
    # The return parameters:
    delta_emissions_gigatons = None
    cost_per_ton = None
    
    #self.update_state(state='PROGRESS',
    #                      meta={'current': 25, 'total': 99,
    #                            'status': 'Finished model tree'})
    '''
      initialize the damage class
    '''
    # DAMAGE CLASS
    my_damage_model = damage_model(my_tree=my_tree, peak_temp=damage_peak_temp, disaster_tail=damage_disaster_tail, draws=draws)
    my_damage_model.damage_function_initialization()
    
    '''
      initialize the cost class
    '''
    # COST CLASS
    my_cost_model = cost_model(tree=my_tree)
    
    print('economic growth', my_tree.growth, 'risk aversion', my_tree.ra, 'elasticity of intertemporal substitution', my_tree.eis)
    
    '''
      initialize the tree and damage function interpolation coefficients
    '''
    my_damage_model.initialize_tree()
    my_damage_model.dfc = my_damage_model.damage_function_interpolation()
    
    '''
      get the initial parameters and output the parameters and initial fit
    '''
    
    #     = optimize_plan(my_tree=my_tree,randomize=float(sys.argv[2]),alt_input=int(sys.argv[3]))
    my_optimization = optimize_plan(my_tree=my_tree)
    if my_tree.nperiods <= 5 :
      my_optimization.get_initial_guess()
    else :
      my_optimization.get_initial_guess6()    
    
    print("initial guess", my_optimization.guess)
    
    base = fm.utility_function( my_optimization.guess, my_tree, my_damage_model, my_cost_model )
    print('initial parameter fit', base)
    
    '''
      numerical derivative  check of the gradient
    '''
    base_grad = fm.analytic_utility_gradient(my_optimization.guess, my_tree, my_damage_model, my_cost_model )
    delta = .00001
    guess = my_optimization.guess
    for p in tqdm(range(0,my_tree.x_dim)):
      guess[p] += delta
      base_plus_p = fm.utility_function( guess, my_tree, my_damage_model, my_cost_model )
      num_deriv = (base_plus_p-base)/delta
      '''
      TODO: Determine if this code block can be commented out for production runs.
      if abs((base_grad[p]-num_deriv)/num_deriv) > .05 :
        print('CHECK GRADIENT: ','p = ', p, 'derivative calculation = ', base_grad[p], 'numerical derivative = ', num_deriv)
      if my_optimization.derivative_check == 1 :
        print('p', p, 'derivative =', base_grad[p], 'numerical derivative', num_deriv)
      '''  
      guess[p] -= delta
      
    # OPTIMIZE WITH SCIPY
    
    '''
       if my_tree.analysis == 1 or 2 then find the unconstrained optimal mitigation plan
       use scipy minimization function fmin_l_bfgs_b to maximize the utility function
       with respect to choices of mitigation
    '''
    
    if my_tree.analysis == 1 or my_tree.analysis == 2 :
      my_optimization.set_constraints(constrain=0)
      res = fmin_l_bfgs_b( fm.utility_function,guess,fprime=fm.analytic_utility_gradient,factr=1.,pgtol=1.0e-5,bounds=(my_optimization.xbounds),maxfun=600,args=([my_tree, my_damage_model, my_cost_model]))
      bestfit = res[1]
      #print('best fit', bestfit)
      bestparams = res[0]
      #print('best parameters', bestparams)
      retparam = res[2]
      #print('gradient', retparam['grad'])
      #print('function calls', retparam['funcalls'])
    else :
      bestfit = base
      bestparams = guess
      
    '''
       if analysis = 1, then the only step is optimization: now print(output to the terminal
    '''
    if my_tree.analysis == 1:
      my_optimization.create_output(bestparams, bestfit, my_damage_model, my_cost_model, my_tree)
    if my_tree.nperiods <= 5 :
      my_optimization.put_optimal_plan(bestparams)
    else :
      my_optimization.put_optimal_plan6(bestparams)
    '''
       if my_tree.analysis = 2 then find the decomposition of the social cost of carbon
       into its risk premium and expected damage components and their decomposition over time
       first step is to save the initial optimized consumption values for all nodes in the utility tree ( in my_tree.d_consumption_by_state[] )
       and the cost component of consumption in decision period 0 (in my_tree.d_cost_by_state), which reflect the costs by node of decision period 0 mitigation
       this is done so that in the next we can increment the mitigation at time 0 and to calculate the marginal changes in consumption and cost
    '''
    if my_tree.analysis == 2:
      for node in tqdm(range(0, my_tree.utility_full_tree)):
        my_tree.d_consumption_by_state[node] = my_tree.consumption_by_state[node]
    
      for sub_period in tqdm(range(0, my_tree.first_period_intervals)):
        potential_consumption = (1.+my_tree.growth)**(my_tree.sub_interval_length * sub_period)
        my_tree.d_cost_by_state[sub_period,0] = potential_consumption * my_tree.cost_by_state[0]
      
      delta_x = .01
      bestparams[0] += delta_x
      '''
          next increment time 0 mitigation and run an optimization in which mitigation at time 0 is constained to = previous optimal mitigation + delta_x
      '''
      my_optimization.set_constraints(constrain=-1, node_0 = bestparams[0])
      guess = bestparams
      res = fmin_l_bfgs_b( fm.utility_function,guess,fprime=fm.analytic_utility_gradient,factr=1.,pgtol=1.0e-5,bounds=(my_optimization.xbounds),maxfun=600,args=([my_tree, my_damage_model, my_cost_model]))
      '''
         now calculate the changes in consumption and the mitigation cost component of consumption per unit change in mitigation in the new optimal plan
      '''
      for node in tqdm(range(0, my_tree.utility_full_tree)):
        my_tree.d_consumption_by_state[node] = (my_tree.consumption_by_state[node]-my_tree.d_consumption_by_state[node])/delta_x
    
      for sub_period in tqdm(range(0, my_tree.first_period_intervals)):
        potential_consumption = (1.+my_tree.growth)**(my_tree.sub_interval_length * sub_period)
        my_tree.d_cost_by_state[sub_period,1] = ( potential_consumption * my_tree.cost_by_state[0] - my_tree.d_cost_by_state[sub_period,0] )/delta_x
      bestparams[0] -= delta_x
      base = fm.utility_function( bestparams, my_tree, my_damage_model, my_cost_model )
      '''
         create the output, including the decomposition of SCC into the time paths of the net present value contributions from expected damage and risk premium components
      '''
    
      '''
         this section of code addresses the question: what is the cost of waiting to start mitigation untilt the end of the first period
         if my_tree.analysis == 3, then calculate the marginal cost of carbon when mitigation is delayed for the first period
         if my_tree.analysis == 4, then calculate the total consumption-equivalent cost of delay (relative to an optimal plan) for the first period
      '''
    if my_tree.analysis >= 3:
      '''
            analysis == 3 calculates the marginal value of reducing emissions by 1 ton
            when the initial endowment has already been incremented by the payment equal to "lump_sum"
            base_x is the starting level of mitigation
            delta_x is used to calculate a numerical derivative of utility as a function of a marginal percentage
              increase in emissions reduction today while reoptimizing emissions reductions at all decision nodes in
              the future
            increment is the amount to increase base_x in each run
            the ipass loop increments base_x and repeats the analysis
      '''
      base_x = 0.0
      delta_x = .01
      increment = .025
      for ipass in range(0, 1):
        '''
          first step is to calculate the optimal plan when mitigation is constrained to base_x for the first period
        '''
        lump_sum = .0
        my_tree.first_period_epsilon = lump_sum
        my_optimization.set_constraints(constrain=1, node_0 = base_x, node_1 = base_x, node_2 = base_x)
        res = fmin_l_bfgs_b( fm.utility_function,guess,fprime=fm.analytic_utility_gradient,factr=1.,pgtol=1.0e-5,bounds=(my_optimization.xbounds),maxfun=600,args=([my_tree, my_damage_model, my_cost_model]))
        '''
          save the parameters for the run with mitigation = base_x in baseparams
          save the utility value in basefit
          save the cost of emissions reductions when mitigation = base_x in marginal_cost
          use the optimal mitigation parameters as the starting point for an optiization with first period
            emissions reduction constrained to be base_x + delta_x
        '''
        baseparams = res[0]
        basefit = res[1]
        marginal_cost = my_cost_model.price_by_state( baseparams[0],0.,0.)
        newparams = baseparams
        '''
           create output for the optimal plan with current mitigation constrained to equal base_x (the optimal plan subject to no action in period 0)
        '''
        my_optimization.create_output(baseparams, basefit, my_damage_model, my_cost_model, my_tree)
        '''
           next calculate the optimal plan 
           when my_tree.analysis = 3 the "optimal" plan is constrained so that current mitigation is equal to
             base_x + delta_x
           when my_tree.analysis = 4 the current mitigation is indeed from the unconstrained optimal plan
        '''
        if my_tree.analysis == 3 :
          #print('base_x', base_x, 'delta_x', delta_x)
          newparams[0] += delta_x
          my_tree.first_period_epsilon = lump_sum
          my_optimization.set_constraints(constrain=1, node_0 = newparams[0], node_1 = newparams[1], node_2 = newparams[2])
          res = fmin_l_bfgs_b( fm.utility_function,newparams,fprime=fm.analytic_utility_gradient,factr=1.,pgtol=1.0e-5,bounds=(my_optimization.xbounds),maxfun=600,args=([my_tree, my_damage_model, my_cost_model]))
        else :
          my_optimization.set_constraints(constrain=0)
          res = fmin_l_bfgs_b( fm.utility_function,guess,fprime=fm.analytic_utility_gradient,factr=1.,pgtol=1.0e-5,bounds=(my_optimization.xbounds),maxfun=600,args=([my_tree, my_damage_model, my_cost_model]))
        '''
          save the parameters for the run with mitigation = base_x + delta_x in newparams
          save the utility value in newfit
        '''
    
        newparams = res[0]
        newfit = res[1]
    
        '''
         create output for the new optimal plan with either a marginal, or a fully optimal mitigation at time 0
        '''
        my_optimization.create_output(newparams, newfit, my_damage_model, my_cost_model, my_tree)
    
        '''
         delta_util_x is the change in utility from base_x to base_x + delta_x
        '''
        delta_util_x = newfit - basefit
      
        #print('delta_util_x', delta_util_x)
    
        if my_tree.analysis == 3:
          '''
            calculate the change in utility for a marginal percentage change in consumption starting from
              the mitigation plan with time 0 mitigation = base_x
            the utility_funtion calculates utility for a given mitigation plan, but if my_tree.first_period_epsilon
              is non-zero, adds the value of first_period_epsilon to first period consumption without changing any
              other consumption value
            the marginal impact on utility of a marginal increase in consumption is saved in delta_util_c
          '''
          delta_con = .01
          my_tree.first_period_epsilon = lump_sum
          my_tree.first_period_epsilon += delta_con
          baseparams[0]= base_x
          #print('epsilon', my_tree.first_period_epsilon)
          util_given_delta_con = fm.utility_function( baseparams, my_tree, my_damage_model, my_cost_model )
          delta_util_c = util_given_delta_con - basefit
          my_tree.first_period_epsilon = 0.0
          #print('basefit', basefit, 'newfit', newfit, 'util_given_delta', util_given_delta_con)
          #print('delta_con', delta_con)
          #print('delta_util_c', delta_util_c)
        else:
          delta_x = newparams[0]
          #print('delta_x', delta_x)
          # BRENTQ OPTIMIZATION
          '''
           my_optimization.constraint_cost is the utility cost of constraining first period mitigation to zero
          '''
          my_optimization.constraint_cost = newfit-basefit
          for miti in range(0, my_tree.x_dim):
            my_optimization.guess[miti] = baseparams[miti]
          '''
           when my_tree.analysis = 4, calculate deadweight loss by using the root finder routine to find a change in consumption
           that increases the zero mitigation constrained optimization utility to equal that of the unconstrained plan plus a lump sum
           that is, find delta_con such that [ delta_utility - my_optimization.constraint_cost] = 0
           where delta_utility = util(constrained plan, consumption[today] + delta_con)-util(unconstrained plan, consumption[today])
          '''
          delta_con = brentq( my_optimization.find_bec, -.1, .99, args=( my_tree, my_damage_model, my_optimization, my_cost_model))
          #print('delta consumption to match unconstrained optimal plan', delta_con)
        cost_per_ton = my_cost_model.consperton0
        if my_tree.analysis == 3:
          #print('Marginal cost of emissions reduction at x = ', base_x, 'is', marginal_cost)
          '''
            my_cost_model.consperton0 is consumption in $ today per ton of emissions
            so the marginal benefit is the slope of the utility function wrt x / slope of the utility function wrt c * ($ consumption / ton of emissions)
          '''
          #print('Marginal benefit emissions reduction is', (delta_util_x / delta_util_c ) * delta_con * my_cost_model.consperton0 / delta_x)
          base_x += increment
        else :
          #print('delta_consumption_billions', delta_con * my_cost_model.consperton0 * my_tree.bau_emit_level[0])
          delta_emissions_gigatons =  delta_x * my_tree.bau_emit_level[0]
          #print('delta_emissions_gigatons', delta_emissions_gigatons)
          deadweight = delta_con * my_cost_model.consperton0 / delta_x
          #print('Deadweight $ loss of consumption per year per ton of mitigation of not pricing carbon in period 0', deadweight)
    '''
      calculate the marginal utility at time 0 of state contingent increases in consumption at each node
    '''
    #print(' marginal utility at time 0 of state contingent increases in consumption at each node ')
    delta_con = .01
    base_util = fm.utility_function( bestparams, my_tree, my_damage_model, my_cost_model )
    #print(' base utility ', base_util)

    for time_period in range(0, my_tree.utility_nperiods-1):
      is_tree_node = my_tree.decision_period[ time_period]
      tree_period = my_tree.utility_decision_period[time_period]
      tree_node = my_tree.decision_period_pointer[ tree_period ]
      if is_tree_node == 1:
        first_u_node = my_tree.utility_period_pointer[time_period]
        for period_node in tqdm(range(0, my_tree.utility_period_nodes[time_period])):
          my_tree.node_consumption_epsilon[first_u_node+period_node] = delta_con
          new_util = fm.utility_function( bestparams, my_tree, my_damage_model, my_cost_model )
          my_tree.node_consumption_epsilon[first_u_node+period_node] = 0.0
          marginal_utility = (base_util - new_util) / delta_con
          #r[tree_period] = {'year':2015+my_tree.utility_times[time_period]}
          #print(' period ', tree_period, ' year ', 2015+my_tree.utility_times[time_period], ' node ', tree_node+period_node, ' utility', new_util, ' marginal_utility ', marginal_utility  )
          
    return cost_per_ton, delta_emissions_gigatons
    '''
Example #4
0
    def create_output(self, best_mitigation_plan, best_fit, my_damage_model,
                      my_cost_model, my_tree):
        '''   writes the output of the optimization to the console
        Parameters
        ----------

        best_mitigation_plan : float
            vector of optimal degrees of mitigation

        best_fit : float
            the value of the utility function at the best_mitigation_plan

        my_damage_model : damage_class object
            the damage model used in the optimization

        my_cost_model : cost_class object
            the cost model used in the optimization

        '''
        '''
        TODO: Determine if these print statements are required for production runs.
        if my_tree.analysis >= 1 :
            if my_tree.print_options[0] == 1:
                print('Print_Option[0] Maximized_utility_=', -best_fit)
        if my_tree.analysis >= 1 :
            if my_tree.print_options[1] == 1:
                print('Print_Option[1] Optimized_mitigation_plan_=')
                lines = int(my_tree.x_dim/6)-1
                for line in range(0, lines) :
                    for i in range(0,5) :
                        print(best_mitigation_plan[line*6+i]) #,
                    print(best_mitigation_plan[line*6+5])
                for i in range(lines*6,my_tree.x_dim-1) :
                        print(best_mitigation_plan[i]) #,
                print(best_mitigation_plan[my_tree.x_dim-1])
        '''
        price = my_cost_model.price_by_state(best_mitigation_plan[0], 0., 0.)

        if my_tree.analysis >= 1:
            if my_tree.print_options[2] == 1:
                log.log_it('Print_Option[2] Social_Cost_of_Carbon_=%f' % price)

        if my_tree.print_options[3] == 1:
            log.log_it("dlw_optimize: my_tree.print_options[3] == 1")
            emissions_to_bau = my_tree.emissions_to_ghg[
                my_tree.nperiods -
                1] / my_tree.emissions_per_period[my_tree.nperiods - 1]
            bau_path = 400
            for p in tqdm(range(0, my_tree.nperiods)):
                ave_price = 0.
                first_node = my_tree.decision_period_pointer[p]
                this_period_time = my_tree.decision_times[
                    p + 1] - my_tree.decision_times[p]
                for n in range(0, my_tree.decision_nodes[p]):
                    average_mitigation = best_mitigation_plan[
                        0] * my_tree.decision_times[1]
                    for pp in range(1, p):
                        j = int(n / 2**(p - pp))
                        average_mitigation += best_mitigation_plan[
                            my_tree.node_map[pp - 1][my_tree.node_mapping[
                                pp -
                                1][j][0]]] * (my_tree.decision_times[pp + 1] -
                                              my_tree.decision_times[pp])
                    price = my_cost_model.g * my_cost_model.a * best_mitigation_plan[
                        first_node + n]**(my_cost_model.a - 1.)
                    if p == 0:
                        consump = 1.
                        average_mitigation = 0.
                    else:
                        consump = self.my_tree.potential_consumption[p]
                        average_mitigation = average_mitigation / my_tree.decision_times[
                            p]
                        price = my_cost_model.price_by_state(
                            best_mitigation_plan[first_node + n],
                            average_mitigation, my_tree.decision_times[p])
                    ave_price += my_tree.node_probs[first_node + n] * price
                    average_mitigation = my_tree.ave_mitigation[first_node + n]
                    average_emissions = my_tree.additional_emissions_by_state[
                        first_node + n] / (this_period_time * emissions_to_bau)
                    #print('Print_Option[3] Period', p, 'time', int(2015+my_tree.decision_times[p]), 'node', first_node+n, 'has_prob', my_tree.node_probs[first_node+n], 'Emission_mitigation_of', best_mitigation_plan[first_node+n], 'Price', price, 'Consumption', consump*(1.-my_tree.damage_by_state[first_node+n])*(1.-my_tree.cost_by_state[first_node+n]),'Average_mitigation', average_mitigation, 'Cost', my_tree.cost_by_state[first_node+n], 'Damage', my_tree.damage_by_state[first_node+n],' GHG_level_in_state', my_tree.ghg_by_state[first_node+n], 'Average_annual_emissions_in_state', average_emissions)
                bau_path += emissions_to_bau * my_tree.emissions_per_period[p]
                #print('Print_Option[3] Period', p, 'time', int(2015+my_tree.decision_times[p]), 'Average_price', ave_price, 'BAU_average_annual_emissions_in_period', my_tree.emissions_per_period[p]/this_period_time, 'end_of_period_bau_ghg_level', bau_path)

            #print('Print_Option[3] Final_period_consumption_and_damage')
            '''
            TODO: Determine if these print statements are required for production runs.
            for state in range(0, my_tree.final_states):
                print('Print_Option[3] Period', my_tree.nperiods, 'time', int(2015+my_tree.decision_times[my_tree.nperiods]), 'final_state', state, 'consumption', my_tree.potential_consumption[my_tree.nperiods]*(1.-my_tree.final_damage_by_state[state]), 'forward_damage', my_tree.final_damage_by_state[state])
            '''
        ''' use root finder and the function "find_term_structure" to find the bond price (and yield) that reflects the value of a fixed $1 payment in all nodes at time np '''
        from scipy.optimize import brentq
        np = my_tree.utility_nperiods - 2
        utility = fm.utility_function(best_mitigation_plan, self.my_tree,
                                      my_damage_model, my_cost_model)
        res = brentq(self.find_term_structure,
                     0.,
                     .9999,
                     args=(my_tree, my_damage_model, self, my_cost_model, np))
        res = max(.00000000001, res)
        my_tree.discount_prices[np] = res
        years_to_maturity = my_tree.utility_times[np]

        if my_tree.print_options[4] == 1:
            log.log_it("dlw_optimize: my_tree.print_options[4] == 1")
            log.log_it(
                'Print_Option[4] Zero_coupon_bond_maturing_at_time %i has_price_=%f and_yield_=%f'
                % ((2015 + 5 * np), res, 100. *
                   (1. / (res**(1. / years_to_maturity)) - 1.)))
        '''
            output for the decomposition of SCC into expected damage and risk premium
        '''
        # TODO: Determine if/how 'utility' and 'base_grad' should be used.
        utility = fm.utility_function(best_mitigation_plan, self.my_tree,
                                      my_damage_model, my_cost_model)
        base_grad = fm.analytic_utility_gradient(best_mitigation_plan,
                                                 self.my_tree, my_damage_model,
                                                 my_cost_model)
        d_cost_sum = 0.0
        discounted_expected_damages = 0.0
        net_discounted_expected_damages = 0.0
        risk_premium = 0.0

        if my_tree.analysis == 2:
            consumption_cost = my_tree.d_consumption_by_state[0]
            if my_tree.print_options[5] == 1:
                log.log_it('Print_Option[5] Period_0_delta_consumption: %f' %
                           consumption_cost)
            if my_tree.print_options[6] == 1:
                log.log_it(
                    'Print_Option[6] Period_0_marginal_utility_wrt_c(0): %i  Period_0_marginal_utility_wrt_c(node1)_up_node: %i  Period_0_marginal_utility_wrt_c(node2)_down_node: %i'
                    % (my_tree.marginal_utility_by_state[0][0],
                       my_tree.marginal_utility_by_state[0][1],
                       my_tree.marginal_utility_by_state[0][2]))
            '''
                in this loop calculate and print the expected damage and the risk premium term at each point in time
            '''
            my_tree.sdf_in_tree[0] = 1.0

            log.log_it("dlw_optimize: my_tree.analysis == 2")
            for time_period in tqdm(range(1, self.my_tree.utility_nperiods)):
                '''  for a given time_period in the utility_tree, tree_node points to the first node in the period of the last decision in the decision tree'''
                tree_node = my_tree.decision_period_pointer[min(
                    my_tree.nperiods - 1,
                    my_tree.utility_decision_period[time_period - 1] + 1)]
                ''' first_node points to the first node in period time_period of the utility tree '''
                first_node = my_tree.utility_period_pointer[time_period]
                expected_damages = 0.0
                expected_sdf = 0.0
                cross_product_sdf_damages = 0.0
                '''
                    Now loop over all the nodes in the utility tree in period time_period
                    find the expected damages, the expected Stochastic Discount Factor, and the Covariance term
                '''
                for period_node in range(
                        0, my_tree.utility_period_nodes[time_period]):
                    '''
                        d_consumption_by_state holds the change in consumption per unit change in emissions mitigation
                        in all periods except the first this "damage_in_node" equals the damage in the node caused by additional emissions
                        note however, in the first period this change also reflects the change in cost, the d_cost term, from reducing the mitigation
                        calculate the expected damage by probability weighting the damage in each node
                    '''
                    damage_in_node = (
                        my_tree.d_consumption_by_state[first_node +
                                                       period_node])
                    expected_damages += damage_in_node * my_tree.node_probs[
                        tree_node + period_node]
                    '''
                    TODO: Determine if these print statements are required for production runs.
                    if time_period <= my_tree.print_options[7] :
                        # if this is a risk decomposition and utility sub-interval output is desired.
                        print('Print_Option[7] Period', time_period, 'node', first_node+period_node, 'delta_consumption', my_tree.d_consumption_by_state[first_node+period_node], ' consumption_level', my_tree.consumption_by_state[first_node+period_node])
                    '''
                    # from_node is the node from the previous period that leads to period_node.
                    if my_tree.information_period[time_period - 1] == 1:
                        from_node = my_tree.utility_period_pointer[
                            time_period - 1] + int(period_node / 2)
                    else:
                        from_node = my_tree.utility_period_pointer[
                            time_period - 1] + period_node

                    if my_tree.information_period[time_period - 1] == 1:
                        '''
                            information_period = 1 when there is a branch in the tree
                            first if (even values of period_node) considers upper branch nodes, second if considers lower branch nodes
                        '''
                        if int(period_node / 2) * 2 == period_node:
                            ''' total_prob is the sum of the probabilities in the up and down nodes reached from "from_node" '''
                            total_prob = my_tree.node_probs[
                                tree_node + period_node] + my_tree.node_probs[
                                    tree_node + period_node + 1]
                            ''' sdf is the stochastic discount factor required to discount consumption in period_node back to from_node '''
                            sdf = (
                                total_prob /
                                my_tree.node_probs[tree_node + period_node]
                            ) * my_tree.marginal_utility_by_state[from_node][
                                1] / my_tree.marginal_utility_by_state[
                                    from_node][0]
                            '''
                            TODO: Determine if these print statements are required for production runs.
                            if time_period <= my_tree.print_options[7] :
                                print('Print_Option[7] Branch_from_node', from_node, 'MU_wrt_c(t)', my_tree.marginal_utility_by_state[from_node][0])
                                print('Print_Option[7] Node:', tree_node+period_node,'SDF',sdf,'MU_wrt_c(t+1)_in_node', my_tree.marginal_utility_by_state[from_node][1])
                                print('Print_Option[7] Probs:_up_node', tree_node+period_node, 'down_node', tree_node+period_node+1, 'probs', my_tree.node_probs[tree_node+period_node], my_tree.node_probs[tree_node+period_node+1])
                            '''
                        else:
                            total_prob = my_tree.node_probs[
                                tree_node + period_node] + my_tree.node_probs[
                                    tree_node + period_node - 1]
                            sdf = (
                                total_prob /
                                my_tree.node_probs[tree_node + period_node]
                            ) * my_tree.marginal_utility_by_state[from_node][
                                2] / my_tree.marginal_utility_by_state[
                                    from_node][0]
                            '''
                            TODO: Determine if these print statements are required for production runs.
                            if time_period <= my_tree.print_options[7] :
                                print('Print_Option[7] Branch_from_node', from_node, 'MU_wrt_c(t)', my_tree.marginal_utility_by_state[from_node][0])
                                print('Print_Option[7] Node:', tree_node+period_node, 'SDF', sdf, 'MU_wrt_c(t+1)_in_node', my_tree.marginal_utility_by_state[from_node][2], 'MU_wrt_c(t)', my_tree.marginal_utility_by_state[from_node][0])
                                print('Print_Option[7] Probs:_down_node', tree_node+period_node, 'up_node', tree_node+period_node-1, 'probs', my_tree.node_probs[tree_node+period_node], my_tree.node_probs[tree_node+period_node-1])
                            '''
                    else:
                        # if no branching occurs this period then the probability of reaching period_node is 1
                        sdf = my_tree.marginal_utility_by_state[from_node][
                            1] / my_tree.marginal_utility_by_state[from_node][0]
                        mu_next = my_tree.marginal_utility_by_state[from_node][
                            1]
                        # in the final period the marginal utility is with respect to the steady-state continuation value
                        if time_period == my_tree.utility_nperiods - 1:
                            sdf = my_tree.final_total_derivative_term[
                                period_node] / my_tree.marginal_utility_by_state[
                                    from_node][0]
                            mu_next = my_tree.final_total_derivative_term[
                                period_node]
                        '''
                        TODO: Determine if these print statements are required for production runs.
                        if time_period <= my_tree.print_options[7] :
                            print('Print_Option[7] No_branch_from_node', from_node, 'SDF', sdf, 'MU_wrt_c(t)', my_tree.marginal_utility_by_state[from_node][0], 'MU_wrt_c(t+1)_in_next_node', mu_next)
                        '''
                    # sdf_in_tree is the discount factor used to present value consumption in node period_node (at time time_period)
                    my_tree.sdf_in_tree[
                        first_node +
                        period_node] = my_tree.sdf_in_tree[from_node] * sdf
                    # the expected_sdf is the probability weighted discount factors in the nodes at time time_period
                    expected_sdf += my_tree.sdf_in_tree[
                        first_node +
                        period_node] * my_tree.node_probs[tree_node +
                                                          period_node]
                    # cross_product_sdf_damages is the expected cross_product of sdf's and damages at time time_period
                    cross_product_sdf_damages += my_tree.sdf_in_tree[
                        first_node +
                        period_node] * damage_in_node * my_tree.node_probs[
                            tree_node + period_node]
                # store the expected sdf, which is the present value at time 0 of $1 received at time time_period.
                my_tree.discount_prices[time_period] = expected_sdf
                # cov_term is the cross_product minus the product of the expected SDF and the expected damage at time time_period.
                cov_term = cross_product_sdf_damages - expected_sdf * expected_damages
                ''' now calculated the components of net discounted damage and the risk premium which arise in period time_period per $ spent on mitigation at time 0 (consumption_cost) '''
                ''' if sub_interval time_period occurs during the first decision period we need to account for the impact of the cost of increased marginal mitigation
                    on consumption during this interval in order to be left with only the marginal damage impact on consumption '''
                if my_tree.utility_decision_period[time_period] == 0:
                    net_discounted_damage = -(
                        expected_damages + my_tree.d_cost_by_state[
                            time_period, 1]) * expected_sdf / consumption_cost
                    '''
                    TODO: Determine if these print statements are required for production runs.
                    if my_tree.print_options[10] == 1 :
                        print('Print_Option[10] Period', time_period, 'expected_damages', -expected_damages/consumption_cost, 'discount_price', expected_sdf, 'cross_product', -cross_product_sdf_damages/consumption_cost, 'cov_term', -cov_term/consumption_cost, 'net_discounted_damage', net_discounted_damage,'d_cost', -my_tree.d_cost_by_state[time_period,1]/consumption_cost)
                    '''
                    # sum the present value of the costs of mitigation throughout the first decision period per $ spent on mitigation at time 0
                    d_cost_sum += -my_tree.d_cost_by_state[
                        time_period, 1] * expected_sdf / consumption_cost
                else:
                    net_discounted_damage = -expected_damages * expected_sdf / consumption_cost
                    '''
                    TODO: Determine if these print statements are required for production runs.
                    if my_tree.print_options[10] == 1 :
                        print('Print_Option[10] Period', time_period, 'expected_damages', -expected_damages/consumption_cost, 'discount_price', expected_sdf, 'cross_product', -cross_product_sdf_damages/consumption_cost, 'cov_term', -cov_term/consumption_cost, 'net_discounted_damage', net_discounted_damage)
                    '''
                my_tree.net_expected_damages[
                    time_period] = net_discounted_damage
                my_tree.risk_premium[
                    time_period] = -cov_term / consumption_cost
                ''' sum (over time) the components of the present value of expected damages and the risk premium'''
                discounted_expected_damages += -expected_damages * expected_sdf / consumption_cost
                ''' the net discounted damages nets out the cost associated with increasing mitigation throughout the first period '''
                net_discounted_expected_damages += net_discounted_damage
                risk_premium += -cov_term / consumption_cost
        ''' if desired, print out the SDF's and Marginal Utilities for the decision tree nodes'''
        for p in range(0, self.my_tree.nperiods):
            first_node = self.my_tree.decision_period_pointer[p]
            for n in range(0, self.my_tree.decision_nodes[p]):
                ''' no branching in the last period '''
                if p == self.my_tree.nperiods-1 :
                    '''
                    TODO: Determine if these print statements are required for production runs.
                    if my_tree.print_options[6] == 1 :
                        print('Print_Option[6] Period', p,'time', int(2015+my_tree.decision_times[p]), 'node', first_node+n, 'SDF=', my_tree.marginal_utility_in_tree[first_node+n,1] / my_tree.marginal_utility_in_tree[ first_node+n, 0 ]) #,
                        print('Print_Option[6] Marginal_utility(c(t)) ', my_tree.marginal_utility_in_tree[first_node+n, 0], 'Marginal_utility(c(t+1))', self.my_tree.marginal_utility_in_tree[first_node+n, 1])
                    '''
                else:
                    if my_tree.print_options[6] == 1:
                        to_node = self.my_tree.next_node[first_node + n][0]
                        prob_up = self.my_tree.node_probs[to_node]
                        prob_down = self.my_tree.node_probs[to_node + 1]
                        total_prob = prob_up + prob_down
                        '''
                        TODO: Determine if these print statements are required for production runs.
                        print('Print_Option[6] Period', p, 'time', int(2015+my_tree.decision_times[p]), 'node', first_node+n, 'SDF_up=  ', (total_prob/prob_up) * self.my_tree.marginal_utility_in_tree[first_node+n,1] / self.my_tree.marginal_utility_in_tree[ first_node+n, 0 ]) #,
                        print('Print_Option[6] SDF_down=', (total_prob/prob_down) * self.my_tree.marginal_utility_in_tree[first_node+n,2] / self.my_tree.marginal_utility_in_tree[ first_node+n, 0 ], 'MU ', self.my_tree.marginal_utility_in_tree[first_node+n, 0], 'Marginal_utility(c(t+1))_up ', self.my_tree.marginal_utility_in_tree[first_node+n, 1]) #,
                        print('Print_Option[6] Marginal_utility(c(t+1))_down ', self.my_tree.marginal_utility_in_tree[first_node+n, 2])
                        '''
        ''' if desired, print out the levels of GHG in each node '''
        if my_tree.print_options[8] == 1:
            for p in range(0, my_tree.nperiods):
                first_node = my_tree.decision_period_pointer[p]
                '''
                TODO: Determine if these print statements are required for production runs.
                for n in range(0, my_tree.decision_nodes[p]):
                    print('Print_Option[8] Period', p, 'time', int(2015+my_tree.decision_times[p]), 'node', first_node+n, 'GHG_level=', my_tree.ghg_by_state[first_node+n])
                '''

            first_node = my_tree.x_dim
            '''
            TODO: Determine if these print statements are required for production runs.
            for n in range(0, my_tree.final_states):
                print('Print_Option[8] Period', my_tree.nperiods, 'time', int(2015+my_tree.decision_times[my_tree.nperiods]), 'node', first_node+n, 'GHG_level= ', my_tree.ghg_by_state[first_node+n])
            '''
        if my_tree.analysis == 2:
            total = net_discounted_expected_damages + risk_premium
            #  decompose the Social Cost of Carbon into the expected damages component versus the risk premium.
            '''
            TODO: Determine if these print statements are required for production runs.
            print('Social_cost_of_carbon', my_cost_model.price_by_state( best_mitigation_plan[0],0.,0.))
            print('Discounted_expected_damages', (net_discounted_expected_damages/total) * my_cost_model.price_by_state( best_mitigation_plan[0],0.,0.))
            print('Risk_premium', (risk_premium/total) * my_cost_model.price_by_state( best_mitigation_plan[0],0.,0.))
            '''
            # print the decomposition of expected damages and risk premium over time.
            if my_tree.print_options[9] == 1:
                damage_scale = my_cost_model.price_by_state(
                    best_mitigation_plan[0], 0.,
                    0.) / (net_discounted_expected_damages + risk_premium)
                for np in range(1, my_tree.utility_nperiods):
                    my_tree.net_expected_damages[np] *= damage_scale
                    my_tree.risk_premium[np] *= damage_scale
                    # TODO: Determine if these print statements are required for production runs.
                    #print('Print_Option[9] Period', np, 'Year', 2015+np*my_tree.sub_interval_length, 'Net_discounted_expected_damage', my_tree.net_expected_damages[np], 'Risk_premium', my_tree.risk_premium[np])
        ''' if desired, print out the yield curve '''
        if my_tree.print_options[4] == 1:
            if my_tree.analysis == 2:
                for np in range(1, my_tree.utility_nperiods - 1):
                    years_to_maturity = self.my_tree.utility_times[np]
                    # TODO: Determine if these print statements are required for production runs.
                    #print('Print_Option[4] Period', np, 'years-to-maturity', years_to_maturity, 'price of bond', self.my_tree.discount_prices[np], ' yield ', 100. * (1./(self.my_tree.discount_prices[np]**(1./years_to_maturity))-1.))
                ''' find the yield on a perpetuity that begins paying at the time of the steady state continuation term '''
                np = my_tree.utility_nperiods - 1
                years_to_maturity = self.my_tree.utility_times[np]
                perp_yield = brentq(self.perpetuity_yield,
                                    0.1,
                                    10.,
                                    args=(np * 5,
                                          self.my_tree.discount_prices[np]))
                # TODO: Determine if these print statements are required for production runs.
                #print('Print_Option[4] Period', my_tree.utility_nperiods-1, 'years-to-maturity', years_to_maturity, 'price of bond', self.my_tree.discount_prices[np], ' yield ', perp_yield)
        return price