Beispiel #1
0
def get_reference_emission_levels_for_region(region: str, year: int) -> float:
    """
    Return the total CO2 emissions (in kT) emitted by a series of countries in a given region for a given year.

    Parameters
    ----------
    region: str
        Region consisting of one or several countries.
    year: int
        Year

    Returns
    -------
    emission_ref: float
        Total Co2 emissions in kT

    """
    return sum([
        get_co2_emission_level_for_country(country, year)
        for country in get_subregions(region)
    ])
Beispiel #2
0
    def build_data(self,
                   use_ex_cap: bool,
                   min_cap_pot: List[float] = None,
                   compute_load: bool = True,
                   regions_shapes: pd.DataFrame = None):
        """Preprocess data.

        Parameters:
        -----------
        use_ex_cap: bool
            Whether to compute or not existing capacity and use it in optimization.
        min_cap_pot: List[float] (default: None)
            List of thresholds per technology. Points with capacity potential under this threshold will be removed.
        """

        # TODO: this function needs to take as argument a vector data specifying which data it must compute
        # Compute total load (in GWh) for each region
        load_df = pd.DataFrame(0., index=self.timestamps, columns=self.regions)
        if compute_load:
            load_df = get_load(timestamps=self.timestamps,
                               regions=self.regions,
                               missing_data='interpolate')

        # Get shape of regions and list of subregions
        onshore_technologies = [
            get_config_values(tech, ["onshore"]) for tech in self.technologies
        ]
        if regions_shapes is None:
            regions_shapes = pd.DataFrame(columns=["onshore", "offshore"],
                                          index=self.regions)
            all_subregions = []
            for region in self.regions:
                subregions = get_subregions(region)
                all_subregions.extend(subregions)
                shapes = get_shapes(subregions, save=True)
                if any(onshore_technologies):
                    regions_shapes.loc[region, "onshore"] = unary_union(
                        shapes[~shapes['offshore']]['geometry'])
                if not all(onshore_technologies):
                    regions_shapes.loc[region, "offshore"] = unary_union(
                        shapes[shapes['offshore']]['geometry'])
        else:
            all_subregions = self.regions

        # Divide the union of all regions shapes into grid cells of a given spatial resolution
        # TODO: this is shitty because you cannot add different technologies in separate regions
        grid_cells_ds = get_grid_cells(self.technologies, self.spatial_res,
                                       regions_shapes["onshore"].dropna(),
                                       regions_shapes["offshore"].dropna())

        # Compute capacities potential
        tech_config = get_config_dict(self.technologies,
                                      ['filters', 'power_density'])
        cap_potential_ds = pd.Series(index=grid_cells_ds.index)
        for tech in self.technologies:
            cap_potential_ds[tech] = \
                get_capacity_potential_for_shapes(grid_cells_ds[tech].values, tech_config[tech]["filters"],
                                                  tech_config[tech]["power_density"])

        # Compute legacy capacity
        existing_cap_ds = pd.Series(0., index=cap_potential_ds.index)
        if use_ex_cap:
            for tech in self.technologies:
                tech_existing_cap_ds = \
                    get_legacy_capacity_in_regions(tech, grid_cells_ds.loc[tech].reset_index(drop=True),
                                                   all_subregions, raise_error=False)
                existing_cap_ds[tech] = tech_existing_cap_ds.values

        # Update capacity potential if existing capacity is bigger
        underestimated_capacity_indexes = existing_cap_ds > cap_potential_ds
        cap_potential_ds[underestimated_capacity_indexes] = existing_cap_ds[
            underestimated_capacity_indexes]

        # Remove sites that have a potential capacity under the desired value or equal to 0
        if min_cap_pot is None:
            min_cap_pot = [0] * len(self.technologies)
        assert len(min_cap_pot) == len(self.technologies), \
            "Error: If you specify threshold on capacity potentials, you need to specify it for each technology."
        min_cap_pot_dict = dict(zip(self.technologies, min_cap_pot))
        sites_to_drop = pd.DataFrame(cap_potential_ds).apply(
            lambda x: x[0] < min_cap_pot_dict[x.name[0]] or x[0] == 0, axis=1)
        # Don't drop sites with existing capacity
        # TODO: this is probably a shitty way to do it
        sites_to_drop = pd.DataFrame(sites_to_drop).apply(
            lambda x: (existing_cap_ds[x.name] == 0 and x[0]), axis=1)
        cap_potential_ds = cap_potential_ds[~sites_to_drop]
        existing_cap_ds = existing_cap_ds[~sites_to_drop]
        grid_cells_ds = grid_cells_ds[~sites_to_drop]

        # Compute capacity factors for each site
        tech_points_dict = {}
        techs = set(grid_cells_ds.index.get_level_values(0))
        for tech in techs:
            tech_points_dict[tech] = list(grid_cells_ds[tech].index)
        cap_factor_df = compute_capacity_factors(tech_points_dict,
                                                 self.spatial_res,
                                                 self.timestamps)

        # Associating coordinates to regions
        tech_points_regions_ds = pd.Series(index=grid_cells_ds.index)
        sites_index = tech_points_regions_ds.index
        for tech in set(sites_index.get_level_values(0)):
            on_off = 'onshore' if get_config_values(
                tech, ['onshore']) else 'offshore'
            tech_sites_index = sites_index[sites_index.get_level_values(0) ==
                                           tech]
            points = list(
                zip(tech_sites_index.get_level_values(1),
                    tech_sites_index.get_level_values(2)))
            tech_points_regions_ds[tech] = match_points_to_regions(
                points, regions_shapes[on_off].dropna()).values

        cap_credit_ds = compute_capacity_credit_from_potential(
            load_df, cap_factor_df, tech_points_regions_ds)

        # Save all data in object
        self.use_ex_cap = use_ex_cap
        self.min_cap_pot_dict = min_cap_pot_dict
        self.tech_points_tuples = grid_cells_ds.index.values
        self.tech_points_dict = tech_points_dict
        self.initial_sites_ds = grid_cells_ds
        self.tech_points_regions_ds = tech_points_regions_ds
        self.data_dict["load"] = load_df
        self.data_dict["cap_potential_ds"] = cap_potential_ds.round(3)
        self.data_dict["existing_cap_ds"] = existing_cap_ds.round(3)
        self.data_dict["cap_factor_df"] = cap_factor_df.round(3)
        self.data_dict["capacity_credit_ds"] = cap_credit_ds.round(3)
