AMR-Wind ABL Boundary I/O

Wind farm simulations typically require ABL inflow conditions. As such, a precursor ABL simulation is often performed to collect inflow conditions for the wind farm. AMR-Wind leverages NetCDF to collect ABL inflow variables at each time step and write the data to a file. This file can then be read during a wind farm simulation to populate the inflow.

Note

  • Currently, it is only possible to write xlo and ylo boundaries.

  • Inflow conditions are linearly interpolated between output times.

  • The time for the simulation that is reading the inflow file must be entirely contained within the inflow times.

  • The simulation reading the inflow file must have the same grid resolution at the boundaries.

Generating the inflow file from an ABL simulation

The following section can be added to the input file to generate an inflow file during an ABL simulation:

ABL.bndry_file = "bndry_file.nc"
ABL.bndry_io_mode = 0
ABL.bndry_planes = ylo xlo
ABL.bndry_output_start_time = 2.0
ABL.bndry_var_names = velocity temperature

In the case of using the OneEqKsgsM84 model the tke field is also needed.

ABL.bndry_var_names = velocity temperature tke

Using an inflow file in an ABL simulation

The following section can be added to the input file to read an inflow file to populate the boundary conditions:

ABL.bndry_file = "../orig/bndry_file.nc"
ABL.bndry_io_mode = 1
ABL.bndry_var_names = velocity temperature

Again, In the case of using the OneEqKsgsM84 model the tke field is also needed.

ABL.bndry_var_names = velocity temperature tke

The boundary conditions need to be adjusted from periodic to inflow/outflow. The following lines show the changes that need to be made to the input file for the x coordinate (similar change for y coordinate when needed):

geometry.is_periodic           = 0 1 0                          # Periodicity x y z (0/1)

xlo.type = "mass_inflow"
xlo.density = 1.0
xhi.type = "pressure_outflow"

Inflow file structure

The inflow file is written by the NetCDF library in the following structure:

  • Top level contains the data common to all the groups (title, dimensions, time)

  • Mid level contains the groups of planes: Each plane (e.g. xlo) is assigned a group at this level. It contains variables such as the plane normal and perpendicular directions.

  • Bottom levels contains groups of AMR levels, i.e. the levels associated with each plane: Each AMR level is assigned a group (e.g. “level_0”, “level_1”, etc) containing the variable dimensions and the inflow variables (e.g. velocity) associated with that level.

For a multi-level file, ncdump -h <file> provides:

 1netcdf bndry_file {
 2dimensions:
 3	sdim = 1 ;
 4	pdim = 2 ;
 5	vdim = 3 ;
 6	nt = UNLIMITED ; // (7 currently)
 7variables:
 8	double time(nt) ;
 9
10// global attributes:
11		:title = "ABL boundary planes" ;
12
13group: xlo {
14  variables:
15  	int normal ;
16  	int side ;
17  	int perpendicular(pdim) ;
18
19  group: level_0 {
20    dimensions:
21    	nx = 48 ;
22    	ny = 48 ;
23    	nz = 48 ;
24    variables:
25    	double lengths(pdim) ;
26    	double lo(pdim) ;
27    	double hi(pdim) ;
28    	double dx(pdim) ;
29    	double velocity(nt, ny, nz, vdim) ;
30    	double temperature(nt, ny, nz) ;
31    } // group level_0
32
33  group: level_1 {
34    dimensions:
35    	nx = 96 ;
36    	ny = 96 ;
37    	nz = 56 ;
38    variables:
39    	double lengths(pdim) ;
40    	double lo(pdim) ;
41    	double hi(pdim) ;
42    	double dx(pdim) ;
43    	double velocity(nt, ny, nz, vdim) ;
44    	double temperature(nt, ny, nz) ;
45    } // group level_1
46  } // group xlo
47
48group: ylo {
49  variables:
50  	int normal ;
51  	int side ;
52  	int perpendicular(pdim) ;
53
54  group: level_0 {
55    dimensions:
56    	nx = 48 ;
57    	ny = 48 ;
58    	nz = 48 ;
59    variables:
60    	double lengths(pdim) ;
61    	double lo(pdim) ;
62    	double hi(pdim) ;
63    	double dx(pdim) ;
64    	double velocity(nt, nx, nz, vdim) ;
65    	double temperature(nt, nx, nz) ;
66    } // group level_0
67
68  group: level_1 {
69    dimensions:
70    	nx = 96 ;
71    	ny = 96 ;
72    	nz = 56 ;
73    variables:
74    	double lengths(pdim) ;
75    	double lo(pdim) ;
76    	double hi(pdim) ;
77    	double dx(pdim) ;
78    	double velocity(nt, nx, nz, vdim) ;
79    	double temperature(nt, nx, nz) ;
80    } // group level_1
81  } // group ylo
82}

