Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSP has capacity factors > 1 when passing shapes #362

Open
1 of 2 tasks
irm-codebase opened this issue Jul 31, 2024 · 7 comments
Open
1 of 2 tasks

CSP has capacity factors > 1 when passing shapes #362

irm-codebase opened this issue Jul 31, 2024 · 7 comments
Labels
help wanted type: bug type: info Issues containing information which will probably be helpful in the future again.

Comments

@irm-codebase
Copy link

Version Checks (indicate both or one)

  • I have confirmed this bug exists on the lastest release of Atlite.

  • I have confirmed this bug exists on the current master branch of Atlite.

Issue Description

Depending on how you call csp capacity factors might be wrong

per_unit = True works as intended

image

capacity_factor=True has issues.

image

Reproducible Example

import geopandas as gpd
import atlite
import cartopy.io.shapereader as shpreader
import pandas as pd

shp = shpreader.Reader(
    shpreader.natural_earth(
        resolution="10m", category="cultural", name="admin_1_states_provinces"
    )
)
prt_records = list(
    filter(lambda r: r.attributes["iso_3166_2"].startswith("PT"), shp.records())
)
portugal_1 = (
    gpd.GeoDataFrame([{**r.attributes, "geometry": r.geometry} for r in prt_records])
    .rename(columns={"iso_3166_2": "state"})
    .set_index("state")
    .set_crs(4236)
)
portugal_1 = portugal_1.cx[portugal.bounds.minx:,:]
portugal_1.plot()

cutout = atlite.Cutout(
    path="csp-cf-series/test/cutout_csp.nc",  # previous file I had, from the Portugal example
)

csp = cutout.csp(  # I work!
    installation="SAM_solar_tower",
    per_unit=True,
    shapes=portugal_1
)
mean = csp.mean("time").to_series()
portugal_1.plot(column=mean, legend=True)

csp = cutout.csp(  # I don't :(
    installation="SAM_solar_tower",
    capacity_factor=True,
    shapes=portugal_1
)
mean = csp.mean("time").to_series()
portugal_1.plot(column=mean, legend=True)

Expected Behavior

CFs should be correct regardless of how you call for them (although... why are there two calls for the same thing? That's how this type of issue happens...)

Installed Versions

atlite = 0.2.13

@irm-codebase
Copy link
Author

Here is the cutout I am using for this.
cutout_csp.zip

@euronion
Copy link
Collaborator

euronion commented Aug 2, 2024

Hi there!

Thanks for reporting.

  • Could you share with details how you create the cutout? (e.g. the atlite.cutout(...) command)
  • I don't understand why you .mean("time") the results afterwards - is this step necessary? Maybe this step messes up the values?

@irm-codebase
Copy link
Author

Hello @euronion !
Sure, here is the cutout command (had to reconstruct it because of snakemake, but it's roughly like this):

cutout = atlite.Cutout(
    path="output/test.nc",
    module=["era5"],
    x=slice(-9.497466600999928, -6.205947224999932),
    y=slice(36.96588776200008, 42.15362966000002),
    time="2019-05",
    **cutout_kwargs,
)
cutout.prepare(features=["influx", "temperature"])

As for .mean("time"), it's just giving me the average across the "time" dimension (so the average CF in the time series).
I'm passing it to the plot to see the average (just as a test).

@sultadar
Copy link

sultadar commented Aug 5, 2024

I'm facing the same issue for wind, so would be glad if you could check there as well!

shp = shpreader.Reader(
    shpreader.natural_earth(
        resolution="110m", category="cultural", name="admin_0_countries"
    )
)

eu_records = list(filter(lambda c: c.attributes["ISO_A2_EH"] in countries_iso2, shp.records()))

country_shapes = (
    gpd.GeoDataFrame([{**r.attributes, "geometry": r.geometry} for r in eu_records])
    .rename(columns={"ISO_A2_EH": "country"})
    .set_index("country")
    .set_crs(4326)
)

# Determine the bounds of the shapefile
minx, miny, maxx, maxy = country_shapes.total_bounds

# Select features
features_sel = ['height','wind','influx', 'temperature']

# Create the atlite cutout
cutout = atlite.Cutout(
    path="EU_land_and_maritime_2020-01.nc",
    module='era5',
    xs=slice(minx, maxx),
    ys=slice(miny, maxy),
    time=time_range,
)

cutout.prepare(features_sel)

capFactors_windon = cutout.wind(
    turbine="Vestas_V112_3MW",
    capacity_factor=True,
    shapes=country_shapes
    )

To then get

<xarray.DataArray 'specific generation' (time: 744, country: 33)> Size: 196kB
array([[432.75802457, 357.47933491,  10.45152178, ..., 324.12242196,
         72.75254568,   3.6701183 ],
       [472.37188334, 317.84874673,   8.4504687 , ..., 336.59759239,
         70.21263398,   4.30421531],
       [517.94336594, 304.68508964,   7.16737895, ..., 348.5062182 ,
         70.8649481 ,   5.6894943 ],
       ...,
       [503.83907213, 347.11715974,  32.26562571, ..., 391.55345446,
        394.82612232,  17.45236282],
       [482.40021769, 321.25376784,  31.6153184 , ..., 363.73621784,
        361.91767644,  18.63654856],
       [483.7726228 , 289.53571345,  29.99167237, ..., 375.60636599,
        400.22536309,  18.62705798]])
Coordinates:
  * time     (time) datetime64[ns] 6kB 2020-01-01 ... 2020-01-31T23:00:00
  * country  (country) object 264B 'SE' 'PL' 'AT' 'HU' ... 'ME' 'NO' 'FR' 'GR'
Attributes:
    units:    MW

@fneum
Copy link
Member

fneum commented Aug 6, 2024

I haven't had the chance to look at this in detail yet, but I think you have to pass per_unit=True to get capacity factor time series @sultadar.

I agree with @irm-codebase here that we may need to clear up the keyword arguments of convert_and_aggregate() (https://atlite.readthedocs.io/en/latest/ref_api.html#atlite.convert.convert_and_aggregate). Currently, the capacity_factor argument is documented to give the static (average) capacity factor, but gives values in MW. It's not clear what capacity_factor_timeseries does other than per_unit (I think it's the aggregation to shapes).

Maybe:

cutout.wind(time_series=True, ...) # -> production time series in MW
cutout.wind(time_series=True, capacity_factor=True) # -> capacity factor time series per-unit
cutout.wind(capacity_factor=True, time_series=False) # -> average capacity factor per unit

And remove capacity_factor_timeseries by something like per_grid_cell.

@irm-codebase
Copy link
Author

irm-codebase commented Aug 6, 2024

@fneum ok, so basically: only use per_unit to avoid trouble 👍
I've been following this and my workflows work well (as far as I've checked)

@sultadar
Copy link

sultadar commented Aug 6, 2024

Same here, seems to work. Thanks for the swift reply @fneum!

@euronion euronion added help wanted type: info Issues containing information which will probably be helpful in the future again. labels Aug 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted type: bug type: info Issues containing information which will probably be helpful in the future again.
Projects
None yet
Development

No branches or pull requests

4 participants