Exemple #1
0
 def __init__(self, scenario_id, api_run=False):
     self.scenario_id = scenario_id
     self.scenario = Scenario(self.scenario_id)
     self.api_run = api_run
     self.outputs = Output()
     self.demand = Demand(self.scenario)
     self.supply = None
     self.demand_solved, self.supply_solved = False, False
    def __init__(self):
        super().__init__([pin_number1, pin_number2])
        self.demand = Demand(mode='', event='key', pulse_time=0.0001)
        self.user_methodd = [user_method1, user_method2]
        self.handler = [handler1, handler2]

        # key指定し、self.sensor_valueにkeyと同じ値が入ったとき
        # user_methodを逐次的に呼び出していく
        super().add_event_handler(key, handler)
Exemple #3
0
 def configure_energy_system(self):
     print 'configuring energy system'
     self.demand = Demand(self.cfgfile_path,
                          self.custom_pint_definitions_path)
     self.supply = Supply(os.path.join(os.getcwd(),
                                       'outputs'), self.cfgfile_path,
                          self.custom_pint_definitions_path)
     self.configure_demand()
     self.configure_supply()
 def __init__(self):
     self.demand = Demand(mode='double', edge=1, pulse_time=0.00001)
     super().__init__([20, 21])
     self.user_methods = [self.sensor_method, self.user_method1]
     self.event_handler.add_event_handler(20, self.user_method2)
     self.pulse_start = 0
     self.pulse_end = 0
     self.pulse_duration = 0
     self.distance = 0
Exemple #5
0
 def __init__(self, scenario_id, api_run=False):
     self.scenario_id = scenario_id
     self.api_run = api_run
     self.scenario = cfg.scenario_dict[self.scenario_id]
     self.demand_case_id = util.sql_read_table('Scenarios', 'demand_case', id=self.scenario_id)
     self.supply_case_id = util.sql_read_table('Scenarios', 'supply_case', id=self.scenario_id)
     self.outputs = Output()
     self.demand = Demand()
     self.supply = None
     self.demand_solved, self.supply_solved = False, False
Exemple #6
0
    def _GetSpfPathList(self, source: Node, target: Node, demand: int,
                        demand_obj: Demand, G):
        """
        Used to create the path list , it's called by the new GetSpfPath that solves
        the issue with unequal load balancing when >3 paths
        """
        """
        G = nx.MultiDiGraph()
        # node_list = [node for node in self.get_nodes() if not node._failed]
        node_list = self.get_nodes()
        for node in node_list:
            for interface in node.interfaces:
                if not interface._failed:
                    G.add_edge(
                        node, interface.target, **interface._networkX(), data=interface
                    )
        G.add_nodes_from(node_list)
        """
        paths = list(nx.all_shortest_paths(G, source, target, weight="metric"))
        num_ecmp_paths = len(paths)
        demand_path = demand / num_ecmp_paths
        if paths:
            demand_obj.demand_path = []
        for p in paths:
            total_metric = 0
            total_latency = 0
            u = p[0]
            for v in p[1:]:
                ecmp_max_latency = []
                values_u_v = G[u][v].values()
                min_weight = min(d["metric"] for d in values_u_v)
                total_metric = total_metric + min_weight
                # delete old path list

                ecmp_links = [
                    k for k, d in G[u][v].items() if d["metric"] == min_weight
                ]
                num_ecmp_links = len(ecmp_links)
                for d in ecmp_links:
                    ecmp_max_latency.append(G[u][v][d]["data"].latency)
                    demand_obj.demand_path.append([
                        u.label,
                        v.label,
                        min_weight,
                        G[u][v][d]["data"].latency,
                    ])
                total_latency = total_latency + max(ecmp_max_latency)
                u = v
            demand_obj.demand_path.append([
                "-", "-", "Total:" + str(total_metric),
                "Total:" + str(total_latency)
            ])
            demand_obj.total_latency = total_latency
            demand_obj.total_metric = total_metric
 def __init__(self,
              db_path,
              cfgfile_path,
              custom_pint_definitions_path=None,
              name=None,
              author=None):
     self.model_config(db_path, cfgfile_path, custom_pint_definitions_path)
     self.name = cfg.cfgfile.get('case',
                                 'scenario') if name is None else name
     self.author = cfg.cfgfile.get('case',
                                   'author') if author is None else author
     self.demand = Demand()
     self.supply = Supply()
def calc_eoq(row):
    cost = row['cost']
    demand = row['demand']

    rc = RawCosts(unit_cost=cost, ordering_cost=c_t, holding_rate=h)
    d = Demand(quantity=demand)
    return EOQ.optimal_order_quantity(rc, d)
Exemple #9
0
def qq_06():
    c_t = 525
    c_e = 1.25
    rc = RawCosts(unit_cost=1.00,
                  ordering_cost=c_t,
                  holding_cost=c_e,
                  holding_time_unit='M')
    d = Demand(quantity=120, time_unit='M')

    Q = EOQ.optimal_order_quantity(raw_costs=rc, demand=d)
    print("EOQ: {}".format(Q))

    B = 170
    L = 1

    mu_dl = 30
    sigma_dl = 9

    inner_value = (B * d.quantity) / (c_e * sigma_dl * Q * sqrt(2 * pi))
    print("inner value: {}".format(inner_value))

    k = sqrt(2 * log(inner_value))
    print("k: {}".format(k))

    s = mu_dl + k * sigma_dl
    print("s: {}".format(s))
 def __init__(self, scenario_id, api_run=False):
     self.scenario_id = scenario_id
     self.scenario = Scenario(self.scenario_id)
     self.api_run = api_run
     self.outputs = Output()
     self.demand = Demand(self.scenario)
     self.supply = None
     self.demand_solved, self.supply_solved = False, False
Exemple #11
0
def arrival_demand(times: Clock,
                   system: Configuration) -> None:
    print("I. Arrival demand", times.current, end="\t###\t")
    demand = Demand(times.arrival)
    print("demand ID:", demand.id)
    if system.queue.empty() and not system.device.serves:
        times.service_start = times.current
    system.queue.put(demand)
    times.update_arrival_time(system.lambd)
class SonicSensorDemand(DemandGpioSensor):
    def __init__(self):
        self.demand = Demand(mode='double', edge=1, pulse_time=0.00001)
        super().__init__([20, 21])
        self.user_methods = [self.sensor_method, self.user_method1]
        self.event_handler.add_event_handler(20, self.user_method2)
        self.pulse_start = 0
        self.pulse_end = 0
        self.pulse_duration = 0
        self.distance = 0

    def main(self):
        print("Distance Measurement in Progress")
        super().demand_issue(self.demand, self.start_method, 'high')
        self.demand.set_mode(None)
        super().demand_issue(self.demand, self.sensor_method, 'low')

    def start_method(self, earg=None):
        self.loop_flag = 0
        self.pulse_start = time.time()
        print(self.pulse_start)

    def sensor_method(self, earg=None):
        self.loop_flag = 0
        self.pulse_end = time.time()
        print(self.pulse_start)
        print(self.pulse_end)
        self.pulse_duration = self.pulse_end - self.pulse_start
        self.distance = self.pulse_duration * 17150
        self.distance = round(self.distance, 2)
        print("Distance: ", self.distance, "cm")
        if (self.distance < 20):
            self.sensor_value = 20
            if self.sensor_value in self.event_handler:
                self.event_handler()
        else:
            self.sensor_value = None

    def user_method1(self, earg=None):
        print('call user method')

    def user_method2(self, earg=None):
        print('hello, world')
def part3():
    d = Demand(240, 'M')
    rc = RawCosts(50.0, 100.0, 0.01, holding_time_unit='M')
    Q = EOQ.optimal_order_quantity(rc, d)
    print(Q)

    T = EOQ.optimal_cycle_time(rc, d)
    print(T)

    print(T * 4)
def test_create_path():
    """Test creation of a path."""
    network = Network()
    demand = Demand(network)
    alice = Router(network, "alice")
    bob = Router(network, "bob")
    _path = Path(demand=demand,
                 name="alice-to-bob",
                 end_point_1=alice,
                 end_point_2=bob,
                 bandwidth=10,
                 fidelity=0.95)
Exemple #15
0
def part01():
    sigma_dl = calc_sigma_dl()
    g_k = (0.0211 + 0.0206) / 2  # from table

    d = Demand(quantity=D)
    rc = RawCosts(unit_cost=c, ordering_cost=c_t, holding_rate=h)
    Q = EOQ.optimal_order_quantity(rc, d)

    IFR = 1 - ((sigma_dl * g_k) / Q)
    print("IFR: {}".format(IFR))

    return Q
def part01():
    sku = '3206-BO1'
    item = df.loc[sku]

    rc = RawCosts(unit_cost=item['cost'], ordering_cost=c_t, holding_rate=h)
    d = Demand(quantity=item['demand'])
    Q = EOQ.optimal_order_quantity(rc, d)

    print("Q: {}".format(Q))
    sigma = item['rmse']
    print("sigma: {}".format(sigma))
    print("sigma_dl: {}".format(sigma * math.sqrt(1 / 12)))
class DemandGpioTemplate(DemandGpioSensor):
    def __init__(self):
        super().__init__([pin_number1, pin_number2])
        self.demand = Demand(mode='', event='key', pulse_time=0.0001)
        self.user_methodd = [user_method1, user_method2]
        self.handler = [handler1, handler2]

        # key指定し、self.sensor_valueにkeyと同じ値が入ったとき
        # user_methodを逐次的に呼び出していく
        super().add_event_handler(key, handler)

    def main_method(self):
        super().periodic_read(self, user_method)
        # add event handler. gave methods and detect edge.
        # edge can receive 'hige', 'low', 'both'.
        super().demand_issue(self.demand, sensor_method1, cathc_edge='high')
        self.demand.set_mode(None)
        super().demand_issue(self.demand, sensor_method2, cathc_edge='high')

    def sensor_method1(self):
        # please describe "loop_flag = 0"
        self.loop_flag = 0
        pass

    def sensor_method2(self):
        self.loop_flag = 0
        # イベントハンドラを呼びたいときは以下のようにしてください
        # self.sensor_value = GPIO.input(self.channel[0])
        # または self.sensor_value = value
        # if self.sensor_value in self.event_handler:
        #     self.eent_handler()
        # else:
        #     pass

    def handler1(self):
        pass

    def handler2(self):
        pass
Exemple #18
0
    def add_demand(self, source: str, target: str, demand: float):
        source_node = self.get_node_based_on_label(source)
        target_node = self.get_node_based_on_label(target)
        if source_node not in self.nodes or target_node not in self.nodes:
            raise Exception(f"{source} or {target} not in the Graph")

        damand_exists = False
        for existing_demand in self.demands:
            if (source_node == existing_demand.source
                    and target_node == existing_demand.target):
                existing_demand.demand += demand
                damand_exists = True
        if not damand_exists:
            demand_object = Demand(source_node, target_node, demand)
            self.demands.append(demand_object)
Exemple #19
0
def test_demand():
    from demand import Demand

    dm = Demand()
    # in-home activities and out-of-home activities
    #   madantory activities: work/business, school/college
    #   maintenance activities: escort passengers, shopping
    #   discretionary activities: eating out, visiting friends
    activity_data = [['home', 1.0, 600, -0.010, 1.0, 720, (0, 1440), 360],
                     ['work', 0.0, 1600, 0.010, 1.0, 720, (240, 1440), 240],
                     ['school', 0.0, 1600, 0.010, 1.0, 720, (240, 1440), 240],
                     ['eating', 0.0, 420, 0.010, 1.0, 1170, (720, 1440), 10],
                     ['shopping', 0.0, 500, 0.010, 1.0, 1110, (720, 1440), 10],
                     ['visiting', 0.0, 500, 0.010, 1.0, 1110, (720, 1440), 10],
                     [
                         'escorting', 0.0, 500, 0.010, 1.0, 1110, (720, 1440),
                         10
                     ]]
    create_objects(dm.add_activity, activity_data)
    print 'activities:'
    pprint(dm.activities.items())

    print "building activity utility..."
    dm.build_activity_util()
    logger.info('activity utility: home')
    logger.debug(pformat(dm.get_activity_util(dm.activities["home"])))
    logger.info('activity utility: work')
    logger.debug(pformat(dm.get_activity_util(dm.activities["work"])))
    logger.info('activity utility: shopping')
    logger.debug(pformat(dm.get_activity_util(dm.activities["shopping"])))

    # intra-household interactions
    #   entire day level:
    #     staying at home, absent together
    #     non-madantory DAP together (day-off for major shopping)
    #   episode level:
    #     shared activity
    #     escorting (children to school)
    #     allocation of maintenance task (shopping)
    #     car allocation
    # types of joint travel
    #   fully-joint tour, joint outbound, joint inbound
    #   drop-off/get-off, pick-up/get-in
    program_data = [[0, []], [1, ['shopping']], [3, ['eating']],
                    [2, ['visiting']], [4, ['shopping', 'visiting']],
                    [5, ['shopping', 'eating']]]
    create_objects(dm.add_program, program_data)
    print 'programs:'
    pprint(dm.programs.items())
    return dm
Exemple #20
0
def test_demand():
    from demand import Demand
    
    dm = Demand()
    # in-home activities and out-of-home activities
    #   madantory activities: work/business, school/college
    #   maintenance activities: escort passengers, shopping
    #   discretionary activities: eating out, visiting friends
    activity_data = [
        ['home',             1.0,  600, -0.010, 1.0,   720, (  0, 1440), 360],
        ['work',             0.0, 1600,  0.010, 1.0,   720, (240, 1440), 240],
        ['school',           0.0, 1600,  0.010, 1.0,   720, (240, 1440), 240],
        ['eating',           0.0,  420,  0.010, 1.0,  1170, (720, 1440),  10],
        ['shopping',         0.0,  500,  0.010, 1.0,  1110, (720, 1440),  10],
        ['visiting',         0.0,  500,  0.010, 1.0,  1110, (720, 1440),  10],
        ['escorting',         0.0,  500,  0.010, 1.0,  1110, (720, 1440),  10]
    ]
    create_objects(dm.add_activity, activity_data)
    print 'activities:'
    pprint(dm.activities.items())
    
    print "building activity utility..."
    dm.build_activity_util()
    logger.info('activity utility: home')
    logger.debug(pformat(dm.get_activity_util(dm.activities["home"])))
    logger.info('activity utility: work')
    logger.debug(pformat(dm.get_activity_util(dm.activities["work"])))
    logger.info('activity utility: shopping')
    logger.debug(pformat(dm.get_activity_util(dm.activities["shopping"])))
    
    # intra-household interactions
    #   entire day level: 
    #     staying at home, absent together
    #     non-madantory DAP together (day-off for major shopping)
    #   episode level:
    #     shared activity
    #     escorting (children to school)
    #     allocation of maintenance task (shopping)
    #     car allocation
    # types of joint travel
    #   fully-joint tour, joint outbound, joint inbound
    #   drop-off/get-off, pick-up/get-in
    program_data = [
        [0, []],
        [1, ['shopping']],
        [3, ['eating']],
        [2, ['visiting']],
        [4, ['shopping', 'visiting']],
        [5, ['shopping', 'eating']]
    ]
    create_objects(dm.add_program, program_data)
    print 'programs:'
    pprint(dm.programs.items())
    return dm
def part02():
    sku = '3206-BO1'
    item = df.loc[sku]

    cost = item['cost']
    demand = item['demand']

    rc = RawCosts(unit_cost=cost, ordering_cost=c_t, holding_rate=h)
    d = Demand(quantity=demand)
    Q = EOQ.optimal_order_quantity(rc, d)

    sigma = item['rmse']
    sigma_dl = sigma * math.sqrt(1 / 12)

    print("sigma_dl * ci: {}".format(sigma_dl * cost))
    print("D/Q: {}".format(demand / Q))
    print("(D/Q) * (sigma_dl * ci): {}".format((demand / Q) * sigma_dl * cost))
Exemple #22
0
 def _GetSpfPath(self, source: Node, target: Node, demand: int, G,
                 demand_obj: Demand):
     """
     Used to put demands on the links , it's called by the new GetSpfPath that solves
     the issue with unequal load balancing when >3 paths
     """
     """
     G = nx.MultiDiGraph()
     # node_list = [node for node in self.get_nodes() if not node._failed]
     node_list = self.get_nodes()
     for node in node_list:
         for interface in node.interfaces:
             if not interface._failed:
                 G.add_edge(
                     node, interface.target, **interface._networkX(), data=interface
                 )
     G.add_nodes_from(node_list)
     """
     paths = list(nx.all_shortest_paths(G, source, target))
     num_ecmp_paths = len(paths)
     demand_path = demand / num_ecmp_paths
     for p in paths:
         u = p[0]
         for v in p[1:]:
             values_u_v = G[u][v].values()
             min_weight = min(d["metric"] for d in values_u_v)
             ecmp_links = [
                 k for k, d in G[u][v].items() if d["metric"] == min_weight
             ]
             num_ecmp_links = len(ecmp_links)
             for d in ecmp_links:
                 G[u][v][d]["data"].util += int(demand_path) / int(
                     num_ecmp_links)
                 if G[u][v][d]["data"].util > G[u][v][d]["data"].capacity:
                     demand_obj.degraded = True
                 G[u][v][d]["data"]._on_spf = True
             u = v
Exemple #23
0
from demand_driven_io import DemandDrivenIo
from sensor_exception import SensorException
from demand import Demand
from event_handler import EventHandler

class DemandSpiSensor(SpiSensorConf, DemandtDrivenIo, SensorException):
    """This class is for the demand driven sensors"""

    def __init__(self, device=0, bus=0):
        super().__init__(self, device, bus)
        self.event_handlers = EventHandler()

    def demand_issue(self, demand, **catch_event)
        self.sensor_data = [] # 読み取ってとってくる値を取得
        if isinstance(demand, Demand):
            demand = Demand()
        else:
            print('Please give a Demand object.')
            return False

        if catch_event != {}:
            # cath_eventは{'1011':method} これがもらえるとよそう
            # main処理 書 -> 読 -> コールバック(値に応じた)
            for event in catch_event:
                self.event_handlers.add(event, catch_event[event])

            for write in demand.write_methods():
                write()

            time.sleep(demand.interval)
