reV Resource File Format#

Prerequisites:

  • Required: None

  • Recommended: None


Introduction#

In this tutorial, we will explore the reV resource HDF5 file format. The goal is to familiarize ourselves with the file structure and be able to create new resource files using custom weather or resource data.

Let’s get started!

File structure#

reV-compliant resource files like the WIND Toolkit (WTK) or the National Solar Radiation Database (NSRDB) are HDF5 files that contain spatiotemporal data. All core datasets in the HDF5 file are composed of two dimensions: time along the first axis and space along the second axis.

In order to define these dimensions, each HDF5 file comes equipped with a time_index dataset and a meta dataset. Both of these are 1D datasets, and both encode information about the dimension they represent. The time_index dataset is a pandas DatetimeIndex, while meta is a pandas DataFrame where each row represents one location.

Every subsequent dataset contained within reV-compliant resource files is of the shape (len(time_index), len(meta)) and typically contains some information about the renewable resource. If you were to look inside a WTK file, for example, it would look something like this:

$ h5ls wtk_conus_2007.h5
meta                     Dataset {2488136}
pressure_0m              Dataset {8760, 2488136}
pressure_100m            Dataset {8760, 2488136}
pressure_200m            Dataset {8760, 2488136}
relativehumidity_2m      Dataset {8760, 2488136}
temperature_100m         Dataset {8760, 2488136}
temperature_10m          Dataset {8760, 2488136}
temperature_120m         Dataset {8760, 2488136}
temperature_140m         Dataset {8760, 2488136}
temperature_160m         Dataset {8760, 2488136}
temperature_200m         Dataset {8760, 2488136}
temperature_2m           Dataset {8760, 2488136}
temperature_40m          Dataset {8760, 2488136}
temperature_60m          Dataset {8760, 2488136}
temperature_80m          Dataset {8760, 2488136}
time_index               Dataset {8760}
winddirection_100m       Dataset {8760, 2488136}
winddirection_10m        Dataset {8760, 2488136}
winddirection_120m       Dataset {8760, 2488136}
winddirection_140m       Dataset {8760, 2488136}
winddirection_160m       Dataset {8760, 2488136}
winddirection_200m       Dataset {8760, 2488136}
winddirection_40m        Dataset {8760, 2488136}
winddirection_60m        Dataset {8760, 2488136}
winddirection_80m        Dataset {8760, 2488136}
windspeed_100m           Dataset {8760, 2488136}
windspeed_10m            Dataset {8760, 2488136}
windspeed_120m           Dataset {8760, 2488136}
windspeed_140m           Dataset {8760, 2488136}
windspeed_160m           Dataset {8760, 2488136}
windspeed_200m           Dataset {8760, 2488136}
windspeed_40m            Dataset {8760, 2488136}
windspeed_60m            Dataset {8760, 2488136}
windspeed_80m            Dataset {8760, 2488136}

We can make several important observations right away:

  1. The data contains 2,488,136 unique locations (the is the shape of the meta and the size of the second dimension for every variable)

  2. The data contains 8760 time steps (i.e. hourly data for a full year ) for each location (the is the size of the first dimension for every variable)

  3. The data contains 5 main variables: pressure, relative humidity, temperature, wind speed, and wind direction

  4. The naming convention is {variable}_{height}m

  5. The variables do not need to be given at the same heights

Based on these observations, we can being putting together custom data.

Custom reV-compliant resource file#

To create our own reV-compliant resource file, we need three main things:

  1. A meta DataFrame detailing our locations

  2. A DatetimeIndex representing our time steps

  3. Resource variables for each location and time

Let’s create a minimal resource file (with dummy data) and run it through reV.

Meta#

We’ll start by creating the meta DataFrame. At a minimum, this DataFrame must contain the following columns:

  • “latitude”: Latitude of the location (degrees)

  • “longitude”: Longitude of the location (degrees; west is denoted with a negative sign)

  • “elevation”: Elevation of the location (meters)

  • “timezone”: Integer representing the UTC offset of the location

The meta can contain any other columns you would want to include to document the location. Typically, information like the county, state, and country are also added as individual columns. Finally, the name of the index of the DataFrame must be set to "gid". Let’s put together a minimal meta for two locations:

meta = pd.DataFrame(
    {
        "latitude": [39.7407, 39.7407, 39.75],
        "longitude": [-105.1686, -105.1, -105.1],
        "elevation": 2900,
        "timezone": -6,
    }
)
meta.index.name = "gid"
meta
latitude longitude elevation timezone
gid
0 39.7407 -105.1686 2900 -6
1 39.7407 -105.1000 2900 -6
2 39.7500 -105.1000 2900 -6

Time index#

Next, we’ll set up the time index. reV requires at least hourly data (i.e. minimum of 8760 time steps) to work properly, but 30 minute or even 5 minute data works as well. For demonstration purposes, we will stick to hourly data:

year = datetime.datetime.now().year
time_index = pd.date_range(
    start=f"1/1/{year}", end=f"1/1/{year + 1}", freq="h", inclusive="left"
)
time_index
DatetimeIndex(['2025-01-01 00:00:00', '2025-01-01 01:00:00',
               '2025-01-01 02:00:00', '2025-01-01 03:00:00',
               '2025-01-01 04:00:00', '2025-01-01 05:00:00',
               '2025-01-01 06:00:00', '2025-01-01 07:00:00',
               '2025-01-01 08:00:00', '2025-01-01 09:00:00',
               ...
               '2025-12-31 14:00:00', '2025-12-31 15:00:00',
               '2025-12-31 16:00:00', '2025-12-31 17:00:00',
               '2025-12-31 18:00:00', '2025-12-31 19:00:00',
               '2025-12-31 20:00:00', '2025-12-31 21:00:00',
               '2025-12-31 22:00:00', '2025-12-31 23:00:00'],
              dtype='datetime64[ns]', length=8760, freq='h')

