Пример #1
0
    def introduce_inputs_override(self, num, introduced_stock_changes, introduced_specified_stock, introduced_specified_sales, decimals=10):
        list_steps = range(self.i, self.num_years*self.spy) if num is None else range(self.i, min(self.i+num*self.spy, self.num_years*self.spy))        
        if introduced_stock_changes is not None:
            if num!=len(util.ensure_iterable_and_not_string(introduced_stock_changes)):
                raise ValueError("length of annual stock_changes must match the number of years to run")
            if np.any(np.isnan(introduced_stock_changes)):
                raise ValueError("introduced annual stock_changes cannot be nan")
            self.stock_changes[list_steps] = np.reshape(np.repeat(introduced_stock_changes/self.spy, self.spy, axis=0), len(list_steps))

        if introduced_specified_stock is not None:
            if num!=len(util.ensure_iterable_and_not_string(introduced_specified_stock)):
                raise ValueError("length of annual specified_stock must match the number of years to run")
            self.specified_stock[list_steps] = np.array(util.flatten_list([[[np.nan]*self.num_techs]*(self.spy-1) + [list(util.ensure_iterable_and_not_string(ss))] for ss in np.round(introduced_specified_stock, decimals)]))
            if np.any(self.specified_stock[list_steps]<0):
                raise ValueError("introduced specified stock cannot be negative")
            i = self.i
            self.prior_year_stock = self.initial_stock if i == 0 else np.sum(self.stock[:, :i + 1, i - 1], axis=1)
            if np.sum(self.prior_year_stock) + np.sum(self.stock_changes[list_steps])>np.nansum(self.specified_stock[list_steps]) and not np.all(np.isnan(self.specified_stock[list_steps])) and self.stock_changes_as_min and self.use_stock_changes:
                self.specified_stock[list_steps] *= (np.sum(self.prior_year_stock) + np.sum(self.stock_changes[list_steps]))/np.nansum(self.specified_stock[list_steps])
#                self.stock_changes[list_steps] = np.reshape(np.repeat(0/self.spy, self.spy, axis=0), len(list_steps))


#            self.specified_stock[list_steps] = np.reshape(np.repeat(introduced_specified_stock, self.spy, axis=0), len(list_steps))

        if introduced_specified_sales is not None:
            if num!=len(util.ensure_iterable_and_not_string(introduced_specified_sales)):
                raise ValueError("length of annual specified_sales must match the number of years to run")
            self.specified_sales[list_steps] = np.reshape(np.repeat(np.round(introduced_specified_sales, decimals)/self.spy, self.spy, axis=0), len(list_steps))
            if np.any(self.specified_sales[list_steps]<0):
                raise ValueError("introduced specified sales cannot be negative")
Пример #2
0
    def introduce_inputs_override(self, num, introduced_stock_changes, introduced_specified_stock, introduced_specified_sales, decimals=10):
        list_steps = range(self.i, self.num_years*self.spy) if num is None else range(self.i, min(self.i+num*self.spy, self.num_years*self.spy))        
        if introduced_stock_changes is not None:
            if num!=len(util.ensure_iterable_and_not_string(introduced_stock_changes)):
                raise ValueError("length of annual stock_changes must match the number of years to run")
            if np.any(np.isnan(introduced_stock_changes)):
                raise ValueError("introduced annual stock_changes cannot be nan")
            self.stock_changes[list_steps] = np.reshape(np.repeat(introduced_stock_changes/self.spy, self.spy, axis=0), len(list_steps))

        if introduced_specified_stock is not None:
            if num!=len(util.ensure_iterable_and_not_string(introduced_specified_stock)):
                raise ValueError("length of annual specified_stock must match the number of years to run")
            self.specified_stock[list_steps] = np.array(util.flatten_list([[[np.nan]*self.num_techs]*(self.spy-1) + [list(util.ensure_iterable_and_not_string(ss))] for ss in np.round(introduced_specified_stock, decimals)]))
            if np.any(self.specified_stock[list_steps]<0):
                raise ValueError("introduced specified stock cannot be negative")
            i = self.i
            self.prior_year_stock = self.initial_stock if i == 0 else np.sum(self.stock[:, :i + 1, i - 1], axis=1)
            if np.sum(self.prior_year_stock) + np.sum(self.stock_changes[list_steps])>np.nansum(self.specified_stock[list_steps]) and not np.all(np.isnan(self.specified_stock[list_steps])) and self.stock_changes_as_min and self.use_stock_changes:
                self.specified_stock[list_steps] *= (np.sum(self.prior_year_stock) + np.sum(self.stock_changes[list_steps]))/np.nansum(self.specified_stock[list_steps])
#                self.stock_changes[list_steps] = np.reshape(np.repeat(0/self.spy, self.spy, axis=0), len(list_steps))


#            self.specified_stock[list_steps] = np.reshape(np.repeat(introduced_specified_stock, self.spy, axis=0), len(list_steps))

        if introduced_specified_sales is not None:
            if num!=len(util.ensure_iterable_and_not_string(introduced_specified_sales)):
                raise ValueError("length of annual specified_sales must match the number of years to run")
            self.specified_sales[list_steps] = np.reshape(np.repeat(np.round(introduced_specified_sales, decimals)/self.spy, self.spy, axis=0), len(list_steps))
            if np.any(self.specified_sales[list_steps]<0):
                raise ValueError("introduced specified sales cannot be negative")
Пример #3
0
def dispatch_to_energy_budget(load, energy_budgets, dispatch_periods=None, pmins=0, pmaxs=None):
    """ Dispatch to energy budget produces a dispatch shape for a load or generating energy budget

    Common uses would be hydro, power2gas, and hydrogen electrolysis

    energy_budget > 0 is generation (hydro)
    energy_budget < 0 is load (P2G)

    Args:
        load: net load shape (ndarray)
        energy_budgets: availabile energy (float or ndarray)
        dispatch_periods: identifiers for each load hour (ndarray) defaults to None
        pmins: min power for the dispatch (float or ndarray) defaults to zero
        pmaxs: max power for the dispatch (float or ndarray) defaults to None

    Returns:
        dispatch: (ndarray)

    This function solves based on a huristic, which returns the same solution as optimization

    Note that every change in dispatch period results in a new dispatch group, for instance,
     range(12) + range(12) will result in 24 dispatch groups, not 12, as might be expected.
    """
    if dispatch_periods is not None and len(dispatch_periods) != len(load):
        raise ValueError('Dispatch period identifiers must match the length of the load data')

    # spit the load into dispatch groups
    load_groups = (load,) if dispatch_periods is None else np.array_split(load,
                                                                          np.where(np.diff(dispatch_periods) != 0)[
                                                                              0] + 1)
    energy_budgets = util.ensure_iterable_and_not_string(energy_budgets)
    pmins, pmaxs = util.ensure_iterable_and_not_string(pmins), util.ensure_iterable_and_not_string(pmaxs)

    if len(energy_budgets) != len(load_groups):
        raise ValueError('Number of energy_budgets must match the number of dispatch periods')

    if len(pmins) != len(load_groups):
        if len(pmins) == 1:
            # expand to match the number of load groups
            pmins *= len(load_groups)
        else:
            raise ValueError('Number of pmin values must match the number of dispatch periods')

    if len(pmaxs) != len(load_groups):
        if len(pmaxs) == 1:
            # expand to match the number of load groups
            pmaxs *= len(load_groups)
        else:
            raise ValueError('Number of pmax values must match the number of dispatch periods')

    # call solve for dispatch on each group and concatenate
    return np.concatenate([solve_for_dispatch_shape(load_group, energy_budget, pmin, pmax)
                           for load_group, energy_budget, pmin, pmax in zip(load_groups, energy_budgets, pmins, pmaxs)])
Пример #4
0
    def map_df(self, subsection, supersection, column=None, reset_index=False, eliminate_zeros=True):
        """ main function that maps geographies to one another
        Two options for two overlapping areas
            (A u B) / A     (A is supersection)
            (A u B) / B     (B is supersection)

        Examples:
            self.map_df('households', subsection=('state'), supersection=('census division'))
            "what fraction of each census division is in each state"

            self.map_df('households', subsection=('census division'), supersection=('state'))
            "what fraction of each state is in each census division
        """
        subsection = util.ensure_iterable_and_not_string(subsection)
        column = config.cfg.cfgfile.get('case', 'default_geography_map_key') if column is None else column
        table = self.values[column].groupby(level=[supersection]+subsection).sum()
        table = pd.DataFrame(table.groupby(level=supersection).transform(self.normalize))
        
        if reset_index or eliminate_zeros:
            names = table.index.names
            table.reset_index(inplace=True)
        if eliminate_zeros:
            table = table[table[column]>0]
        if not reset_index and eliminate_zeros:
            table.set_index(names, inplace=True)
        
        return table
Пример #5
0
    def map_df(self, current_geography, converted_geography, normalize_as='total', map_key=None, reset_index=False,
               eliminate_zeros=True, primary_subset_id='from config', geomap_data='from self',filter_geo=True):
        """ main function that maps geographies to one another
        Two options for two overlapping areas
            (A u B) / A     (A is supersection)
            (A u B) / B     (B is supersection)

        Examples:
            self.map_df('households', subsection=('state'), supersection=('census division'))
            "what fraction of each census division is in each state"

            self.map_df('households', subsection=('census division'), supersection=('state'))
            "what fraction of each state is in each census division
        """
        assert normalize_as=='total' or normalize_as=='intensity'
        geomap_data = self.values if geomap_data=='from self' else geomap_data
        if primary_subset_id=='from config' and filter_geo:
            primary_subset_id = cfg.primary_subset_id
        elif (primary_subset_id is None) or (primary_subset_id is False) or (not filter_geo):
            primary_subset_id = []
        
        subset_geographies = set(cfg.geo.gau_to_geography[id] for id in primary_subset_id)
        current_geography = util.ensure_iterable_and_not_string(current_geography)
        converted_geography = util.ensure_iterable_and_not_string(converted_geography)
        union_geo = list(subset_geographies | set(current_geography) | set(converted_geography))
        level_to_remove = list(subset_geographies - set(current_geography) - set(converted_geography))
        map_key = cfg.cfgfile.get('case', 'default_geography_map_key') if map_key is None else map_key
        table = geomap_data[map_key].groupby(level=union_geo).sum().to_frame()
        if normalize_as=='total':
            table = self._normalize(table, current_geography)
        if primary_subset_id:
            # filter the table
            table = table.iloc[self._get_iloc_geo_subset(table, primary_subset_id)]
            table = util.remove_df_levels(table, level_to_remove)
            table = table.reset_index().set_index(table.index.names)
        if normalize_as=='intensity':
            table = self._normalize(table, converted_geography)
        
        if reset_index:
            table = table.reset_index()

        if not eliminate_zeros:
            index = pd.MultiIndex.from_product(table.index.levels, names=table.index.names)
            table = table.reorder_levels(index.names)
            table = table.reindex(index, fill_value=0.0)
            
        return table