class PathwaysModel(object):
    """
    Highest level classification of the definition of an energy system.
    """
    def __init__(self, scenario_id, api_run=False):
        self.scenario_id = scenario_id
        self.scenario = Scenario(self.scenario_id)
        self.api_run = api_run
        self.outputs = Output()
        self.demand = Demand(self.scenario)
        self.supply = None
        self.demand_solved, self.supply_solved = False, False

    def run(self, scenario_id, solve_demand, solve_supply, load_demand, load_supply, export_results, save_models, append_results):
        try:
            if solve_demand and not (load_demand or load_supply):
                self.calculate_demand(save_models)
            
            if not append_results:
                self.remove_old_results()

            # it is nice if when loading a demand side object to rerun supply, it doesn't re-output these results every time
            if self.demand_solved and export_results and not self.api_run and not (load_demand and solve_supply):
                self.export_result_to_csv('demand_outputs')

            if solve_supply and not load_supply:
                if load_demand:
                    # if we are loading the demand, we are changing the supply measures and want to reload our scenarios
                    self.scenario = Scenario(self.scenario_id)
                self.supply = Supply(self.scenario, demand_object=self.demand)
                self.calculate_supply(save_models)

            if load_demand and solve_supply:
                # we do this now because we delayed before
                self.export_result_to_csv('demand_outputs')

            if self.supply_solved and export_results:
                self.supply.calculate_supply_outputs()
                self.pass_supply_results_back_to_demand()
                self.calculate_combined_results()
                self.outputs.electricity_reconciliation = self.demand.electricity_reconciliation # we want to write these to outputs
                if self.api_run:
                    self.export_results_to_db()
                else:
                    self.export_result_to_csv('supply_outputs')
                    self.export_result_to_csv('combined_outputs')
                    self.export_io()
        except:
            # pickle the model in the event that it crashes
            if save_models:
                Output.pickle(self, file_name=str(scenario_id) + cfg.model_error_append_name, path=cfg.workingdir)
            raise

    def calculate_demand(self, save_models):
        self.demand.setup_and_solve()
        self.demand_solved = True
        if cfg.output_payback == 'true':
            if self.demand.d_all_energy_demand_payback is not None:
                self.calculate_d_payback()
                self.calculate_d_payback_energy()
        if save_models:
            Output.pickle(self, file_name=str(self.scenario_id) + cfg.demand_model_append_name, path=cfg.workingdir)

    def calculate_supply(self, save_models):
        if not self.demand_solved:
            raise ValueError('demand must be solved first before supply')
        logging.info('Configuring energy system supply')
        self.supply.add_nodes()
        self.supply.add_measures()
        self.supply.initial_calculate()
        self.supply.calculated_years = []
        self.supply.calculate_loop(self.supply.years, self.supply.calculated_years)
        self.supply.final_calculate()
        self.supply_solved = True
        if save_models:
            Output.pickle(self, file_name=str(self.scenario_id) + cfg.full_model_append_name, path=cfg.workingdir)
            # we don't need the demand side object any more, so we can remove it to save drive space
            if os.path.isfile(os.path.join(cfg.workingdir, str(self.scenario_id) + cfg.demand_model_append_name)):
                os.remove(os.path.join(cfg.workingdir, str(self.scenario_id) + cfg.demand_model_append_name))

    def pass_supply_results_back_to_demand(self):
        logging.info("Calculating link to supply")
        self.demand.link_to_supply(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.energy_demand_link, self.supply.cost_demand_link)
        if cfg.output_tco == 'true':
            if hasattr(self,'d_energy_tco'):
                self.demand.link_to_supply_tco(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.cost_demand_link) 
            else:
               print  "demand side has not been run with tco outputs set to 'true'"
        if cfg.output_payback == 'true':
            if hasattr(self,'demand.d_all_energy_demand_payback'):
                self.demand.link_to_supply_payback(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.cost_demand_link) 
            else:
               print  "demand side has not been run with tco outputs set to 'true'"
    
    def calculate_combined_results(self):
        logging.info("Calculating combined emissions results")
        self.calculate_combined_emissions_results()
        logging.info("Calculating combined cost results")
        self.calculate_combined_cost_results()
        logging.info("Calculating combined energy results")
        self.calculate_combined_energy_results()
        if cfg.output_tco == 'true':
            if self.demand.d_energy_tco is not None:
                self.calculate_tco()
        if cfg.output_payback == 'true':
            if self.demand.d_all_energy_demand_payback is not None:
                self.calculate_payback()

    def remove_old_results(self):
        folder_names = ['combined_outputs', 'demand_outputs', 'supply_outputs', 'dispatch_outputs']
        for folder_name in folder_names:
            folder = os.path.join(cfg.workingdir, folder_name)
            if os.path.isdir(folder):
                shutil.rmtree(folder)

    def export_result_to_csv(self, result_name):
        if result_name=='combined_outputs':
            res_obj = self.outputs
        elif result_name=='demand_outputs':
            res_obj = self.demand.outputs
        elif result_name=='supply_outputs':
            res_obj = self.supply.outputs
        else:
            raise ValueError('result_name not recognized')

        for attribute in dir(res_obj):
            if not isinstance(getattr(res_obj, attribute), pd.DataFrame):
                continue

            result_df = getattr(res_obj, 'return_cleaned_output')(attribute)
            keys = [self.scenario.name.upper(), cfg.timestamp]
            names = ['SCENARIO','TIMESTAMP']
            for key, name in zip(keys, names):
                result_df = pd.concat([result_df], keys=[key], names=[name])

            if attribute in ('hourly_dispatch_results', 'electricity_reconciliation', 'hourly_marginal_cost', 'hourly_production_cost'):
                # Special case for hourly dispatch results where we want to write them outside of supply_outputs
                Output.write(result_df, attribute + '.csv', os.path.join(cfg.workingdir, 'dispatch_outputs'))
            else:
                Output.write(result_df, attribute+'.csv', os.path.join(cfg.workingdir, result_name))

    def export_results_to_db(self):
        scenario_run_id = util.active_scenario_run_id(self.scenario_id)
        # Levelized costs
        costs = self.outputs.c_costs.groupby(level=['SUPPLY/DEMAND', 'YEAR']).sum()
        util.write_output_to_db(scenario_run_id, 1, costs)

        #Energy
        energy = self.outputs.c_energy.xs('FINAL', level='ENERGY ACCOUNTING')\
            .groupby(level=['SECTOR', 'FINAL_ENERGY', 'YEAR']).sum()
        # Energy demand by sector
        util.write_output_to_db(scenario_run_id, 2, energy.groupby(level=['SECTOR', 'YEAR']).sum())
        # Residential Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 6, energy.xs('RESIDENTIAL', level='SECTOR'))
        # Commercial Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 8, energy.xs('COMMERCIAL', level='SECTOR'))
        # Transportation Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 10, energy.xs('TRANSPORTATION', level='SECTOR'))
        # Productive Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 12, energy.xs('PRODUCTIVE', level='SECTOR'))

        #Emissions
        emissions = self.outputs.c_emissions.xs('DOMESTIC', level='EXPORT/DOMESTIC')\
            .groupby(level=['SECTOR', 'FINAL_ENERGY', 'YEAR']).sum()
        emissions = util.DfOper.mult((emissions, 1-(emissions.abs()<1E-10).groupby(level='FINAL_ENERGY').all())) # get rid of noise
        # Annual emissions by sector
        util.write_output_to_db(scenario_run_id, 3, emissions.groupby(level=['SECTOR', 'YEAR']).sum())
        # Residential Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 7, emissions.xs('RESIDENTIAL', level='SECTOR'))
        # Commercial Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 9, emissions.xs('COMMERCIAL', level='SECTOR'))
        # Transportation Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 11, emissions.xs('TRANSPORTATION', level='SECTOR'))
        # Productive Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 13, emissions.xs('PRODUCTIVE', level='SECTOR'))

        # Domestic emissions per capita
        annual_emissions = self.outputs.c_emissions.xs('DOMESTIC', level='EXPORT/DOMESTIC').groupby(level=['YEAR']).sum()
        population_driver = self.demand.drivers[2].values.groupby(level='year').sum().loc[annual_emissions.index]
        population_driver.index.name = 'YEAR'
        factor = 1E6
        df = util.DfOper.divi((annual_emissions, population_driver)) * factor
        df.columns = ['TONNE PER CAPITA']
        util.write_output_to_db(scenario_run_id, 4, df)

        # Electricity supply
        electricity_node_names = [self.supply.nodes[nodeid].name.upper() for nodeid in util.flatten_list(self.supply.injection_nodes.values())]
        df = self.outputs.c_energy.xs('ELECTRICITY', level='FINAL_ENERGY')\
            .xs('EMBODIED', level='ENERGY ACCOUNTING')\
            .groupby(level=['SUPPLY_NODE', 'YEAR']).sum()
        util.write_output_to_db(scenario_run_id, 5, df.loc[electricity_node_names])

    def calculate_combined_cost_results(self):
        #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        if self.supply.export_costs is not None:
            setattr(self.outputs,'export_costs',self.supply.export_costs)
            self.export_costs_df = self.outputs.return_cleaned_output('export_costs')
            del self.outputs.export_costs
            util.replace_index_name(self.export_costs_df, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","SUPPLY"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.export_costs_df = pd.concat([self.export_costs_df],keys=[key],names=[name])
            self.export_costs_df.columns = [cost_unit.upper()]  
        else:
            self.export_costs_df = None
        #calculate and format emobodied supply costs
        self.embodied_energy_costs_df = self.demand.outputs.return_cleaned_output('demand_embodied_energy_costs')
        self.embodied_energy_costs_df.columns = [cost_unit.upper()]
        keys = ["DOMESTIC","SUPPLY"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
           self.embodied_energy_costs_df = pd.concat([self.embodied_energy_costs_df],keys=[key],names=[name])
        #calculte and format direct demand costs
        self.demand_costs_df = self.demand.outputs.return_cleaned_output('d_levelized_costs')
        if self.demand_costs_df is not None:
            levels_to_keep = [x.upper() for x in cfg.output_combined_levels]
            levels_to_keep = [x for x in levels_to_keep if x in self.demand_costs_df.index.names]
            self.demand_costs_df = self.demand_costs_df.groupby(level=levels_to_keep).sum()
            keys = ["DOMESTIC","DEMAND"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.demand_costs_df = pd.concat([self.demand_costs_df],keys=[key],names=[name])
        keys = ['EXPORTED', 'SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_costs = util.df_list_concatenate([self.export_costs_df, self.embodied_energy_costs_df, self.demand_costs_df],keys=keys,new_names=names)
        self.outputs.c_costs[self.outputs.c_costs<0]=0
        self.outputs.c_costs= self.outputs.c_costs[self.outputs.c_costs[cost_unit.upper()]!=0]
        
    def calculate_tco(self):
#        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions_tco')
#        del self.demand.outputs.demand_embodied_emissions
        #calculte and format direct demand emissions        
#        self.direct_emissions_df = self.demand.outputs.return_cleaned_output('demand_direct_emissions')
##        del self.demand.outputs.demand_direct_emissions
#        emissions = util.DfOper.add([self.embodied_emissions_df,self.direct_emissions_df])
#         #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        initial_vintage = min(cfg.supply_years)
        supply_side_df = self.demand.outputs.demand_embodied_energy_costs_tco
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = self.demand.d_levelized_costs_tco
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        service_demand_df = self.demand.d_service_demand_tco
        service_demand_df = service_demand_df[service_demand_df.index.get_level_values('vintage')>=initial_vintage]
        keys = ['SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_tco = pd.concat([util.DfOper.divi([supply_side_df,util.remove_df_levels(service_demand_df,'unit')]),
                                        util.DfOper.divi([demand_side_df,util.remove_df_levels(service_demand_df,'unit')])],
                                        keys=keys,names=names) 
        self.outputs.c_tco = self.outputs.c_tco.replace([np.inf,np.nan],0)
        self.outputs.c_tco[self.outputs.c_tco<0]=0        
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'service_demand') and hasattr(subsector,'stock'):
                    indexer = util.level_specific_indexer(self.outputs.c_tco,'subsector',subsector.id)
                    self.outputs.c_tco.loc[indexer,'unit'] = subsector.service_demand.unit.upper()
        self.outputs.c_tco = self.outputs.c_tco.set_index('unit',append=True)
        self.outputs.c_tco.columns = [cost_unit.upper()]
        self.outputs.c_tco= self.outputs.c_tco[self.outputs.c_tco[cost_unit.upper()]!=0]
        self.outputs.c_tco = self.outputs.return_cleaned_output('c_tco')
        
        
        
    def calculate_payback(self):
#        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions_tco')
#        del self.demand.outputs.demand_embodied_emissions
        #calculte and format direct demand emissions        
#        self.direct_emissions_df = self.demand.outputs.return_cleaned_output('demand_direct_emissions')
##        del self.demand.outputs.demand_direct_emissions
#        emissions = util.DfOper.add([self.embodied_emissions_df,self.direct_emissions_df])
#         #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        initial_vintage = min(cfg.supply_years)
        supply_side_df = self.demand.outputs.demand_embodied_energy_costs_payback
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('vintage')>=initial_vintage]
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('year')>=initial_vintage]
        supply_side_df = supply_side_df.sort_index()
        demand_side_df = self.demand.d_annual_costs_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        demand_side_df = demand_side_df.reindex(supply_side_df.index).sort_index()
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
        sales_df.index = sales_df.index.reorder_levels(supply_side_df.index.names)
        sales_df = sales_df.reindex(supply_side_df.index).sort_index()
        keys = ['SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_payback = pd.concat([util.DfOper.divi([supply_side_df, sales_df]), util.DfOper.divi([demand_side_df, sales_df])],keys=keys,names=names)
        self.outputs.c_payback = self.outputs.c_payback[np.isfinite(self.outputs.c_payback.values)]        
        self.outputs.c_payback = self.outputs.c_payback.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.outputs.c_payback,'subsector',subsector.id)
                    self.outputs.c_payback.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.outputs.c_payback = self.outputs.c_payback.set_index('unit', append=True)
        self.outputs.c_payback.columns = [cost_unit.upper()]
        self.outputs.c_payback['lifetime_year'] = self.outputs.c_payback.index.get_level_values('year')-self.outputs.c_payback.index.get_level_values('vintage')+1    
        self.outputs.c_payback = self.outputs.c_payback.set_index('lifetime_year',append=True)
        self.outputs.c_payback = util.remove_df_levels(self.outputs.c_payback,'year')
        self.outputs.c_payback = self.outputs.c_payback.groupby(level = [x for x in self.outputs.c_payback.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.outputs.c_payback = self.outputs.c_payback[self.outputs.c_payback[cost_unit.upper()]!=0]
        self.outputs.c_payback = self.outputs.return_cleaned_output('c_payback')
        
        
    def calculate_d_payback(self):
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        initial_vintage = min(cfg.supply_years)
        demand_side_df = self.demand.d_annual_costs_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
        sales_df.index = sales_df.index.reorder_levels(demand_side_df.index.names)
        sales_df = sales_df.reindex(demand_side_df.index).sort_index()
        self.demand.outputs.d_payback = util.DfOper.divi([demand_side_df, sales_df])
        self.demand.outputs.d_payback = self.demand.outputs.d_payback[np.isfinite(self.demand.outputs.d_payback.values)]        
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.demand.outputs.d_payback,'subsector',subsector.id)
                    self.demand.outputs.d_payback.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.set_index('unit', append=True)
        self.demand.outputs.d_payback.columns = [cost_unit.upper()]
        self.demand.outputs.d_payback['lifetime_year'] = self.demand.outputs.d_payback.index.get_level_values('year')-self.demand.outputs.d_payback.index.get_level_values('vintage')+1    
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.set_index('lifetime_year',append=True)
        self.demand.outputs.d_payback = util.remove_df_levels(self.demand.outputs.d_payback,'year')
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.groupby(level = [x for x in self.demand.outputs.d_payback.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.demand.outputs.d_payback = self.demand.outputs.d_payback[self.demand.outputs.d_payback[cost_unit.upper()]!=0]
        self.demand.outputs.d_payback = self.demand.outputs.return_cleaned_output('d_payback')
   
    def calculate_d_payback_energy(self):
        initial_vintage = min(cfg.supply_years)
        demand_side_df = self.demand.d_all_energy_demand_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
#        sales_df.index = sales_df.index.reorder_levels(demand_side_df.index.names)
#        sales_df = sales_df.reindex(demand_side_df.index).sort_index()
        self.demand.outputs.d_payback_energy = util.DfOper.divi([demand_side_df, sales_df])
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy[np.isfinite(self.demand.outputs.d_payback_energy.values)]        
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.demand.outputs.d_payback_energy,'subsector',subsector.id)
                    self.demand.outputs.d_payback_energy.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.set_index('unit', append=True)
        self.demand.outputs.d_payback_energy.columns = [cfg.calculation_energy_unit.upper()]
        self.demand.outputs.d_payback_energy['lifetime_year'] = self.demand.outputs.d_payback_energy.index.get_level_values('year')-self.demand.outputs.d_payback_energy.index.get_level_values('vintage')+1    
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.set_index('lifetime_year',append=True)
        self.demand.outputs.d_payback_energy = util.remove_df_levels(self.demand.outputs.d_payback_energy,'year')
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.groupby(level = [x for x in self.demand.outputs.d_payback_energy.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy[self.demand.outputs.d_payback_energy[cfg.calculation_energy_unit.upper()]!=0]
        self.demand.outputs.d_payback_energy = self.demand.outputs.return_cleaned_output('d_payback_energy')
            
        
    def calculate_combined_emissions_results(self):
        #calculate and format export emissions
        if self.supply.export_emissions is not None:
            setattr(self.outputs,'export_emissions',self.supply.export_emissions)
            if 'supply_geography' not in cfg.output_combined_levels:
                util.remove_df_levels(self.outputs.export_emissions, cfg.primary_geography +'_supply')
            self.export_emissions_df = self.outputs.return_cleaned_output('export_emissions')
            del self.outputs.export_emissions
            util.replace_index_name(self.export_emissions_df, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","SUPPLY"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.export_emissions_df = pd.concat([self.export_emissions_df],keys=[key],names=[name])
        else:
            self.export_emissions_df = None
       #calculate and format emobodied supply emissions
        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions')
#        del self.demand.outputs.demand_embodied_emissions
        keys = ["DOMESTIC","SUPPLY"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
           self.embodied_emissions_df = pd.concat([self.embodied_emissions_df],keys=[key],names=[name])       
        #calculte and format direct demand emissions        
        self.direct_emissions_df = self.demand.outputs.return_cleaned_output('demand_direct_emissions')
#        del self.demand.outputs.demand_direct_emissions
        keys = ["DOMESTIC","DEMAND"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key, name in zip(keys, names):
            self.direct_emissions_df = pd.concat([self.direct_emissions_df], keys=[key], names=[name])
        if cfg.primary_geography+'_supply' in cfg.output_combined_levels:
             keys = self.direct_emissions_df.index.get_level_values(cfg.primary_geography.upper()).values
             names = cfg.primary_geography.upper() +'_SUPPLY'
             self.direct_emissions_df[names] = keys
             self.direct_emissions_df.set_index(names,append=True,inplace=True)
        keys = ['EXPORTED', 'SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['EMISSIONS TYPE']
        self.outputs.c_emissions = util.df_list_concatenate([self.export_emissions_df, self.embodied_emissions_df, self.direct_emissions_df],keys=keys,new_names = names)
        util.replace_index_name(self.outputs.c_emissions, cfg.primary_geography.upper() +'-EMITTED', cfg.primary_geography.upper() +'_SUPPLY')
        util.replace_index_name(self.outputs.c_emissions, cfg.primary_geography.upper() +'-CONSUMED', cfg.primary_geography.upper())
        self.outputs.c_emissions= self.outputs.c_emissions[self.outputs.c_emissions['VALUE']!=0]
        emissions_unit = cfg.cfgfile.get('case','mass_unit')
        self.outputs.c_emissions.columns = [emissions_unit.upper()]
            
    def calculate_combined_energy_results(self):
         energy_unit = cfg.calculation_energy_unit
         if self.supply.export_costs is not None:
            setattr(self.outputs,'export_energy',self.supply.export_energy)
            self.export_energy = self.outputs.return_cleaned_output('export_energy')
            del self.outputs.export_energy
            util.replace_index_name(self.export_energy, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","EMBODIED"]
            names = ['EXPORT/DOMESTIC', 'ENERGY ACCOUNTING']
            for key,name in zip(keys,names):
                self.export_energy = pd.concat([self.export_energy],keys=[key],names=[name])
         else:
            self.export_energy = None
         self.embodied_energy = self.demand.outputs.return_cleaned_output('demand_embodied_energy')
         self.embodied_energy = self.embodied_energy[self.embodied_energy ['VALUE']!=0]
         keys = ['DOMESTIC','EMBODIED']
         names = ['EXPORT/DOMESTIC', 'ENERGY ACCOUNTING']
         for key,name in zip(keys,names):
             self.embodied_energy = pd.concat([self.embodied_energy],keys=[key],names=[name])
         self.final_energy = self.demand.outputs.return_cleaned_output('d_energy')
         self.final_energy = self.final_energy[self.final_energy.index.get_level_values('YEAR')>=int(cfg.cfgfile.get('case','current_year'))]
         keys = ['DOMESTIC','FINAL']
         names = ['EXPORT/DOMESTIC', 'ENERGY ACCOUNTING']
         for key,name in zip(keys,names):
             self.final_energy = pd.concat([self.final_energy],keys=[key],names=[name])
    #         self.outputs.c_energy = pd.concat([self.embodied_energy, self.final_energy],keys=['DROP'],names=['DROP'])
         for name in [x for x in self.embodied_energy.index.names if x not in self.final_energy.index.names]:
             self.final_energy[name] = "N/A"
             self.final_energy.set_index(name,append=True,inplace=True)
         if self.export_energy is not None:
             for name in [x for x in self.embodied_energy.index.names if x not in self.export_energy.index.names]:
                 self.export_energy[name] = "N/A"
                 self.export_energy.set_index(name,append=True,inplace=True)
             self.export_energy = self.export_energy.groupby(level=self.embodied_energy.index.names).sum()
             self.export_energy = self.export_energy.reorder_levels(self.embodied_energy.index.names)
         self.final_energy = self.final_energy.groupby(level=self.embodied_energy.index.names).sum()
         self.final_energy = self.final_energy.reorder_levels(self.embodied_energy.index.names)
         self.outputs.c_energy = pd.concat([self.embodied_energy,self.final_energy,self.export_energy])
         self.outputs.c_energy= self.outputs.c_energy[self.outputs.c_energy['VALUE']!=0]
         self.outputs.c_energy.columns = [energy_unit.upper()]

    def export_io(self):
        io_table_write_step = int(cfg.cfgfile.get('output_detail','io_table_write_step'))
        io_table_years = sorted([min(cfg.supply_years)] + range(max(cfg.supply_years), min(cfg.supply_years), -io_table_write_step))
        df_list = []
        for year in io_table_years:
            sector_df_list = []
            keys = self.supply.demand_sectors
            name = ['sector']
            for sector in self.supply.demand_sectors:
                sector_df_list.append(self.supply.io_dict[year][sector])
            year_df = pd.concat(sector_df_list, keys=keys,names=name)
            year_df = pd.concat([year_df]*len(keys),keys=keys,names=name,axis=1)
            df_list.append(year_df)
        keys = io_table_years
        name = ['year']
        df = pd.concat(df_list,keys=keys,names=name)
        for row_sector in self.supply.demand_sectors:
            for col_sector in self.supply.demand_sectors:
                if row_sector != col_sector:
                    df.loc[util.level_specific_indexer(df,'sector',row_sector),util.level_specific_indexer(df,'sector',col_sector,axis=1)] = 0
        self.supply.outputs.io = df
        result_df = self.supply.outputs.return_cleaned_output('io')
        keys = [self.scenario.name.upper(), cfg.timestamp]
        names = ['SCENARIO','TIMESTAMP']
        for key, name in zip(keys,names):
            result_df = pd.concat([result_df], keys=[key],names=[name])
        Output.write(result_df, 's_io.csv', os.path.join(cfg.workingdir, 'supply_outputs'))
#        self.export_stacked_io()

    def export_stacked_io(self):
        df = copy.deepcopy(self.supply.outputs.io)
        df.index.names = [x + '_input'if x!= 'year' else x for x in df.index.names ]
        df = df.stack(level=df.columns.names).to_frame()
        df.columns = ['value']
        self.supply.outputs.stacked_io = df
        result_df = self.supply.outputs.return_cleaned_output('stacked_io')
        keys = [self.scenario.name.upper(), cfg.timestamp]
        names = ['SCENARIO','TIMESTAMP']
        for key, name in zip(keys,names):
            result_df = pd.concat([result_df], keys=[key],names=[name])
        Output.write(result_df, 's_stacked_io.csv', os.path.join(cfg.workingdir, 'supply_outputs'))
def test_init_with_negative_quantity_raises():
    with pytest.raises(ValueError):
        Demand(quantity=-1)
def test_init_with_missing_quantity_raises():
    with pytest.raises(ValueError):
        Demand(quantity=None)
def test_init_with_invalid_time_unit_raises():
    with pytest.raises(ValueError):
        Demand(quantity=1, time_unit='X')
def test_init_with_valid_time_unit_captures():
    d = Demand(quantity=1, time_unit='W')
    assert d.time_unit == 'W'
def test_init_with_positive_quantity_captures():
    d = Demand(quantity=1)
    assert d.quantity == 1
class PathwaysModel(object):
    """
    Highest level classification of the definition of an energy system.
    Includes the primary geography of the energy system (i.e. country name) as well as the author.
    """
    def __init__(self, cfgfile_path, custom_pint_definitions_path=None, name=None, author=None):
        self.cfgfile_path = cfgfile_path
        self.custom_pint_definitions_path = custom_pint_definitions_path
        self.model_config(cfgfile_path, custom_pint_definitions_path)
        self.name = cfg.cfgfile.get('case', 'scenario') if name is None else name
        self.author = cfg.cfgfile.get('case', 'author') if author is None else author      
        self.scenario_dict = dict(zip(util.sql_read_table('Scenarios','id', return_iterable=True, is_active=True),
                                  util.sql_read_table('Scenarios','name', return_iterable=True, is_active=True)))
        self.outputs = Output()
        self.geography = cfg.cfgfile.get('case', 'primary_geography')
        

    def model_config(self, cfgfile_path, custom_pint_definitions_path):
        cfg.init_cfgfile(cfgfile_path)
        cfg.init_db()
        cfg.path = custom_pint_definitions_path
        cfg.init_pint(custom_pint_definitions_path)
        cfg.init_geo()
        cfg.init_date_lookup()
        if shape.shapes.rerun:
            shape.shapes.create_empty_shapes()
#            shape.shapes.activate_shape(cfg.electricity_energy_type_shape_id)
        cfg.init_outputs_id_map()

    def configure_energy_system(self):
        print 'configuring energy system'
        self.demand = Demand(self.cfgfile_path, self.custom_pint_definitions_path)
        self.supply = Supply(os.path.join(os.getcwd(),'outputs'),self.cfgfile_path, self.custom_pint_definitions_path)
        self.configure_demand()
        self.configure_supply()

    def populate_energy_system(self):
        self.populate_demand_system()
        self.populate_supply_system()
    
    def populate_shapes(self):
        print 'processing shapes'
        if shape.shapes.rerun:
            shape.shapes.initiate_active_shapes()
            shape.shapes.process_active_shapes()

    def populate_measures(self, scenario_id):
        self.scenario_id = scenario_id
        self.scenario = self.scenario_dict[self.scenario_id]
        self.demand_case_id = util.sql_read_table('Scenarios','demand_case',id=self.scenario_id)
        self.populate_demand_measures()
        self.supply_case_id = util.sql_read_table('Scenarios','supply_case',id=self.scenario_id)
        self.populate_supply_measures()

    def calculate(self):
        self.calculate_demand_only()
        self.pass_results_to_supply()
        self.calculate_supply()

    def configure_demand(self):
        """Read in and initialize data"""
        # Drivers must come first
        self.demand.add_drivers()

        # Sectors requires drivers be read in
        self.demand.add_sectors()
        for sector in self.demand.sectors.values():
            # print 'configuring the %s sector'  %sector.name
            sector.add_subsectors()
            
    def configure_supply(self):
        self.supply.add_node_list()

    def populate_demand_system(self):
        print 'remapping drivers'
        self.demand.remap_drivers()
        print 'populating energy system data'
        for sector in self.demand.sectors.values():
            print '  '+sector.name+' sector'
#            print 'reading energy system data for the %s sector' %sector.name
            for subsector in sector.subsectors.values():
                print '    '+subsector.name
                subsector.add_energy_system_data()
            sector.precursor_dict()

    def populate_supply_system(self):
        self.supply.add_nodes()

    def populate_demand_measures(self):
        for sector in self.demand.sectors.values():
            #            print 'reading %s measures' %sector.name
            for subsector in sector.subsectors.values():
                subsector.add_measures(self.demand_case_id)
        
    def populate_supply_measures(self):
        self.supply.add_measures(self.supply_case_id)

    def calculate_demand_only(self):
        self.demand.calculate_demand()
        print "aggregating demand results"
        self.demand.aggregate_results()
    
    def calculate_supply(self):
        self.supply.initial_calculate()     
        self.supply.calculate_loop()
        self.supply.final_calculate()
             
    def pass_results_to_supply(self):
        for sector in self.demand.sectors.values():
             sector.aggregate_subsector_energy_for_supply_side()
        self.demand.aggregate_sector_energy_for_supply_side()
        self.supply.demand_object = self.demand
        
    def pass_results_to_demand(self):
        print "calculating link to supply"
        self.demand.link_to_supply(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.energy_demand_link, self.supply.cost_demand_link)
    
    def calculate_combined_results(self):
        print "calculating combined emissions results"
        self.calculate_combined_emissions_results()
        print "calculating combined cost results"
        self.calculate_combined_cost_results()
        print "calculating combined energy results"
        self.calculate_combined_energy_results()
    
    def export_results(self):
        for attribute in dir(self.outputs):
            if isinstance(getattr(self.outputs,attribute), pd.DataFrame):
                result_df = getattr(self.outputs, attribute)
                keys = [self.scenario.upper(),str(datetime.now().replace(second=0,microsecond=0))]
                names = ['SCENARIO','TIMESTAMP']
                for key, name in zip(keys,names):
                    result_df = pd.concat([result_df],keys=[key],names=[name])
                ExportMethods.writeobj(attribute,result_df, os.path.join(os.getcwd(),'combined_outputs'), append_results=True)
        for attribute in dir(self.demand.outputs):
            if isinstance(getattr(self.demand.outputs,attribute), pd.DataFrame):
                result_df = self.demand.outputs.return_cleaned_output(attribute)
                keys = [self.scenario.upper(),str(datetime.now().replace(second=0,microsecond=0))]
                names = ['SCENARIO','TIMESTAMP']
                for key, name in zip(keys,names):
                    result_df = pd.concat([result_df],keys=[key],names=[name])
                ExportMethods.writeobj(attribute,result_df, os.path.join(os.getcwd(),'demand_outputs'), append_results=True)
        for attribute in dir(self.supply.outputs):
            if isinstance(getattr(self.supply.outputs,attribute), pd.DataFrame):
                result_df = self.supply.outputs.return_cleaned_output(attribute)
                keys = [self.scenario.upper(),str(datetime.now().replace(second=0,microsecond=0))]
                names = ['SCENARIO','TIMESTAMP']
                for key, name in zip(keys,names):
                    result_df = pd.concat([result_df],keys=[key],names=[name])
                ExportMethods.writeobj(attribute,result_df, os.path.join(os.getcwd(),'supply_outputs'), append_results=True)
        
    def calculate_combined_cost_results(self):
        #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        if self.supply.export_costs is not None:
            setattr(self.outputs,'export_costs',self.supply.export_costs)
            self.export_costs_df = self.outputs.return_cleaned_output('export_costs')
            del self.outputs.export_costs
            util.replace_index_name(self.export_costs_df, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","SUPPLY"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.export_costs_df = pd.concat([self.export_costs_df],keys=[key],names=[name])
            self.export_costs_df.columns = [cost_unit.upper()]  
        else:
            self.export_costs_df = None
        #calculate and format emobodied supply costs
        self.embodied_energy_costs_df = self.demand.outputs.return_cleaned_output('demand_embodied_energy_costs')
        self.embodied_energy_costs_df.columns = [cost_unit.upper()]  
#        del self.demand.outputs.demand_embodied_energy_costs
        keys = ["DOMESTIC","SUPPLY"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
           self.embodied_energy_costs_df = pd.concat([self.embodied_energy_costs_df],keys=[key],names=[name])       
        #calculte and format direct demand emissions        
        self.demand_costs_df= self.demand.outputs.return_cleaned_output('levelized_costs')  
#        del self.demand.outputs.levelized_costs
        keys = ["DOMESTIC","DEMAND"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
            self.demand_costs_df = pd.concat([self.demand_costs_df],keys=[key],names=[name])      
#        levels_to_keep = cfg.output_levels      
#        levels_to_keep = [x.upper() for x in levels_to_keep]
#        levels_to_keep += names + [self.geography.upper() +'_SUPPLY', 'SUPPLY_NODE']
        keys = ['EXPORTED', 'SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.costs = util.df_list_concatenate([self.export_costs_df, self.embodied_energy_costs_df, self.demand_costs_df],keys=keys,new_names=names)
#        util.replace_index_name(self.outputs.costs, self.geography.upper() +'_EARNED', self.geography.upper() +'_SUPPLY')
#        util.replace_index_name(self.outputs.costs, self.geography.upper() +'_CONSUMED', self.geography.upper())
        self.outputs.costs[self.outputs.costs<0]=0
        self.outputs.costs= self.outputs.costs[self.outputs.costs[cost_unit.upper()]!=0]
#        self.outputs.costs.sort(inplace=True)       

    
        
    def calculate_combined_emissions_results(self):
        #calculate and format export emissions
        if self.supply.export_emissions is not None:
            setattr(self.outputs,'export_emissions',self.supply.export_emissions)
            if 'supply_geography' not in cfg.output_combined_levels:
                util.remove_df_levels(self.outputs.export_emissions,self.geography +'_supply')
            self.export_emissions_df = self.outputs.return_cleaned_output('export_emissions')
            del self.outputs.export_emissions
            util.replace_index_name(self.export_emissions_df, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","SUPPLY"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.export_emissions_df = pd.concat([self.export_emissions_df],keys=[key],names=[name])
        else:
            self.export_emissions_df = None
       #calculate and format emobodied supply emissions
        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions')
#        del self.demand.outputs.demand_embodied_emissions
        keys = ["DOMESTIC","SUPPLY"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
           self.embodied_emissions_df = pd.concat([self.embodied_emissions_df],keys=[key],names=[name])       
        #calculte and format direct demand emissions        
        self.direct_emissions_df= self.demand.outputs.return_cleaned_output('demand_direct_emissions')  
#        del self.demand.outputs.demand_direct_emissions
        keys = ["DOMESTIC","DEMAND"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
            self.direct_emissions_df = pd.concat([self.direct_emissions_df],keys=[key],names=[name])   
        if 'supply_geography' in cfg.output_combined_levels:
            keys = self.direct_emissions_df.index.get_level_values(self.geography.upper()).values
            names = self.geography.upper() +'_SUPPLY'
            self.direct_emissions_df[names] = keys
            self.direct_emissions_df.set_index(names,append=True,inplace=True)
#        levels_to_keep = cfg.output_levels      
#        levels_to_keep = [x.upper() for x in levels_to_keep]
#        levels_to_keep += names + [self.geography.upper() +'_SUPPLY', 'SUPPLY_NODE']
        keys = ['EXPORTED', 'SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['EMISSIONS TYPE']
        self.outputs.emissions = util.df_list_concatenate([self.export_emissions_df, self.embodied_emissions_df, self.direct_emissions_df],keys=keys,new_names = names)
#        util.replace_index_name(self.outputs.emissions, "ENERGY","FINAL_ENERGY")
        util.replace_index_name(self.outputs.emissions, self.geography.upper() +'_EMITTED', self.geography.upper() +'_SUPPLY')
        util.replace_index_name(self.outputs.emissions, self.geography.upper() +'_CONSUMED', self.geography.upper())
        self.outputs.emissions= self.outputs.emissions[self.outputs.emissions['VALUE']!=0]
        emissions_unit = cfg.cfgfile.get('case','mass_unit')
        self.outputs.emissions.columns = [emissions_unit.upper()]
        
#        self.outputs.emissions.sort(inplace=True)        
            
    def calculate_combined_energy_results(self):
         self.embodied_energy = self.demand.outputs.return_cleaned_output('demand_embodied_energy')
         self.embodied_energy = self.embodied_energy[self.embodied_energy ['VALUE']!=0]
         self.final_energy = self.demand.outputs.return_cleaned_output('energy')
         self.final_energy = self.final_energy[self.final_energy.index.get_level_values('YEAR')>=int(cfg.cfgfile.get('case','current_year'))]  
         self.embodied_energy['ENERGY ACCOUNTING'] = 'EMBODIED'
         self.final_energy['ENERGY ACCOUNTING'] = 'FINAL'
         self.embodied_energy.set_index('ENERGY ACCOUNTING',append=True,inplace=True)
         self.final_energy.set_index('ENERGY ACCOUNTING',append=True,inplace=True)
    #         self.outputs.energy = pd.concat([self.embodied_energy, self.final_energy],keys=['DROP'],names=['DROP'])
         for name in [x for x in self.embodied_energy.index.names if x not in self.final_energy.index.names]:
             self.final_energy[name] = "N/A"
             self.final_energy.set_index(name,append=True,inplace=True)
         self.final_energy = self.final_energy.groupby(level=self.embodied_energy.index.names).sum()
         self.final_energy = self.final_energy.reorder_levels(self.embodied_energy.index.names)
         self.outputs.energy = pd.concat([self.embodied_energy,self.final_energy])
         self.outputs.energy= self.outputs.energy[self.outputs.energy['VALUE']!=0]
         energy_unit = cfg.cfgfile.get('case','energy_unit')
         self.outputs.energy.columns = [energy_unit.upper()]
 def configure_energy_system(self):
     print 'configuring energy system'
     self.demand = Demand(self.cfgfile_path, self.custom_pint_definitions_path)
     self.supply = Supply(os.path.join(os.getcwd(),'outputs'),self.cfgfile_path, self.custom_pint_definitions_path)
     self.configure_demand()
     self.configure_supply()
Exemple #32
0
class PathwaysModel(object):
    """
    Highest level classification of the definition of an energy system.
    """
    def __init__(self, scenario_id, api_run=False):
        self.scenario_id = scenario_id
        self.scenario = Scenario(self.scenario_id)
        self.api_run = api_run
        self.outputs = Output()
        self.demand = Demand(self.scenario)
        self.supply = None
        self.demand_solved, self.supply_solved = False, False

    def run(self, scenario_id, solve_demand, solve_supply, load_demand, load_supply, export_results, save_models, append_results, rio_scenario):
        #try:
        self.scenario_id = scenario_id
        self.scenario = Scenario(self.scenario_id)
        self.rio_scenario = rio_scenario
        if solve_demand and not (load_demand or load_supply):
            self.calculate_demand(save_models)

        if not append_results:
            self.remove_old_results()

        # it is nice if when loading a demand side object to rerun supply, it doesn't re-output these results every time
        if self.demand_solved and export_results and not self.api_run and not (load_demand and solve_supply):
            # self.demand.create_electricity_reconciliation()
            # self.demand.output_subsector_electricity_profiles()
            self.export_result_to_csv('demand_outputs')

        if solve_supply:
            if load_demand:
                # if we are loading the demand, we are changing the supply measures and want to reload our scenarios
                self.scenario = Scenario(self.scenario_id)
            self.supply = Supply(self.scenario, demand_object=self.demand,rio_scenario=rio_scenario)
            self.calculate_supply(save_models)

        if load_demand and solve_supply and export_results:
            # we do this now because we delayed before
            self.export_result_to_csv('demand_outputs')

        if self.supply_solved and export_results and (load_supply or solve_supply):
            self.supply.calculate_supply_outputs()
            self.pass_supply_results_back_to_demand()
            self.calculate_combined_results()
            self.outputs.electricity_reconciliation = self.demand.electricity_reconciliation # we want to write these to outputs
            self.export_result_to_csv('supply_outputs')
            self.export_result_to_csv('combined_outputs')
            self.export_io()
        #except:
            # pickle the model in the event that it crashes
            #if save_models:
             #   if cfg.rio_supply_run:
              #      Output.pickle(self, file_name=self.rio_scenario + cfg.model_error_append_name, path=cfg.workingdir)
               # else:
                #    Output.pickle(self, file_name=str(self.scenario_id) + cfg.model_error_append_name,
                 #                 path=cfg.workingdir)


    def calculate_demand(self, save_models):
        self.demand.setup_and_solve()
        self.demand_solved = True
        if cfg.output_payback == 'true':
            if self.demand.d_all_energy_demand_payback is not None:
                self.calculate_d_payback()
                self.calculate_d_payback_energy()
        if save_models:
            if cfg.rio_supply_run:
                Output.pickle(self, file_name=str(self.scenario_id) + cfg.demand_model_append_name, path=cfg.workingdir)
            else:
                Output.pickle(self, file_name=str(self.scenario_id) + cfg.demand_model_append_name, path=cfg.workingdir)

    def calculate_supply(self, save_models):
        if not self.demand_solved:
            raise ValueError('demand must be solved first before supply')
        logging.info('Configuring energy system supply')
        self.supply.add_nodes()
        self.supply.add_measures()
        self.supply.initial_calculate()
        self.supply.calculated_years = []
        self.supply.calculate_loop(self.supply.years, self.supply.calculated_years)
        self.supply.final_calculate()
        self.supply_solved = True
        if save_models:
            if cfg.rio_supply_run:
                Output.pickle(self, file_name=self.rio_scenario + cfg.full_model_append_name, path=cfg.workingdir)
            else:
                Output.pickle(self, file_name=str(self.scenario_id) + cfg.full_model_append_name, path=cfg.workingdir)
            # we don't need the demand side object any more, so we can remove it to save drive space
            # if not cfg.rio_supply_run:
            #     if os.path.isfile(os.path.join(cfg.workingdir, str(self.scenario_id) + cfg.demand_model_append_name)):
            #         os.remove(os.path.join(cfg.workingdir, str(self.scenario_id) + cfg.demand_model_append_name))

    def pass_supply_results_back_to_demand(self):
        # we need to geomap to the combined output geography
        emissions_demand_link = GeoMapper.geo_map(self.supply.emissions_demand_link, GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'intensity')
        demand_emissions_rates = GeoMapper.geo_map(self.supply.demand_emissions_rates, GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'intensity')
        energy_demand_link = GeoMapper.geo_map(self.supply.energy_demand_link, GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'intensity')
        cost_demand_link = GeoMapper.geo_map(self.supply.cost_demand_link, GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'intensity')

        logging.info("Calculating link to supply")
        self.demand.link_to_supply(emissions_demand_link, demand_emissions_rates, energy_demand_link, cost_demand_link)
        if cfg.output_tco == 'true':
            if hasattr(self,'d_energy_tco'):
                self.demand.link_to_supply_tco(emissions_demand_link, demand_emissions_rates, cost_demand_link)
            else:
               print  "demand side has not been run with tco outputs set to 'true'"
        if cfg.output_payback == 'true':
            if hasattr(self,'demand.d_all_energy_demand_payback'):
                self.demand.link_to_supply_payback(emissions_demand_link, demand_emissions_rates, cost_demand_link)
            else:
               print  "demand side has not been run with tco outputs set to 'true'"
    
    def calculate_combined_results(self):
        logging.info("Calculating combined emissions results")
        self.calculate_combined_emissions_results()
        logging.info("Calculating combined cost results")
        self.calculate_combined_cost_results()
        logging.info("Calculating combined energy results")
        self.calculate_combined_energy_results()
        if cfg.output_tco == 'true':
            if self.demand.d_energy_tco is not None:
                self.calculate_tco()
        if cfg.output_payback == 'true':
            if self.demand.d_all_energy_demand_payback is not None:
                self.calculate_payback()

    def remove_old_results(self):
        folder_names = ['combined_outputs', 'demand_outputs', 'supply_outputs', 'dispatch_outputs']
        for folder_name in folder_names:
            folder = os.path.join(cfg.workingdir, folder_name)
            if os.path.isdir(folder):
                shutil.rmtree(folder)

    def export_result_to_csv(self, result_name):
        if result_name=='combined_outputs':
            res_obj = self.outputs
        elif result_name=='demand_outputs':
            res_obj = self.demand.outputs
        elif result_name=='supply_outputs':
            res_obj = self.supply.outputs
        else:
            raise ValueError('result_name not recognized')

        def clean_and_write(result_df, attribute):
            """

            :param result_df: pandas dataframe
            :param attribute: string
            """
            if cfg.rio_supply_run and self.supply is not None:
                keys = [self.supply.rio_scenario.upper(), cfg.timestamp]
            else:
                keys = [self.scenario.name.upper(), cfg.timestamp]
            names = ['SCENARIO', 'TIMESTAMP']
            for key, name in zip(keys, names):
                result_df = pd.concat([result_df], keys=[key], names=[name])
                result_df = result_df.fillna(0)
            if attribute in (
            'hourly_dispatch_results', 'electricity_reconciliation', 'hourly_marginal_cost', 'hourly_production_cost'):
                # Special case for hourly dispatch results where we want to write them outside of supply_outputs
                Output.write(result_df, attribute + '.csv', os.path.join(cfg.workingdir, 'dispatch_outputs'))
            else:
                Output.write(result_df, attribute + '.csv', os.path.join(cfg.workingdir, result_name))

        for attribute in dir(res_obj):
            if isinstance(getattr(res_obj, attribute), list):
                for df in getattr(res_obj, attribute):
                    result_df = getattr(res_obj, 'clean_df')(df)
                    clean_and_write(result_df,attribute)
            elif isinstance(getattr(res_obj, attribute), pd.DataFrame):
                result_df = getattr(res_obj, 'clean_df')(getattr(res_obj, attribute))
                clean_and_write(result_df, attribute)
            else:
                continue

    def calculate_tco(self):
        cost_unit = cfg.getParam('currency_year') + " " + cfg.getParam('currency_name')
        initial_vintage = min(cfg.supply_years)
        supply_side_df = self.demand.outputs.demand_embodied_energy_costs_tco
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = self.demand.d_levelized_costs_tco
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        service_demand_df = self.demand.d_service_demand_tco
        service_demand_df = service_demand_df[service_demand_df.index.get_level_values('vintage')>=initial_vintage]
        keys = ['SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_tco = pd.concat([util.DfOper.divi([supply_side_df,util.remove_df_levels(service_demand_df,'unit')]),
                                        util.DfOper.divi([demand_side_df,util.remove_df_levels(service_demand_df,'unit')])],
                                        keys=keys,names=names) 
        self.outputs.c_tco = self.outputs.c_tco.replace([np.inf,np.nan],0)
        self.outputs.c_tco[self.outputs.c_tco<0]=0        
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'service_demand') and hasattr(subsector,'stock'):
                    indexer = util.level_specific_indexer(self.outputs.c_tco,'subsector',subsector.id)
                    self.outputs.c_tco.loc[indexer,'unit'] = subsector.service_demand.unit.upper()
        self.outputs.c_tco = self.outputs.c_tco.set_index('unit',append=True)
        self.outputs.c_tco.columns = [cost_unit.upper()]
        self.outputs.c_tco= self.outputs.c_tco[self.outputs.c_tco[cost_unit.upper()]!=0]
        self.outputs.c_tco = self.outputs.return_cleaned_output('c_tco')
        
    def calculate_payback(self):
        cost_unit = cfg.getParam('currency_year') + " " + cfg.getParam('currency_name')
        initial_vintage = min(cfg.supply_years)
        supply_side_df = self.demand.outputs.demand_embodied_energy_costs_payback
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('vintage')>=initial_vintage]
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('year')>=initial_vintage]
        supply_side_df = supply_side_df.sort_index()
        demand_side_df = self.demand.d_annual_costs_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        demand_side_df = demand_side_df.reindex(supply_side_df.index).sort_index()
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
        sales_df.index = sales_df.index.reorder_levels(supply_side_df.index.names)
        sales_df = sales_df.reindex(supply_side_df.index).sort_index()
        keys = ['SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_payback = pd.concat([util.DfOper.divi([supply_side_df, sales_df]), util.DfOper.divi([demand_side_df, sales_df])],keys=keys,names=names)
        self.outputs.c_payback = self.outputs.c_payback[np.isfinite(self.outputs.c_payback.values)]        
        self.outputs.c_payback = self.outputs.c_payback.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.outputs.c_payback,'subsector',subsector.id)
                    self.outputs.c_payback.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.outputs.c_payback = self.outputs.c_payback.set_index('unit', append=True)
        self.outputs.c_payback.columns = [cost_unit.upper()]
        self.outputs.c_payback['lifetime_year'] = self.outputs.c_payback.index.get_level_values('year')-self.outputs.c_payback.index.get_level_values('vintage')+1    
        self.outputs.c_payback = self.outputs.c_payback.set_index('lifetime_year',append=True)
        self.outputs.c_payback = util.remove_df_levels(self.outputs.c_payback,'year')
        self.outputs.c_payback = self.outputs.c_payback.groupby(level = [x for x in self.outputs.c_payback.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.outputs.c_payback = self.outputs.c_payback[self.outputs.c_payback[cost_unit.upper()]!=0]
        self.outputs.c_payback = self.outputs.return_cleaned_output('c_payback')
        
    def calculate_d_payback(self):
        cost_unit = cfg.getParam('currency_year') + " " + cfg.getParam('currency_name')
        initial_vintage = min(cfg.supply_years)
        demand_side_df = self.demand.d_annual_costs_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
        sales_df.index = sales_df.index.reorder_levels(demand_side_df.index.names)
        sales_df = sales_df.reindex(demand_side_df.index).sort_index()
        self.demand.outputs.d_payback = util.DfOper.divi([demand_side_df, sales_df])
        self.demand.outputs.d_payback = self.demand.outputs.d_payback[np.isfinite(self.demand.outputs.d_payback.values)]        
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.demand.outputs.d_payback,'subsector',subsector.id)
                    self.demand.outputs.d_payback.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.set_index('unit', append=True)
        self.demand.outputs.d_payback.columns = [cost_unit.upper()]
        self.demand.outputs.d_payback['lifetime_year'] = self.demand.outputs.d_payback.index.get_level_values('year')-self.demand.outputs.d_payback.index.get_level_values('vintage')+1    
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.set_index('lifetime_year',append=True)
        self.demand.outputs.d_payback = util.remove_df_levels(self.demand.outputs.d_payback,'year')
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.groupby(level = [x for x in self.demand.outputs.d_payback.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.demand.outputs.d_payback = self.demand.outputs.d_payback[self.demand.outputs.d_payback[cost_unit.upper()]!=0]
        self.demand.outputs.d_payback = self.demand.outputs.return_cleaned_output('d_payback')
   
    def calculate_d_payback_energy(self):
        initial_vintage = min(cfg.supply_years)
        demand_side_df = self.demand.d_all_energy_demand_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
#        sales_df.index = sales_df.index.reorder_levels(demand_side_df.index.names)
#        sales_df = sales_df.reindex(demand_side_df.index).sort_index()
        self.demand.outputs.d_payback_energy = util.DfOper.divi([demand_side_df, sales_df])
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy[np.isfinite(self.demand.outputs.d_payback_energy.values)]        
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.demand.outputs.d_payback_energy,'subsector',subsector.id)
                    self.demand.outputs.d_payback_energy.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.set_index('unit', append=True)
        self.demand.outputs.d_payback_energy.columns = [cfg.calculation_energy_unit.upper()]
        self.demand.outputs.d_payback_energy['lifetime_year'] = self.demand.outputs.d_payback_energy.index.get_level_values('year')-self.demand.outputs.d_payback_energy.index.get_level_values('vintage')+1    
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.set_index('lifetime_year',append=True)
        self.demand.outputs.d_payback_energy = util.remove_df_levels(self.demand.outputs.d_payback_energy,'year')
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.groupby(level = [x for x in self.demand.outputs.d_payback_energy.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy[self.demand.outputs.d_payback_energy[cfg.calculation_energy_unit.upper()]!=0]
        self.demand.outputs.d_payback_energy = self.demand.outputs.return_cleaned_output('d_payback_energy')

    def calc_and_format_export_costs(self):
        #calculate and format export costs
        if self.supply.export_costs is None:
            return None
        export_costs = GeoMapper.geo_map(self.supply.export_costs.copy(), GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'total')
        export_costs = Output.clean_df(export_costs)
        util.replace_index_name(export_costs, 'FINAL_ENERGY', 'SUPPLY_NODE_EXPORT')
        export_costs = util.add_to_df_index(export_costs, names=['EXPORT/DOMESTIC', "SUPPLY/DEMAND"], keys=["EXPORT", "SUPPLY"])
        cost_unit = cfg.getParam('currency_year') + " " + cfg.getParam('currency_name')
        export_costs.columns = [cost_unit.upper()]
        return export_costs

    def calc_and_format_embodied_costs(self):
        #calculate and format embodied supply costs
        embodied_costs_list = [Output.clean_df(x) for x in self.demand.outputs.demand_embodied_energy_costs]
        cost_unit = cfg.getParam('currency_year') + " " + cfg.getParam('currency_name')
        for embodied_costs in embodied_costs_list: embodied_costs.columns = [cost_unit.upper()]
        embodied_costs_list = [util.add_to_df_index(x, names=['EXPORT/DOMESTIC', "SUPPLY/DEMAND"], keys=["DOMESTIC","SUPPLY"]) for x in embodied_costs_list]
        return embodied_costs_list

    def calc_and_format_direct_demand_costs(self):
        #calculte and format direct demand costs
        if self.demand.outputs.d_levelized_costs is None:
            return None
        direct_costs = GeoMapper.geo_map(self.demand.outputs.d_levelized_costs.copy(), GeoMapper.demand_primary_geography, GeoMapper.combined_outputs_geography, 'total')
        direct_costs = direct_costs[direct_costs.index.get_level_values('year').isin(cfg.combined_years_subset)]
        levels_to_keep = [x for x in cfg.output_combined_levels if x in direct_costs.index.names]
        direct_costs = direct_costs.groupby(level=levels_to_keep).sum()
        direct_costs = Output.clean_df(direct_costs)
        direct_costs = util.add_to_df_index(direct_costs, names=['EXPORT/DOMESTIC', "SUPPLY/DEMAND"], keys=["DOMESTIC","DEMAND"])
        return direct_costs

    def calculate_combined_cost_results(self):
        cost_unit = cfg.getParam('currency_year') + " " + cfg.getParam('currency_name')
        export_costs = self.calc_and_format_export_costs()
        embodied_costs_list = self.calc_and_format_embodied_costs()
        direct_costs = self.calc_and_format_direct_demand_costs()
        export_costs = util.add_and_set_index(export_costs,['COST_TYPE'],['EXPORTED'])
        embodied_costs_list = [util.add_and_set_index(x,['COST_TYPE'],['SUPPLY-SIDE']) for x in embodied_costs_list]
        direct_costs = util.add_and_set_index(direct_costs,['COST_TYPE'],['DEMAND-SIDE'])
        if export_costs is not None:
            for name in [x for x in embodied_costs_list[0].index.names if x not in export_costs.index.names]:
                export_costs[name] = "N/A"
                export_costs.set_index(name,append=True,inplace=True)
            export_costs = export_costs.groupby(level=embodied_costs_list[0].index.names).sum()
        if direct_costs is not None:
            for name in [x for x in embodied_costs_list[0].index.names if x not in direct_costs.index.names]:
                direct_costs[name] = "N/A"
                direct_costs.set_index(name, append=True, inplace=True)
            direct_costs = direct_costs.groupby(level=embodied_costs_list[0].index.names).sum()
        self.outputs.c_costs = embodied_costs_list + [direct_costs] + [export_costs]
        self.outputs.c_costs= [x[x.values!=0] for x in self.outputs.c_costs]
        for x in self.outputs.c_costs: x.index = x.index.reorder_levels(embodied_costs_list[0].index.names)


    def calc_and_format_export_emissions(self):
        #calculate and format export emissions
        if self.supply.export_emissions is None:
            return None
        export_emissions = GeoMapper.geo_map(self.supply.export_emissions.copy(), GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'total')
        if 'supply_geography' not in cfg.output_combined_levels:
            util.remove_df_levels(export_emissions, GeoMapper.supply_primary_geography +'_supply')
        export_emissions = Output.clean_df(export_emissions)
        util.replace_index_name(export_emissions, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
        index_names = export_emissions.index.names
        export_emissions = export_emissions.reset_index()
        export_emissions['FINAL_ENERGY'] = 'export ' + export_emissions['FINAL_ENERGY']
        export_emissions = export_emissions.set_index(index_names).sort_index()
        export_emissions = util.add_to_df_index(export_emissions, names=['EXPORT/DOMESTIC', "SUPPLY/DEMAND"], keys=["EXPORT", "SUPPLY"])
        return export_emissions

    def calc_and_format_embodied_supply_emissions(self):
        # calculate and format embodied supply emissions
        embodied_emissions_list = [Output.clean_df(x) for x in self.demand.outputs.demand_embodied_emissions]
        embodied_emissions_list = [util.add_to_df_index(x, names=['EXPORT/DOMESTIC', "SUPPLY/DEMAND"], keys=["DOMESTIC", "SUPPLY"]) for x in embodied_emissions_list]
        return embodied_emissions_list

    def calc_and_format_direct_demand_emissions(self):
        #calculte and format direct demand emissions
        direct_emissions_list = [Output.clean_df(x) for x in self.demand.outputs.demand_direct_emissions]
        direct_emissions_list = [util.add_to_df_index(x, names=['EXPORT/DOMESTIC', "SUPPLY/DEMAND"], keys=["DOMESTIC", "DEMAND"]) for x in direct_emissions_list]
        if GeoMapper.combined_outputs_geography + '_supply' in cfg.output_combined_levels:
             keys = direct_emissions_list[0].index.get_level_values(GeoMapper.combined_outputs_geography.upper()).values
             names = GeoMapper.combined_outputs_geography.upper() + '_SUPPLY'
             for x in direct_emissions_list:
                x[names] = keys

             direct_emissions_list = [x.set_index(names, append=True, inplace=True) for x in direct_emissions_list]
        return direct_emissions_list

    def calculate_combined_emissions_results(self):
        export_emissions = self.calc_and_format_export_emissions()
        embodied_emissions_list = self.calc_and_format_embodied_supply_emissions()
        direct_emissions_list = self.calc_and_format_direct_demand_emissions()
        export_emissions = util.add_and_set_index(export_emissions,['EMISSIONS_TYPE'],['EXPORTED'])
        embodied_emissions_list = [util.add_and_set_index(x, ['EMISSIONS_TYPE'], ['SUPPLY_SIDE']) for x in embodied_emissions_list]
        direct_emissions_list = [util.add_and_set_index(x,['EMISSIONS_TYPE'],['DEMAND_SIDE']) for x in direct_emissions_list]
        if export_emissions is not None:
            for name in [x for x in embodied_emissions_list[0].index.names if x not in export_emissions.index.names]:
                export_emissions[name] = "N/A"
                export_emissions.set_index(name,append=True,inplace=True)
            export_emissions = export_emissions.groupby(level=embodied_emissions_list[0].index.names).sum()
        if direct_emissions_list is not None:
            for df in direct_emissions_list:
                for name in [x for x in embodied_emissions_list[0].index.names if x not in df.index.names]:
                    df[name] = "N/A"
                    df.set_index(name,append=True,inplace=True)
        self.outputs.c_emissions = [export_emissions] + embodied_emissions_list + direct_emissions_list
        self.outputs.c_emissions = [util.replace_index_name(x, GeoMapper.combined_outputs_geography.upper() +'-EMITTED', GeoMapper.combined_outputs_geography.upper() +'_SUPPLY',inplace=True) for x in self.outputs.c_emissions]
        self.outputs.c_emissions = [util.replace_index_name(x, GeoMapper.combined_outputs_geography.upper() +'-CONSUMED', GeoMapper.combined_outputs_geography.upper(),inplace=True) for x in self.outputs.c_emissions]
        self.outputs.c_emissions = [x[x['VALUE']!=0] for x in  self.outputs.c_emissions]
        emissions_unit = cfg.getParam('mass_unit')
        for x in self.outputs.c_emissions:
            x.columns = [emissions_unit.upper()]
        for x in self.outputs.c_emissions: x.index = x.index.reorder_levels([l for l in embodied_emissions_list[0].index.names if l in x.index.names])
    def calc_and_format_export_energy(self):
        if self.supply.export_energy is None:
            return None
        export_energy = GeoMapper.geo_map(self.supply.export_energy.copy(), GeoMapper.supply_primary_geography, GeoMapper.combined_outputs_geography, 'total')
        export_energy = Output.clean_df(export_energy)
        util.replace_index_name(export_energy, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
        export_energy = util.add_to_df_index(export_energy, names=['EXPORT/DOMESTIC', "ENERGY ACCOUNTING"], keys=["EXPORT", "EMBODIED"])
        for x in cfg.output_combined_levels:
            if x not in export_energy.index.names:
                export_energy = util.add_and_set_index(export_energy,[x],["N/A"])
        return export_energy

    def calc_and_format_embodied_supply_energy(self):
        embodied_energy_list = [Output.clean_df(x) for x in self.demand.outputs.demand_embodied_energy]
        embodied_energy_list = [x[x['VALUE']!=0] for x in embodied_energy_list]
        embodied_energy_list = [util.add_to_df_index(x, names=['EXPORT/DOMESTIC', "ENERGY ACCOUNTING"], keys=['DOMESTIC','EMBODIED']) for x in embodied_energy_list]
        return embodied_energy_list

    def calc_and_format_direct_demand_energy(self):
        demand_energy = GeoMapper.geo_map(self.demand.outputs.d_energy.copy(), GeoMapper.demand_primary_geography, GeoMapper.combined_outputs_geography, 'total')
        demand_energy = Output.clean_df(demand_energy)
        demand_energy = demand_energy[demand_energy.index.get_level_values('YEAR')>=cfg.getParamAsInt('current_year')]
        demand_energy = util.add_to_df_index(demand_energy, names=['EXPORT/DOMESTIC', "ENERGY ACCOUNTING"], keys=['DOMESTIC','FINAL'])
        return demand_energy

    def calculate_combined_energy_results(self):
        export_energy = self.calc_and_format_export_energy()
        embodied_energy_list = self.calc_and_format_embodied_supply_energy()
        demand_energy = self.calc_and_format_direct_demand_energy()
        # reorder levels so dfs match
        for name in [x for x in embodied_energy_list[0].index.names if x not in demand_energy.index.names]:
            demand_energy[name] = "N/A"
            demand_energy.set_index(name, append=True, inplace=True)
        demand_energy = demand_energy.groupby(level=embodied_energy_list[0].index.names).sum()
        if export_energy is not None:
            for name in [x for x in embodied_energy_list[0].index.names if x not in export_energy.index.names]:
                export_energy[name] = "N/A"
                export_energy.set_index(name,append=True,inplace=True)
            export_energy = export_energy.groupby(level=embodied_energy_list[0].index.names).sum()

        self.outputs.c_energy = embodied_energy_list + [demand_energy] + [export_energy]
        self.outputs.c_energy = [x[x['VALUE']!=0] for x in self.outputs.c_energy]
        energy_unit = cfg.calculation_energy_unit
        for x in self.outputs.c_energy: x.columns = [energy_unit.upper()]
        for x in self.outputs.c_energy: x.index = x.index.reorder_levels(embodied_energy_list[0].index.names)

    def export_io(self):
        io_table_write_step = cfg.getParamAsInt('io_table_write_step', 'output_detail')
        io_table_years = sorted([min(self.supply.years)] + range(max(self.supply.years), min(self.supply.years), -io_table_write_step))
        df_list = []
        for year in io_table_years:
            keys = self.supply.demand_sectors
            year_df = pd.concat([self.supply.io_dict[year][sector] for sector in keys], keys=keys,names=['sector'])
            year_df = pd.concat([year_df]*len(keys), keys=keys, names=['sector'], axis=1)
            df_list.append(year_df)
        keys = io_table_years
        name = ['year']
        df = pd.concat(df_list,keys=keys,names=name)
        for row_sector in self.supply.demand_sectors:
            for col_sector in self.supply.demand_sectors:
                if row_sector != col_sector:
                    df.loc[util.level_specific_indexer(df,'sector',row_sector),util.level_specific_indexer(df,'sector',col_sector,axis=1)] = 0
        self.supply.outputs.io = df
        result_df = self.supply.outputs.return_cleaned_output('io')
        keys = [self.scenario.name.upper(), cfg.timestamp]
        names = ['SCENARIO','TIMESTAMP']
        for key, name in zip(keys,names):
            result_df = pd.concat([result_df], keys=[key],names=[name])
        Output.write(result_df, 's_io.csv', os.path.join(cfg.workingdir, 'supply_outputs'))
#        self.export_stacked_io()

    def export_stacked_io(self):
        df = copy.deepcopy(self.supply.outputs.io)
        df.index.names = [x + '_input'if x!= 'year' else x for x in df.index.names ]
        df = df.stack(level=df.columns.names).to_frame()
        df.columns = ['value']
        self.supply.outputs.stacked_io = df
        result_df = self.supply.outputs.return_cleaned_output('stacked_io')
        keys = [self.scenario.name.upper(), cfg.timestamp]
        names = ['SCENARIO','TIMESTAMP']
        for key, name in zip(keys,names):
            result_df = pd.concat([result_df], keys=[key],names=[name])
        Output.write(result_df, 's_stacked_io.csv', os.path.join(cfg.workingdir, 'supply_outputs'))
np.random.seed(42)  # Reproducibility MPR

# Initializing vehicles
Tampere.reset()

# ==============================================================================
# Defaults
# ==============================================================================

# Traffic Network
trf_net = {"lengths_per_link": (20000, 10000), "lanes_per_link": (1, 2)}
traffic_network = TrafficNetwork(**trf_net)

# Demand
demands = Demand((C / 2, ), (12, ))
trf_dmd = {"lks": (1, ), "demands": (demands, demands)}

traffic_demand = TrafficDemand(**trf_dmd)

# ==============================================================================
# Classes
# ==============================================================================


class Scenario:
    def __init__(
        self,
        traffic_network=traffic_network,
        traffic_demand=traffic_demand,
        mpr=MPR,
class PathwaysModel(object):
    """
    Highest level classification of the definition of an energy system.
    """
    def __init__(self, scenario_id, api_run=False):
        self.scenario_id = scenario_id
        self.scenario = Scenario(self.scenario_id)
        self.api_run = api_run
        self.outputs = Output()
        self.demand = Demand(self.scenario)
        self.supply = None
        self.demand_solved, self.supply_solved = False, False

    def run(self, scenario_id, solve_demand, solve_supply, load_demand, load_supply, export_results, save_models, append_results):
        try:
            if solve_demand and not (load_demand or load_supply):
                self.calculate_demand(save_models)
            
            if not append_results:
                self.remove_old_results()

            # it is nice if when loading a demand side object to rerun supply, it doesn't re-output these results every time
            if self.demand_solved and export_results and not self.api_run and not (load_demand and solve_supply):
                self.export_result_to_csv('demand_outputs')

            if solve_supply and not load_supply:
                if load_demand:
                    # if we are loading the demand, we are changing the supply measures and want to reload our scenarios
                    self.scenario = Scenario(self.scenario_id)
                self.supply = Supply(self.scenario, demand_object=self.demand)
                self.calculate_supply(save_models)

            if load_demand and solve_supply:
                # we do this now because we delayed before
                self.export_result_to_csv('demand_outputs')

            if self.supply_solved and export_results and load_supply or solve_supply:
                self.supply.calculate_supply_outputs()
                self.pass_supply_results_back_to_demand()
                self.calculate_combined_results()
                self.outputs.electricity_reconciliation = self.demand.electricity_reconciliation # we want to write these to outputs
                if self.api_run:
                    self.export_results_to_db()
                else:
                    self.export_result_to_csv('supply_outputs')
                    self.export_result_to_csv('combined_outputs')
                    self.export_io()
        except:
            # pickle the model in the event that it crashes
            if save_models:
                Output.pickle(self, file_name=str(scenario_id) + cfg.model_error_append_name, path=cfg.workingdir)
            raise

    def calculate_demand(self, save_models):
        self.demand.setup_and_solve()
        self.demand_solved = True
        if cfg.output_payback == 'true':
            if self.demand.d_all_energy_demand_payback is not None:
                self.calculate_d_payback()
                self.calculate_d_payback_energy()
        if save_models:
            Output.pickle(self, file_name=str(self.scenario_id) + cfg.demand_model_append_name, path=cfg.workingdir)

    def calculate_supply(self, save_models):
        if not self.demand_solved:
            raise ValueError('demand must be solved first before supply')
        logging.info('Configuring energy system supply')
        self.supply.add_nodes()
        self.supply.add_measures()
        self.supply.initial_calculate()
        self.supply.calculated_years = []
        self.supply.calculate_loop(self.supply.years, self.supply.calculated_years)
        self.supply.final_calculate()
        self.supply_solved = True
        if save_models:
            Output.pickle(self, file_name=str(self.scenario_id) + cfg.full_model_append_name, path=cfg.workingdir)
            # we don't need the demand side object any more, so we can remove it to save drive space
            if os.path.isfile(os.path.join(cfg.workingdir, str(self.scenario_id) + cfg.demand_model_append_name)):
                os.remove(os.path.join(cfg.workingdir, str(self.scenario_id) + cfg.demand_model_append_name))

    def pass_supply_results_back_to_demand(self):
        logging.info("Calculating link to supply")
        self.demand.link_to_supply(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.energy_demand_link, self.supply.cost_demand_link)
        if cfg.output_tco == 'true':
            if hasattr(self,'d_energy_tco'):
                self.demand.link_to_supply_tco(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.cost_demand_link) 
            else:
               print  "demand side has not been run with tco outputs set to 'true'"
        if cfg.output_payback == 'true':
            if hasattr(self,'demand.d_all_energy_demand_payback'):
                self.demand.link_to_supply_payback(self.supply.emissions_demand_link, self.supply.demand_emissions_rates, self.supply.cost_demand_link) 
            else:
               print  "demand side has not been run with tco outputs set to 'true'"
    
    def calculate_combined_results(self):
        logging.info("Calculating combined emissions results")
        self.calculate_combined_emissions_results()
        logging.info("Calculating combined cost results")
        self.calculate_combined_cost_results()
        logging.info("Calculating combined energy results")
        self.calculate_combined_energy_results()
        if cfg.output_tco == 'true':
            if self.demand.d_energy_tco is not None:
                self.calculate_tco()
        if cfg.output_payback == 'true':
            if self.demand.d_all_energy_demand_payback is not None:
                self.calculate_payback()

    def remove_old_results(self):
        folder_names = ['combined_outputs', 'demand_outputs', 'supply_outputs', 'dispatch_outputs']
        for folder_name in folder_names:
            folder = os.path.join(cfg.workingdir, folder_name)
            if os.path.isdir(folder):
                shutil.rmtree(folder)

    def export_result_to_csv(self, result_name):
        if result_name=='combined_outputs':
            res_obj = self.outputs
        elif result_name=='demand_outputs':
            res_obj = self.demand.outputs
        elif result_name=='supply_outputs':
            res_obj = self.supply.outputs
        else:
            raise ValueError('result_name not recognized')

        for attribute in dir(res_obj):
            if not isinstance(getattr(res_obj, attribute), pd.DataFrame):
                continue

            result_df = getattr(res_obj, 'return_cleaned_output')(attribute)
            keys = [self.scenario.name.upper(), cfg.timestamp]
            names = ['SCENARIO','TIMESTAMP']
            for key, name in zip(keys, names):
                result_df = pd.concat([result_df], keys=[key], names=[name])

            if attribute in ('hourly_dispatch_results', 'electricity_reconciliation', 'hourly_marginal_cost', 'hourly_production_cost'):
                # Special case for hourly dispatch results where we want to write them outside of supply_outputs
                Output.write(result_df, attribute + '.csv', os.path.join(cfg.workingdir, 'dispatch_outputs'))
            else:
                Output.write(result_df, attribute+'.csv', os.path.join(cfg.workingdir, result_name))

    def export_results_to_db(self):
        scenario_run_id = util.active_scenario_run_id(self.scenario_id)
        # Levelized costs
        costs = self.outputs.c_costs.groupby(level=['SUPPLY/DEMAND', 'YEAR']).sum()
        util.write_output_to_db(scenario_run_id, 1, costs)

        #Energy
        energy = self.outputs.c_energy.xs('FINAL', level='ENERGY ACCOUNTING')\
            .groupby(level=['SECTOR', 'FINAL_ENERGY', 'YEAR']).sum()
        # Energy demand by sector
        util.write_output_to_db(scenario_run_id, 2, energy.groupby(level=['SECTOR', 'YEAR']).sum())
        # Residential Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 6, energy.xs('RESIDENTIAL', level='SECTOR'))
        # Commercial Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 8, energy.xs('COMMERCIAL', level='SECTOR'))
        # Transportation Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 10, energy.xs('TRANSPORTATION', level='SECTOR'))
        # Productive Energy by Fuel Type
        util.write_output_to_db(scenario_run_id, 12, energy.xs('PRODUCTIVE', level='SECTOR'))

        #Emissions
        emissions = self.outputs.c_emissions.xs('DOMESTIC', level='EXPORT/DOMESTIC')\
            .groupby(level=['SECTOR', 'FINAL_ENERGY', 'YEAR']).sum()
        emissions = util.DfOper.mult((emissions, 1-(emissions.abs()<1E-10).groupby(level='FINAL_ENERGY').all())) # get rid of noise
        # Annual emissions by sector
        util.write_output_to_db(scenario_run_id, 3, emissions.groupby(level=['SECTOR', 'YEAR']).sum())
        # Residential Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 7, emissions.xs('RESIDENTIAL', level='SECTOR'))
        # Commercial Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 9, emissions.xs('COMMERCIAL', level='SECTOR'))
        # Transportation Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 11, emissions.xs('TRANSPORTATION', level='SECTOR'))
        # Productive Emissions by Fuel Type
        util.write_output_to_db(scenario_run_id, 13, emissions.xs('PRODUCTIVE', level='SECTOR'))

        # Domestic emissions per capita
        annual_emissions = self.outputs.c_emissions.xs('DOMESTIC', level='EXPORT/DOMESTIC').groupby(level=['YEAR']).sum()
        population_driver = self.demand.drivers[2].values.groupby(level='year').sum().loc[annual_emissions.index]
        population_driver.index.name = 'YEAR'
        factor = 1E6
        df = util.DfOper.divi((annual_emissions, population_driver)) * factor
        df.columns = ['TONNE PER CAPITA']
        util.write_output_to_db(scenario_run_id, 4, df)

        # Electricity supply
        electricity_node_names = [self.supply.nodes[nodeid].name.upper() for nodeid in util.flatten_list(self.supply.injection_nodes.values())]
        df = self.outputs.c_energy.xs('ELECTRICITY', level='FINAL_ENERGY')\
            .xs('EMBODIED', level='ENERGY ACCOUNTING')\
            .groupby(level=['SUPPLY_NODE', 'YEAR']).sum()
        util.write_output_to_db(scenario_run_id, 5, df.loc[electricity_node_names])

    def calculate_combined_cost_results(self):
        #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        if self.supply.export_costs is not None:
            setattr(self.outputs,'export_costs',self.supply.export_costs)
            self.export_costs_df = self.outputs.return_cleaned_output('export_costs')
            del self.outputs.export_costs
            util.replace_index_name(self.export_costs_df, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","SUPPLY"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.export_costs_df = pd.concat([self.export_costs_df],keys=[key],names=[name])
            self.export_costs_df.columns = [cost_unit.upper()]  
        else:
            self.export_costs_df = None
        #calculate and format emobodied supply costs
        self.embodied_energy_costs_df = self.demand.outputs.return_cleaned_output('demand_embodied_energy_costs')
        self.embodied_energy_costs_df.columns = [cost_unit.upper()]
        keys = ["DOMESTIC","SUPPLY"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
           self.embodied_energy_costs_df = pd.concat([self.embodied_energy_costs_df],keys=[key],names=[name])
        #calculte and format direct demand costs
        self.demand_costs_df = self.demand.outputs.return_cleaned_output('d_levelized_costs')
        if self.demand_costs_df is not None:
            levels_to_keep = [x.upper() for x in cfg.output_combined_levels]
            levels_to_keep = [x for x in levels_to_keep if x in self.demand_costs_df.index.names]
            self.demand_costs_df = self.demand_costs_df.groupby(level=levels_to_keep).sum()
            keys = ["DOMESTIC","DEMAND"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.demand_costs_df = pd.concat([self.demand_costs_df],keys=[key],names=[name])
        keys = ['EXPORTED', 'SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_costs = util.df_list_concatenate([self.export_costs_df, self.embodied_energy_costs_df, self.demand_costs_df],keys=keys,new_names=names)
        self.outputs.c_costs[self.outputs.c_costs<0]=0
        self.outputs.c_costs= self.outputs.c_costs[self.outputs.c_costs[cost_unit.upper()]!=0]
        
    def calculate_tco(self):
#        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions_tco')
#        del self.demand.outputs.demand_embodied_emissions
        #calculte and format direct demand emissions        
#        self.direct_emissions_df = self.demand.outputs.return_cleaned_output('demand_direct_emissions')
##        del self.demand.outputs.demand_direct_emissions
#        emissions = util.DfOper.add([self.embodied_emissions_df,self.direct_emissions_df])
#         #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        initial_vintage = min(cfg.supply_years)
        supply_side_df = self.demand.outputs.demand_embodied_energy_costs_tco
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = self.demand.d_levelized_costs_tco
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        service_demand_df = self.demand.d_service_demand_tco
        service_demand_df = service_demand_df[service_demand_df.index.get_level_values('vintage')>=initial_vintage]
        keys = ['SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_tco = pd.concat([util.DfOper.divi([supply_side_df,util.remove_df_levels(service_demand_df,'unit')]),
                                        util.DfOper.divi([demand_side_df,util.remove_df_levels(service_demand_df,'unit')])],
                                        keys=keys,names=names) 
        self.outputs.c_tco = self.outputs.c_tco.replace([np.inf,np.nan],0)
        self.outputs.c_tco[self.outputs.c_tco<0]=0        
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'service_demand') and hasattr(subsector,'stock'):
                    indexer = util.level_specific_indexer(self.outputs.c_tco,'subsector',subsector.id)
                    self.outputs.c_tco.loc[indexer,'unit'] = subsector.service_demand.unit.upper()
        self.outputs.c_tco = self.outputs.c_tco.set_index('unit',append=True)
        self.outputs.c_tco.columns = [cost_unit.upper()]
        self.outputs.c_tco= self.outputs.c_tco[self.outputs.c_tco[cost_unit.upper()]!=0]
        self.outputs.c_tco = self.outputs.return_cleaned_output('c_tco')
        
        
        
    def calculate_payback(self):
#        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions_tco')
#        del self.demand.outputs.demand_embodied_emissions
        #calculte and format direct demand emissions        
#        self.direct_emissions_df = self.demand.outputs.return_cleaned_output('demand_direct_emissions')
##        del self.demand.outputs.demand_direct_emissions
#        emissions = util.DfOper.add([self.embodied_emissions_df,self.direct_emissions_df])
#         #calculate and format export costs
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        initial_vintage = min(cfg.supply_years)
        supply_side_df = self.demand.outputs.demand_embodied_energy_costs_payback
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('vintage')>=initial_vintage]
        supply_side_df = supply_side_df[supply_side_df.index.get_level_values('year')>=initial_vintage]
        supply_side_df = supply_side_df.sort_index()
        demand_side_df = self.demand.d_annual_costs_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        demand_side_df = demand_side_df.reindex(supply_side_df.index).sort_index()
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
        sales_df.index = sales_df.index.reorder_levels(supply_side_df.index.names)
        sales_df = sales_df.reindex(supply_side_df.index).sort_index()
        keys = ['SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['COST TYPE']
        self.outputs.c_payback = pd.concat([util.DfOper.divi([supply_side_df, sales_df]), util.DfOper.divi([demand_side_df, sales_df])],keys=keys,names=names)
        self.outputs.c_payback = self.outputs.c_payback[np.isfinite(self.outputs.c_payback.values)]        
        self.outputs.c_payback = self.outputs.c_payback.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.outputs.c_payback,'subsector',subsector.id)
                    self.outputs.c_payback.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.outputs.c_payback = self.outputs.c_payback.set_index('unit', append=True)
        self.outputs.c_payback.columns = [cost_unit.upper()]
        self.outputs.c_payback['lifetime_year'] = self.outputs.c_payback.index.get_level_values('year')-self.outputs.c_payback.index.get_level_values('vintage')+1    
        self.outputs.c_payback = self.outputs.c_payback.set_index('lifetime_year',append=True)
        self.outputs.c_payback = util.remove_df_levels(self.outputs.c_payback,'year')
        self.outputs.c_payback = self.outputs.c_payback.groupby(level = [x for x in self.outputs.c_payback.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.outputs.c_payback = self.outputs.c_payback[self.outputs.c_payback[cost_unit.upper()]!=0]
        self.outputs.c_payback = self.outputs.return_cleaned_output('c_payback')
        
        
    def calculate_d_payback(self):
        cost_unit = cfg.cfgfile.get('case','currency_year_id') + " " + cfg.cfgfile.get('case','currency_name')
        initial_vintage = min(cfg.supply_years)
        demand_side_df = self.demand.d_annual_costs_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
        sales_df.index = sales_df.index.reorder_levels(demand_side_df.index.names)
        sales_df = sales_df.reindex(demand_side_df.index).sort_index()
        self.demand.outputs.d_payback = util.DfOper.divi([demand_side_df, sales_df])
        self.demand.outputs.d_payback = self.demand.outputs.d_payback[np.isfinite(self.demand.outputs.d_payback.values)]        
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.demand.outputs.d_payback,'subsector',subsector.id)
                    self.demand.outputs.d_payback.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.set_index('unit', append=True)
        self.demand.outputs.d_payback.columns = [cost_unit.upper()]
        self.demand.outputs.d_payback['lifetime_year'] = self.demand.outputs.d_payback.index.get_level_values('year')-self.demand.outputs.d_payback.index.get_level_values('vintage')+1    
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.set_index('lifetime_year',append=True)
        self.demand.outputs.d_payback = util.remove_df_levels(self.demand.outputs.d_payback,'year')
        self.demand.outputs.d_payback = self.demand.outputs.d_payback.groupby(level = [x for x in self.demand.outputs.d_payback.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.demand.outputs.d_payback = self.demand.outputs.d_payback[self.demand.outputs.d_payback[cost_unit.upper()]!=0]
        self.demand.outputs.d_payback = self.demand.outputs.return_cleaned_output('d_payback')
   
    def calculate_d_payback_energy(self):
        initial_vintage = min(cfg.supply_years)
        demand_side_df = self.demand.d_all_energy_demand_payback
        demand_side_df.columns = ['value']
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('vintage')>=initial_vintage]
        demand_side_df = demand_side_df[demand_side_df.index.get_level_values('year')>=initial_vintage]
        sales_df = copy.deepcopy(self.demand.outputs.d_sales)
        util.replace_index_name(sales_df,'vintage','year')
        sales_df = sales_df[sales_df.index.get_level_values('vintage')>=initial_vintage]     
        sales_df = util.add_and_set_index(sales_df,'year',cfg.supply_years)
#        sales_df.index = sales_df.index.reorder_levels(demand_side_df.index.names)
#        sales_df = sales_df.reindex(demand_side_df.index).sort_index()
        self.demand.outputs.d_payback_energy = util.DfOper.divi([demand_side_df, sales_df])
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy[np.isfinite(self.demand.outputs.d_payback_energy.values)]        
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.replace([np.inf,np.nan],0)
        for sector in self.demand.sectors.values():
          for subsector in sector.subsectors.values():
                if hasattr(subsector,'stock') and subsector.sub_type!='link':
                    indexer = util.level_specific_indexer(self.demand.outputs.d_payback_energy,'subsector',subsector.id)
                    self.demand.outputs.d_payback_energy.loc[indexer,'unit'] = subsector.stock.unit.upper()
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.set_index('unit', append=True)
        self.demand.outputs.d_payback_energy.columns = [cfg.calculation_energy_unit.upper()]
        self.demand.outputs.d_payback_energy['lifetime_year'] = self.demand.outputs.d_payback_energy.index.get_level_values('year')-self.demand.outputs.d_payback_energy.index.get_level_values('vintage')+1    
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.set_index('lifetime_year',append=True)
        self.demand.outputs.d_payback_energy = util.remove_df_levels(self.demand.outputs.d_payback_energy,'year')
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy.groupby(level = [x for x in self.demand.outputs.d_payback_energy.index.names if x !='lifetime_year']).transform(lambda x: x.cumsum())
        self.demand.outputs.d_payback_energy = self.demand.outputs.d_payback_energy[self.demand.outputs.d_payback_energy[cfg.calculation_energy_unit.upper()]!=0]
        self.demand.outputs.d_payback_energy = self.demand.outputs.return_cleaned_output('d_payback_energy')
            
        
    def calculate_combined_emissions_results(self):
        #calculate and format export emissions
        if self.supply.export_emissions is not None:
            setattr(self.outputs,'export_emissions',self.supply.export_emissions)
            if 'supply_geography' not in cfg.output_combined_levels:
                util.remove_df_levels(self.outputs.export_emissions, cfg.primary_geography +'_supply')
            self.export_emissions_df = self.outputs.return_cleaned_output('export_emissions')
            del self.outputs.export_emissions
            util.replace_index_name(self.export_emissions_df, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","SUPPLY"]
            names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
            for key,name in zip(keys,names):
                self.export_emissions_df = pd.concat([self.export_emissions_df],keys=[key],names=[name])
        else:
            self.export_emissions_df = None
       #calculate and format emobodied supply emissions
        self.embodied_emissions_df = self.demand.outputs.return_cleaned_output('demand_embodied_emissions')
#        del self.demand.outputs.demand_embodied_emissions
        keys = ["DOMESTIC","SUPPLY"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key,name in zip(keys,names):
           self.embodied_emissions_df = pd.concat([self.embodied_emissions_df],keys=[key],names=[name])       
        #calculte and format direct demand emissions        
        self.direct_emissions_df = self.demand.outputs.return_cleaned_output('demand_direct_emissions')
#        del self.demand.outputs.demand_direct_emissions
        keys = ["DOMESTIC","DEMAND"]
        names = ['EXPORT/DOMESTIC', "SUPPLY/DEMAND"]
        for key, name in zip(keys, names):
            self.direct_emissions_df = pd.concat([self.direct_emissions_df], keys=[key], names=[name])
        if cfg.primary_geography+'_supply' in cfg.output_combined_levels:
             keys = self.direct_emissions_df.index.get_level_values(cfg.primary_geography.upper()).values
             names = cfg.primary_geography.upper() +'_SUPPLY'
             self.direct_emissions_df[names] = keys
             self.direct_emissions_df.set_index(names,append=True,inplace=True)
        keys = ['EXPORTED', 'SUPPLY-SIDE', 'DEMAND-SIDE']
        names = ['EMISSIONS TYPE']
        self.outputs.c_emissions = util.df_list_concatenate([self.export_emissions_df, self.embodied_emissions_df, self.direct_emissions_df],keys=keys,new_names = names)
        util.replace_index_name(self.outputs.c_emissions, cfg.primary_geography.upper() +'-EMITTED', cfg.primary_geography.upper() +'_SUPPLY')
        util.replace_index_name(self.outputs.c_emissions, cfg.primary_geography.upper() +'-CONSUMED', cfg.primary_geography.upper())
        self.outputs.c_emissions= self.outputs.c_emissions[self.outputs.c_emissions['VALUE']!=0]
        emissions_unit = cfg.cfgfile.get('case','mass_unit')
        self.outputs.c_emissions.columns = [emissions_unit.upper()]
            
    def calculate_combined_energy_results(self):
         energy_unit = cfg.calculation_energy_unit
         if self.supply.export_costs is not None:
            setattr(self.outputs,'export_energy',self.supply.export_energy)
            self.export_energy = self.outputs.return_cleaned_output('export_energy')
            del self.outputs.export_energy
            util.replace_index_name(self.export_energy, 'FINAL_ENERGY','SUPPLY_NODE_EXPORT')
            keys = ["EXPORT","EMBODIED"]
            names = ['EXPORT/DOMESTIC', 'ENERGY ACCOUNTING']
            for key,name in zip(keys,names):
                self.export_energy = pd.concat([self.export_energy],keys=[key],names=[name])
         else:
            self.export_energy = None
         self.embodied_energy = self.demand.outputs.return_cleaned_output('demand_embodied_energy')
         self.embodied_energy = self.embodied_energy[self.embodied_energy ['VALUE']!=0]
         keys = ['DOMESTIC','EMBODIED']
         names = ['EXPORT/DOMESTIC', 'ENERGY ACCOUNTING']
         for key,name in zip(keys,names):
             self.embodied_energy = pd.concat([self.embodied_energy],keys=[key],names=[name])
         self.final_energy = self.demand.outputs.return_cleaned_output('d_energy')
         self.final_energy = self.final_energy[self.final_energy.index.get_level_values('YEAR')>=int(cfg.cfgfile.get('case','current_year'))]
         keys = ['DOMESTIC','FINAL']
         names = ['EXPORT/DOMESTIC', 'ENERGY ACCOUNTING']
         for key,name in zip(keys,names):
             self.final_energy = pd.concat([self.final_energy],keys=[key],names=[name])
    #         self.outputs.c_energy = pd.concat([self.embodied_energy, self.final_energy],keys=['DROP'],names=['DROP'])
         for name in [x for x in self.embodied_energy.index.names if x not in self.final_energy.index.names]:
             self.final_energy[name] = "N/A"
             self.final_energy.set_index(name,append=True,inplace=True)
         if self.export_energy is not None:
             for name in [x for x in self.embodied_energy.index.names if x not in self.export_energy.index.names]:
                 self.export_energy[name] = "N/A"
                 self.export_energy.set_index(name,append=True,inplace=True)
             self.export_energy = self.export_energy.groupby(level=self.embodied_energy.index.names).sum()
             self.export_energy = self.export_energy.reorder_levels(self.embodied_energy.index.names)
         self.final_energy = self.final_energy.groupby(level=self.embodied_energy.index.names).sum()
         self.final_energy = self.final_energy.reorder_levels(self.embodied_energy.index.names)
         self.outputs.c_energy = pd.concat([self.embodied_energy,self.final_energy,self.export_energy])
         self.outputs.c_energy= self.outputs.c_energy[self.outputs.c_energy['VALUE']!=0]
         self.outputs.c_energy.columns = [energy_unit.upper()]

    def export_io(self):
        io_table_write_step = int(cfg.cfgfile.get('output_detail','io_table_write_step'))
        io_table_years = sorted([min(cfg.supply_years)] + range(max(cfg.supply_years), min(cfg.supply_years), -io_table_write_step))
        df_list = []
        for year in io_table_years:
            sector_df_list = []
            keys = self.supply.demand_sectors
            name = ['sector']
            for sector in self.supply.demand_sectors:
                sector_df_list.append(self.supply.io_dict[year][sector])
            year_df = pd.concat(sector_df_list, keys=keys,names=name)
            year_df = pd.concat([year_df]*len(keys),keys=keys,names=name,axis=1)
            df_list.append(year_df)
        keys = io_table_years
        name = ['year']
        df = pd.concat(df_list,keys=keys,names=name)
        for row_sector in self.supply.demand_sectors:
            for col_sector in self.supply.demand_sectors:
                if row_sector != col_sector:
                    df.loc[util.level_specific_indexer(df,'sector',row_sector),util.level_specific_indexer(df,'sector',col_sector,axis=1)] = 0
        self.supply.outputs.io = df
        result_df = self.supply.outputs.return_cleaned_output('io')
        keys = [self.scenario.name.upper(), cfg.timestamp]
        names = ['SCENARIO','TIMESTAMP']
        for key, name in zip(keys,names):
            result_df = pd.concat([result_df], keys=[key],names=[name])
        Output.write(result_df, 's_io.csv', os.path.join(cfg.workingdir, 'supply_outputs'))
#        self.export_stacked_io()

    def export_stacked_io(self):
        df = copy.deepcopy(self.supply.outputs.io)
        df.index.names = [x + '_input'if x!= 'year' else x for x in df.index.names ]
        df = df.stack(level=df.columns.names).to_frame()
        df.columns = ['value']
        self.supply.outputs.stacked_io = df
        result_df = self.supply.outputs.return_cleaned_output('stacked_io')
        keys = [self.scenario.name.upper(), cfg.timestamp]
        names = ['SCENARIO','TIMESTAMP']
        for key, name in zip(keys,names):
            result_df = pd.concat([result_df], keys=[key],names=[name])
        Output.write(result_df, 's_stacked_io.csv', os.path.join(cfg.workingdir, 'supply_outputs'))
from smbus_sensor_conf import SmbusSensorConf
from demand_driven_io import DemandDrivenIo
from sensor_exception import SensorException
from demand import Demand


class DemandSmbusSensor(SmbusSensorConf, DemandDrivenIo, SensorException):
    """This class is for the demand driven sensors"""

    def __init__(self, address=None, bus=1):
        super().__init__(self, address, bus)
        self.event_handlers = EventHandler()

    def demand_issue(self, demand, **catch_event)
        self.sensor_data = [] # 読み取ってとってくる値を格納する箱
        if isinstance(demand, Demand()):
            self.demand = demand
        else:
            print('Please give a Demand object.')
            return False

        if catch_event != {}:
            # cath_eventは{'1011':method} これがもらえるとよそう
            # main処理 書 -> 読 -> コールバック(値に応じた)
            for event in catch_event:
                self.event_handlers.add(event, catch_event[event])

            for write in demand.write_methods():
                write()

            time.sleep(demand.interval)
class PathwaysModel(object):
    """
    Highest level classification of the definition of an energy system.
    Includes the primary geography of the energy system (i.e. country name) as well as the author.
    """

    def __init__(self, db_path, cfgfile_path, custom_pint_definitions_path=None, name=None, author=None):
        self.model_config(db_path, cfgfile_path, custom_pint_definitions_path)
        self.name = cfg.cfgfile.get("case", "scenario") if name is None else name
        self.author = cfg.cfgfile.get("case", "author") if author is None else author
        self.demand = Demand()
        self.supply = Supply()

    def model_config(self, db_path, cfgfile_path, custom_pint_definitions_path):
        cfg.init_cfgfile(cfgfile_path)
        cfg.init_db(db_path)
        cfg.init_pint(custom_pint_definitions_path)
        cfg.init_geo()
        cfg.init_shapes()
        cfg.init_outputs_id_map()

    def configure_energy_system(self):
        print "configuring energy system"
        self.configure_demand()
        self.configure_supply()
        cfg.init_outputs_id_map()

    def populate_energy_system(self):
        self.populate_demand_system()
        self.populate_supply_system()

    def populate_measures(self):
        self.populate_demand_measures()
        self.populate_supply_measures()

    def calculate(self):
        self.calculate_demand_only()
        self.pass_results_to_supply()
        self.calculate_supply()

    def configure_demand(self):
        """Read in and initialize data"""
        # Drivers must come first
        self.demand.add_drivers()

        # Sectors requires drivers be read in
        self.demand.add_sectors()
        for sector in self.demand.sectors.values():
            # print 'configuring the %s sector'  %sector.name
            sector.add_subsectors()

    def configure_supply(self):
        self.supply.add_nodes()

    def populate_demand_system(self):
        print "remapping drivers"
        self.demand.remap_drivers()
        print "populating energy system data"
        for sector in self.demand.sectors.values():
            print "  " + sector.name + " sector"
            for subsector in sector.subsectors.values():
                print "    " + subsector.name
                subsector.add_energy_system_data()
        self.demand.precursor_dict()

    def populate_supply_system(self):
        self.supply.add_energy_system_data()

    def populate_demand_measures(self):
        for sector in self.demand.sectors.values():
            for subsector in sector.subsectors.values():
                subsector.add_measures()

    def populate_supply_measures(self):
        self.supply.add_measures()

    def calculate_demand_only(self):
        self.demand.manage_calculations()

    def calculate_supply(self):
        self.supply.calculate()

    def pass_results_to_supply(self):
        for sector in self.demand.sectors.values():
            sector.aggregate_subsector_energy_for_supply_side()
        self.demand.aggregate_sector_energy_for_supply_side()
        self.supply.demand_df = self.demand.energy_demand

    def pass_results_to_demand(self):
        self.demand.aggregate_results()
        self.demand.link_to_supply(
            self.supply.emissions_demand_link, self.supply.energy_demand_link, self.supply.cost_demand_link
        )

    def export_results(self, specified_directory=None):
        if specified_directory is None:
            specified_directory = os.path.join(os.getcwd())
        else:
            specified_directory = os.path.join(specified_directory)
        attributes = dir(self.demand.outputs)
        for att in attributes:
            if isinstance(getattr(self.demand.outputs, att), pd.core.frame.DataFrame):
                output = self.demand.outputs.return_cleaned_output(att)
                ExportMethods.writedataframe(att, output, specified_directory)
 def __init__(self, db_path, cfgfile_path, custom_pint_definitions_path=None, name=None, author=None):
     self.model_config(db_path, cfgfile_path, custom_pint_definitions_path)
     self.name = cfg.cfgfile.get("case", "scenario") if name is None else name
     self.author = cfg.cfgfile.get("case", "author") if author is None else author
     self.demand = Demand()
     self.supply = Supply()