Beispiel #1
0
def get_legacy_capacity_in_countries(tech: str,
                                     countries: List[str],
                                     raise_error: bool = True) -> pd.Series:
    """
    Return the total existing capacity (in GW) for the given tech for a set of countries.

    If there is not data for a certain country, returns a capacity of 0.

    Parameters
    ----------
    tech: str
        Name of technology for which we want to retrieve legacy data.
    countries: List[str]
        List of ISO codes of countries
    raise_error: bool (default: True)
        Whether to raise an error if no legacy data is available for this technology.

    Returns
    -------
    capacities: pd.Series
        Legacy capacities (in GW) of technology 'tech' for each country.

    """

    assert len(countries) != 0, "Error: List of countries is empty."

    # Read per grid cell capacity file
    legacy_dir = f"{data_path}/generation/vres/legacy/generated/"
    capacities_df = pd.read_csv(f"{legacy_dir}aggregated_capacity.csv",
                                index_col=[0, 1])

    plant, plant_type = get_config_values(tech, ["plant", "type"])
    available_plant_types = set(capacities_df.index)
    if (plant, plant_type) not in available_plant_types:
        if raise_error:
            raise ValueError(
                f"Error: no legacy data exists for tech {tech} with plant {plant} and type {plant_type}."
            )
        else:
            warnings.warn(f"Warning: No legacy data exists for tech {tech}.")
            return pd.Series(0.,
                             name="Legacy capacity (GW)",
                             index=countries,
                             dtype=float)

    # Get only capacity for the desired technology and aggregated per country
    capacities_df = capacities_df.loc[(plant, plant_type),
                                      ("ISO2", "Capacity (GW)")]
    capacities_ds = capacities_df.groupby("ISO2").sum().squeeze()
    capacities_ds = capacities_ds.reindex(countries).fillna(0.)
    capacities_ds.name = "Legacy capacity (GW)"

    return capacities_ds
Beispiel #2
0
def plot_diff(tech: str, show: bool = True):

    plant, plant_type = get_config_values(tech, ["plant", "type"])
    capacities_df = pd.read_csv(
        f"{data_path}generation/vres/legacy/generated/aggregated_capacity.csv",
        index_col=[0, 1]).loc[plant].loc[plant_type]
    capacities_df = capacities_df[capacities_df["ISO2"] != 'IS']
    capacities_df = capacities_df[capacities_df["Capacity (GW)"] != 0.0]

    capacities_df_h = pd.read_csv(
        f"{data_path}generation/vres/legacy/generated/aggregated_capacity_harmonized.csv",
        index_col=[0, 1]).loc[plant].loc[plant_type]
    capacities_df_h = capacities_df_h[capacities_df_h["ISO2"] != 'IS']
    capacities_df_h = capacities_df_h[capacities_df_h["Capacity (GW)"] != 0.0]

    capacities_df["Difference (GW)"] = capacities_df[
        "Capacity (GW)"] - capacities_df_h["Capacity (GW)"]

    land_50m = cf.NaturalEarthFeature('physical',
                                      'land',
                                      '50m',
                                      edgecolor='darkgrey',
                                      facecolor=cf.COLORS['land_alt1'])

    fig = plt.figure(figsize=(13, 13))
    ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
    ax.add_feature(land_50m, linewidth=0.5, zorder=-1)
    ax.add_feature(cf.BORDERS.with_scale('50m'),
                   edgecolor='darkgrey',
                   linewidth=0.5,
                   zorder=-1)
    ax.set_extent([-15, 42.5, 30, 72.5])

    map = ax.scatter(capacities_df["Longitude"],
                     capacities_df["Latitude"],
                     c=capacities_df["Difference (GW)"],
                     s=1,
                     vmax=1.2,
                     vmin=0.0)
    fig.colorbar(map)

    if show:
        plt.show()
    else:
        return ax