Пример #6
0
    def map_df(self, current_geography, converted_geography, normalize_as='total', map_key=None, reset_index=False,
               eliminate_zeros=True, primary_subset_id='from config', geomap_data='from self',filter_geo=True):
        """ main function that maps geographies to one another
        Two options for two overlapping areas
            (A u B) / A     (A is supersection)
            (A u B) / B     (B is supersection)

        Examples:
            self.map_df('households', subsection=('state'), supersection=('census division'))
            "what fraction of each census division is in each state"

            self.map_df('households', subsection=('census division'), supersection=('state'))
            "what fraction of each state is in each census division
        """
        assert normalize_as=='total' or normalize_as=='intensity'
        geomap_data = self.values if geomap_data=='from self' else geomap_data
        if primary_subset_id=='from config' and filter_geo:
            primary_subset_id = cfg.primary_subset_id
        elif (primary_subset_id is None) or (primary_subset_id is False) or (not filter_geo):
            primary_subset_id = []
        
        subset_geographies = set(cfg.geo.gau_to_geography[id] for id in primary_subset_id)
        current_geography = util.ensure_iterable_and_not_string(current_geography)
        converted_geography = util.ensure_iterable_and_not_string(converted_geography)
        union_geo = list(subset_geographies | set(current_geography) | set(converted_geography))
        level_to_remove = list(subset_geographies - set(current_geography) - set(converted_geography))
        map_key = cfg.cfgfile.get('case', 'default_geography_map_key') if map_key is None else map_key
        table = geomap_data[map_key].groupby(level=union_geo).sum().to_frame()
        if normalize_as=='total':
            table = self._normalize(table, current_geography)
        if primary_subset_id:
            # filter the table
            table = table.iloc[self._get_iloc_geo_subset(table, primary_subset_id)]
            table = util.remove_df_levels(table, level_to_remove)
            table = table.reset_index().set_index(table.index.names)
        if normalize_as=='intensity':
            table = self._normalize(table, converted_geography)
        
        if reset_index:
            table = table.reset_index()

        if not eliminate_zeros:
            index = pd.MultiIndex.from_product(table.index.levels, names=table.index.names)
            table = table.reorder_levels(index.names)
            table = table.reindex(index, fill_value=0.0)
            
        return table
Пример #7
0
 def initialize_initial_stock(self, initial_stock):
     if initial_stock is None:
         self.initial_stock = np.zeros(self.num_techs)
     else:
         self.initial_stock = np.nanmax(
             (np.zeros(self.num_techs),
              np.array(util.ensure_iterable_and_not_string(initial_stock))),
             axis=0)
Пример #8
0
    def account_for_specified_stock(self):
        i = self.i
#        if np.any((self.specified_sales[i]!=self.specified_stock[i])[np.array(list(set(self.stock_specified) & set(self.sales_specified)), dtype=int)]):
#            raise RuntimeError('Missmatch between specified sales and specified stock with no existing stock')
#        
        # if you have specified stock, it implies sales that supersede natural sales
        self.defined_sales = self.specified_stock[i] - (self.prior_year_stock - self.rolloff) # may result in a negative
        self.defined_sales[self.sales_specified] = self.specified_sales[i, self.sales_specified]
        
        # a negative specified sale means you need to do early retirement
        self.sum_defined_sales = np.sum(self.defined_sales[self.specified])

        if len(self.specified) and np.sum(self.prior_year_stock):
            if not self.stock_changes_as_min and (round(sum(self.specified_stock[i][self.stock_specified]), 5) / round(np.sum(self.prior_year_stock) + self.stock_changes[i], 5))>1.01:
                print round(sum(self.specified_stock[i][self.stock_specified]), 5)
                print round(np.sum(self.prior_year_stock) + self.stock_changes[i], 5)
                print  (round(sum(self.specified_stock[i][self.stock_specified]), 5) / round(np.sum(self.prior_year_stock) + self.stock_changes[i], 5))
                raise RuntimeError('Specified stock in a given year is greater than the total stock')

            # if we have both a specified stock and specified sales for a tech, we need to true up the vintaged stock to make both match, if possible
            if len(set(self.sales_specified) & set(self.stock_specified)):
                for overlap_index in set(self.sales_specified) & set(self.stock_specified):
                    retireable = np.array([overlap_index], dtype=int)
                    needed_retirements = self.specified_sales[i, overlap_index] - (self.specified_stock[i, overlap_index] - (self.prior_year_stock[overlap_index] - self.rolloff[overlap_index]))
                    if needed_retirements < 0:
                        if len(util.ensure_iterable_and_not_string(overlap_index))>1:
                            self.specified_sales[i, overlap_index] *= (sum(self.specified_sales[i, overlap_index]) - needed_retirements)/sum(self.specified_sales[i, overlap_index])
                        else:
                            self.specified_sales[i, overlap_index] *= (self.specified_sales[i, overlap_index] - needed_retirements)/self.specified_sales[i, overlap_index]
                    else:
                        self.update_prinxy(needed_retirements, retireable)
                # Because of the retirements, we have a new natural rolloff number
                self.update_rolloff()

            # if specified sales are negative, we need to accelerate retirement for that technology
            if np.any(self.defined_sales[self.specified] < 0):
                for neg_index in np.nonzero(self.defined_sales < 0)[0]:
                    retireable = np.array([neg_index], dtype=int)
                    # negative is needed before self.defined_sales because a postive number indicates retirement
                    self.update_prinxy(-self.defined_sales[neg_index], retireable)
                    self.defined_sales[neg_index] = 0
                self.sum_defined_sales = np.sum(self.defined_sales[self.specified])
                # Because of the retirements, we have a new natural rolloff number
                self.update_rolloff()

            # Here, if stock changes as min
            if self.stock_changes_as_min:
                self.stock_changes[i] = max(self.stock_changes[i], self.sum_defined_sales - self.rolloff_summed)

            # We need additional early retirement to make room for defined_sales
            if round(self.sum_defined_sales, 6) > round(self.rolloff_summed + self.stock_changes[i], 6):
                # print 'We need additional early retirement to make room for defined_sales ' + str(i)
                incremental_retirement = self.sum_defined_sales - (self.rolloff_summed + self.stock_changes[i])
                self.update_prinxy(incremental_retirement, self.solvable)

                # Because of the retirements, we have a new natural rolloff number
                self.update_rolloff()
Пример #9
0
def init_output_parameters():
    global currency_name, output_currency, output_tco, output_payback, evolved_run, evolved_blend_nodes, evolved_years
    currency_name = cfgfile.get('case', 'currency_name')
    output_currency = cfgfile.get('case', 'currency_year_id') + ' ' + currency_name
    output_tco = cfgfile.get('output_detail', 'output_tco').lower()
    output_payback = cfgfile.get('output_detail', 'output_payback').lower()
    evolved_run = cfgfile.get('evolved','evolved_run').lower()
    evolved_years = [int(x) for x in util.ensure_iterable_and_not_string(cfgfile.get('evolved','evolved_years'))]
    evolved_blend_nodes =  [int(g) for g in cfgfile.get('evolved','evolved_blend_nodes').split(',') if len(g)]
    init_output_levels()
    init_outputs_id_map()
 def add_service_links(self):
     """adds all technology service links"""
     self.service_links = {}
     service_links = util.sql_read_table('DemandTechsServiceLink', 'service_link_id', return_unique=True,
                                         demand_tech_id=self.id)
     if service_links is not None:
         service_links = util.ensure_iterable_and_not_string(service_links)
         for service_link in service_links:
             id = util.sql_read_table('DemandTechsServiceLink', 'id', return_unique=True, demand_tech_id=self.id,
                                      service_link_id=service_link)
             self.service_links[service_link] = DemandTechServiceLink(self, id, 'DemandTechsServiceLink',
                                                                      'DemandTechsServiceLinkData')