Beispiel #3
0
        Longitudinal range over which to display the map. Automatically set if not specified.
    lat_range: List[float] (default: None)
        Latitudinal range over which to display the map. Automatically set if not specified.
    """

    df = get_powerplants(tech, countries)[["ISO2", "Capacity"]]
    df = df.groupby(["ISO2"]).sum() / 1000.0
    df = df[df["Capacity"] != 0]
    df["ISO_3"] = convert_country_codes(df.index.values, 'alpha_2', 'alpha_3')

    fig = go.Figure(data=go.Choropleth(
        locations=df['ISO_3'],  # Spatial coordinates
        z=df['Capacity'],  # Data to be color-coded
        text=[f"{cap} GW" for cap in df["Capacity"].values],
        colorscale='Reds',
        colorbar_title=f"Capacity (GW)"))

    fig.update_layout(geo=dict(lonaxis=dict(range=lon_range, ),
                               lataxis=dict(range=lat_range, ),
                               scope='europe'))

    fig.show()


if __name__ == '__main__':
    regions = get_subregions("EU2")
    plot_capacity_per_country("nuclear",
                              regions,
                              lon_range=[-12, 30],
                              lat_range=[35, 75])
Beispiel #4
0
def test_get_eez_and_land_union_shapes():
    codes = get_subregions("EU2")
    ds = get_eez_and_land_union_shapes(codes)
    assert isinstance(ds, pd.Series)
    assert not (set(ds.index).symmetric_difference(set(codes)))
Beispiel #5
0
def get_load(timestamps: pd.DatetimeIndex = None,
             years_range: List[int] = None,
             countries: List[str] = None,
             regions: List[str] = None,
             missing_data: str = "error") -> pd.DataFrame:
    """
    Compute hourly load time series (in GWh) for given countries or regions.

    The desired time slice can be either given as as series of time stamps or
    as a range of years for which we want full data.

    Parameters
    ----------
    timestamps: pd.DatetimeIndex (default: None)
        Datetime index
    years_range: List[int]
        Range of years (if you desire to obtain data for only one year just pass a list with twice the year)
    countries: List[str] (default: None)
        ISO codes of countries
    regions: List[str] (default: None)
        List of codes referring to regions made of several countries defined in 'data_path'/geographics/region_definition.csv
    missing_data: str (default: error)
        Defines how to deal with missing data. If value is 'error', throws an error. If value is 'interpolate', uses
        data from another country

    Returns
    -------
    pd.DataFrame (index = timestamps, columns = regions or countries)
        DataFrame associating to each country or region the corresponding its hourly load (in GWh)

    """

    assert (countries is None) != (regions is None), "Error: You must either specify a list of countries or " \
                                                     "a list of regions made of countries, but not both."
    assert (timestamps is None) != (years_range is None), "Error: You must either specify a range of years or " \
                                                          "a series of time stamps, but not both."
    assert years_range is None or (len(years_range) == 2 and years_range[0] <= years_range[1]), \
        f"The desired years range must be specified as a list of two ints (the first one being smaller" \
        f" or equal to the second one, received {years_range}"
    assert missing_data in [
        "error", "interpolate"
    ], f"Error: missing_data must be one of 'error' or 'interpolate'"

    if years_range is not None:
        timestamps = pd.date_range(f"{years_range[0]}-01-01 00:00:00",
                                   f"{years_range[1]}-12-31 23:00:00",
                                   freq='1H')

    opsd_load_fn = f"{data_path}load/generated/opsd_load.csv"
    load = pd.read_csv(opsd_load_fn, index_col=0, engine='python')
    load.index = pd.DatetimeIndex(load.index)
    missing_timestamps = set(timestamps) - set(load.index)
    assert not missing_timestamps, f"Error: Load is not available " \
                                   f"for the following timestamps {sorted(list(missing_timestamps))}"

    # Slice on time and remove columns in which we don't have available data for the full time period
    load = load.loc[timestamps].dropna(axis=1)
    # Convert to GWh
    load = load * 1e-3

    def get_countries_load(countries_: List[str]):
        countries_load = pd.DataFrame(index=timestamps, columns=countries_)
        missing_countries = set(countries_) - set(load.columns)
        if missing_countries:
            if missing_data == "error":
                raise ValueError(
                    f"Error: Load is not available for countries {sorted(list(missing_countries))} "
                    f"for the required timestamps.")
            else:
                countries_load[list(missing_countries)] = \
                    get_load_from_source_country(list(missing_countries), load.index)
        countries_with_data = list(set(countries) - set(missing_countries))
        countries_load[countries_with_data] = load[countries_with_data]
        return countries_load

    # Get load per country
    if countries is not None:
        return get_countries_load(countries).round(6)
    # Get load aggregated by region
    elif regions is not None:
        load_per_region = pd.DataFrame(columns=regions, index=timestamps)
        for region in regions:
            # Get load date for all subregions and sum it
            countries = get_subregions(region)
            load_per_region[region] = get_countries_load(countries).sum(
                axis=1).values

        return load_per_region.round(6)
Beispiel #6
0
    tech_config_dict = get_config_dict(
        [tech], ["onshore", "power_density", "filters"])[tech]
    cap_pot_ds = get_capacity_potential_per_country(
        countries, tech_config_dict["onshore"], tech_config_dict["filters"],
        tech_config_dict["power_density"])
    cap_pot_df = cap_pot_ds.to_frame()
    cap_pot_df["ISO_3"] = convert_country_codes(cap_pot_df.index.values,
                                                'alpha_2', 'alpha_3')

    fig = go.Figure(data=go.Choropleth(
        locations=cap_pot_df['ISO_3'],  # Spatial coordinates
        z=cap_pot_df[0],  # Data to be color-coded
        text=[f"{cap} GW" for cap in cap_pot_df[0].values],
        colorscale='Reds',
        colorbar_title=f"Capacity (GW)"))

    fig.update_layout(geo=dict(lonaxis=dict(range=lon_range, ),
                               lataxis=dict(range=lat_range, ),
                               scope='europe'),
                      title=f"Capacity potential for {tech}")

    fig.show()


if __name__ == '__main__':
    from iepy.geographics import get_subregions
    countries_ = get_subregions("BENELUX")
    tech_ = "pv_residential"
    plot_capacity(tech_, countries_, lon_range=[-12, 30], lat_range=[35, 75])