Beispiel #3
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 #4
0
def aggregate_legacy_capacity(spatial_resolution: float):
    """
    Aggregate legacy data at a given spatial resolution.

    Parameters
    ----------
    spatial_resolution: float
        Spatial resolution at which we want to aggregate.

    """

    countries = [
        "AL", "AT", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE",
        "ES", "FI", "FO", "FR", "GB", "GR", "HR", "HU", "IE", "IS", "IT", "LT",
        "LU", "LV", "ME", "MK", "NL", "NO", "PL", "PT", "RO", "RS", "SE", "SI",
        "SK", "UA"
    ]

    technologies = [
        "wind_onshore", "wind_offshore", "pv_utility", "pv_residential"
    ]

    capacities_df_ls = []
    for country in countries:
        print(f"Country: {country}")
        shapes = get_shapes([country])
        onshore_shape = shapes[~shapes["offshore"]]["geometry"].values[0]
        offshore_shape = shapes[shapes["offshore"]]["geometry"].values
        # If not offshore shape for country, remove offshore technologies from set
        offshore_shape = None if len(
            offshore_shape) == 0 else offshore_shape[0]
        technologies_in_country = technologies
        if offshore_shape is None:
            technologies_in_country = [
                tech for tech in technologies
                if get_config_values(tech, ['onshore'])
            ]

        # Divide shapes into grid cells
        grid_cells_ds = get_grid_cells(technologies_in_country,
                                       spatial_resolution, onshore_shape,
                                       offshore_shape)
        technologies_in_country = set(grid_cells_ds.index.get_level_values(0))

        # Get capacity in each grid cell
        capacities_per_country_ds = pd.Series(index=grid_cells_ds.index,
                                              name="Capacity (GW)")
        for tech in technologies_in_country:
            capacities_per_country_ds[tech] = \
                get_legacy_capacity_in_regions_from_non_open(tech, grid_cells_ds.loc[tech].reset_index()[0], [country],
                                                             match_distance=100)
        capacities_per_country_df = capacities_per_country_ds.to_frame()
        capacities_per_country_df.loc[:, "ISO2"] = country
        capacities_df_ls += [capacities_per_country_df]

    # Aggregate dataframe from each country
    capacities_df = pd.concat(capacities_df_ls).sort_index()

    # Replace technology name by plant and type
    tech_to_plant_type = {
        tech: get_config_values(tech, ["plant", "type"])
        for tech in technologies
    }
    capacities_df = capacities_df.reset_index()
    capacities_df["Plant"] = capacities_df["Technology Name"].apply(
        lambda x: tech_to_plant_type[x][0])
    capacities_df["Type"] = capacities_df["Technology Name"].apply(
        lambda x: tech_to_plant_type[x][1])
    capacities_df = capacities_df.drop("Technology Name", axis=1)
    capacities_df = capacities_df.set_index(
        ["Plant", "Type", "Longitude", "Latitude"])

    legacy_dir = f"{data_path}generation/vres/legacy/generated/"
    capacities_df.round(4).to_csv(f"{legacy_dir}aggregated_capacity.csv",
                                  header=True,
                                  columns=["ISO2", "Capacity (GW)"])
Beispiel #5
0
def get_legacy_capacity_in_regions_from_non_open(
        tech: str,
        regions_shapes: pd.Series,
        countries: List[str],
        match_distance: float = 50.,
        raise_error: bool = True) -> pd.Series:
    """
    Return the total existing capacity (in GW) for the given tech for a set of geographical regions.

    This function is using proprietary data.

    Parameters
    ----------
    tech: str
        Technology name.
    regions_shapes: pd.Series [Union[Polygon, MultiPolygon]]
        Geographical regions
    countries: List[str]
        List of ISO codes of countries in which the regions are situated
    match_distance: float (default: 50)
        Distance threshold (in km) used when associating points to shape.
    raise_error: bool (default: True)
        Whether to raise an error if no legacy data is available for this technology.

    Returns
    -------
    capacities: pd.Series
        Legacy capacities (in GW) of technology 'tech' for each region

    """

    path_legacy_data = f"{data_path}generation/vres/legacy/source/"

    capacities = pd.Series(0., index=regions_shapes.index)
    plant, plant_type = get_config_values(tech, ["plant", "type"])
    if (plant, plant_type) in [("Wind", "Onshore"), ("Wind", "Offshore"),
                               ("PV", "Utility")]:

        if plant == "Wind":

            data = pd.read_excel(
                f"{path_legacy_data}Windfarms_Europe_20200127.xls",
                sheet_name='Windfarms',
                header=0,
                usecols=[2, 5, 9, 10, 18, 23],
                skiprows=[1],
                na_values='#ND')
            data = data.dropna(subset=['Latitude', 'Longitude', 'Total power'])
            data = data[data['Status'] != 'Dismantled']
            if countries is not None:
                data = data[data['ISO code'].isin(countries)]

            if len(data) == 0:
                return capacities

            # Converting from kW to GW
            data['Total power'] *= 1e-6
            data["Location"] = data[["Longitude", "Latitude"
                                     ]].apply(lambda x:
                                              (x.Longitude, x.Latitude),
                                              axis=1)

            # Keep only onshore or offshore point depending on technology
            if plant_type == 'Onshore':
                data = data[data['Area'] != 'Offshore']
            else:  # Offshore
                data = data[data['Area'] == 'Offshore']

            if len(data) == 0:
                return capacities

        else:  # plant == "PV":

            data = pd.read_excel(
                f"{path_legacy_data}Solarfarms_Europe_20200208.xlsx",
                sheet_name='ProjReg_rpt',
                header=0,
                usecols=[0, 4, 8])
            data = data[pd.notnull(data['Coords'])]
            data["Location"] = data["Coords"].apply(
                lambda x: (float(x.split(',')[1]), float(x.split(',')[0])))
            if countries is not None:
                data['Country'] = convert_country_codes(
                    data['Country'].values, 'name', 'alpha_2')
                data = data[data['Country'].isin(countries)]

            if len(data) == 0:
                return capacities

            # Converting from MW to GW
            data['Total power'] = data['MWac'] * 1e-3

        data = data[["Location", "Total power"]]

        points_region = match_points_to_regions(
            data["Location"].values,
            regions_shapes,
            distance_threshold=match_distance).dropna()

        for region in regions_shapes.index:
            points_in_region = points_region[points_region ==
                                             region].index.values
            capacities[region] = data[data["Location"].isin(
                points_in_region)]["Total power"].sum()

    elif (plant, plant_type) == ("PV", "Residential"):

        legacy_capacity_fn = join(path_legacy_data,
                                  'SolarEurope_Residential_deployment.xlsx')
        data = pd.read_excel(legacy_capacity_fn,
                             header=0,
                             index_col=0,
                             usecols=[0, 4],
                             squeeze=True).sort_index()
        data = data[data.index.isin(countries)]

        if len(data) == 0:
            return capacities

        # Get countries shapes
        countries_shapes = get_shapes(data.index.values,
                                      which='onshore',
                                      save=True)["geometry"]

        for region_id, region_shape in regions_shapes.items():
            for country_id, country_shape in countries_shapes.items():
                capacities[region_id] += \
                    (region_shape.intersection(country_shape).area/country_shape.area) * data[country_id]

    else:
        if raise_error:
            raise ValueError(
                f"Error: No legacy data exists for tech {tech} with plant {plant} and type {plant_type}."
            )
        else:
            warnings.warn(f"Warning: No legacy data exists for tech {tech}.")

    return capacities
Beispiel #6
0
def get_legacy_capacity_in_regions(tech: str,
                                   regions_shapes: pd.Series,
                                   countries: List[str],
                                   match_distance: float = 50.,
                                   raise_error: bool = True) -> pd.Series:
    """
    Return the total existing capacity (in GW) for the given tech for a set of geographical regions.

    Parameters
    ----------
    tech: str
        Technology name.
    regions_shapes: pd.Series [Union[Polygon, MultiPolygon]]
        Geographical regions
    countries: List[str]
        List of ISO codes of countries in which the regions are situated.
    match_distance: float (default: 50)
        Distance threshold (in km) used when associating points to shape.
    raise_error: bool (default: True)
        Whether to raise an error if no legacy data is available for this technology.

    Returns
    -------
    capacities: pd.Series
        Legacy capacities (in GW) of technology 'tech' for each region

    """

    # Read per grid cell capacity file
    legacy_dir = f"{data_path}generation/vres/legacy/generated/"
    capacities_df = pd.read_csv(f"{legacy_dir}aggregated_capacity.csv",
                                index_col=[0, 1])

    plant, plant_type = get_config_values(tech, ["plant", "type"])
    available_plant_types = set(capacities_df.index)
    if (plant, plant_type) not in available_plant_types:
        if raise_error:
            raise ValueError(
                f"Error: no legacy data exists for tech {tech} with plant {plant} and type {plant_type}."
            )
        else:
            warnings.warn(f"Warning: No legacy data exists for tech {tech}.")
            return pd.Series(0.,
                             name="Legacy capacity (GW)",
                             index=regions_shapes.index,
                             dtype=float)

    # Get only capacity for the desired technology and desired countries
    capacities_df = capacities_df.loc[(plant, plant_type)]
    capacities_df = capacities_df[capacities_df.ISO2.isin(countries)]
    if len(capacities_df) == 0:
        return pd.Series(0.,
                         name="Legacy capacity (GW)",
                         index=regions_shapes.index,
                         dtype=float)

    # Aggregate capacity per region by adding capacity of points falling in those regions
    capacities_df["Location"] = capacities_df[["Longitude",
                                               "Latitude"]].apply(lambda x:
                                                                  (x[0], x[1]),
                                                                  axis=1)
    points_region = match_points_to_regions(
        capacities_df["Location"].values,
        regions_shapes,
        distance_threshold=match_distance).dropna()
    capacities_ds = pd.Series(0.,
                              name="Legacy capacity (GW)",
                              index=regions_shapes.index,
                              dtype=float)
    for region in regions_shapes.index:
        points_in_region = points_region[points_region == region].index.values
        capacities_ds[region] = capacities_df[capacities_df["Location"].isin(
            points_in_region)]["Capacity (GW)"].sum()

    return capacities_ds
Beispiel #7
0
def compute_capacity_factors(tech_points_dict: Dict[str, List[Tuple[float, float]]],
                             spatial_res: float, timestamps: pd.DatetimeIndex,
                             smooth_wind_power_curve: bool = True) -> pd.DataFrame:
    """
    Compute capacity factors for a list of points associated to a list of technologies.

    Parameters
    ----------
    tech_points_dict : Dict[str, List[Tuple[float, float]]]
        Dictionary associating to each tech a list of points.
    spatial_res: float
        Spatial resolution of coordinates
    timestamps: pd.DatetimeIndex
        Time stamps for which we want capacity factors
    smooth_wind_power_curve : boolean (default True)
        If "True", the transfer function of wind assets replicates the one of a wind farm,
        rather than one of a wind turbine.

    Returns
    -------
    cap_factor_df : pd.DataFrame
         DataFrame storing capacity factors for each technology and each point

    """

    for tech, points in tech_points_dict.items():
        assert len(points) != 0, f"Error: No points were defined for tech {tech}"

    assert len(timestamps) != 0, f"Error: No timestamps were defined."

    # Get the converters corresponding to the input technologies
    # Dictionary indicating for each technology which converter(s) to use.
    #    For each technology in the dictionary:
    #        - if it is pv-based, the name of the converter must be specified as a string
    #        - if it is wind, a dictionary must be defined associated for the four wind regimes
    #        defined below (I, II, III, IV), the name of the converter as a string
    converters_dict = get_config_dict(list(tech_points_dict.keys()), ["converter"])

    vres_profiles_dir = f"{data_path}generation/vres/profiles/source/"
    transfer_function_dir = f"{vres_profiles_dir}transfer_functions/"
    data_converter_wind = pd.read_csv(f"{transfer_function_dir}data_wind_turbines.csv", sep=';', index_col=0)
    data_converter_pv = pd.read_csv(f"{transfer_function_dir}data_pv_modules.csv", sep=';', index_col=0)

    dataset = read_resource_database(spatial_res).sel(time=timestamps)

    # Create output dataframe with MultiIndex (tech, coords)
    tech_points_tuples = sorted([(tech, point[0], point[1]) for tech, points in tech_points_dict.items()
                                 for point in points])
    cap_factor_df = pd.DataFrame(index=timestamps,
                                 columns=pd.MultiIndex.from_tuples(tech_points_tuples,
                                                                   names=['technologies', 'lon', 'lat']),
                                 dtype=float)

    for tech in tech_points_dict.keys():

        resource = get_config_values(tech, ["plant"])
        sub_dataset = dataset.sel(locations=sorted(tech_points_dict[tech]))

        if resource == 'Wind':

            wind_speed_reference_height = 100.
            roughness = sub_dataset.fsr

            # Compute wind speed for the all the coordinates
            wind = xu.sqrt(sub_dataset.u100 ** 2 + sub_dataset.v100 ** 2)

            wind_mean = wind.mean(dim='time')

            # Split according to the IEC 61400 WTG classes
            wind_classes = {'IV': [0., 6.5], 'III': [6.5, 8.], 'II': [8., 9.5], 'I': [9.5, 99.]}

            for cls in wind_classes:

                filtered_wind_data = wind_mean.where((wind_mean.data >= wind_classes[cls][0]) &
                                                     (wind_mean.data < wind_classes[cls][1]), 0)
                coords_classes = filtered_wind_data[da.nonzero(filtered_wind_data)].locations.values.tolist()

                if len(coords_classes) > 0:

                    wind_filtered = wind.sel(locations=coords_classes)
                    roughness_filtered = roughness.sel(locations=coords_classes)

                    # Get the transfer function curve
                    # literal_eval converts a string to an array (in this case)
                    converter = converters_dict[tech]["converter"][cls]
                    power_curve_array = literal_eval(data_converter_wind.loc['Power curve', converter])
                    wind_speed_references = np.asarray([i[0] for i in power_curve_array])
                    capacity_factor_references = np.asarray([i[1] for i in power_curve_array])
                    capacity_factor_references_pu = capacity_factor_references / max(capacity_factor_references)

                    wind_log = windpowerlib.wind_speed.logarithmic_profile(
                        wind_filtered.values, wind_speed_reference_height,
                        float(data_converter_wind.loc['Hub height [m]', converter]),
                        roughness_filtered.values)
                    wind_data = da.from_array(wind_log, chunks='auto', asarray=True)

                    # The transfer function of wind assets replicates the one of a
                    # wind farm rather than one of a wind turbine.
                    if smooth_wind_power_curve:

                        turbulence_intensity = wind_filtered.std(dim='time') / wind_filtered.mean(dim='time')

                        capacity_factor_farm = windpowerlib.power_curves.smooth_power_curve(
                            pd.Series(wind_speed_references), pd.Series(capacity_factor_references_pu),
                            standard_deviation_method='turbulence_intensity',
                            turbulence_intensity=float(turbulence_intensity.min().values),
                            wind_speed_range=10.0)

                        power_output = da.map_blocks(np.interp, wind_data,
                                                     capacity_factor_farm['wind_speed'].values,
                                                     capacity_factor_farm['value'].values).compute()
                    else:

                        power_output = da.map_blocks(np.interp, wind_data,
                                                     wind_speed_references,
                                                     capacity_factor_references_pu).compute()

                    tech_points_tuples = [(tech, lon, lat) for lon, lat in coords_classes]
                    cap_factor_df.loc[:, tech_points_tuples] = np.array(power_output)

                else:

                    continue

        elif resource == 'PV':

            converter = converters_dict[tech]["converter"]

            # Get irradiance in W from J
            # TODO: getting NANs here... and not always the same number
            irradiance = sub_dataset.ssrd / 3600.
            # Get temperature in C from K
            temperature = sub_dataset.t2m - 273.15

            # Homer equation here:
            # https://www.homerenergy.com/products/pro/docs/latest/how_homer_calculates_the_pv_array_power_output.html
            # https://enphase.com/sites/default/files/Enphase_PVWatts_Derate_Guide_ModSolar_06-2014.pdf
            power_output = (float(data_converter_pv.loc['f', converter]) *
                            (irradiance/float(data_converter_pv.loc['G_ref', converter])) *
                            (1. + float(data_converter_pv.loc['k_P [%/C]', converter])/100. *
                             (temperature - float(data_converter_pv.loc['t_ref', converter]))))

            power_output = np.array(power_output)

            cap_factor_df[tech] = power_output

        else:
            raise ValueError(' Profiles for the specified resource is not available yet.')

    # Check that we do not have NANs
    assert cap_factor_df.isna().to_numpy().sum() == 0, "Error: Some capacity factors are not available."

    # Decrease precision of capacity factors
    cap_factor_df = cap_factor_df.round(3)

    return cap_factor_df
Beispiel #8
0
def get_cap_factor_for_countries(tech: str, countries: List[str], timestamps: pd.DatetimeIndex,
                                 throw_error: bool = True) -> pd.DataFrame:
    """
    Return capacity factors time-series for a set of countries over a given timestamps, for a given technology.

    Parameters
    ----------
    tech: str
        One of the technology associated to plant 'PV' or 'Wind' (with type 'Onshore', 'Offshore' or 'Floating').
    countries: List[str]
        List of ISO codes of countries.
    timestamps: pd.DatetimeIndex
        List of time stamps.
    throw_error: bool (default True)
        Whether to throw an error when capacity factors are not available for a given country or
        compute capacity factors from another method.

    Returns
    -------
    pd.DataFrame
        Capacity factors dataframe indexed by timestamps and with columns corresponding to countries.

    """

    plant, plant_type = get_config_values(tech, ["plant", "type"])

    profiles_dir = f"{data_path}generation/vres/profiles/generated/"
    if plant == 'PV':
        capacity_factors_df = pd.read_csv(f"{profiles_dir}pv_cap_factors.csv", index_col=0)
    elif plant == "Wind" and plant_type == "Onshore":
        capacity_factors_df = pd.read_csv(f"{profiles_dir}onshore_wind_cap_factors.csv", index_col=0)
    elif plant == "Wind" and plant_type in ["Offshore", "Floating"]:
        capacity_factors_df = pd.read_csv(f"{profiles_dir}offshore_wind_cap_factors.csv", index_col=0)
    else:
        raise ValueError(f"Error: No capacity factors for technology {tech} of plant {plant} and type {type}.")

    capacity_factors_df.index = pd.DatetimeIndex(capacity_factors_df.index)

    # Slicing on time
    missing_timestamps = set(timestamps) - set(capacity_factors_df.index)
    assert not missing_timestamps, f"Error: {tech} data for timestamps {missing_timestamps} is not available."
    capacity_factors_df = capacity_factors_df.loc[timestamps]

    # Slicing on country
    missing_countries = set(countries) - set(capacity_factors_df.columns)
    if missing_countries:
        if throw_error:
            raise ValueError(f"Error: {tech} data for countries {missing_countries} is not available.")
        else:
            # Compute capacity factors from centroid of country (onshore/offshore) shape
            spatial_res = 0.5
            missing_countries = sorted(list(missing_countries))
            which = 'onshore' if get_config_values(tech, ["onshore"]) else 'offshore'
            shapes_df = get_shapes(missing_countries, which=which)
            centroids = shapes_df["geometry"].centroid
            points = [(round(p.x / spatial_res) * spatial_res, round(p.y / spatial_res) * spatial_res)
                      for p in centroids]
            cap_factor_df = compute_capacity_factors({tech: points}, spatial_res, timestamps)[tech]
            cap_factor_df.columns = missing_countries
            capacity_factors_df = pd.concat([capacity_factors_df, cap_factor_df], axis=1)

    return capacity_factors_df[countries].round(3)
Beispiel #9
0
    power_density: float
        Power density in MW/km2
    processes: int (default: None)
        Number of parallel processes

    Returns
    -------
    pd.Series
        Series containing the capacity potentials (GW) for each code.

    """
    which = 'onshore' if is_onshore else 'offshore'
    shapes = get_shapes(countries, which=which, save=True)["geometry"]
    land_availability = get_land_availability_for_shapes(
        shapes, filters, processes)

    return pd.Series(land_availability * power_density / 1e3,
                     index=shapes.index)


if __name__ == '__main__':
    from iepy.geographics import get_shapes
    from iepy.technologies import get_config_values
    filters_ = get_config_values("wind_onshore_national", ["filters"])
    print(filters_)
    # filters_ = {"depth_thresholds": {"high": -200, "low": None}}
    full_gl_shape = get_shapes(["DE"], "onshore")["geometry"][0]
    filters_ = {"glaes_priors": {"": (None, 500)}}
    # trunc_gl_shape = full_gl_shape.intersection(Polygon([(11.5, 52.5), (11.5, 53.5), (12.5, 53.5), (12.5, 52.5)]))
    print(get_capacity_potential_for_shapes([full_gl_shape], filters_, 5))