Пример #11
0
 def add_service_links(self):
     """adds all technology service links"""
     self.service_links = {}
     service_links = util.sql_read_table('DemandTechsServiceLink', 'service_link_id', return_unique=True,
                                         demand_tech_id=self.id)
     if service_links is not None:
         service_links = util.ensure_iterable_and_not_string(service_links)
         for service_link in service_links:
             id = util.sql_read_table('DemandTechsServiceLink', 'id', return_unique=True, demand_tech_id=self.id,
                                      service_link_id=service_link)
             self.service_links[service_link] = DemandTechServiceLink(self, id, 'DemandTechsServiceLink',
                                                                      'DemandTechsServiceLinkData')
Пример #12
0
def init_output_parameters():
    global currency_name, output_currency, output_tco, output_payback, evolved_run, evolved_blend_nodes, evolved_years
    currency_name = cfgfile.get('case', 'currency_name')
    output_currency = cfgfile.get('case',
                                  'currency_year_id') + ' ' + currency_name
    output_tco = cfgfile.get('output_detail', 'output_tco').lower()
    output_payback = cfgfile.get('output_detail', 'output_payback').lower()
    evolved_run = cfgfile.get('evolved', 'evolved_run').lower()
    evolved_years = [
        int(x) for x in util.ensure_iterable_and_not_string(
            cfgfile.get('evolved', 'evolved_years'))
    ]
    evolved_blend_nodes = [
        int(g)
        for g in cfgfile.get('evolved', 'evolved_blend_nodes').split(',')
        if len(g)
    ]
    init_removed_levels()
    init_output_levels()
    init_outputs_id_map()
Пример #13
0
    def initialize_specified_stock(self, specified_stock):
        """ Stock gets specified in the past period of the year
        """
        shape = (self.num_years * self.spy, self.num_techs)
        if specified_stock is None:
            self.specified_stock = np.empty(shape)
            self.specified_stock.fill(np.nan)
        else:
            self.specified_stock = np.array(
                util.flatten_list(
                    [[[np.nan] * self.num_techs] * (self.spy - 1) +
                     [list(util.ensure_iterable_and_not_string(ss))]
                     for ss in specified_stock]))
#            if self.spy == 1:
#                self.specified_stock = specified_stock
#            else:
#                self.specified_stock = np.array(util.flatten_list([[[np.nan]*self.num_techs]*(self.spy-1) + [list(util.ensure_iterable_and_not_string(ss))] for ss in specified_stock]))
#                if self.num_techs == 1:
#                    self.specified_stock = self.specified_stock.flatten()
        if np.any(self.specified_stock < 0):
            raise ValueError(
                "Specified stock cannot be initialized with negative numbers")
Пример #14
0
    def account_for_specified_stock(self):
        i = self.i
        #        if np.any((self.specified_sales[i]!=self.specified_stock[i])[np.array(list(set(self.stock_specified) & set(self.sales_specified)), dtype=int)]):
        #            raise RuntimeError('Missmatch between specified sales and specified stock with no existing stock')
        #
        # if you have specified stock, it implies sales that supersede natural sales
        #        if self.stock_changes_as_min and self.use_stock_changes:
        #            self.defined_sales = np.maximum(self.specified_stock[i] - (self.prior_year_stock - self.rolloff),np.array(range(1))) # may result in a negative
        #        else:
        self.defined_sales = self.specified_stock[i] - (self.prior_year_stock -
                                                        self.rolloff)
        self.defined_sales[self.sales_specified] = self.specified_sales[
            i, self.sales_specified]

        # a negative specified sale means you need to do early retirement
        self.sum_defined_sales = np.sum(self.defined_sales[self.specified])

        if len(self.specified) and np.sum(self.prior_year_stock):
            if round(np.sum(self.prior_year_stock) + self.stock_changes[i],
                     5) > 0:
                if not self.stock_changes_as_min and util.percent_larger(
                        sum(self.specified_stock[i][self.stock_specified]),
                        np.sum(self.prior_year_stock) +
                        self.stock_changes[i]) > self.exceedance_tolerance:
                    logging.error(
                        'round(sum(self.specified_stock[i][self.stock_specified]), 5) = {}'
                        .format(
                            round(
                                sum(self.specified_stock[i][
                                    self.stock_specified]), 5)))
                    logging.error(
                        'round(np.sum(self.prior_year_stock) + self.stock_changes[i], 5) = {}'
                        .format(
                            round(
                                np.sum(self.prior_year_stock) +
                                self.stock_changes[i], 5)))
                    logging.error(
                        '(round(sum(self.specified_stock[i][self.stock_specified]), 5) / round(np.sum(self.prior_year_stock) + self.stock_changes[i], 5)) = {}'
                        .format((round(
                            sum(self.specified_stock[i][self.stock_specified]),
                            5) / round(
                                np.sum(self.prior_year_stock) +
                                self.stock_changes[i], 5))))
                    raise RuntimeError(
                        'Specified stock in a given year is greater than the total stock'
                    )

            # if we have both a specified stock and specified sales for a tech, we need to true up the vintaged stock to make both match, if possible
            if len(set(self.sales_specified) & set(self.stock_specified)):
                for overlap_index in set(self.sales_specified) & set(
                        self.stock_specified):
                    retireable = np.array([overlap_index], dtype=int)
                    needed_retirements = self.specified_sales[
                        i, overlap_index] - (
                            self.specified_stock[i, overlap_index] -
                            (self.prior_year_stock[overlap_index] -
                             self.rolloff[overlap_index]))
                    if needed_retirements < 0:
                        if len(
                                util.ensure_iterable_and_not_string(
                                    overlap_index)) > 1:
                            self.specified_sales[i, overlap_index] *= (
                                sum(self.specified_sales[i, overlap_index]) -
                                needed_retirements) / sum(
                                    self.specified_sales[i, overlap_index])
                        else:
                            self.specified_sales[i, overlap_index] *= (
                                self.specified_sales[i, overlap_index] -
                                needed_retirements
                            ) / self.specified_sales[i, overlap_index]
                    else:
                        self.update_prinxy(needed_retirements, retireable)
                # Because of the retirements, we have a new natural rolloff number
                self.update_rolloff()

            # if specified sales are negativ/e, we need to accelerate retirement for that technology
            if np.any(self.defined_sales[self.specified] < 0):
                for neg_index in np.nonzero(self.defined_sales < 0)[0]:
                    retireable = np.array([neg_index], dtype=int)
                    # negative is needed before self.defined_sales because a postive number indicates retirement
                    self.update_prinxy(-self.defined_sales[neg_index],
                                       retireable)
                    self.defined_sales[neg_index] = 0
                self.sum_defined_sales = np.sum(
                    self.defined_sales[self.specified])
                # Because of the retirements, we have a new natural rolloff number
                self.update_rolloff()

            # Here, if stock changes as min, gross up stock changes
            if self.stock_changes_as_min and self.use_stock_changes:
                if self.stock_changes[i] > 0 and self.sum_defined_sales <> 0:
                    self.stock_changes[i] = max(
                        self.stock_changes[i],
                        self.sum_defined_sales - self.rolloff_summed)
                elif self.stock_changes[i] < 0 and self.sum_defined_sales <> 0:
                    self.stock_changes[i] = min(
                        self.stock_changes[i],
                        self.sum_defined_sales - self.rolloff_summed)
            elif self.stock_changes_as_min and not self.use_stock_changes:
                self.stock_changes[
                    i] = self.sum_defined_sales - self.rolloff_summed
            # We need additional early retirement to make room for defined_sales
            if round(self.sum_defined_sales, 6) > round(
                    self.rolloff_summed + self.stock_changes[i], 6):
                incremental_retirement = self.sum_defined_sales - (
                    self.rolloff_summed + self.stock_changes[i])
                self.update_prinxy(incremental_retirement, self.solvable)

                # Because of the retirements, we have a new natural rolloff number
                self.update_rolloff()
Пример #15
0
 def initialize_initial_stock(self, initial_stock):
     if initial_stock is None:
         self.initial_stock = np.zeros(self.num_techs)
     else:
         self.initial_stock = np.nanmax((np.zeros(self.num_techs), np.array(util.ensure_iterable_and_not_string(initial_stock))), axis=0)
Пример #16
0
    def initialize_specified_stock(self, specified_stock):
        """ Stock gets specified in the past period of the year
        """
        shape = (self.num_years*self.spy, self.num_techs)
        if specified_stock is None:
            self.specified_stock = np.empty(shape)
            self.specified_stock.fill(np.nan)
        else:
            self.specified_stock = np.array(util.flatten_list([[[np.nan]*self.num_techs]*(self.spy-1) + [list(util.ensure_iterable_and_not_string(ss))] for ss in specified_stock]))
#            if self.spy == 1:
#                self.specified_stock = specified_stock
#            else:
#                self.specified_stock = np.array(util.flatten_list([[[np.nan]*self.num_techs]*(self.spy-1) + [list(util.ensure_iterable_and_not_string(ss))] for ss in specified_stock]))
#                if self.num_techs == 1:
#                    self.specified_stock = self.specified_stock.flatten()
        if np.any(self.specified_stock<0):
            raise ValueError("Specified stock cannot be initialized with negative numbers")
Пример #17
0
def dispatch_to_energy_budget(load,
                              energy_budgets,
                              dispatch_periods=None,
                              pmins=0,
                              pmaxs=None):
    """ Dispatch to energy budget produces a dispatch shape for a load or generating energy budget

    Common uses would be hydro, power2gas, and hydrogen electrolysis

    energy_budget > 0 is generation (hydro)
    energy_budget < 0 is load (P2G)

    Args:
        load: net load shape (ndarray)
        energy_budgets: availabile energy (float or ndarray)
        dispatch_periods: identifiers for each load hour (ndarray) defaults to None
        pmins: min power for the dispatch (float or ndarray) defaults to zero
        pmaxs: max power for the dispatch (float or ndarray) defaults to None

    Returns:
        dispatch: (ndarray)

    This function solves based on a huristic, which returns the same solution as optimization

    Note that every change in dispatch period results in a new dispatch group, for instance,
     range(12) + range(12) will result in 24 dispatch groups, not 12, as might be expected.
    """
    if dispatch_periods is not None and len(dispatch_periods) != len(load):
        raise ValueError(
            'Dispatch period identifiers must match the length of the load data'
        )

    # spit the load into dispatch groups
    load_groups = (load, ) if dispatch_periods is None else np.array_split(
        load,
        np.where(np.diff(dispatch_periods) != 0)[0] + 1)
    energy_budgets = util.ensure_iterable_and_not_string(energy_budgets)
    pmins, pmaxs = util.ensure_iterable_and_not_string(
        pmins), util.ensure_iterable_and_not_string(pmaxs)

    if len(energy_budgets) != len(load_groups):
        raise ValueError(
            'Number of energy_budgets must match the number of dispatch periods'
        )

    if len(pmins) != len(load_groups):
        if len(pmins) == 1:
            # expand to match the number of load groups
            pmins *= len(load_groups)
        else:
            raise ValueError(
                'Number of pmin values must match the number of dispatch periods'
            )

    if len(pmaxs) != len(load_groups):
        if len(pmaxs) == 1:
            # expand to match the number of load groups
            pmaxs *= len(load_groups)
        else:
            raise ValueError(
                'Number of pmax values must match the number of dispatch periods'
            )

    # call solve for dispatch on each group and concatenate
    return np.concatenate([
        solve_for_dispatch_shape(load_group, energy_budget, pmin, pmax)
        for load_group, energy_budget, pmin, pmax in zip(
            load_groups, energy_budgets, pmins, pmaxs)
    ])