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

FLORIS wakes #1

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions amrwind_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,7 @@ def setupfigax(self, clear=True, subplot=111):
from farmfunctions import turbines_createAllTurbines, turbines_previewAllTurbines
from farmfunctions import sampling_createAllProbes
from farmfunctions import sweep_SetupRunParamSweep
from farmfunctions import floris_setup

def getMaxLevel(self):
max_level = 0
Expand Down Expand Up @@ -1087,6 +1088,9 @@ def ABL_calculateWindVector(self):

def ABL_calculateWDirWS(self):
Wvec = self.inputvars['ABL_velocity'].getval()
if Wvec is None:
print('ABL_calculateWDirWS() called',
'but ABL_velocity vector not specified')
Uhoriz = np.sqrt(Wvec[0]**2 + Wvec[1]**2)
# Check for North/East vector
thetaoffset = self.get_N_angle_to_Y()
Expand Down
7 changes: 6 additions & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2224,7 +2224,7 @@ popupwindow:
title: Plot domain # optional
loadonstart: True # optional
width: 350
height: 680
height: 720
frames:
- name: plot_frameoverall
title: Plot settings
Expand Down Expand Up @@ -2265,6 +2265,11 @@ popupwindow:
label: Plot wind & N arrows
inputtype: bool
defaultval: True
- name: plot_florissoln
frame: plot_frameoverall
label: Plot FLORIS wake solution
inputtype: bool
defaultval: False
- name: plot_sampleprobes
frame: plot_framesampleprobes
label: #Plot sample probes
Expand Down
28 changes: 27 additions & 1 deletion farm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ frames:
row: 2
kwargs:
borderwidth: 0

- name: frame_floris
tab: Farm
title: Setup FLORIS
toggled: True

inputwidgets:
- name: farm_setupfile
Expand Down Expand Up @@ -473,6 +478,23 @@ inputwidgets:
help: Log file to record each run
farmsetup: sweep_logfile

# -------- FLORIS inputs ---------------------------------
- name: floris_inputfile
frame: frame_floris
label: YAML file
inputtype: str #filename
row: 1
defaultval: 'floris.yaml'
entryopt:
width: 25
fileopenopt:
selecttype: saveas
kwargs:
filetypes:
- ["YAML files", "*.yaml"]
- ["all files", "*.*"]
outputdef:
help: YAML file with FLORIS settings

# -------- Hidden/extra inputs ---------------------------
- name: wfarm_embedamrwindinput
Expand Down Expand Up @@ -574,6 +596,11 @@ buttons:
command: self.sweep_SetupRunParamSweep
#command: self.donothing_button

- name: farmbutton_generatefloris
text: Generate
frame: frame_floris
command: "self.floris_setup"

# --- Help buttons ---
- name: helpbutton_farmturbinecsv
text: "[?]"
Expand Down Expand Up @@ -627,7 +654,6 @@ buttons:
gridoptions:
sticky: 'NE'


# Add all help messages and buttons in here
helpwindows:
frame_farmturbinecsv:
Expand Down
35 changes: 35 additions & 0 deletions farmfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,41 @@ def sweep_SetupRunParamSweep(self, verbose=False):

return

def floris_setup(self, templatefile='floris_template.yaml', verbose=False):
if not useruamel:
print('Ruamel not found, FLORIS inputs not generated')
return

# Go thorugh all turbines
layout_x = []
layout_y = []
allturbines = self.listboxpopupwindict['listboxactuator']
alltags = allturbines.getitemlist()
keystr = lambda n, d1, d2: d2.name
for turb in alltags:
tdict = allturbines.dumpdict('AMR-Wind', subset=[turb], keyfunc=keystr)
layout_x.append(tdict['Actuator_base_position'][0])
layout_y.append(tdict['Actuator_base_position'][1])

# modify template
with open(templatefile,'r') as f:
inp = yaml.load(f, yaml.RoundTripLoader) # _should_ preserve comments
inp['farm']['layout_x'] = layout_x
inp['farm']['layout_y'] = layout_y
self.ABL_calculateWDirWS()
wspd = self.inputvars['ABL_windspeed'].getval()
wdir = self.inputvars['ABL_winddir'].getval()
inp['flow_field']['wind_speeds'] = [wspd]
inp['flow_field']['wind_directions'] = [wdir]

# write new input file
florisfile = self.inputvars['floris_inputfile'].getval()
with open(florisfile,'w') as f:
yaml.dump(inp, f, Dumper=yaml.RoundTripDumper)
print('wrote FLORIS input file',florisfile)

return

# ----------- Functions related to I/O ----------------
def writeFarmSetupYAML(self, filename, verbose=True):
"""
Expand Down
89 changes: 89 additions & 0 deletions floris_template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@

name: GCH
description: Template based on three-turbine Gauss Curl Hybrid model example
floris_version: v3.0.0

logging:
console:
enable: true
level: WARNING
file:
enable: false
level: WARNING

solver:
type: turbine_grid
turbine_grid_points: 3

farm:
layout_x:
- 0.0
- 630.0
- 1260.0
layout_y:
- 0.0
- 0.0
- 0.0
turbine_type:
- nrel_5MW

flow_field:
air_density: 1.225
reference_wind_height: -1 # -1 is code for use the hub height
turbulence_intensity: 0.06
wind_directions:
- 270.0
wind_shear: 0.12
wind_speeds:
- 8.0
wind_veer: 0.0

wake:
model_strings:
combination_model: sosfs
deflection_model: gauss
turbulence_model: crespo_hernandez
velocity_model: gauss

enable_secondary_steering: true
enable_yaw_added_recovery: true
enable_transverse_velocities: true

wake_deflection_parameters:
gauss:
ad: 0.0
alpha: 0.58
bd: 0.0
beta: 0.077
dm: 1.0
ka: 0.38
kb: 0.004
jimenez:
ad: 0.0
bd: 0.0
kd: 0.05

wake_velocity_parameters:
cc:
a_s: 0.179367259
b_s: 0.0118889215
c_s1: 0.0563691592
c_s2: 0.13290157
a_f: 3.11
b_f: -0.68
c_f: 2.41
alpha_mod: 1.0
gauss:
alpha: 0.58
beta: 0.077
ka: 0.38
kb: 0.004
jensen:
we: 0.05

wake_turbulence_parameters:
crespo_hernandez:
initial: 0.1
constant: 0.5
ai: 0.8
downstream: -0.32
67 changes: 67 additions & 0 deletions plotfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""
Plotting functions
"""
import os
import numpy as np
from collections import OrderedDict
from matplotlib.collections import PatchCollection
Expand Down Expand Up @@ -440,6 +441,72 @@ def plotDomain(self, ax=None):
plotTurbine(ax, basepos, turbhh, turbD, yaw, ix, iy,
lw=1, color='k', alpha=0.75)

# Plot the FLORIS wake solution
# -----------------------------
if plotparams['plot_florissoln']:
try:
from floris.tools import FlorisInterface
except ImportError:
print('Need to install FLORIS')
else:
inpfile = self.inputvars['floris_inputfile'].getval()
if not os.path.isfile(inpfile):
print('Need to run Farm > Setup FLORIS > Generate')
else:
print('Running FLORIS')
fi = FlorisInterface(inpfile)

# can optionally override wind speed/dir here
self.ABL_calculateWDirWS()
wspd = self.inputvars['ABL_windspeed'].getval()
wdir = self.inputvars['ABL_winddir'].getval()
#fi.reinitialize(wind_directions=[wdir], wind_speeds=[wspd])

# setup yaw (offset) angles, relative to wind direction
yaw_angles = []
allturbines = self.listboxpopupwindict['listboxactuator']
alltags = allturbines.getitemlist()
keystr = lambda n, d1, d2: d2.name
for turb in alltags:
tdict = allturbines.dumpdict('AMR-Wind', subset=[turb], keyfunc=keystr)
yaw_angles.append(wdir - tdict['Actuator_yaw'])
yaw_angles = np.array([[yaw_angles]])

# finally, run FLORIS
fi.calculate_wake(yaw_angles=yaw_angles)
nx = self.inputvars['n_cell'].getval()[0]
ny = self.inputvars['n_cell'].getval()[1]
zhub = 90.0 # TODO update this
horizontal_plane = fi.calculate_horizontal_plane(
x_resolution=nx,
y_resolution=ny,
height=zhub,
yaw_angles=yaw_angles)

# calculate normalized wind speed
print('Postprocessing FLORIS')
df = horizontal_plane.df.set_index(['x1','x2'])
floris_vel = np.sqrt(df['u']**2 + df['v']**2) / wspd
floris_vel = floris_vel.sort_index().unstack()

# rotate grid from wind frame back to inertial frame
# note: currently FLORIS v3.2 does not provide this function
xx,yy = np.meshgrid(floris_vel.index, floris_vel.columns, indexing='ij')
coordinates = fi.floris.grid.turbine_coordinates_array # shape==(Nturb,3)
x_center_of_rotation = (np.min(coordinates[:,0]) + np.max(coordinates[:,0])) / 2
y_center_of_rotation = (np.min(coordinates[:,1]) + np.max(coordinates[:,1])) / 2
x_delta = xx - x_center_of_rotation
y_delta = yy - y_center_of_rotation
wind_delta = np.radians(wdir - 270)
xx0 = x_delta * np.cos(wind_delta) + y_delta * np.sin(wind_delta) + x_center_of_rotation
yy0 = -x_delta * np.sin(wind_delta) + y_delta * np.cos(wind_delta) + y_center_of_rotation

print('Plotting wakes')
cm = ax.contour(xx0, yy0, floris_vel,
levels=[0.5, 0.75, 0.9, 0.95, 0.99, 0.995],
linewidths=0.5)
#cb = ax.colorbar(cm)

# --------------------------------
# Set some plot formatting parameters
ax.set_aspect('equal')
Expand Down