Skip to content

Commit

Permalink
Add alternative standin for AMR-wind that runs FLORIS (NREL#63)
Browse files Browse the repository at this point in the history
* Setting up example to work through.

* bugfix: fix old dummy name.

* Constructs fi object.

* appears to run as expected.

* Various updates for smoother running.

* Ruff.

* Remove gch yaml in favor of a default dictionary defined in floris_standin.py

* Undo ruff format, which fights with ruff.

* Tests for FlorisStandin.

* ruff

* Adding correct floris branch to github workflow for testing.

* try again...

* and again.

* ready to test external data.

* Test for external data built out and passing.

* ruff.

* Remove obselete handling.

* match reconfiguration of turbine package.

* add pip install instructions.

* ruff :(

* Standin power specified in kW; emulator uses correct actuator.

* Update test to reflect kW units.

* remove unused JoukowskyDisk information.

* Adding a comment explaining units in Floris power calculation

* Adjusting to meet Ruff standards

* Line shortened.

---------

Co-authored-by: Genevieve Starke <[email protected]>
  • Loading branch information
misi9170 and genevievestarke authored Jan 30, 2024
1 parent d862f0a commit d4e65b2
Show file tree
Hide file tree
Showing 15 changed files with 948 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/continuous-integration-workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
pip install -e ".[develop]"
pip install git+https://github.com/NREL/electrolyzer.git
pip install https://github.com/NREL/SEAS/blob/main/SEAS.tar.gz?raw=true
pip install git+https://github.com/NREL/floris.git@v4
# - uses: pre-commit/[email protected]
- name: Run ruff
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ hercules/local_amr_wind_demo/sample_copy.nc

#Ignore csv files
*.csv
!tests/test_inputs/amr_standin_data.csv

# Some output files to ignore
t_00*
logdummy
loghercules
logstandin
logfloris
*echo
*out-example.json

Expand Down
7 changes: 7 additions & 0 deletions docs/install_old.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ NREL's PySAM software is also required for hercules. To install, use
pip install nrel-pysam==4.2.0
```

You may also want to run the FLORIS standin for AMR-Wind for a steady-state representation
of wind farm flows. In this case, run
```
pip install git+https://github.com/NREL/floris.git@v4
```
to get the correct branch of FLORIS installed.

If you run hercules and get an error that `pyyaml` is missing, you may also need to install it using
```
conda install -c conda-forge pyyaml
Expand Down
165 changes: 165 additions & 0 deletions example_case_folders/08_floris_only/amr_input.inp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# SIMULATION STOP #
#.......................................#
time.stop_time = 100.0 # Max (simulated) time to evolve
time.max_step = -1 # Max number of time steps

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# TIME STEP COMPUTATION #
#.......................................#
time.fixed_dt = 0.5 # Use this constant dt if > 0
time.cfl = 0.95 # CFL factor

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# INPUT AND OUTPUT #
#.......................................#
time.plot_interval = 3600 # Steps between plot files
time.checkpoint_interval = 3600 # Steps between checkpoint files
io.restart_file = "/projects/ssc/amr_precursors/b_abl_neutral_lowTI_redo/chk14400"

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# PHYSICS #
#.......................................#
incflo.gravity = 0. 0. -9.81 # Gravitational force (3D)
incflo.density = 1.0 # Reference density

incflo.use_godunov = 1
incflo.godunov_type = weno_z
incflo.diffusion_type = 1
transport.viscosity = 1.0e-5
transport.laminar_prandtl = 0.7
transport.turbulent_prandtl = 0.3333
turbulence.model = OneEqKsgsM84

incflo.physics = ABL Actuator
ICNS.source_terms = BoussinesqBuoyancy CoriolisForcing ABLMeanBoussinesq ActuatorForcing
TKE.source_terms = KsgsM84Src
BoussinesqBuoyancy.reference_temperature = 300.0
CoriolisForcing.latitude = 41.3
ABLForcing.abl_forcing_height = 90
incflo.velocity = 6.928203230275509 4.0 0.0


# Atmospheric boundary layer
ABL.temperature_heights = 0.0 700.0 800.0 1280.0
ABL.temperature_values = 300.0 300.0 308.0 309.44
ABL.reference_temperature = 300.0
ABL.kappa = .40
ABL.surface_roughness_z0 = 1.0E-4
ABL.Uperiods = 25.0
ABL.Vperiods = 25.0
ABL.cutoff_height = 50.0
ABL.deltaU = 1.0
ABL.deltaV = 1.0
ABL.normal_direction = 2
ABL.perturb_ref_height = 50.0
ABL.perturb_temperature = false
ABL.perturb_velocity = true
ABL.stats_output_format = netcdf
ABL.stats_output_frequency = 1
ABL.surface_temp_flux = 0.00
ABL.wall_shear_stress_type = "Moeng"

ABL.bndry_file = "/projects/ssc/amr_precursors/b_abl_neutral_lowTI_redo/bndry_files"
ABL.bndry_io_mode = 1
ABL.bndry_planes = ylo xlo # I'm (Paul) adding this but not sure if I have to
ABL.bndry_var_names = velocity temperature tke


# Output boundary files
ABL.bndry_planes = ylo xlo
ABL.bndry_output_start_time = 7200.0
ABL.bndry_var_names = velocity temperature tke
ABL.bndry_output_format = native
ABL.stats_output_frequency = 1
ABL.stats_output_format = netcdf

# Whether to use helics
helics.activated = true
helics.broker_port = 32000

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# ADAPTIVE MESH REFINEMENT #
#.......................................#
amr.n_cell = 512 512 128 # Grid cells at coarsest AMRlevel
amr.max_level = 0 # Max AMR level in hierarchy

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# GEOMETRY #
#.......................................#
geometry.prob_lo = 0. 0. 0. # Lo corner coordinates
geometry.prob_hi = 5120. 5120. 1280. # Hi corner coordinates
geometry.is_periodic = 0 0 0
xlo.type = "mass_inflow"
xlo.density = 1.0
xlo.temperature = 0.0 # value required but ignored
xlo.tke = 0.0
xhi.type = "pressure_outflow"

ylo.type = "mass_inflow"
ylo.density = 1.0
ylo.temperature = 0.0
ylo.tke = 0.0
yhi.type = "pressure_outflow"

# Boundary conditions
zlo.type = "wall_model"
zlo.tke_type = "zero_gradient"

zhi.type = "slip_wall"
zhi.temperature_type = "fixed_gradient"
zhi.temperature = 0.003 # tracer is used to specify potential temperature gradient

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# VERBOSITY #
#.......................................#
incflo.verbose = 0 # incflo_level



#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# SAMPLING #
#.......................................#
incflo.post_processing = samplingPlane samplingLine

samplingPlane.output_frequency = 600
samplingPlane.labels = z_plane
samplingPlane.fields = velocity temperature
samplingPlane.z_plane.type = PlaneSampler
samplingPlane.z_plane.axis1 = 5110 0.0 0.0
samplingPlane.z_plane.axis2 = 0.0 5110 0.0
samplingPlane.z_plane.origin = 5.0 5.0 0.0
samplingPlane.z_plane.num_points = 512 512
samplingPlane.z_plane.normal = 0.0 0.0 1.0
samplingPlane.z_plane.offsets = 5.0 85.0 155.0 255.0


samplingLine.output_frequency = 1
samplingLine.labels = z_line
samplingLine.fields = velocity temperature
samplingLine.z_line.type = LineSampler
samplingLine.z_line.num_points = 128
samplingLine.z_line.start = 5.0 5.0 5.0
samplingLine.z_line.end = 5.0 5.0 1275.0

#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
# TURBINES #
#.......................................#

Actuator.type = UniformCtDisk
Actuator.UniformCtDisk.rotor_diameter = 126.0
Actuator.UniformCtDisk.hub_height = 90.0
Actuator.UniformCtDisk.thrust_coeff = 0.0 0.0 1.132034888 0.999470963 0.917697381 0.860849503 0.815371198 0.811614904 0.807939328 0.80443352 0.800993851 0.79768116 0.794529244 0.791495834 0.788560434 0.787217182 0.787127977 0.785839257 0.783812219 0.783568108 0.783328285 0.781194418 0.777292539 0.773464375 0.769690236 0.766001924 0.762348072 0.758760824 0.755242872 0.751792927 0.748434131 0.745113997 0.717806682 0.672204789 0.63831272 0.610176496 0.585456847 0.563222111 0.542912273 0.399312061 0.310517829 0.248633226 0.203543725 0.169616419 0.143478955 0.122938861 0.106515296 0.093026095 0.081648606 0.072197368 0.064388275 0.057782745 0.0 0.0
Actuator.UniformCtDisk.wind_speed = 0.0 2.9 3.0 4.0 5.0 6.0 7.0 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 8.0 9.0 10.0 10.1 10.2 10.3 10.4 10.5 10.6 10.7 10.8 10.9 11.0 11.1 11.2 11.3 11.4 11.5 11.6 11.7 11.8 11.9 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 25.1 50.0
Actuator.UniformCtDisk.epsilon = 10.0
Actuator.UniformCtDisk.density = 1.225
Actuator.UniformCtDisk.diameters_to_sample = 1.0
Actuator.UniformCtDisk.num_points_r = 20
Actuator.UniformCtDisk.num_points_t = 5

Actuator.UniformCtDisk.yaw = 240.0

Actuator.labels = T00 T01

Actuator.T00.base_position = 2000.0 2000.0 0.0
Actuator.T01.base_position = 2500.0 2500.0 0.0
28 changes: 28 additions & 0 deletions example_case_folders/08_floris_only/bash_script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Example bash for running things locally
# I just run these one at a t time

# A lot of modules and conda stuff
conda activate hercules

# Set the helics port to use:
export HELICS_PORT=32000

#make sure you use the same port number in the amr_input.inp and hercules_input_000.yaml files.

# Clear old log files for clarity
rm loghercules logfloris

# Set up the helics broker
helics_broker -t zmq -f 2 --loglevel="debug" --local_port=$HELICS_PORT &
#helics_broker -f 2 --consoleloglevel=trace --loglevel=debug --local_port=$HELICS_PORT >> loghelics &

# Need to set this to your hercules folder
# cd /home/pfleming/hercules/hercules
python3 hercules_runscript.py hercules_input_000.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file


python3 floris_runscript.py amr_input.inp >> logfloris 2>&1
# Now go back to scratch folder and launch the job

# cd /scratch/pfleming/c2c/example_sim_02
# mpirun -n 72 /home/pfleming/amr-wind/build/amr_wind amr_input.inp >> logamr
15 changes: 15 additions & 0 deletions example_case_folders/08_floris_only/floris_runscript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import sys

from hercules.floris_standin import launch_floris

# Check that one command line argument was given
if len(sys.argv) != 2:
raise Exception("Usage: python floris_runscript.py <amr_input_file>")

# # Get the first command line argument
# This is the name of the file to read
amr_input_file = sys.argv[1]
print(f"Running FLORIS standin with input file: {amr_input_file}")


launch_floris(amr_input_file)
49 changes: 49 additions & 0 deletions example_case_folders/08_floris_only/hercules_input_000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Input YAML for emy_python

# Name
name: example_000

###
# Describe this emulator setup
description: AMR-wind (Standin) Only

dt: 0.5

hercules_comms:

amr_wind:

wind_farm_0:
type: amr_wind_local #options are amr_wind or amr_wind_local
amr_wind_input_file: amr_input.inp

helics:

config:
name: hercules # What is the purpose of this name
use_dash_frontend: False
KAFKA: False
KAFKA_topics: EMUV1py
helics:
# deltat: 1 # This will be assigned in software
subscription_topics: [status]
publication_topics: [control]
endpoints: []
helicsport : 32000
publication_interval: 1
endpoint_interval: 1
starttime: 0
stoptime: 100

Agent: ControlCenter

py_sims:

controller:







17 changes: 17 additions & 0 deletions example_case_folders/08_floris_only/hercules_runscript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import sys

from hercules.controller_standin import ControllerStandin
from hercules.emulator import Emulator
from hercules.py_sims import PySims
from hercules.utilities import load_yaml

input_dict = load_yaml(sys.argv[1])


controller = ControllerStandin(input_dict)
py_sims = PySims(input_dict)


emulator = Emulator(controller, py_sims, input_dict)
emulator.run_helics_setup()
emulator.enter_execution(function_targets=[], function_arguments=[[]])
1 change: 1 addition & 0 deletions example_case_folders/08_floris_only/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This example runs FLORIS instead of AMR-Wind
10 changes: 7 additions & 3 deletions hercules/amr_wind_standin.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,16 @@ def run(self):
logger.info("Calculating simulation time: %.1f" % sim_time_s)

# Compute the turbine power using a simple formula
if self.message_from_server is not None:
yaw_angles = self.message_from_server[-self.num_turbines :]
else:
yaw_angles = None
(
amr_wind_speed,
amr_wind_direction,
turbine_powers,
turbine_wind_directions,
) = self.get_step(sim_time_s)
) = self.get_step(sim_time_s, yaw_angles)

# ================================================================
# Communicate with control center
Expand Down Expand Up @@ -232,7 +236,7 @@ def run(self):

# TODO cleanup code to move publish and subscribe here.

def get_step(self, sim_time_s):
def get_step(self, sim_time_s, yaw_angles=None):
"""Retreive or calculate wind speed, direction, and turbine powers
Input:
Expand Down Expand Up @@ -327,7 +331,7 @@ def launch_amr_wind_standin(amr_input_file, amr_standin_data_file=None):
"endpoint_interval": 1,
"starttime": 0,
"stoptime": temp["stop_time"],
"Agent": "dummy_amr_wind",
"Agent": "amr_wind_standin",
}

if amr_standin_data_file is not None:
Expand Down
5 changes: 4 additions & 1 deletion hercules/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ def read_amr_wind_input(self, amr_wind_input):
if "Actuator.labels" in line:
turbine_labels = line.split()[2:]
num_turbines = len(turbine_labels)
for line in Lines:
if "Actuator.type" in line:
actuator_type = line.split()[-1]

self.num_turbines = num_turbines
print("Number of turbines in amrwind: ", num_turbines)
Expand All @@ -397,7 +400,7 @@ def read_amr_wind_input(self, amr_wind_input):

# Find the diameter
for line in Lines:
if "rotor_diameter" in line:
if "Actuator.%s.rotor_diameter" % actuator_type in line:
D = float(line.split()[-1])

# Get the turbine locations
Expand Down
Loading

0 comments on commit d4e65b2

Please sign in to comment.