Resource data#

The most challenging part of putting together reV-compliant resource files is to get the data into the correct format. For simplicity, we will use randomly generate resource data of the correct shape (and units), since the data-mangling step should be done aon a case-by-case basis.

For wind resource, four main variables are required: windspeed, winddirection, pressure, and temperature. relativehumidity is only required for add-on functionality like icing cutoffs. Solar and geothermal have their own minimum requirements (solar requires dni, dhi, wind_speed, and air_temperature variables, while geothermal requires temperature and potential_MW).

wind_speed = np.random.rand(len(time_index), len(meta)) * 15  # m/s
wind_direction = np.random.rand(len(time_index), len(meta)) * 360  # degrees
pressure = np.random.rand(len(time_index), len(meta)) * 20_000 + 90_000  # Pa
temperature = np.random.rand(len(time_index), len(meta)) * 25 - 5  # C

Putting it all together#

Now that we have the three main components (meta, time index, and data variables), we can write the resource file using the Outputs class available in rex:

RES_FILE = "sample_wtk.h5"

with Outputs(RES_FILE, "w") as out:
    out.meta = meta
    out.time_index = time_index

    out.write_dataset(
        "windspeed_100m",
        wind_speed,
        dtype="float32",
        attrs={"units": "m s-1"}
    )
    out.write_dataset(
        "winddirection_100m",
        wind_direction,
        dtype="float32",
        attrs={"units": "degree"}
    )
    out.write_dataset(
        "pressure_0m",
        pressure,
        dtype="float32",
        attrs={"units": "Pa"},
    )
    out.write_dataset(
        "temperature_0m",
        temperature,
        dtype="float32",
        attrs={"units": "C"},
    )
Note: Specifying the units using the "attrs" key is optional but helps avoid some warnings being thrown while running reV.

This is the minimum amount of information we need to provide. Let’s make sure this file can be used to run reV!

Running reV#

Before we can run reV, we need to specify some details about the turbine technology in the form of a SAM config:

# Custom power curve
power_curve_wind_speeds = list(range(26))
power_curve_output = [0, 0, 0, 1, 81, 259, 504, 808, 1159, 1426, 1571, 1609]
power_curve_output += [1620] * 13
power_curve_output += [0]

sam_config = {
    "wind_turbine_hub_ht": 80,
    "wind_turbine_rotor_diameter": 77,
    "wind_farm_wake_model": 0,
    "wind_farm_xCoordinates": [0],
    "wind_farm_yCoordinates": [0],
    "wind_resource_shear": 0.14,
    "wind_resource_turbulence_coeff": 0.1,
    "wind_turbine_powercurve_windspeeds": power_curve_wind_speeds,
    "wind_turbine_powercurve_powerout": power_curve_output,
}

Note that we are running a turbine with a hub height of 80m through reV using a wind resource set at 100m. Since we only provided one resource height, reV will default to just using the wind speeds and directions at 100m. This is confirmed to us via a warning that is thrown during execution:

gen = Gen(
    technology="windpower",
    project_points=[0, 2],  # index values of rows in our meta!
    sam_files={"default": sam_config},
    resource_file=RES_FILE,
    output_request=("cf_mean", "cf_profile")
)
gen.run(max_workers=1)
gen.out["cf_mean"]
/home/runner/work/SitingLab/SitingLab/.pixi/envs/doc/lib/python3.11/site-packages/rex/renewable_resource.py:1179: ResourceWarning: Wind speed is only available at 100m, all variables will be extracted at 100m
  warnings.warn('Wind speed is only available at {h}m, '
array([0.5437967 , 0.53954947], dtype=float32)

Despite the warning, everything works as intended!

Adding variables at other heights#

If we want reV to use more appropriate wind speed heights, we have to add more variables to allow for interpolation:

wind_speed = np.random.rand(len(time_index), len(meta)) * 60  # m/s
wind_direction = np.random.rand(len(time_index), len(meta)) * 360  # degrees

with Outputs(RES_FILE, "a") as out:

    out.write_dataset(
        "windspeed_60m",
        wind_speed,
        dtype="float32",
        attrs={"units": "m s-1"}
    )
    out.write_dataset(
        "winddirection_60m",
        wind_direction,
        dtype="float32",
        attrs={"units": "degree"}
    )

with Resource(RES_FILE) as res:
    print(res.datasets)
['meta', 'pressure_0m', 'temperature_0m', 'time_index', 'winddirection_100m', 'winddirection_60m', 'windspeed_100m', 'windspeed_60m']
gen = Gen(
    technology="windpower",
    project_points=[0, 2],
    sam_files={"default": sam_config},
    resource_file=RES_FILE,
    output_request=("cf_mean", "cf_profile")
)
gen.run(max_workers=1)
gen.out["cf_mean"]
array([0.56369334, 0.5770708 ], dtype=float32)

No more warning, since reV is interpolating between 60m and 100m to get the wind speed at 80m!

Conclusion#

In this tutorial, we have walked through the basic steps required to create custom reV-compliant resource files. You should now be able to:

  • Understand reV-complaint resource HDF5 file structure

  • Create a meta and time index to represent your spatiotemporal data

  • Create a reV-compliant HDF5 file with custom data using the Outputs class from rex

  • Run reV on your newly-created resource file