The inflow file can be inspected with Python as such:

inspect_abl_io
In [1]:
from netCDF4 import Dataset
import matplotlib.pyplot as plt

Load the file and inspect the top level

In [2]:
rg = Dataset("bndry_file.nc", "r")
print(rg)
<class 'netCDF4._netCDF4.Dataset'>
root group (NETCDF4 data model, file format HDF5):
    title: ABL boundary planes
    dimensions(sizes): pdim(2), sdim(1), vdim(3), nt(7)
    variables(dimensions): float64 time(nt)
    groups: xlo, ylo

Looping through the planes

In [3]:
for grp in rg.groups:
    print(f"""Accessing {grp}:""")
    print(rg.groups[grp])
Accessing xlo:
<class 'netCDF4._netCDF4.Group'>
group /xlo:
    dimensions(sizes): 
    variables(dimensions): int32 normal(sdim), int32 side(sdim), int32 perpendicular(pdim)
    groups: level_0, level_1
Accessing ylo:
<class 'netCDF4._netCDF4.Group'>
group /ylo:
    dimensions(sizes): 
    variables(dimensions): int32 normal(sdim), int32 side(sdim), int32 perpendicular(pdim)
    groups: level_0, level_1

Inspect the output times

In [4]:
print(rg.variables["time"][:])
[2.  2.5 3.  3.5 4.  4.5 5. ]

Looping through the AMR levels in a given plane

In [5]:
plane = "ylo"
for grp in rg.groups[plane].groups:
    print(f"""Accessing {grp} in plane {plane}:""")
    print(rg.groups[plane].groups[grp])
Accessing level_0 in plane ylo:
<class 'netCDF4._netCDF4.Group'>
group /ylo/level_0:
    dimensions(sizes): nx(48), ny(48), nz(48)
    variables(dimensions): float64 lengths(pdim), float64 lo(pdim), float64 hi(pdim), float64 dx(pdim), float64 velocity(nt,nx,nz,vdim), float64 temperature(nt,nx,nz)
    groups: 
Accessing level_1 in plane ylo:
<class 'netCDF4._netCDF4.Group'>
group /ylo/level_1:
    dimensions(sizes): nx(96), ny(96), nz(56)
    variables(dimensions): float64 lengths(pdim), float64 lo(pdim), float64 hi(pdim), float64 dx(pdim), float64 velocity(nt,nx,nz,vdim), float64 temperature(nt,nx,nz)
    groups: 

An example of plotting the data in the different planes and levels

In [6]:
def plot_field(plane, name):
    nlevels = len(plane.groups)
    shp = (plane.groups["level_0"].variables[name][:]).shape
    if len(shp) == 4:
        ncomp = shp[-1]
    else:
        ncomp = 1
    fig, axs = plt.subplots(nrows=nlevels, ncols=ncomp, sharex=True, figsize=(8*ncomp,6*nlevels), squeeze=False)
    fig.suptitle(f"{name}_{plane.name}", fontsize=20)
    for component in range(ncomp):
        for i, lev in enumerate(plane.groups):
            fld = plane.groups[lev].variables[name][:]
            lo = plane.groups[lev].variables["lo"][:]
            hi = plane.groups[lev].variables["hi"][:]

            if ncomp == 1:
                arr = fld[0, :, :].T
            else:
                arr = fld[0, :, :, component].T
            axs[i, component].imshow(
                arr,
                extent=[lo[0], hi[0], lo[1], hi[1]],
                origin="lower",
                aspect="auto",
            )
            axs[i, component].set_title(lev)
    plt.savefig(f"{name}_{plane.name}.png")

for plane in ["ylo", "xlo"]:

    plot_field(rg.groups[plane], "velocity")
    plot_field(rg.groups[plane], "temperature")
In [ ]: