From 162d655d53d0d8b580e7ae01aa1917967c6f827e Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 7 Feb 2023 14:18:48 -0700 Subject: [PATCH 01/38] Skeleton for a turbine yaw simulator. --- emu_python/emulator.py | 2 -- emu_python/python_simulators/yaw_simulator.py | 12 ++++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 emu_python/python_simulators/yaw_simulator.py diff --git a/emu_python/emulator.py b/emu_python/emulator.py index 63783fd6..fc080232 100644 --- a/emu_python/emulator.py +++ b/emu_python/emulator.py @@ -2,8 +2,6 @@ import pandas as pd import datetime as dt -from federateaccesspoint import federateagent - diff --git a/emu_python/python_simulators/yaw_simulator.py b/emu_python/python_simulators/yaw_simulator.py new file mode 100644 index 00000000..4ff30f64 --- /dev/null +++ b/emu_python/python_simulators/yaw_simulator.py @@ -0,0 +1,12 @@ +class YawSimulator: + + def __init__(self): + + pass + + def step(self, inputs): + + outputs = None + + return outputs + From 6910ea7fed9e387014938bb1a4558a2f5eb66e41 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 7 Feb 2023 17:17:45 -0700 Subject: [PATCH 02/38] Implementation mostly in place. --- emu_python/python_simulators/yaw_simulator.py | 50 ++++++++++++++-- .../001_yaw_simulator/emu_input_001.yaml | 60 +++++++++++++++++++ .../001_yaw_simulator/emu_runscript.py | 22 +++++++ 3 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 local_examples/001_yaw_simulator/emu_input_001.yaml create mode 100644 local_examples/001_yaw_simulator/emu_runscript.py diff --git a/emu_python/python_simulators/yaw_simulator.py b/emu_python/python_simulators/yaw_simulator.py index 4ff30f64..55c41860 100644 --- a/emu_python/python_simulators/yaw_simulator.py +++ b/emu_python/python_simulators/yaw_simulator.py @@ -1,12 +1,54 @@ -class YawSimulator: +from wfc_framework.yaw_controller import yaw_controller - def __init__(self): +class IndirectControlYawSystem: + """ + Wrapper for wfc_framework's yaw_controller to align with emu_python's + syntax. - pass + IndirectControlYawSystem uses target offsets applied to the turbine's + wind vane signal to implement misalignments. + """ + + def __init__(self, input_dict): + + if input_dict["wd_init"] == "from_wind_sim": + input_dict["wd_init"] = 0.0 # TODO: get initial value from AMR-Wind + if input_dict["yaw_init"] == "use_wd_init": + input_dict["yaw_init"] == input_dict["wd_init"] + + self.closed_loop_yaw_controller = yaw_controller( + options_dict = {k: input_dict[k] for k in \ + ("yaw_init", "wd_init", "time_const", "deadband", "yaw_rate") + } + ) def step(self, inputs): - outputs = None + self.closed_loop_yaw_controller.compute( + wd=inputs["wind_direction"], + target_vane_offset=inputs["offset_target"] + ) + + outputs = { + "yaw_position":self.closed_loop_yaw_controller.yaw_position, + "yaw_state":self.closed_loop_yaw_controller.yaw_state + } return outputs +class DirectControlYawSystem: + """ + DirectControlYawSystem initiates yaw actions directly. + + Not yet implemented. + """ + + def __init__(self, input_dict): + + raise NotImplementedError("DirectControlYawSystem not implemented.") + + def step(self, inputs): + + outputs = None + + return outputs \ No newline at end of file diff --git a/local_examples/001_yaw_simulator/emu_input_001.yaml b/local_examples/001_yaw_simulator/emu_input_001.yaml new file mode 100644 index 00000000..1da46f2d --- /dev/null +++ b/local_examples/001_yaw_simulator/emu_input_001.yaml @@ -0,0 +1,60 @@ +# Input YAML for emy_python + +# Name +name: example_000 + +### +# Describe this emulator setup +description: Just a solar plant + +emu_helics: + + wind_farm_0: + type: amr_wind + use: True + amr_wind_input_file: input.inp + + + helics: + + config: + # "name": "controlcenter", + # "use_dash_frontend": False, + KAFKA: False + KAFKA_topics: EMUV1py + helics: + deltat: 1 + subscription_topic: [status] + publication_topic: [control] + endpoints: [] + publication_interval: 1 + endpoint_interval: 1 + starttime: 0 + stoptime: 36000 + Agent: ControlCenter + +py_sims: + + solar_farm_0: # The name of py_sim object 1 + + type: simple_solar_farm + use: False + capacity: 50 # MW + + yaw_system_0: # The name of py_sim object n + type: IndirectControlYawSystem + num_turbines: 5 + yaw_init: 0.0 # Or, "use_wd_init" + wd_init: 0.0 # Or, "from_wind_sim" + time_const: 30. + deadband: 8. + yaw_rate: 0.3 + +controller: + + + + + + + diff --git a/local_examples/001_yaw_simulator/emu_runscript.py b/local_examples/001_yaw_simulator/emu_runscript.py new file mode 100644 index 00000000..74010b82 --- /dev/null +++ b/local_examples/001_yaw_simulator/emu_runscript.py @@ -0,0 +1,22 @@ +from emu_python.emulator import Emulator +from emu_python.controller import Controller +from emu_python.emu_helics import EmuHelics +from emu_python.py_sims import PySims +from emu_python.utilities import load_yaml + +import sys + + + +input_dict = load_yaml(sys.argv[1]) + +print(input_dict) + +# controller = Controller(input_dict) +# emu_helics = EmuHelics(input_dict) +# py_sims = PySims(input_dict) + + +# emulator = Emulator(controller, emu_helics, py_sims) + +# emulator.run() From c62b5cf5e314a7b6df32cff192696fa142afdd98 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 28 Mar 2023 16:32:40 -0600 Subject: [PATCH 03/38] Simple yaw controller running on dummy. Need to make back compatible. --- emu_python/dummy_amr_wind.py | 8 +- emu_python/emulator.py | 18 +- emu_python/py_sims.py | 26 +- emu_python/python_simulators/simple_yaw.py | 32 ++ .../example_sim_yaw_control/amr_input.inp | 180 ++++++++++ .../example_sim_yaw_control/bash_script.sh | 19 + .../emu_input_000.yaml | 74 ++++ .../example_sim_yaw_control/emu_runscript.py | 26 ++ .../emu_runscript_dummy_amr.py | 4 + .../example_sim_yaw_control/input_dict.echo | 1 + .../example_sim_yaw_control/readme.txt | 1 + .../test_read_output.ipynb | 328 ++++++++++++++++++ 12 files changed, 704 insertions(+), 13 deletions(-) create mode 100644 emu_python/python_simulators/simple_yaw.py create mode 100755 example_case_folders/example_sim_yaw_control/amr_input.inp create mode 100644 example_case_folders/example_sim_yaw_control/bash_script.sh create mode 100644 example_case_folders/example_sim_yaw_control/emu_input_000.yaml create mode 100644 example_case_folders/example_sim_yaw_control/emu_runscript.py create mode 100644 example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py create mode 100644 example_case_folders/example_sim_yaw_control/input_dict.echo create mode 100644 example_case_folders/example_sim_yaw_control/readme.txt create mode 100644 example_case_folders/example_sim_yaw_control/test_read_output.ipynb diff --git a/emu_python/dummy_amr_wind.py b/emu_python/dummy_amr_wind.py index 6c5b76d8..40e53750 100644 --- a/emu_python/dummy_amr_wind.py +++ b/emu_python/dummy_amr_wind.py @@ -38,6 +38,7 @@ # PARAMETERS num_turbines = 2 +np.random.seed(0) # Initialize to all zeros turbine_powers = np.zeros(num_turbines) @@ -123,6 +124,11 @@ def run(self): # Convert to a list turbine_powers = turbine_powers.tolist() + # Set dummy wind directions to be passed out + turbine_wind_directions = list( + wind_direction + 5.*np.random.randn(num_turbines) + ) + # ================================================================ # Communicate with control center # Send the turbine powers for this time step and get wind speed and wind direction for the @@ -140,7 +146,7 @@ def run(self): message_code = 0 # [34 + random.random(), 45.3+random.random() , 67+random.random()] message_from_client_array = [ - sim_time_s] + [wind_speed, wind_direction] + turbine_powers + sim_time_s] + [wind_speed, wind_direction] + turbine_powers + turbine_wind_directions # Send helics message to Control Center # publish on topic: status diff --git a/emu_python/emulator.py b/emu_python/emulator.py index b84563c4..ee50d6ee 100644 --- a/emu_python/emulator.py +++ b/emu_python/emulator.py @@ -101,6 +101,8 @@ def __init__(self, controller, py_sims, input_dict): self.turbine_power_array = np.zeros(self.num_turbines) self.amr_wind_dict[self.amr_wind_names[0] ]['turbine_powers'] = np.zeros(self.num_turbines) + self.amr_wind_dict[self.amr_wind_names[0] + ]['turbine_wind_directions'] = [0.]*self.num_turbines # TODO Could set up logging here @@ -201,6 +203,8 @@ def run(self): # TODO hard-coded for now assuming only one AMR-WIND self.amr_wind_dict[self.amr_wind_names[0] ]['turbine_powers'] = turbine_power_array + self.amr_wind_dict[self.amr_wind_names[0] + ]['turbine_wind_directions'] = turbine_wd_array self.turbine_power_array = turbine_power_array self.amr_wind_dict[self.amr_wind_names[0] ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind @@ -303,13 +307,13 @@ def process_subscription_messages(self, msg): def process_periodic_publication(self): # Periodically publish data to the surrogate - # self.get_signals_from_front_end() - # self.set_wind_speed_direction() - - #yaw_angles = [270 for t in range(self.num_turbines)] - yaw_angles = [240 for t in range(self.num_turbines)] - # log these in kafka - #yaw_angles[1] = 260 + # Hard coded to single wind farm for the moment + wind_farm_name = list(self.input_dict["emu_comms"]["amr_wind"].keys())[0] + yaw_angles = self.input_dict["py_sims"]\ + [self.input_dict["emu_comms"]["amr_wind"][wind_farm_name] + ["yaw_simulator_name"] + ]\ + ["outputs"]["turbine_yaws"] # Send timing and yaw information to AMRWind via helics # publish on topic: control diff --git a/emu_python/py_sims.py b/emu_python/py_sims.py index 5fbdd689..cddbe0ac 100644 --- a/emu_python/py_sims.py +++ b/emu_python/py_sims.py @@ -1,4 +1,5 @@ from emu_python.python_simulators.simple_solar import SimpleSolar +from emu_python.python_simulators.simple_yaw import SimpleYaw class PySims(): @@ -25,19 +26,34 @@ def __init__(self, input_dict): def get_py_sim(self, py_sim_obj_dict): if py_sim_obj_dict['py_sim_type'] == 'SimpleSolar': - return SimpleSolar(py_sim_obj_dict, self.dt) + if py_sim_obj_dict['py_sim_type'] == 'SimpleYaw': + return SimpleYaw(py_sim_obj_dict, self.dt) + def get_py_sim_dict(self): return self.py_sim_dict def step(self, input_dict): + # Exchange information between objects + self.set_pysim_inputs(input_dict) + # Collect the py_sim objects for py_sim_name in self.py_sim_names: + self.py_sim_dict[py_sim_name]['outputs'] = self.py_sim_dict[py_sim_name]['object'].step(self.py_sim_dict[py_sim_name]['inputs']) - self.py_sim_dict[py_sim_name]['outputs'] = self.py_sim_dict[py_sim_name]['object'].step(self.py_sim_dict[py_sim_name]['inputs']) - - - + def set_pysim_inputs(self, input_dict): + + # TODO: should this logic be handled INSIDE the py_sim object? + for py_sim_name in self.py_sim_names: + if self.py_sim_dict[py_sim_name]['py_sim_type'] == 'SimpleYaw': + # Need access to the wind directions from AMR-Wind + wind_directions = input_dict['emu_comms']['amr_wind']\ + [self.py_sim_dict[py_sim_name]['object'].wind_farm_name]\ + ['turbine_wind_directions'] + + self.py_sim_dict[py_sim_name]['inputs'] = { + 'turbine_wind_directions': wind_directions + } diff --git a/emu_python/python_simulators/simple_yaw.py b/emu_python/python_simulators/simple_yaw.py new file mode 100644 index 00000000..360956de --- /dev/null +++ b/emu_python/python_simulators/simple_yaw.py @@ -0,0 +1,32 @@ +# Very simple solar farm model +import numpy as np + + +class SimpleYaw(): + """ + Simple pass-through of wind direction to set yaw angle. + """ + + def __init__(self, input_dict, dt): + + # Perhaps these should inherit from an abstract class that + # requires a certain input structure. + + self.dt = dt # Won't be needed here, but generally good to have + self.n_turbines = input_dict['num_turbines'] + self.turbines = range(self.n_turbines) + self.wind_farm_name = input_dict['wind_farm_name'] + + # Set initial conditions + self.yaw_angles = [input_dict['initial_conditions']['yaw']]*self.n_turbines + + def return_outputs(self): + + return {'turbine_yaws':self.yaw_angles} + + def step(self, input_dict): + + # Commands are simply the current wind directions + self.yaw_angles = input_dict['turbine_wind_directions'] + + return self.return_outputs() \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/amr_input.inp b/example_case_folders/example_sim_yaw_control/amr_input.inp new file mode 100755 index 00000000..a3428040 --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/amr_input.inp @@ -0,0 +1,180 @@ +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# SIMULATION STOP # +#.......................................# +time.stop_time = 10800.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 + +#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# +# 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.7 1.2 +Actuator.UniformCtDisk.wind_speed = 0.0 10.0 12.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.labels = T00 T01 +# T02 T03 T04 T05 T06 T07 +Actuator.JoukowskyDisk.thrust_coeff = 8.1672e-01 7.9044e-01 7.8393e-01 7.8624e-01 7.8824e-01 7.8942e-01 7.8902e-01 7.8740e-01 7.8503e-01 7.8237e-01 7.7955e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.6922e-01 7.4270e-01 5.5949e-01 4.6163e-01 3.8786e-01 3.2901e-01 2.8093e-01 2.4114e-01 2.0795e-01 1.8010e-01 1.5663e-01 1.3679e-01 1.1995e-01 1.0562e-01 9.3384e-02 8.2908e-02 7.3910e-02 6.6159e-02 5.9463e-02 5.3662e-02 4.8622e-02 4.4230e-02 +Actuator.JoukowskyDisk.wind_speed = 3.0000e+00 3.5495e+00 4.0679e+00 4.5539e+00 5.0064e+00 5.4244e+00 5.8069e+00 6.1530e+00 6.4619e+00 6.7330e+00 6.9655e+00 7.1589e+00 7.3128e+00 7.4269e+00 7.5009e+00 7.5345e+00 7.5412e+00 7.5883e+00 7.6757e+00 7.8031e+00 7.9702e+00 8.1767e+00 8.4221e+00 8.7059e+00 9.0273e+00 9.3856e+00 9.7800e+00 1.0210e+01 1.0659e+01 1.0673e+01 1.1170e+01 1.1699e+01 1.2259e+01 1.2848e+01 1.3465e+01 1.4109e+01 1.4778e+01 1.5471e+01 1.6185e+01 1.6921e+01 1.7674e+01 1.8445e+01 1.9231e+01 2.0030e+01 2.0841e+01 2.1661e+01 2.2489e+01 2.3323e+01 2.4160e+01 2.5000e+01 +Actuator.JoukowskyDisk.rpm = 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0861e+00 5.1954e+00 5.2765e+00 5.3290e+00 5.3529e+00 5.3577e+00 5.3912e+00 5.4532e+00 5.5437e+00 5.6625e+00 5.8092e+00 5.9836e+00 6.1851e+00 6.4135e+00 6.6681e+00 6.9483e+00 7.2535e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 +Actuator.JoukowskyDisk.rotor_diameter = 240.0 +Actuator.JoukowskyDisk.hub_height = 150.0 +Actuator.JoukowskyDisk.output_frequency = 100 +Actuator.JoukowskyDisk.diameters_to_sample = 1.0 +Actuator.JoukowskyDisk.num_points_r = 40 +Actuator.JoukowskyDisk.num_points_t = 5 +Actuator.JoukowskyDisk.num_blades = 3 +Actuator.JoukowskyDisk.use_tip_correction = true +Actuator.JoukowskyDisk.use_root_correction = true +Actuator.JoukowskyDisk.epsilon = 5.0 +Actuator.JoukowskyDisk.vortex_core_size = 24.0 + +Actuator.UniformCtDisk.yaw = 240.0 + +Actuator.T00.base_position = 2000.0 2000.0 0.0 +Actuator.T01.base_position = 2500.0 2500.0 0.0 diff --git a/example_case_folders/example_sim_yaw_control/bash_script.sh b/example_case_folders/example_sim_yaw_control/bash_script.sh new file mode 100644 index 00000000..9be1521b --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/bash_script.sh @@ -0,0 +1,19 @@ +# Example bash for running things locally +# I just run these one at a t time + +# A lot of modules and conda stuff +conda activate emupy + +# Set up the helics broker +helics_broker -t zmq -f 2 --loglevel="debug" & + +# Need to set this to your emu_python folder +# cd /home/pfleming/emu_python/emu_python +python3 emu_runscript.py emu_input_000.yaml >> logemu 2>&1 & # Start the controller center and pass in input file + + +python3 emu_runscript_dummy_amr.py >> logdummy 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 \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml new file mode 100644 index 00000000..8ef1800d --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml @@ -0,0 +1,74 @@ +# Input YAML for emy_python + +# Name +name: example_000 + +### +# Describe this emulator setup +description: Just a solar plant + +dt: 0.5 + +emu_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 + yaw_simulator_name: yaw_system_0 # can also use "none" + + helics: + + config: + name: emu_python # 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 : 23405 + publication_interval: 1 + endpoint_interval: 1 + starttime: 0 + stoptime: 100 + + Agent: ControlCenter + +py_sims: + + solar_farm_0: # The name of py_sim object 1 + + py_sim_type: SimpleSolar + capacity: 50 # MW + efficiency: 0.5 #Fraction + + initial_conditions: + + power: 25 # MW + irradiance: 1000 + + yaw_system_0: # The name of py_sim object 2 [TODO: one for each turbine?] + + py_sim_type: SimpleYaw + num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind + wind_farm_name: wind_farm_0 + + initial_conditions: + + yaw: 270. # degrees (same for all turbines) (will this work?) + + + + +controller: + + + + + + + diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript.py b/example_case_folders/example_sim_yaw_control/emu_runscript.py new file mode 100644 index 00000000..8103a23e --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/emu_runscript.py @@ -0,0 +1,26 @@ +from emu_python.emulator import Emulator +from emu_python.controller import Controller +# from emu_python.emu_comms import EmuComms +from emu_python.py_sims import PySims +from emu_python.utilities import load_yaml + +import sys + + + +input_dict = load_yaml(sys.argv[1]) + +#print(input_dict) + +controller = Controller(input_dict) +# emu_comms = EmuComms(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=[[]]) + +print("runscript complete.") + diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py b/example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py new file mode 100644 index 00000000..ed630d79 --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py @@ -0,0 +1,4 @@ +from emu_python.dummy_amr_wind import launch_dummy_amr_wind + + +launch_dummy_amr_wind() \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/input_dict.echo b/example_case_folders/example_sim_yaw_control/input_dict.echo new file mode 100644 index 00000000..88426a01 --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/input_dict.echo @@ -0,0 +1 @@ +{'name': 'example_000', 'description': 'Just a solar plant', 'dt': 0.5, 'emu_comms': {'amr_wind': {'wind_farm_0': {'type': 'amr_wind_local', 'amr_wind_input_file': 'amr_input.inp', 'yaw_simulator_name': 'yaw_system_0', 'num_turbines': 2, 'turbine_labels': ['T00', 'T01'], 'rotor_diameter': 240.0, 'turbine_locations': [(2000.0, 2000.0), (2500.0, 2500.0)], 'turbine_powers': [0, 0], 'turbine_wind_directions': [0, 0], 'sim_time_s_amr_wind': 0}}, 'helics': {'config': {'name': 'emu_python', 'use_dash_frontend': False, 'KAFKA': False, 'KAFKA_topics': 'EMUV1py', 'helics': {'subscription_topics': ['status'], 'publication_topics': ['control'], 'endpoints': [], 'helicsport': 23405, 'deltat': 0.5}, 'publication_interval': 1, 'endpoint_interval': 1, 'starttime': 0, 'stoptime': 100, 'Agent': 'ControlCenter'}}}, 'py_sims': {'solar_farm_0': {'py_sim_type': 'SimpleSolar', 'capacity': 50, 'efficiency': 0.5, 'initial_conditions': {'power': 25, 'irradiance': 1000}, 'object': , 'outputs': {'power': 2.5e-05, 'irradiance': 1000.0}, 'inputs': {}}, 'yaw_system_0': {'py_sim_type': 'SimpleYaw', 'num_turbines': 2, 'wind_farm_name': 'wind_farm_0', 'initial_conditions': {'yaw': 270.0}, 'object': , 'outputs': {'turbine_yaws': [0.0, 0.0]}, 'inputs': {'turbine_wind_directions': [0.0, 0.0]}}}, 'controller': {}} diff --git a/example_case_folders/example_sim_yaw_control/readme.txt b/example_case_folders/example_sim_yaw_control/readme.txt new file mode 100644 index 00000000..92fa3a96 --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/readme.txt @@ -0,0 +1 @@ +This example is using new framework trying to run a local (non-eagle) example with the dummy_amr_wind \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/test_read_output.ipynb b/example_case_folders/example_sim_yaw_control/test_read_output.ipynb new file mode 100644 index 00000000..5eb34e1b --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/test_read_output.ipynb @@ -0,0 +1,328 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dtemu_comms.amr_wind.wind_farm_0.num_turbinesemu_comms.amr_wind.wind_farm_0.rotor_diameteremu_comms.amr_wind.wind_farm_0.turbine_powers.000emu_comms.amr_wind.wind_farm_0.turbine_powers.001emu_comms.helics.config.use_dash_frontendemu_comms.helics.config.KAFKAemu_comms.helics.config.helics.helicsportemu_comms.helics.config.helics.deltatemu_comms.helics.config.publication_intervalemu_comms.helics.config.endpoint_intervalemu_comms.helics.config.starttimeemu_comms.helics.config.stoptimepy_sims.solar_farm_0.capacitypy_sims.solar_farm_0.efficiencypy_sims.solar_farm_0.initial_conditions.powerpy_sims.solar_farm_0.initial_conditions.irradiancepy_sims.solar_farm_0.outputs.powerpy_sims.solar_farm_0.outputs.irradianceclock_time
00.52240.00.0000000.000000FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.043832
10.52240.00.0000000.000000FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.047719
20.52240.00.0000000.000000FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.052373
30.52240.0525.344333409.219457FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.055797
40.52240.0525.344333409.219457FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.057075
\n", + "
" + ], + "text/plain": [ + " dt emu_comms.amr_wind.wind_farm_0.num_turbines \\\n", + "0 0.5 2 \n", + "1 0.5 2 \n", + "2 0.5 2 \n", + "3 0.5 2 \n", + "4 0.5 2 \n", + "\n", + " emu_comms.amr_wind.wind_farm_0.rotor_diameter \\\n", + "0 240.0 \n", + "1 240.0 \n", + "2 240.0 \n", + "3 240.0 \n", + "4 240.0 \n", + "\n", + " emu_comms.amr_wind.wind_farm_0.turbine_powers.000 \\\n", + "0 0.000000 \n", + "1 0.000000 \n", + "2 0.000000 \n", + "3 525.344333 \n", + "4 525.344333 \n", + "\n", + " emu_comms.amr_wind.wind_farm_0.turbine_powers.001 \\\n", + "0 0.000000 \n", + "1 0.000000 \n", + "2 0.000000 \n", + "3 409.219457 \n", + "4 409.219457 \n", + "\n", + " emu_comms.helics.config.use_dash_frontend emu_comms.helics.config.KAFKA \\\n", + "0 False False \n", + "1 False False \n", + "2 False False \n", + "3 False False \n", + "4 False False \n", + "\n", + " emu_comms.helics.config.helics.helicsport \\\n", + "0 23405 \n", + "1 23405 \n", + "2 23405 \n", + "3 23405 \n", + "4 23405 \n", + "\n", + " emu_comms.helics.config.helics.deltat \\\n", + "0 0.5 \n", + "1 0.5 \n", + "2 0.5 \n", + "3 0.5 \n", + "4 0.5 \n", + "\n", + " emu_comms.helics.config.publication_interval \\\n", + "0 1 \n", + "1 1 \n", + "2 1 \n", + "3 1 \n", + "4 1 \n", + "\n", + " emu_comms.helics.config.endpoint_interval \\\n", + "0 1 \n", + "1 1 \n", + "2 1 \n", + "3 1 \n", + "4 1 \n", + "\n", + " emu_comms.helics.config.starttime emu_comms.helics.config.stoptime \\\n", + "0 0 100 \n", + "1 0 100 \n", + "2 0 100 \n", + "3 0 100 \n", + "4 0 100 \n", + "\n", + " py_sims.solar_farm_0.capacity py_sims.solar_farm_0.efficiency \\\n", + "0 50 0.5 \n", + "1 50 0.5 \n", + "2 50 0.5 \n", + "3 50 0.5 \n", + "4 50 0.5 \n", + "\n", + " py_sims.solar_farm_0.initial_conditions.power \\\n", + "0 25 \n", + "1 25 \n", + "2 25 \n", + "3 25 \n", + "4 25 \n", + "\n", + " py_sims.solar_farm_0.initial_conditions.irradiance \\\n", + "0 1000 \n", + "1 1000 \n", + "2 1000 \n", + "3 1000 \n", + "4 1000 \n", + "\n", + " py_sims.solar_farm_0.outputs.power \\\n", + "0 0.000025 \n", + "1 0.000025 \n", + "2 0.000025 \n", + "3 0.000025 \n", + "4 0.000025 \n", + "\n", + " py_sims.solar_farm_0.outputs.irradiance clock_time \n", + "0 1000.0 2023-03-14 16:00:42.043832 \n", + "1 1000.0 2023-03-14 16:00:42.047719 \n", + "2 1000.0 2023-03-14 16:00:42.052373 \n", + "3 1000.0 2023-03-14 16:00:42.055797 \n", + "4 1000.0 2023-03-14 16:00:42.057075 " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Read the output file\n", + "df = pd.read_csv('emu_output.csv', index_col=False)\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "emupy", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 6036258deb7f5361e291d29e67988eb5e6942b10 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 28 Mar 2023 16:43:19 -0600 Subject: [PATCH 04/38] Handling case where no yaw simulator specified (or specified as none) --- emu_python/emulator.py | 19 ++++++++++++++----- .../emu_input_000.yaml | 2 +- .../example_sim_yaw_control/input_dict.echo | 1 - 3 files changed, 15 insertions(+), 7 deletions(-) delete mode 100644 example_case_folders/example_sim_yaw_control/input_dict.echo diff --git a/emu_python/emulator.py b/emu_python/emulator.py index ee50d6ee..139dedb9 100644 --- a/emu_python/emulator.py +++ b/emu_python/emulator.py @@ -309,11 +309,20 @@ def process_periodic_publication(self): # Hard coded to single wind farm for the moment wind_farm_name = list(self.input_dict["emu_comms"]["amr_wind"].keys())[0] - yaw_angles = self.input_dict["py_sims"]\ - [self.input_dict["emu_comms"]["amr_wind"][wind_farm_name] - ["yaw_simulator_name"] - ]\ - ["outputs"]["turbine_yaws"] + if "yaw_simulator_name" in \ + self.input_dict["emu_comms"]["amr_wind"][wind_farm_name].keys() and \ + self.input_dict["emu_comms"]["amr_wind"][wind_farm_name]["yaw_simulator_name"] != \ + "none": + + yaw_angles = self.input_dict["py_sims"]\ + [self.input_dict["emu_comms"]["amr_wind"][wind_farm_name]["yaw_simulator_name"] + ]\ + ["outputs"]["turbine_yaws"] + + else: # set yaw_angles based on self.wind_direction + yaw_angles = [self.wind_direction]*self.num_turbines + + print(yaw_angles) # Send timing and yaw information to AMRWind via helics # publish on topic: control diff --git a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml index 8ef1800d..b61f6afe 100644 --- a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml +++ b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml @@ -16,7 +16,7 @@ emu_comms: wind_farm_0: type: amr_wind_local #options are amr_wind or amr_wind_local amr_wind_input_file: amr_input.inp - yaw_simulator_name: yaw_system_0 # can also use "none" + yaw_simulator_name: yaw_system_0 # can also use "none" (without quotes) helics: diff --git a/example_case_folders/example_sim_yaw_control/input_dict.echo b/example_case_folders/example_sim_yaw_control/input_dict.echo deleted file mode 100644 index 88426a01..00000000 --- a/example_case_folders/example_sim_yaw_control/input_dict.echo +++ /dev/null @@ -1 +0,0 @@ -{'name': 'example_000', 'description': 'Just a solar plant', 'dt': 0.5, 'emu_comms': {'amr_wind': {'wind_farm_0': {'type': 'amr_wind_local', 'amr_wind_input_file': 'amr_input.inp', 'yaw_simulator_name': 'yaw_system_0', 'num_turbines': 2, 'turbine_labels': ['T00', 'T01'], 'rotor_diameter': 240.0, 'turbine_locations': [(2000.0, 2000.0), (2500.0, 2500.0)], 'turbine_powers': [0, 0], 'turbine_wind_directions': [0, 0], 'sim_time_s_amr_wind': 0}}, 'helics': {'config': {'name': 'emu_python', 'use_dash_frontend': False, 'KAFKA': False, 'KAFKA_topics': 'EMUV1py', 'helics': {'subscription_topics': ['status'], 'publication_topics': ['control'], 'endpoints': [], 'helicsport': 23405, 'deltat': 0.5}, 'publication_interval': 1, 'endpoint_interval': 1, 'starttime': 0, 'stoptime': 100, 'Agent': 'ControlCenter'}}}, 'py_sims': {'solar_farm_0': {'py_sim_type': 'SimpleSolar', 'capacity': 50, 'efficiency': 0.5, 'initial_conditions': {'power': 25, 'irradiance': 1000}, 'object': , 'outputs': {'power': 2.5e-05, 'irradiance': 1000.0}, 'inputs': {}}, 'yaw_system_0': {'py_sim_type': 'SimpleYaw', 'num_turbines': 2, 'wind_farm_name': 'wind_farm_0', 'initial_conditions': {'yaw': 270.0}, 'object': , 'outputs': {'turbine_yaws': [0.0, 0.0]}, 'inputs': {'turbine_wind_directions': [0.0, 0.0]}}}, 'controller': {}} From 40d411777ff6f263561fa296bb909661228a71f6 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 28 Mar 2023 16:43:41 -0600 Subject: [PATCH 05/38] Removing input_dict.echo from git repo. Can indo if needed. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9c188954..c05ece15 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ emu_python/local_amr_wind_demo/sample_copy.nc t_00* logdummy logemu +input_dict.echo From c15ed2d664b613a5c8c4782491c490ab5dd2a84c Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 28 Mar 2023 16:49:23 -0600 Subject: [PATCH 06/38] Removing previous, more realistic implementation that has not yet been fleshed out. --- emu_python/python_simulators/yaw_simulator.py | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 emu_python/python_simulators/yaw_simulator.py diff --git a/emu_python/python_simulators/yaw_simulator.py b/emu_python/python_simulators/yaw_simulator.py deleted file mode 100644 index 55c41860..00000000 --- a/emu_python/python_simulators/yaw_simulator.py +++ /dev/null @@ -1,54 +0,0 @@ -from wfc_framework.yaw_controller import yaw_controller - -class IndirectControlYawSystem: - """ - Wrapper for wfc_framework's yaw_controller to align with emu_python's - syntax. - - IndirectControlYawSystem uses target offsets applied to the turbine's - wind vane signal to implement misalignments. - """ - - def __init__(self, input_dict): - - if input_dict["wd_init"] == "from_wind_sim": - input_dict["wd_init"] = 0.0 # TODO: get initial value from AMR-Wind - if input_dict["yaw_init"] == "use_wd_init": - input_dict["yaw_init"] == input_dict["wd_init"] - - self.closed_loop_yaw_controller = yaw_controller( - options_dict = {k: input_dict[k] for k in \ - ("yaw_init", "wd_init", "time_const", "deadband", "yaw_rate") - } - ) - - def step(self, inputs): - - self.closed_loop_yaw_controller.compute( - wd=inputs["wind_direction"], - target_vane_offset=inputs["offset_target"] - ) - - outputs = { - "yaw_position":self.closed_loop_yaw_controller.yaw_position, - "yaw_state":self.closed_loop_yaw_controller.yaw_state - } - - return outputs - -class DirectControlYawSystem: - """ - DirectControlYawSystem initiates yaw actions directly. - - Not yet implemented. - """ - - def __init__(self, input_dict): - - raise NotImplementedError("DirectControlYawSystem not implemented.") - - def step(self, inputs): - - outputs = None - - return outputs \ No newline at end of file From 391becd3aac846b6ba6a033c864c396686826199 Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 30 Mar 2023 16:44:32 -0600 Subject: [PATCH 07/38] Clean up commit. --- emu_python/dummy_amr_wind.py | 1 - example_case_folders/example_sim_05/amr_input.inp | 15 ++++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/emu_python/dummy_amr_wind.py b/emu_python/dummy_amr_wind.py index 40e53750..13812f13 100644 --- a/emu_python/dummy_amr_wind.py +++ b/emu_python/dummy_amr_wind.py @@ -38,7 +38,6 @@ # PARAMETERS num_turbines = 2 -np.random.seed(0) # Initialize to all zeros turbine_powers = np.zeros(num_turbines) diff --git a/example_case_folders/example_sim_05/amr_input.inp b/example_case_folders/example_sim_05/amr_input.inp index a3428040..cf5e2f20 100755 --- a/example_case_folders/example_sim_05/amr_input.inp +++ b/example_case_folders/example_sim_05/amr_input.inp @@ -157,8 +157,7 @@ Actuator.UniformCtDisk.num_points_r = 20 Actuator.UniformCtDisk.num_points_t = 5 -Actuator.labels = T00 T01 -# T02 T03 T04 T05 T06 T07 +Actuator.labels = T00 T01 T02 T03 T04 T05 T06 T07 Actuator.JoukowskyDisk.thrust_coeff = 8.1672e-01 7.9044e-01 7.8393e-01 7.8624e-01 7.8824e-01 7.8942e-01 7.8902e-01 7.8740e-01 7.8503e-01 7.8237e-01 7.7955e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.6922e-01 7.4270e-01 5.5949e-01 4.6163e-01 3.8786e-01 3.2901e-01 2.8093e-01 2.4114e-01 2.0795e-01 1.8010e-01 1.5663e-01 1.3679e-01 1.1995e-01 1.0562e-01 9.3384e-02 8.2908e-02 7.3910e-02 6.6159e-02 5.9463e-02 5.3662e-02 4.8622e-02 4.4230e-02 Actuator.JoukowskyDisk.wind_speed = 3.0000e+00 3.5495e+00 4.0679e+00 4.5539e+00 5.0064e+00 5.4244e+00 5.8069e+00 6.1530e+00 6.4619e+00 6.7330e+00 6.9655e+00 7.1589e+00 7.3128e+00 7.4269e+00 7.5009e+00 7.5345e+00 7.5412e+00 7.5883e+00 7.6757e+00 7.8031e+00 7.9702e+00 8.1767e+00 8.4221e+00 8.7059e+00 9.0273e+00 9.3856e+00 9.7800e+00 1.0210e+01 1.0659e+01 1.0673e+01 1.1170e+01 1.1699e+01 1.2259e+01 1.2848e+01 1.3465e+01 1.4109e+01 1.4778e+01 1.5471e+01 1.6185e+01 1.6921e+01 1.7674e+01 1.8445e+01 1.9231e+01 2.0030e+01 2.0841e+01 2.1661e+01 2.2489e+01 2.3323e+01 2.4160e+01 2.5000e+01 Actuator.JoukowskyDisk.rpm = 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0861e+00 5.1954e+00 5.2765e+00 5.3290e+00 5.3529e+00 5.3577e+00 5.3912e+00 5.4532e+00 5.5437e+00 5.6625e+00 5.8092e+00 5.9836e+00 6.1851e+00 6.4135e+00 6.6681e+00 6.9483e+00 7.2535e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 @@ -174,7 +173,13 @@ Actuator.JoukowskyDisk.use_root_correction = true Actuator.JoukowskyDisk.epsilon = 5.0 Actuator.JoukowskyDisk.vortex_core_size = 24.0 -Actuator.UniformCtDisk.yaw = 240.0 +Actuator.JoukowskyDisk.yaw = 240.0 -Actuator.T00.base_position = 2000.0 2000.0 0.0 -Actuator.T01.base_position = 2500.0 2500.0 0.0 +Actuator.T00.base_position               = 2000    1000    0 +Actuator.T01.base_position               = 1321.17749    1678.82251    0 +Actuator.T02.base_position               = 642.35498    2357.64502    0 +Actuator.T03.base_position               = 2509.116882    2187.939392    0 +Actuator.T04.base_position               = 1830.294372    2866.761902    0 +Actuator.T05.base_position               = 3697.056274    2697.056274    0 +Actuator.T06.base_position               = 3018.233764    3375.878784    0 +Actuator.T07.base_position               = 2339.411254    4054.701294    0 From cdb9d93dc1bf41fafc9d987a72ea4386ad6e1ab7 Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 13 Apr 2023 14:30:11 -0600 Subject: [PATCH 08/38] Running, but seems theres a timing issue. --- emu_python/controller.py | 17 ------ emu_python/controllers/controller_base.py | 19 +++++++ .../controllers/simple_yaw_controller.py | 57 +++++++++++++++++++ emu_python/emulator.py | 45 ++++++++++----- .../example_sim_yaw_control/bash_script.sh | 0 .../emu_input_000.yaml | 6 ++ .../example_sim_yaw_control/emu_runscript.py | 4 +- 7 files changed, 114 insertions(+), 34 deletions(-) delete mode 100644 emu_python/controller.py create mode 100644 emu_python/controllers/controller_base.py create mode 100644 emu_python/controllers/simple_yaw_controller.py mode change 100644 => 100755 example_case_folders/example_sim_yaw_control/bash_script.sh diff --git a/emu_python/controller.py b/emu_python/controller.py deleted file mode 100644 index 08e06f71..00000000 --- a/emu_python/controller.py +++ /dev/null @@ -1,17 +0,0 @@ - - - - -class Controller(): - - def __init__(self, input_dict): - pass - - def step(self, input_dict): - - pass - - def get_controller_dict(self): - - return {} - diff --git a/emu_python/controllers/controller_base.py b/emu_python/controllers/controller_base.py new file mode 100644 index 00000000..31d2a0a4 --- /dev/null +++ b/emu_python/controllers/controller_base.py @@ -0,0 +1,19 @@ +from abc import abstractmethod + +class Controller(): + + def __init__(self, input_dict): + pass + + @abstractmethod + def step(self, input_dict): + + pass + + @abstractmethod + def get_controller_dict(self): + # This method may not be needed, if the controller sets the inputs + # to amr-wind and the py_sims directly. Still, could be used for + # recording the controllers internal state for logging purposes. + + return {} \ No newline at end of file diff --git a/emu_python/controllers/simple_yaw_controller.py b/emu_python/controllers/simple_yaw_controller.py new file mode 100644 index 00000000..265808c6 --- /dev/null +++ b/emu_python/controllers/simple_yaw_controller.py @@ -0,0 +1,57 @@ +from abc import abstractmethod + +class SimpleYawController(): + + def __init__(self, input_dict): + + # Perhaps these should inherit from an abstract class that + # requires a certain input structure. + + self.dt = input_dict["dt"] # Won't be needed here, but generally good to have + self.n_turbines = input_dict["controller"]["num_turbines"] + self.turbines = range(self.n_turbines) + + # Set initial conditions + yaw_IC = input_dict["controller"]["initial_conditions"]["yaw"] + if hasattr(yaw_IC, '__len__'): + if len(yaw_IC) == self.n_turbines: + self.yaw_angles = yaw_IC + else: + raise TypeError("yaw initial condition should be a float or "+\ + "a list of floats of length num_turbines.") + else: + self.yaw_angles = [yaw_IC]*self.n_turbines + + def return_outputs(self, input_dict): + + return input_dict + + + def step(self, input_dict): + + # In this case, the controller doesn't need memory (yaw instantaneously). + # Still, to demonstrate, we can save the current wind directions. + + # Grab name of wind farm (assumes there is only one!) + wf_name = list(input_dict["emu_comms"]["amr_wind"].keys())[0] + + # How would we do this part, if not saved in emu_comms? might be though? + self.wind_directions = input_dict["emu_comms"]\ + ["amr_wind"]\ + [wf_name]\ + ["turbine_wind_directions"] + + # Now, set the amr-wind yaw angles + yaw_angles = self.wind_directions # Yaws instantaneously + input_dict["emu_comms"]\ + ["amr_wind"]\ + [wf_name]\ + ["turbine_yaw_angles"] = yaw_angles + + return self.return_outputs(input_dict) + + + @abstractmethod + def get_controller_dict(self): + + return {} \ No newline at end of file diff --git a/emu_python/emulator.py b/emu_python/emulator.py index 139dedb9..c0816b5c 100644 --- a/emu_python/emulator.py +++ b/emu_python/emulator.py @@ -103,6 +103,11 @@ def __init__(self, controller, py_sims, input_dict): ]['turbine_powers'] = np.zeros(self.num_turbines) self.amr_wind_dict[self.amr_wind_names[0] ]['turbine_wind_directions'] = [0.]*self.num_turbines + # Write to emu_comms so that controller can access + self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['turbine_powers'] = [0.]*self.num_turbines + self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['turbine_wind_directions'] = [0.]*self.num_turbines # TODO Could set up logging here @@ -140,11 +145,21 @@ def run(self): continue # Update controller and py sims - self.controller.step(self.input_dict) + self.input_dict = self.controller.step(self.input_dict) self.input_dict['controller'] = self.controller.get_controller_dict() self.py_sims.step(self.input_dict) self.input_dict['py_sims'] = self.py_sims.get_py_sim_dict() + # TODO: usage differs a little here between controller and py_sims. + # controller returns an altered input_dict directly (and alters + # fields outside of input_dict['controller'], whereas py_sims.step + # returns None and then the 'py_sims' field of input_dict is populated + # using self.py_sims.get_py_sim_dict(). This is consistent with + # the sims (and amr_wind) running as individual systems, with the + # controller coordinating them. As well as handling controllable + # inputs, the controller should then also pass other signals between + # amr-wind/py_sims (such as the turbines' powers). + # Print the input dict # print(self.input_dict) @@ -184,7 +199,7 @@ def run(self): print("AMRWindSpeed:", wind_speed_amr_wind) print("AMRWindDirection:", wind_direction_amr_wind) print("AMRWindTurbinePowers:", turbine_power_array) - print(" AMRWIND number of turbines here: ", self.num_turbines) + print("AMRWIND number of turbines here: ", self.num_turbines) print("AMRWindTurbineWD:", turbine_wd_array) print("=======================================") @@ -208,6 +223,12 @@ def run(self): self.turbine_power_array = turbine_power_array self.amr_wind_dict[self.amr_wind_names[0] ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind + # TODO: write these to the emu_comms object, too? + self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['turbine_powers'] = turbine_power_array + self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['turbine_wind_directions'] = turbine_wd_array + # Send turbine powers through Kafka if enabled: if self.KAFKA: @@ -308,22 +329,16 @@ def process_periodic_publication(self): # Periodically publish data to the surrogate # Hard coded to single wind farm for the moment - wind_farm_name = list(self.input_dict["emu_comms"]["amr_wind"].keys())[0] - if "yaw_simulator_name" in \ - self.input_dict["emu_comms"]["amr_wind"][wind_farm_name].keys() and \ - self.input_dict["emu_comms"]["amr_wind"][wind_farm_name]["yaw_simulator_name"] != \ - "none": - - yaw_angles = self.input_dict["py_sims"]\ - [self.input_dict["emu_comms"]["amr_wind"][wind_farm_name]["yaw_simulator_name"] - ]\ - ["outputs"]["turbine_yaws"] - + if "turbine_yaw_angles" in self.input_dict["emu_comms"]\ + ["amr_wind"]\ + [self.amr_wind_names[0]]: + yaw_angles = self.input_dict["emu_comms"]\ + ["amr_wind"]\ + [self.amr_wind_names[0]]\ + ["turbine_yaw_angles"] else: # set yaw_angles based on self.wind_direction yaw_angles = [self.wind_direction]*self.num_turbines - print(yaw_angles) - # Send timing and yaw information to AMRWind via helics # publish on topic: control tmp = np.array([self.absolute_helics_time, self.wind_speed, diff --git a/example_case_folders/example_sim_yaw_control/bash_script.sh b/example_case_folders/example_sim_yaw_control/bash_script.sh old mode 100644 new mode 100755 diff --git a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml index b61f6afe..02c9b3d7 100644 --- a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml +++ b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml @@ -51,6 +51,7 @@ py_sims: power: 25 # MW irradiance: 1000 + # REMOVE THE py_sim BELOW! REPLACING WITH CONTROLLER yaw_system_0: # The name of py_sim object 2 [TODO: one for each turbine?] py_sim_type: SimpleYaw @@ -66,6 +67,11 @@ py_sims: controller: + controller_type: SimpleYawController # This may not be needed + num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind + initial_conditions: + yaw: 270. # degrees (same for all turbines) (will this work?) + diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript.py b/example_case_folders/example_sim_yaw_control/emu_runscript.py index 8103a23e..62a43034 100644 --- a/example_case_folders/example_sim_yaw_control/emu_runscript.py +++ b/example_case_folders/example_sim_yaw_control/emu_runscript.py @@ -1,5 +1,5 @@ from emu_python.emulator import Emulator -from emu_python.controller import Controller +from emu_python.controllers.simple_yaw_controller import SimpleYawController # from emu_python.emu_comms import EmuComms from emu_python.py_sims import PySims from emu_python.utilities import load_yaml @@ -12,7 +12,7 @@ #print(input_dict) -controller = Controller(input_dict) +controller = SimpleYawController(input_dict) # emu_comms = EmuComms(input_dict) py_sims = PySims(input_dict) From 8b0638bc475e4a80597711433f1e5f47b516daa6 Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 19 Apr 2023 11:24:28 -0600 Subject: [PATCH 09/38] Small updates to installation procedure; also updated intstuctions for running locally.; --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9394cde6..11088a53 100644 --- a/README.md +++ b/README.md @@ -3,32 +3,65 @@ # emu_python Python (python >=3.6) front-end to emulator + -# New recommended install to work with era 5 +# Installation +``` conda create --name emupy python=3.8 conda activate emupy git clone https://github.com/NREL/OpenOA.git pip install ./OpenOA +``` +If you haven't already cloned emu_python: +``` +git clone https://github.com/NREL/emu_python +``` +Then, +``` pip install -e emu_python +``` +Possibly, `cd` into emu_python and switch to the +develop branch. + -# Please install SEAS as follows: +SEAS is also required for emu_python. To install +SEAS, use ``` pip install git+https://github.nrel.gov/SEAS/SEAS.git@dv/emuwind ``` +If this fails, the following may work instead: + + + + +``` +git clone https://github.nrel.gov/SEAS/SEAS cd SEAS git fetch --all +git switch dv/emuwind +``` +Older versions of git (e.g. the one on Eagle) don't have the `switch` feature; instead, use +``` git checkout dv/emuwind +``` +Then, +``` cd .. pip install -e SEAS +``` + # Running [Local] +To run locally using a dummy placeholder for AMR-Wind, launch 3 separate +terminals. In each, `conda activate` your emu_python environment (`conda +activate emu_python`). + +In the first terminal, run +``` +helics_broker -t zmq -f 2 --loglevel="debug" +``` +from any directory. + +In the second and third terminals, navigate to +emu_python/example_case_folders/example_sim_05 (you'll need to be on the +develop branch of emu_python). Then, in one of these +terminals, run +``` +python emu_runscript_dummy_amr.py amr_input.inp +``` +and in the other, run +``` +python emu_runscript.py emu_input_000.yaml +``` + +The first of these launches the dummy stand-in for AMR-wind, and the second +launches the emu_python emulator. These will connect to the helics_broker and +run the co-simulation. You should see printout from both the dummy AMR-wind +process and the emu_python emulator printed to your screen. + + # Running [Eagle] From 360576af9bc7c5f5e6efd613609b465813b4e1cf Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 20 Apr 2023 07:03:09 -0600 Subject: [PATCH 10/38] Updates to allow example_06 to run --- .../example_sim_06/amr_input.inp | 2 ++ .../example_sim_06/batch_script.sh | 19 +++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) mode change 100644 => 100755 example_case_folders/example_sim_06/batch_script.sh diff --git a/example_case_folders/example_sim_06/amr_input.inp b/example_case_folders/example_sim_06/amr_input.inp index 1ef4cfb5..a7f11640 100755 --- a/example_case_folders/example_sim_06/amr_input.inp +++ b/example_case_folders/example_sim_06/amr_input.inp @@ -76,6 +76,8 @@ ABL.stats_output_format = netcdf # Whether to use helics helics.activated = true +helics.broker_port = 23404 +helics.broker_address = "127.0.0.1" #¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# # ADAPTIVE MESH REFINEMENT # diff --git a/example_case_folders/example_sim_06/batch_script.sh b/example_case_folders/example_sim_06/batch_script.sh old mode 100644 new mode 100755 index d33f77ee..d93b16ad --- a/example_case_folders/example_sim_06/batch_script.sh +++ b/example_case_folders/example_sim_06/batch_script.sh @@ -7,20 +7,19 @@ # #SBATCH --qos=high # A lot of modules and conda stuff -source /nopt/nrel/apps/anaconda/5.3/etc/profile.d/conda.sh +# source /nopt/nrel/apps/anaconda/5.3/etc/profile.d/conda.sh # module use /nopt/nrel/apps/modules/centos74/modulefiles -module use /not/nrel/apps/modules/default/modulefiles +# module use /not/nrel/apps/modules/default/modulefiles module purge # module load conda/5.3 -module load conda -export PREFIX=~/.conda-envs/emupy -export PATH=$PREFIX/bin:$PATH -export FI_PROVIDER_PATH=$PREFIX/lib/libfabric/prov -export LD_LIBRARY_PATH=$PREFIX/lib/libfabric:$PREFIX/lib/release_mt:$LD_LIBRARY_PATH -source activate emupy -# module load intel-mpi/2018.0.3 module load helics/helics-3.1.0_openmpi module load netcdf-c/4.7.3/gcc-mpi +module load conda +#export FI_PROVIDER_PATH=$PREFIX/lib/libfabric/prov +#export LD_LIBRARY_PATH=$PREFIX/lib/libfabric:$PREFIX/lib/release_mt:$LD_LIBRARY_PATH +conda activate /home/msinner/emu_moa_dev/conda-env/emupy +# module load intel-mpi/2018.0.3 + # module load mkl # module load mpt @@ -34,4 +33,4 @@ python3 emu_runscript.py emu_input_000.yaml >> logemu 2>&1 & # Start the contro # 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 2>&1 \ No newline at end of file +mpirun -n 36 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file From d3dde26f2722300fce58f9999edba7c9e58b7620 Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 20 Apr 2023 07:13:00 -0600 Subject: [PATCH 11/38] Renaming dummy example for clarity. --- .../amr_input.inp | 0 .../bash_script.sh | 0 .../emu_input_000.yaml | 0 .../emu_runscript.py | 0 .../emu_runscript_dummy_amr.py | 0 .../readme.txt | 0 .../test_read_output.ipynb | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/amr_input.inp (100%) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/bash_script.sh (100%) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/emu_input_000.yaml (100%) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/emu_runscript.py (100%) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/emu_runscript_dummy_amr.py (100%) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/readme.txt (100%) rename example_case_folders/{example_sim_yaw_control => example_sim_yaw_control_dummy}/test_read_output.ipynb (100%) diff --git a/example_case_folders/example_sim_yaw_control/amr_input.inp b/example_case_folders/example_sim_yaw_control_dummy/amr_input.inp similarity index 100% rename from example_case_folders/example_sim_yaw_control/amr_input.inp rename to example_case_folders/example_sim_yaw_control_dummy/amr_input.inp diff --git a/example_case_folders/example_sim_yaw_control/bash_script.sh b/example_case_folders/example_sim_yaw_control_dummy/bash_script.sh similarity index 100% rename from example_case_folders/example_sim_yaw_control/bash_script.sh rename to example_case_folders/example_sim_yaw_control_dummy/bash_script.sh diff --git a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml b/example_case_folders/example_sim_yaw_control_dummy/emu_input_000.yaml similarity index 100% rename from example_case_folders/example_sim_yaw_control/emu_input_000.yaml rename to example_case_folders/example_sim_yaw_control_dummy/emu_input_000.yaml diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript.py b/example_case_folders/example_sim_yaw_control_dummy/emu_runscript.py similarity index 100% rename from example_case_folders/example_sim_yaw_control/emu_runscript.py rename to example_case_folders/example_sim_yaw_control_dummy/emu_runscript.py diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py b/example_case_folders/example_sim_yaw_control_dummy/emu_runscript_dummy_amr.py similarity index 100% rename from example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py rename to example_case_folders/example_sim_yaw_control_dummy/emu_runscript_dummy_amr.py diff --git a/example_case_folders/example_sim_yaw_control/readme.txt b/example_case_folders/example_sim_yaw_control_dummy/readme.txt similarity index 100% rename from example_case_folders/example_sim_yaw_control/readme.txt rename to example_case_folders/example_sim_yaw_control_dummy/readme.txt diff --git a/example_case_folders/example_sim_yaw_control/test_read_output.ipynb b/example_case_folders/example_sim_yaw_control_dummy/test_read_output.ipynb similarity index 100% rename from example_case_folders/example_sim_yaw_control/test_read_output.ipynb rename to example_case_folders/example_sim_yaw_control_dummy/test_read_output.ipynb From 0b2ce146a135fec4f7164a1c483dea7f5cbcd0ef Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 20 Apr 2023 07:20:06 -0600 Subject: [PATCH 12/38] Reverting; plan to use one directory only, if possible. --- .../amr_input.inp | 4 +++- .../bash_script.sh | 0 .../emu_input_000.yaml | 0 .../emu_runscript.py | 0 .../emu_runscript_dummy_amr.py | 0 .../readme.txt | 0 .../test_read_output.ipynb | 0 7 files changed, 3 insertions(+), 1 deletion(-) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/amr_input.inp (99%) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/bash_script.sh (100%) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/emu_input_000.yaml (100%) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/emu_runscript.py (100%) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/emu_runscript_dummy_amr.py (100%) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/readme.txt (100%) rename example_case_folders/{example_sim_yaw_control_dummy => example_sim_yaw_control}/test_read_output.ipynb (100%) diff --git a/example_case_folders/example_sim_yaw_control_dummy/amr_input.inp b/example_case_folders/example_sim_yaw_control/amr_input.inp similarity index 99% rename from example_case_folders/example_sim_yaw_control_dummy/amr_input.inp rename to example_case_folders/example_sim_yaw_control/amr_input.inp index a3428040..024a282e 100755 --- a/example_case_folders/example_sim_yaw_control_dummy/amr_input.inp +++ b/example_case_folders/example_sim_yaw_control/amr_input.inp @@ -75,7 +75,9 @@ ABL.stats_output_frequency = 1 ABL.stats_output_format = netcdf # Whether to use helics -helics.activated = true +helics.activated = true +helics.broker_port = 23404 +helics.broker_address = "127.0.0.1" #¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# # ADAPTIVE MESH REFINEMENT # diff --git a/example_case_folders/example_sim_yaw_control_dummy/bash_script.sh b/example_case_folders/example_sim_yaw_control/bash_script.sh similarity index 100% rename from example_case_folders/example_sim_yaw_control_dummy/bash_script.sh rename to example_case_folders/example_sim_yaw_control/bash_script.sh diff --git a/example_case_folders/example_sim_yaw_control_dummy/emu_input_000.yaml b/example_case_folders/example_sim_yaw_control/emu_input_000.yaml similarity index 100% rename from example_case_folders/example_sim_yaw_control_dummy/emu_input_000.yaml rename to example_case_folders/example_sim_yaw_control/emu_input_000.yaml diff --git a/example_case_folders/example_sim_yaw_control_dummy/emu_runscript.py b/example_case_folders/example_sim_yaw_control/emu_runscript.py similarity index 100% rename from example_case_folders/example_sim_yaw_control_dummy/emu_runscript.py rename to example_case_folders/example_sim_yaw_control/emu_runscript.py diff --git a/example_case_folders/example_sim_yaw_control_dummy/emu_runscript_dummy_amr.py b/example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py similarity index 100% rename from example_case_folders/example_sim_yaw_control_dummy/emu_runscript_dummy_amr.py rename to example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py diff --git a/example_case_folders/example_sim_yaw_control_dummy/readme.txt b/example_case_folders/example_sim_yaw_control/readme.txt similarity index 100% rename from example_case_folders/example_sim_yaw_control_dummy/readme.txt rename to example_case_folders/example_sim_yaw_control/readme.txt diff --git a/example_case_folders/example_sim_yaw_control_dummy/test_read_output.ipynb b/example_case_folders/example_sim_yaw_control/test_read_output.ipynb similarity index 100% rename from example_case_folders/example_sim_yaw_control_dummy/test_read_output.ipynb rename to example_case_folders/example_sim_yaw_control/test_read_output.ipynb From fd96a1bec54870a4e150f6d2316dedfc1a5c8dd2 Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 20 Apr 2023 08:42:48 -0600 Subject: [PATCH 13/38] Runs on eagle and local. --- .../batch_script_amrwind.sh | 34 ++ .../{bash_script.sh => batch_script_dummy.sh} | 4 +- .../emu_input_longsim.yaml | 80 +++++ ...input_000.yaml => emu_input_shortsim.yaml} | 2 +- .../example_sim_yaw_control/emu_runscript.py | 6 - .../example_sim_yaw_control/readme.txt | 3 +- .../test_read_output.ipynb | 328 ------------------ 7 files changed, 119 insertions(+), 338 deletions(-) create mode 100755 example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh rename example_case_folders/example_sim_yaw_control/{bash_script.sh => batch_script_dummy.sh} (72%) create mode 100644 example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml rename example_case_folders/example_sim_yaw_control/{emu_input_000.yaml => emu_input_shortsim.yaml} (98%) delete mode 100644 example_case_folders/example_sim_yaw_control/test_read_output.ipynb diff --git a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh new file mode 100755 index 00000000..5e924a91 --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh @@ -0,0 +1,34 @@ +#!/bin/bash +#SBATCH --job-name=emu +#SBATCH --time=1:00:00 +#SBATCH --nodes=2 +#SBATCH --ntasks-per-node=36 +#SBATCH --account=ssc +# #SBATCH --qos=high + +# A lot of modules and conda stuff +# source /nopt/nrel/apps/anaconda/5.3/etc/profile.d/conda.sh +# module use /nopt/nrel/apps/modules/centos74/modulefiles +# module use /not/nrel/apps/modules/default/modulefiles +module purge +# module load conda/5.3 +module load helics/helics-3.1.0_openmpi +module load netcdf-c/4.7.3/gcc-mpi +module load conda +#export FI_PROVIDER_PATH=$PREFIX/lib/libfabric/prov +#export LD_LIBRARY_PATH=$PREFIX/lib/libfabric:$PREFIX/lib/release_mt:$LD_LIBRARY_PATH +conda activate /home/msinner/emu_moa_dev/conda-env/emupy +# module load intel-mpi/2018.0.3 + +# module load mkl +# module load mpt + + +# Set up the helics broker +helics_broker -t zmq -f 2 --loglevel="debug" & + +# Need to set this to your emu_python folder +python emu_runscript.py emu_input_longsim.yaml >> logemu 2>&1 & # Start the controller center and pass in input file + +# Now go back to scratch folder and launch the job +mpirun -n 36 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/bash_script.sh b/example_case_folders/example_sim_yaw_control/batch_script_dummy.sh similarity index 72% rename from example_case_folders/example_sim_yaw_control/bash_script.sh rename to example_case_folders/example_sim_yaw_control/batch_script_dummy.sh index 21d37428..db18ba9e 100755 --- a/example_case_folders/example_sim_yaw_control/bash_script.sh +++ b/example_case_folders/example_sim_yaw_control/batch_script_dummy.sh @@ -9,10 +9,10 @@ helics_broker -t zmq -f 2 --loglevel="debug" & # Need to set this to your emu_python folder # cd /home/pfleming/emu_python/emu_python -python3 emu_runscript.py emu_input_000.yaml >> logemu 2>&1 & # Start the controller center and pass in input file +python emu_runscript.py emu_input_shortsim.yaml >> logemu 2>&1 & # Start the controller center and pass in input file -python3 emu_runscript_dummy_amr.py amr_input.inp >> logdummy 2>&1 +python emu_runscript_dummy_amr.py amr_input.inp >> logdummy 2>&1 # Now go back to scratch folder and launch the job # cd /scratch/pfleming/c2c/example_sim_02 diff --git a/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml b/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml new file mode 100644 index 00000000..a5e3ba6f --- /dev/null +++ b/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml @@ -0,0 +1,80 @@ +# Input YAML for emy_python + +# Name +name: example_000 + +### +# Describe this emulator setup +description: Just a solar plant + +dt: 0.5 + +emu_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 + yaw_simulator_name: yaw_system_0 # can also use "none" (without quotes) + + helics: + + config: + name: emu_python # 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: 23405 + publication_interval: 1 + endpoint_interval: 1 + starttime: 0 + stoptime: 36000 + + Agent: ControlCenter + +py_sims: + + solar_farm_0: # The name of py_sim object 1 + + py_sim_type: SimpleSolar + capacity: 50 # MW + efficiency: 0.5 #Fraction + + initial_conditions: + + power: 25 # MW + irradiance: 1000 + + # REMOVE THE py_sim BELOW! REPLACING WITH CONTROLLER + yaw_system_0: # The name of py_sim object 2 [TODO: one for each turbine?] + + py_sim_type: SimpleYaw + num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind + wind_farm_name: wind_farm_0 + + initial_conditions: + + yaw: 270. # degrees (same for all turbines) (will this work?) + + + + +controller: + + controller_type: SimpleYawController # This may not be needed + num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind + initial_conditions: + yaw: 270. # degrees (same for all turbines) (will this work?) + + + + + + + diff --git a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml b/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml similarity index 98% rename from example_case_folders/example_sim_yaw_control/emu_input_000.yaml rename to example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml index 02c9b3d7..c8d7b7e2 100644 --- a/example_case_folders/example_sim_yaw_control/emu_input_000.yaml +++ b/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml @@ -30,7 +30,7 @@ emu_comms: subscription_topics: [status] publication_topics: [control] endpoints: [] - helicsport : 23405 + helicsport: 23405 publication_interval: 1 endpoint_interval: 1 starttime: 0 diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript.py b/example_case_folders/example_sim_yaw_control/emu_runscript.py index 62a43034..76d818c7 100644 --- a/example_case_folders/example_sim_yaw_control/emu_runscript.py +++ b/example_case_folders/example_sim_yaw_control/emu_runscript.py @@ -1,6 +1,5 @@ from emu_python.emulator import Emulator from emu_python.controllers.simple_yaw_controller import SimpleYawController -# from emu_python.emu_comms import EmuComms from emu_python.py_sims import PySims from emu_python.utilities import load_yaml @@ -10,17 +9,12 @@ input_dict = load_yaml(sys.argv[1]) -#print(input_dict) - controller = SimpleYawController(input_dict) -# emu_comms = EmuComms(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=[[]]) print("runscript complete.") - diff --git a/example_case_folders/example_sim_yaw_control/readme.txt b/example_case_folders/example_sim_yaw_control/readme.txt index 92fa3a96..f12eec32 100644 --- a/example_case_folders/example_sim_yaw_control/readme.txt +++ b/example_case_folders/example_sim_yaw_control/readme.txt @@ -1 +1,2 @@ -This example is using new framework trying to run a local (non-eagle) example with the dummy_amr_wind \ No newline at end of file +This example demonstrates the use of SimpleYawController, either using the +dummy or the real AMR-Wind. \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/test_read_output.ipynb b/example_case_folders/example_sim_yaw_control/test_read_output.ipynb deleted file mode 100644 index 5eb34e1b..00000000 --- a/example_case_folders/example_sim_yaw_control/test_read_output.ipynb +++ /dev/null @@ -1,328 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dtemu_comms.amr_wind.wind_farm_0.num_turbinesemu_comms.amr_wind.wind_farm_0.rotor_diameteremu_comms.amr_wind.wind_farm_0.turbine_powers.000emu_comms.amr_wind.wind_farm_0.turbine_powers.001emu_comms.helics.config.use_dash_frontendemu_comms.helics.config.KAFKAemu_comms.helics.config.helics.helicsportemu_comms.helics.config.helics.deltatemu_comms.helics.config.publication_intervalemu_comms.helics.config.endpoint_intervalemu_comms.helics.config.starttimeemu_comms.helics.config.stoptimepy_sims.solar_farm_0.capacitypy_sims.solar_farm_0.efficiencypy_sims.solar_farm_0.initial_conditions.powerpy_sims.solar_farm_0.initial_conditions.irradiancepy_sims.solar_farm_0.outputs.powerpy_sims.solar_farm_0.outputs.irradianceclock_time
00.52240.00.0000000.000000FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.043832
10.52240.00.0000000.000000FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.047719
20.52240.00.0000000.000000FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.052373
30.52240.0525.344333409.219457FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.055797
40.52240.0525.344333409.219457FalseFalse234050.5110100500.52510000.0000251000.02023-03-14 16:00:42.057075
\n", - "
" - ], - "text/plain": [ - " dt emu_comms.amr_wind.wind_farm_0.num_turbines \\\n", - "0 0.5 2 \n", - "1 0.5 2 \n", - "2 0.5 2 \n", - "3 0.5 2 \n", - "4 0.5 2 \n", - "\n", - " emu_comms.amr_wind.wind_farm_0.rotor_diameter \\\n", - "0 240.0 \n", - "1 240.0 \n", - "2 240.0 \n", - "3 240.0 \n", - "4 240.0 \n", - "\n", - " emu_comms.amr_wind.wind_farm_0.turbine_powers.000 \\\n", - "0 0.000000 \n", - "1 0.000000 \n", - "2 0.000000 \n", - "3 525.344333 \n", - "4 525.344333 \n", - "\n", - " emu_comms.amr_wind.wind_farm_0.turbine_powers.001 \\\n", - "0 0.000000 \n", - "1 0.000000 \n", - "2 0.000000 \n", - "3 409.219457 \n", - "4 409.219457 \n", - "\n", - " emu_comms.helics.config.use_dash_frontend emu_comms.helics.config.KAFKA \\\n", - "0 False False \n", - "1 False False \n", - "2 False False \n", - "3 False False \n", - "4 False False \n", - "\n", - " emu_comms.helics.config.helics.helicsport \\\n", - "0 23405 \n", - "1 23405 \n", - "2 23405 \n", - "3 23405 \n", - "4 23405 \n", - "\n", - " emu_comms.helics.config.helics.deltat \\\n", - "0 0.5 \n", - "1 0.5 \n", - "2 0.5 \n", - "3 0.5 \n", - "4 0.5 \n", - "\n", - " emu_comms.helics.config.publication_interval \\\n", - "0 1 \n", - "1 1 \n", - "2 1 \n", - "3 1 \n", - "4 1 \n", - "\n", - " emu_comms.helics.config.endpoint_interval \\\n", - "0 1 \n", - "1 1 \n", - "2 1 \n", - "3 1 \n", - "4 1 \n", - "\n", - " emu_comms.helics.config.starttime emu_comms.helics.config.stoptime \\\n", - "0 0 100 \n", - "1 0 100 \n", - "2 0 100 \n", - "3 0 100 \n", - "4 0 100 \n", - "\n", - " py_sims.solar_farm_0.capacity py_sims.solar_farm_0.efficiency \\\n", - "0 50 0.5 \n", - "1 50 0.5 \n", - "2 50 0.5 \n", - "3 50 0.5 \n", - "4 50 0.5 \n", - "\n", - " py_sims.solar_farm_0.initial_conditions.power \\\n", - "0 25 \n", - "1 25 \n", - "2 25 \n", - "3 25 \n", - "4 25 \n", - "\n", - " py_sims.solar_farm_0.initial_conditions.irradiance \\\n", - "0 1000 \n", - "1 1000 \n", - "2 1000 \n", - "3 1000 \n", - "4 1000 \n", - "\n", - " py_sims.solar_farm_0.outputs.power \\\n", - "0 0.000025 \n", - "1 0.000025 \n", - "2 0.000025 \n", - "3 0.000025 \n", - "4 0.000025 \n", - "\n", - " py_sims.solar_farm_0.outputs.irradiance clock_time \n", - "0 1000.0 2023-03-14 16:00:42.043832 \n", - "1 1000.0 2023-03-14 16:00:42.047719 \n", - "2 1000.0 2023-03-14 16:00:42.052373 \n", - "3 1000.0 2023-03-14 16:00:42.055797 \n", - "4 1000.0 2023-03-14 16:00:42.057075 " - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Read the output file\n", - "df = pd.read_csv('emu_output.csv', index_col=False)\n", - "df.head()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "emupy", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.13" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} From c36a14818041898021fe8abc4d75050b3db9d2e7 Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 20 Apr 2023 10:31:41 -0600 Subject: [PATCH 14/38] Revert, take 2. --- emu_python/controllers/simple_yaw_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/emu_python/controllers/simple_yaw_controller.py b/emu_python/controllers/simple_yaw_controller.py index 265808c6..25b80f9b 100644 --- a/emu_python/controllers/simple_yaw_controller.py +++ b/emu_python/controllers/simple_yaw_controller.py @@ -1,6 +1,7 @@ from abc import abstractmethod +from emu_python.controllers.controller_base import Controller -class SimpleYawController(): +class SimpleYawController(Controller): def __init__(self, input_dict): From ea7c5175545965eca8b42e6243e5793792b18758 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 21 Apr 2023 10:51:47 -0600 Subject: [PATCH 15/38] Removing old yaw simulator from pysims. --- emu_python/py_sims.py | 22 ------------- emu_python/python_simulators/simple_yaw.py | 32 ------------------- .../emu_input_longsim.yaml | 14 -------- .../emu_input_shortsim.yaml | 14 -------- 4 files changed, 82 deletions(-) delete mode 100644 emu_python/python_simulators/simple_yaw.py diff --git a/emu_python/py_sims.py b/emu_python/py_sims.py index 390fe74b..1a758870 100644 --- a/emu_python/py_sims.py +++ b/emu_python/py_sims.py @@ -1,5 +1,4 @@ from emu_python.python_simulators.simple_solar import SimpleSolar -from emu_python.python_simulators.simple_yaw import SimpleYaw from emu_python.python_simulators.simple_battery import SimpleBattery @@ -31,35 +30,14 @@ def get_py_sim(self, py_sim_obj_dict): return SimpleSolar(py_sim_obj_dict, self.dt) if py_sim_obj_dict['py_sim_type'] == 'SimpleBattery': - return SimpleBattery(py_sim_obj_dict, self.dt) - if py_sim_obj_dict['py_sim_type'] == 'SimpleYaw': - return SimpleYaw(py_sim_obj_dict, self.dt) - def get_py_sim_dict(self): return self.py_sim_dict def step(self, input_dict): - # Exchange information between objects - self.set_pysim_inputs(input_dict) - # Collect the py_sim objects for py_sim_name in self.py_sim_names: self.py_sim_dict[py_sim_name]['outputs'] = self.py_sim_dict[py_sim_name]['object'].step(self.py_sim_dict[py_sim_name]['inputs']) - - def set_pysim_inputs(self, input_dict): - - # TODO: should this logic be handled INSIDE the py_sim object? - for py_sim_name in self.py_sim_names: - if self.py_sim_dict[py_sim_name]['py_sim_type'] == 'SimpleYaw': - # Need access to the wind directions from AMR-Wind - wind_directions = input_dict['emu_comms']['amr_wind']\ - [self.py_sim_dict[py_sim_name]['object'].wind_farm_name]\ - ['turbine_wind_directions'] - - self.py_sim_dict[py_sim_name]['inputs'] = { - 'turbine_wind_directions': wind_directions - } diff --git a/emu_python/python_simulators/simple_yaw.py b/emu_python/python_simulators/simple_yaw.py deleted file mode 100644 index 360956de..00000000 --- a/emu_python/python_simulators/simple_yaw.py +++ /dev/null @@ -1,32 +0,0 @@ -# Very simple solar farm model -import numpy as np - - -class SimpleYaw(): - """ - Simple pass-through of wind direction to set yaw angle. - """ - - def __init__(self, input_dict, dt): - - # Perhaps these should inherit from an abstract class that - # requires a certain input structure. - - self.dt = dt # Won't be needed here, but generally good to have - self.n_turbines = input_dict['num_turbines'] - self.turbines = range(self.n_turbines) - self.wind_farm_name = input_dict['wind_farm_name'] - - # Set initial conditions - self.yaw_angles = [input_dict['initial_conditions']['yaw']]*self.n_turbines - - def return_outputs(self): - - return {'turbine_yaws':self.yaw_angles} - - def step(self, input_dict): - - # Commands are simply the current wind directions - self.yaw_angles = input_dict['turbine_wind_directions'] - - return self.return_outputs() \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml b/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml index a5e3ba6f..1652ed6c 100644 --- a/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml +++ b/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml @@ -51,20 +51,6 @@ py_sims: power: 25 # MW irradiance: 1000 - # REMOVE THE py_sim BELOW! REPLACING WITH CONTROLLER - yaw_system_0: # The name of py_sim object 2 [TODO: one for each turbine?] - - py_sim_type: SimpleYaw - num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind - wind_farm_name: wind_farm_0 - - initial_conditions: - - yaw: 270. # degrees (same for all turbines) (will this work?) - - - - controller: controller_type: SimpleYawController # This may not be needed diff --git a/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml b/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml index c8d7b7e2..c84d5191 100644 --- a/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml +++ b/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml @@ -51,20 +51,6 @@ py_sims: power: 25 # MW irradiance: 1000 - # REMOVE THE py_sim BELOW! REPLACING WITH CONTROLLER - yaw_system_0: # The name of py_sim object 2 [TODO: one for each turbine?] - - py_sim_type: SimpleYaw - num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind - wind_farm_name: wind_farm_0 - - initial_conditions: - - yaw: 270. # degrees (same for all turbines) (will this work?) - - - - controller: controller_type: SimpleYawController # This may not be needed From 7b83aa57d721a5d42b72c1e34dedf90567ab2c58 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 21 Apr 2023 11:28:29 -0600 Subject: [PATCH 16/38] Removing temporary examples. --- .../001_yaw_simulator/emu_input_001.yaml | 60 ------------------- .../001_yaw_simulator/emu_runscript.py | 22 ------- 2 files changed, 82 deletions(-) delete mode 100644 local_examples/001_yaw_simulator/emu_input_001.yaml delete mode 100644 local_examples/001_yaw_simulator/emu_runscript.py diff --git a/local_examples/001_yaw_simulator/emu_input_001.yaml b/local_examples/001_yaw_simulator/emu_input_001.yaml deleted file mode 100644 index 1da46f2d..00000000 --- a/local_examples/001_yaw_simulator/emu_input_001.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# Input YAML for emy_python - -# Name -name: example_000 - -### -# Describe this emulator setup -description: Just a solar plant - -emu_helics: - - wind_farm_0: - type: amr_wind - use: True - amr_wind_input_file: input.inp - - - helics: - - config: - # "name": "controlcenter", - # "use_dash_frontend": False, - KAFKA: False - KAFKA_topics: EMUV1py - helics: - deltat: 1 - subscription_topic: [status] - publication_topic: [control] - endpoints: [] - publication_interval: 1 - endpoint_interval: 1 - starttime: 0 - stoptime: 36000 - Agent: ControlCenter - -py_sims: - - solar_farm_0: # The name of py_sim object 1 - - type: simple_solar_farm - use: False - capacity: 50 # MW - - yaw_system_0: # The name of py_sim object n - type: IndirectControlYawSystem - num_turbines: 5 - yaw_init: 0.0 # Or, "use_wd_init" - wd_init: 0.0 # Or, "from_wind_sim" - time_const: 30. - deadband: 8. - yaw_rate: 0.3 - -controller: - - - - - - - diff --git a/local_examples/001_yaw_simulator/emu_runscript.py b/local_examples/001_yaw_simulator/emu_runscript.py deleted file mode 100644 index 74010b82..00000000 --- a/local_examples/001_yaw_simulator/emu_runscript.py +++ /dev/null @@ -1,22 +0,0 @@ -from emu_python.emulator import Emulator -from emu_python.controller import Controller -from emu_python.emu_helics import EmuHelics -from emu_python.py_sims import PySims -from emu_python.utilities import load_yaml - -import sys - - - -input_dict = load_yaml(sys.argv[1]) - -print(input_dict) - -# controller = Controller(input_dict) -# emu_helics = EmuHelics(input_dict) -# py_sims = PySims(input_dict) - - -# emulator = Emulator(controller, emu_helics, py_sims) - -# emulator.run() From 3102e5ca78ee36abc71d5211884edb857d6b8f46 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 14 Jul 2023 15:52:12 -0600 Subject: [PATCH 17/38] Starting renaming to hercules. --- .../batch_script_amrwind.sh | 29 +++++++++---------- ...ngsim.yaml => hercules_input_longsim.yaml} | 0 ...tsim.yaml => hercules_input_shortsim.yaml} | 0 ...emu_runscript.py => hercules_runscript.py} | 8 ++--- ...amr.py => hercules_runscript_dummy_amr.py} | 0 5 files changed, 17 insertions(+), 20 deletions(-) rename example_case_folders/example_sim_yaw_control/{emu_input_longsim.yaml => hercules_input_longsim.yaml} (100%) rename example_case_folders/example_sim_yaw_control/{emu_input_shortsim.yaml => hercules_input_shortsim.yaml} (100%) rename example_case_folders/example_sim_yaw_control/{emu_runscript.py => hercules_runscript.py} (62%) rename example_case_folders/example_sim_yaw_control/{emu_runscript_dummy_amr.py => hercules_runscript_dummy_amr.py} (100%) diff --git a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh index 5e924a91..a214c928 100755 --- a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh +++ b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh @@ -1,5 +1,5 @@ #!/bin/bash -#SBATCH --job-name=emu +#SBATCH --job-name=hercules #SBATCH --time=1:00:00 #SBATCH --nodes=2 #SBATCH --ntasks-per-node=36 @@ -7,28 +7,25 @@ # #SBATCH --qos=high # A lot of modules and conda stuff -# source /nopt/nrel/apps/anaconda/5.3/etc/profile.d/conda.sh -# module use /nopt/nrel/apps/modules/centos74/modulefiles -# module use /not/nrel/apps/modules/default/modulefiles +source /nopt/nrel/apps/anaconda/5.3/etc/profile.d/conda.sh +module use /not/nrel/apps/modules/default/modulefiles module purge -# module load conda/5.3 +module load conda +export PREFIX=~/.conda-envs/hercules +export PATH=$PREFIX/bin:$PATH +export FI_PROVIDER_PATH=$PREFIX/lib/libfabric/prov +export LD_LIBRARY_PATH=$PREFIX/lib/libfabric:$PREFIX/lib/release_mt:$LD_LIBRARY_PATH +source activate hercules module load helics/helics-3.1.0_openmpi module load netcdf-c/4.7.3/gcc-mpi -module load conda -#export FI_PROVIDER_PATH=$PREFIX/lib/libfabric/prov -#export LD_LIBRARY_PATH=$PREFIX/lib/libfabric:$PREFIX/lib/release_mt:$LD_LIBRARY_PATH -conda activate /home/msinner/emu_moa_dev/conda-env/emupy -# module load intel-mpi/2018.0.3 - -# module load mkl -# module load mpt +export HELICS_PORT=32000 # Set up the helics broker -helics_broker -t zmq -f 2 --loglevel="debug" & +helics_broker -t zmq -f 2 --loglevel="debug" --local_port=$HELICS_PORT & -# Need to set this to your emu_python folder -python emu_runscript.py emu_input_longsim.yaml >> logemu 2>&1 & # Start the controller center and pass in input file +# Need to set this to your hercules folder +python hercules_runscript.py hercules_input_longsim.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file # Now go back to scratch folder and launch the job mpirun -n 36 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml b/example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml similarity index 100% rename from example_case_folders/example_sim_yaw_control/emu_input_longsim.yaml rename to example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml diff --git a/example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml b/example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml similarity index 100% rename from example_case_folders/example_sim_yaw_control/emu_input_shortsim.yaml rename to example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript.py b/example_case_folders/example_sim_yaw_control/hercules_runscript.py similarity index 62% rename from example_case_folders/example_sim_yaw_control/emu_runscript.py rename to example_case_folders/example_sim_yaw_control/hercules_runscript.py index 76d818c7..87a222fe 100644 --- a/example_case_folders/example_sim_yaw_control/emu_runscript.py +++ b/example_case_folders/example_sim_yaw_control/hercules_runscript.py @@ -1,7 +1,7 @@ -from emu_python.emulator import Emulator -from emu_python.controllers.simple_yaw_controller import SimpleYawController -from emu_python.py_sims import PySims -from emu_python.utilities import load_yaml +from hercules.emulator import Emulator +from hercules.controllers.simple_yaw_controller import SimpleYawController +from hercules.py_sims import PySims +from hercules.utilities import load_yaml import sys diff --git a/example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py b/example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py similarity index 100% rename from example_case_folders/example_sim_yaw_control/emu_runscript_dummy_amr.py rename to example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py From 017b6963e98317bf3d088ae69f7f5a0805dc238e Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 14 Jul 2023 16:17:44 -0600 Subject: [PATCH 18/38] More name updates. --- .../batch_script_amrwind.sh | 2 +- .../hercules_input_longsim.yaml | 4 ++-- .../hercules_input_shortsim.yaml | 4 ++-- hercules/emulator.py | 16 ++++++++-------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh index a214c928..d460e0e5 100755 --- a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh +++ b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh @@ -28,4 +28,4 @@ helics_broker -t zmq -f 2 --loglevel="debug" --local_port=$HELICS_PORT & python hercules_runscript.py hercules_input_longsim.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file # Now go back to scratch folder and launch the job -mpirun -n 36 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file +mpirun -n 72 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml b/example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml index 1652ed6c..0360ec6a 100644 --- a/example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml +++ b/example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml @@ -9,7 +9,7 @@ description: Just a solar plant dt: 0.5 -emu_comms: +hercules_comms: amr_wind: @@ -21,7 +21,7 @@ emu_comms: helics: config: - name: emu_python # What is the purpose of this name + name: hercules # What is the purpose of this name use_dash_frontend: False KAFKA: False KAFKA_topics: EMUV1py diff --git a/example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml b/example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml index c84d5191..5a2bf7f3 100644 --- a/example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml +++ b/example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml @@ -9,7 +9,7 @@ description: Just a solar plant dt: 0.5 -emu_comms: +hercules_comms: amr_wind: @@ -21,7 +21,7 @@ emu_comms: helics: config: - name: emu_python # What is the purpose of this name + name: hercules # What is the purpose of this name use_dash_frontend: False KAFKA: False KAFKA_topics: EMUV1py diff --git a/hercules/emulator.py b/hercules/emulator.py index aecf1b20..2580a001 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -103,10 +103,10 @@ def __init__(self, controller, py_sims, input_dict): ]['turbine_powers'] = np.zeros(self.num_turbines) self.amr_wind_dict[self.amr_wind_names[0] ]['turbine_wind_directions'] = [0.]*self.num_turbines - # Write to emu_comms so that controller can access - self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + # Write to hercules_comms so that controller can access + self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_powers'] = [0.]*self.num_turbines - self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = [0.]*self.num_turbines # TODO Could set up logging here @@ -228,10 +228,10 @@ def run(self): self.turbine_power_array = turbine_power_array self.amr_wind_dict[self.amr_wind_names[0] ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind - # TODO: write these to the emu_comms object, too? - self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + # TODO: write these to the hercules_comms object, too? + self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_powers'] = turbine_power_array - self.input_dict['emu_comms']['amr_wind'][self.amr_wind_names[0]]\ + self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = turbine_wd_array @@ -334,10 +334,10 @@ def process_periodic_publication(self): # Periodically publish data to the surrogate # Hard coded to single wind farm for the moment - if "turbine_yaw_angles" in self.input_dict["emu_comms"]\ + if "turbine_yaw_angles" in self.input_dict["hercules_comms"]\ ["amr_wind"]\ [self.amr_wind_names[0]]: - yaw_angles = self.input_dict["emu_comms"]\ + yaw_angles = self.input_dict["hercules_comms"]\ ["amr_wind"]\ [self.amr_wind_names[0]]\ ["turbine_yaw_angles"] From 7f1d21252b5bc0a04c5510fc15cfcf6d44c134af Mon Sep 17 00:00:00 2001 From: misha Date: Sat, 15 Jul 2023 16:53:40 -0600 Subject: [PATCH 19/38] Cleaning up, renaming more. --- .../controllers/controller_base.py | 0 .../controllers/simple_yaw_controller.py | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) rename {emu_python => hercules}/controllers/controller_base.py (100%) rename {emu_python => hercules}/controllers/simple_yaw_controller.py (84%) diff --git a/emu_python/controllers/controller_base.py b/hercules/controllers/controller_base.py similarity index 100% rename from emu_python/controllers/controller_base.py rename to hercules/controllers/controller_base.py diff --git a/emu_python/controllers/simple_yaw_controller.py b/hercules/controllers/simple_yaw_controller.py similarity index 84% rename from emu_python/controllers/simple_yaw_controller.py rename to hercules/controllers/simple_yaw_controller.py index 25b80f9b..20c78fbf 100644 --- a/emu_python/controllers/simple_yaw_controller.py +++ b/hercules/controllers/simple_yaw_controller.py @@ -1,5 +1,5 @@ from abc import abstractmethod -from emu_python.controllers.controller_base import Controller +from hercules.controllers.controller_base import Controller class SimpleYawController(Controller): @@ -34,17 +34,17 @@ def step(self, input_dict): # Still, to demonstrate, we can save the current wind directions. # Grab name of wind farm (assumes there is only one!) - wf_name = list(input_dict["emu_comms"]["amr_wind"].keys())[0] + wf_name = list(input_dict["hercules_comms"]["amr_wind"].keys())[0] - # How would we do this part, if not saved in emu_comms? might be though? - self.wind_directions = input_dict["emu_comms"]\ + # How would we do this part, if not saved in hercules_comms? might be though? + self.wind_directions = input_dict["hercules_comms"]\ ["amr_wind"]\ [wf_name]\ ["turbine_wind_directions"] # Now, set the amr-wind yaw angles yaw_angles = self.wind_directions # Yaws instantaneously - input_dict["emu_comms"]\ + input_dict["hercules_comms"]\ ["amr_wind"]\ [wf_name]\ ["turbine_yaw_angles"] = yaw_angles From 7512b336bebe7ad0a956232382d0c42f8a16b6c9 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 21 Jul 2023 13:53:03 -0600 Subject: [PATCH 20/38] AMR wind version now runs. --- .../example_sim_yaw_control/amr_input.inp | 41 ++++++++----------- .../batch_script_amrwind.sh | 2 +- .../hercules_runscript_dummy_amr.py | 2 +- hercules/controllers/simple_yaw_controller.py | 2 +- hercules/emulator.py | 24 +++++------ 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/example_case_folders/example_sim_yaw_control/amr_input.inp b/example_case_folders/example_sim_yaw_control/amr_input.inp index 024a282e..6670155e 100755 --- a/example_case_folders/example_sim_yaw_control/amr_input.inp +++ b/example_case_folders/example_sim_yaw_control/amr_input.inp @@ -61,7 +61,7 @@ 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_io_mode = 1 #Input mode ABL.bndry_planes = ylo xlo # I'm (Paul) adding this but not sure if I have to ABL.bndry_var_names = velocity temperature tke @@ -75,9 +75,8 @@ ABL.stats_output_frequency = 1 ABL.stats_output_format = netcdf # Whether to use helics -helics.activated = true -helics.broker_port = 23404 -helics.broker_address = "127.0.0.1" +helics.activated = true +helics.broker_port = 32000 #¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# # ADAPTIVE MESH REFINEMENT # @@ -147,36 +146,28 @@ 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.7 1.2 -Actuator.UniformCtDisk.wind_speed = 0.0 10.0 12.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 - +# 2.3 MW Turbine inputs Actuator.labels = T00 T01 -# T02 T03 T04 T05 T06 T07 -Actuator.JoukowskyDisk.thrust_coeff = 8.1672e-01 7.9044e-01 7.8393e-01 7.8624e-01 7.8824e-01 7.8942e-01 7.8902e-01 7.8740e-01 7.8503e-01 7.8237e-01 7.7955e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.6922e-01 7.4270e-01 5.5949e-01 4.6163e-01 3.8786e-01 3.2901e-01 2.8093e-01 2.4114e-01 2.0795e-01 1.8010e-01 1.5663e-01 1.3679e-01 1.1995e-01 1.0562e-01 9.3384e-02 8.2908e-02 7.3910e-02 6.6159e-02 5.9463e-02 5.3662e-02 4.8622e-02 4.4230e-02 -Actuator.JoukowskyDisk.wind_speed = 3.0000e+00 3.5495e+00 4.0679e+00 4.5539e+00 5.0064e+00 5.4244e+00 5.8069e+00 6.1530e+00 6.4619e+00 6.7330e+00 6.9655e+00 7.1589e+00 7.3128e+00 7.4269e+00 7.5009e+00 7.5345e+00 7.5412e+00 7.5883e+00 7.6757e+00 7.8031e+00 7.9702e+00 8.1767e+00 8.4221e+00 8.7059e+00 9.0273e+00 9.3856e+00 9.7800e+00 1.0210e+01 1.0659e+01 1.0673e+01 1.1170e+01 1.1699e+01 1.2259e+01 1.2848e+01 1.3465e+01 1.4109e+01 1.4778e+01 1.5471e+01 1.6185e+01 1.6921e+01 1.7674e+01 1.8445e+01 1.9231e+01 2.0030e+01 2.0841e+01 2.1661e+01 2.2489e+01 2.3323e+01 2.4160e+01 2.5000e+01 -Actuator.JoukowskyDisk.rpm = 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0861e+00 5.1954e+00 5.2765e+00 5.3290e+00 5.3529e+00 5.3577e+00 5.3912e+00 5.4532e+00 5.5437e+00 5.6625e+00 5.8092e+00 5.9836e+00 6.1851e+00 6.4135e+00 6.6681e+00 6.9483e+00 7.2535e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 -Actuator.JoukowskyDisk.rotor_diameter = 240.0 -Actuator.JoukowskyDisk.hub_height = 150.0 -Actuator.JoukowskyDisk.output_frequency = 100 -Actuator.JoukowskyDisk.diameters_to_sample = 1.0 +Actuator.type = JoukowskyDisk +Actuator.JoukowskyDisk.rotor_diameter = 116.0 +Actuator.JoukowskyDisk.hub_height = 90.0 +Actuator.JoukowskyDisk.output_frequency = 10 +Actuator.JoukowskyDisk.diameters_to_sample = 2.5 Actuator.JoukowskyDisk.num_points_r = 40 Actuator.JoukowskyDisk.num_points_t = 5 Actuator.JoukowskyDisk.num_blades = 3 Actuator.JoukowskyDisk.use_tip_correction = true Actuator.JoukowskyDisk.use_root_correction = true Actuator.JoukowskyDisk.epsilon = 5.0 -Actuator.JoukowskyDisk.vortex_core_size = 24.0 +Actuator.JoukowskyDisk.vortex_core_size = 13.0 + +Actuator.JoukowskyDisk.wind_speed = 3.0 3.889649963239854 4.684006996752303 5.377830233987229 5.966542092267928 6.44625847394617 6.8138143922059236 7.066784852446481 7.203500851477444 7.22306038896904 7.320786359429763 7.535153078939617 7.864746237154081 8.30739130337076 8.860167873258558 9.519428936578247 10.280824938773394 10.589724312062877 11.13933247768231 12.08928744604103 13.12442240111568 14.237907914913496 15.422397632159566 16.670076738763772 17.972713521001396 19.321713675239476 20.708177009893884 22.122956165519163 23.556716965618207 25.0 +Actuator.JoukowskyDisk.rpm = 5.500000000399841 5.7503730274604 6.924733009169061 7.950466035772244 8.820804709636782 9.530006866155707 10.073393458373337 10.447379427563192 10.649497356086282 10.678413746477254 10.82288967359941 11.139804720841314 11.627068002870239 12.28146474128283 13.098677474494233 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 +Actuator.JoukowskyDisk.thrust_coeff = 0.795419507524108 0.8163759621542088 0.8163759621542089 0.8163759621542088 0.8163759621542088 0.8163759621542093 0.8163759621542093 0.816375962154209 0.8163759621542089 0.816375962154209 0.8163759621542089 0.8163759621542088 0.8163759621542088 0.7816497292837605 0.6881024487256834 0.5960935792514858 0.5110705883010171 0.48169007343985104 0.3992418445397665 0.301726241148816 0.23137184415660814 0.17948146484830918 0.1408250546606467 0.11178366795199553 0.08975645587417404 0.07310080594700819 0.060299514659720915 0.0504505646442757 0.04286514300370846 0.03692885272686376 + -Actuator.UniformCtDisk.yaw = 240.0 +Actuator.JoukowskyDisk.yaw = 240.0 Actuator.T00.base_position = 2000.0 2000.0 0.0 Actuator.T01.base_position = 2500.0 2500.0 0.0 diff --git a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh index d460e0e5..ec4c7242 100755 --- a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh +++ b/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh @@ -28,4 +28,4 @@ helics_broker -t zmq -f 2 --loglevel="debug" --local_port=$HELICS_PORT & python hercules_runscript.py hercules_input_longsim.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file # Now go back to scratch folder and launch the job -mpirun -n 72 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file +mpirun -n 72 /home/msinner/emu_moa_dev/amr-wind-2/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file diff --git a/example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py b/example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py index 39d85a06..8321fa9a 100644 --- a/example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py +++ b/example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py @@ -1,5 +1,5 @@ import sys -from emu_python.dummy_amr_wind import launch_dummy_amr_wind +from hercules.dummy_amr_wind import launch_dummy_amr_wind # Check that one command line argument was given if len(sys.argv) != 2: diff --git a/hercules/controllers/simple_yaw_controller.py b/hercules/controllers/simple_yaw_controller.py index 20c78fbf..64daa2c9 100644 --- a/hercules/controllers/simple_yaw_controller.py +++ b/hercules/controllers/simple_yaw_controller.py @@ -37,7 +37,7 @@ def step(self, input_dict): wf_name = list(input_dict["hercules_comms"]["amr_wind"].keys())[0] # How would we do this part, if not saved in hercules_comms? might be though? - self.wind_directions = input_dict["hercules_comms"]\ + self.wind_directions = input_dict["hercules_comms"]\ ["amr_wind"]\ [wf_name]\ ["turbine_wind_directions"] diff --git a/hercules/emulator.py b/hercules/emulator.py index 2580a001..5735765e 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -104,9 +104,9 @@ def __init__(self, controller, py_sims, input_dict): self.amr_wind_dict[self.amr_wind_names[0] ]['turbine_wind_directions'] = [0.]*self.num_turbines # Write to hercules_comms so that controller can access - self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_powers'] = [0.]*self.num_turbines - self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = [0.]*self.num_turbines # TODO Could set up logging here @@ -161,7 +161,7 @@ def run(self): # amr-wind/py_sims (such as the turbines' powers). # Print the input dict - # print(self.input_dict) + # print(self.main_dict) # Subscribe to helics messages: incoming_messages = self.helics_connector.get_all_waiting_messages() @@ -229,9 +229,9 @@ def run(self): self.amr_wind_dict[self.amr_wind_names[0] ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind # TODO: write these to the hercules_comms object, too? - self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_powers'] = turbine_power_array - self.input_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = turbine_wd_array @@ -334,13 +334,13 @@ def process_periodic_publication(self): # Periodically publish data to the surrogate # Hard coded to single wind farm for the moment - if "turbine_yaw_angles" in self.input_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.amr_wind_names[0]]: - yaw_angles = self.input_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.amr_wind_names[0]]\ - ["turbine_yaw_angles"] + if "turbine_yaw_angles" in self.main_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.amr_wind_names[0]]: + yaw_angles = self.main_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.amr_wind_names[0]]\ + ["turbine_yaw_angles"] else: # set yaw_angles based on self.wind_direction yaw_angles = [self.wind_direction]*self.num_turbines From 9083cd83a4a0114c7d405fe0f7a4abe1e914cbbe Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 21 Jul 2023 14:37:43 -0600 Subject: [PATCH 21/38] Removing controller.py in favor of controllers/controller_base.py --- emu_python/py_sims.py | 43 ------------------- .../01_amr_wind_only/batch_script.sh | 2 +- .../01_amr_wind_only/hercules_runscript.py | 2 +- .../hercules_runscript.py | 2 +- .../hercules_runscript.py | 2 +- .../hercules_runscript.py | 2 +- .../hercules_runscript.py | 2 +- .../amr_input.inp | 0 .../batch_script_amrwind.sh | 0 .../batch_script_dummy.sh | 6 +-- .../hercules_input_longsim.yaml | 0 .../hercules_input_shortsim.yaml | 0 .../hercules_runscript.py | 0 .../hercules_runscript_dummy_amr.py | 0 .../readme.txt | 0 hercules/controller.py | 16 ------- 16 files changed, 9 insertions(+), 68 deletions(-) delete mode 100644 emu_python/py_sims.py rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/amr_input.inp (100%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/batch_script_amrwind.sh (100%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/batch_script_dummy.sh (66%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/hercules_input_longsim.yaml (100%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/hercules_input_shortsim.yaml (100%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/hercules_runscript.py (100%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/hercules_runscript_dummy_amr.py (100%) rename example_case_folders/{example_sim_yaw_control => 06_simple_yaw_control}/readme.txt (100%) delete mode 100644 hercules/controller.py diff --git a/emu_python/py_sims.py b/emu_python/py_sims.py deleted file mode 100644 index 1a758870..00000000 --- a/emu_python/py_sims.py +++ /dev/null @@ -1,43 +0,0 @@ -from emu_python.python_simulators.simple_solar import SimpleSolar -from emu_python.python_simulators.simple_battery import SimpleBattery - - -class PySims(): - - def __init__(self, input_dict): - - # Save timt step - self.dt = input_dict['dt'] - - # Grab py sim details - self.py_sim_dict = input_dict['py_sims'] - self.n_py_sim = len(self.py_sim_dict ) - self.py_sim_names = self.py_sim_dict.keys() - print(self.py_sim_names) - - # Collect the py_sim objects, inputs and outputs - for py_sim_name in self.py_sim_names: - print((self.py_sim_dict[py_sim_name])) - self.py_sim_dict[py_sim_name]['object'] = self.get_py_sim(self.py_sim_dict[py_sim_name]) - self.py_sim_dict[py_sim_name]['outputs'] = self.py_sim_dict[py_sim_name]['object'].return_outputs() - self.py_sim_dict[py_sim_name]['inputs'] = {} - - # print(self.py_sim_dict['solar_farm_0']['object']) - - def get_py_sim(self, py_sim_obj_dict): - - if py_sim_obj_dict['py_sim_type'] == 'SimpleSolar': - return SimpleSolar(py_sim_obj_dict, self.dt) - - if py_sim_obj_dict['py_sim_type'] == 'SimpleBattery': - return SimpleBattery(py_sim_obj_dict, self.dt) - - def get_py_sim_dict(self): - return self.py_sim_dict - - - def step(self, input_dict): - - # Collect the py_sim objects - for py_sim_name in self.py_sim_names: - self.py_sim_dict[py_sim_name]['outputs'] = self.py_sim_dict[py_sim_name]['object'].step(self.py_sim_dict[py_sim_name]['inputs']) diff --git a/example_case_folders/01_amr_wind_only/batch_script.sh b/example_case_folders/01_amr_wind_only/batch_script.sh index 9d81bf6d..7d7225f0 100755 --- a/example_case_folders/01_amr_wind_only/batch_script.sh +++ b/example_case_folders/01_amr_wind_only/batch_script.sh @@ -30,4 +30,4 @@ python3 hercules_runscript.py hercules_input_000.yaml >> loghercules 2>&1 & # S # Now go back to scratch folder and launch the job # cd /scratch/pfleming/c2c/example_sim_02 -mpirun -n 36 /home/msinner/emu_moa_dev/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 +mpirun -n 72 /home/pfleming/amr-wind/build/amr_wind amr_input.inp >> logamr 2>&1 diff --git a/example_case_folders/01_amr_wind_only/hercules_runscript.py b/example_case_folders/01_amr_wind_only/hercules_runscript.py index 2a84ff04..b8f73ad2 100644 --- a/example_case_folders/01_amr_wind_only/hercules_runscript.py +++ b/example_case_folders/01_amr_wind_only/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controller import Controller +from hercules.controllers.controller_base import Controller from hercules.py_sims import PySims from hercules.utilities import load_yaml diff --git a/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py b/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py index 12591d16..6113a5e9 100644 --- a/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py +++ b/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controller import Controller +from hercules.controllers.controller_base import Controller from hercules.py_sims import PySims from hercules.utilities import load_yaml diff --git a/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py b/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py index 2a84ff04..b8f73ad2 100644 --- a/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py +++ b/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controller import Controller +from hercules.controllers.controller_base import Controller from hercules.py_sims import PySims from hercules.utilities import load_yaml diff --git a/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py b/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py index 2a84ff04..b8f73ad2 100644 --- a/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py +++ b/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controller import Controller +from hercules.controllers.controller_base import Controller from hercules.py_sims import PySims from hercules.utilities import load_yaml diff --git a/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py b/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py index 1d1081c3..222457a2 100644 --- a/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py +++ b/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controller import Controller +from hercules.controllers.controller_base import Controller from hercules.py_sims import PySims from hercules.utilities import load_yaml diff --git a/example_case_folders/example_sim_yaw_control/amr_input.inp b/example_case_folders/06_simple_yaw_control/amr_input.inp similarity index 100% rename from example_case_folders/example_sim_yaw_control/amr_input.inp rename to example_case_folders/06_simple_yaw_control/amr_input.inp diff --git a/example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh b/example_case_folders/06_simple_yaw_control/batch_script_amrwind.sh similarity index 100% rename from example_case_folders/example_sim_yaw_control/batch_script_amrwind.sh rename to example_case_folders/06_simple_yaw_control/batch_script_amrwind.sh diff --git a/example_case_folders/example_sim_yaw_control/batch_script_dummy.sh b/example_case_folders/06_simple_yaw_control/batch_script_dummy.sh similarity index 66% rename from example_case_folders/example_sim_yaw_control/batch_script_dummy.sh rename to example_case_folders/06_simple_yaw_control/batch_script_dummy.sh index db18ba9e..10c6d221 100755 --- a/example_case_folders/example_sim_yaw_control/batch_script_dummy.sh +++ b/example_case_folders/06_simple_yaw_control/batch_script_dummy.sh @@ -2,17 +2,17 @@ # I just run these one at a t time # A lot of modules and conda stuff -conda activate emupy +conda activate hercules # Set up the helics broker helics_broker -t zmq -f 2 --loglevel="debug" & # Need to set this to your emu_python folder # cd /home/pfleming/emu_python/emu_python -python emu_runscript.py emu_input_shortsim.yaml >> logemu 2>&1 & # Start the controller center and pass in input file +python hercules_runscript.py hercules_input_shortsim.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file -python emu_runscript_dummy_amr.py amr_input.inp >> logdummy 2>&1 +python hercules_runscript_dummy_amr.py amr_input.inp >> logdummy 2>&1 # Now go back to scratch folder and launch the job # cd /scratch/pfleming/c2c/example_sim_02 diff --git a/example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml b/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml similarity index 100% rename from example_case_folders/example_sim_yaw_control/hercules_input_longsim.yaml rename to example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml diff --git a/example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml b/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml similarity index 100% rename from example_case_folders/example_sim_yaw_control/hercules_input_shortsim.yaml rename to example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml diff --git a/example_case_folders/example_sim_yaw_control/hercules_runscript.py b/example_case_folders/06_simple_yaw_control/hercules_runscript.py similarity index 100% rename from example_case_folders/example_sim_yaw_control/hercules_runscript.py rename to example_case_folders/06_simple_yaw_control/hercules_runscript.py diff --git a/example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py b/example_case_folders/06_simple_yaw_control/hercules_runscript_dummy_amr.py similarity index 100% rename from example_case_folders/example_sim_yaw_control/hercules_runscript_dummy_amr.py rename to example_case_folders/06_simple_yaw_control/hercules_runscript_dummy_amr.py diff --git a/example_case_folders/example_sim_yaw_control/readme.txt b/example_case_folders/06_simple_yaw_control/readme.txt similarity index 100% rename from example_case_folders/example_sim_yaw_control/readme.txt rename to example_case_folders/06_simple_yaw_control/readme.txt diff --git a/hercules/controller.py b/hercules/controller.py deleted file mode 100644 index 0bdf6d93..00000000 --- a/hercules/controller.py +++ /dev/null @@ -1,16 +0,0 @@ - - - -class Controller(): - - def __init__(self, input_dict): - pass - - def step(self, main_dict): - - pass - - def get_controller_dict(self): - - return {} - From 0aca070280cf6410d9e005129721c5cd96cadfc5 Mon Sep 17 00:00:00 2001 From: misha Date: Sun, 23 Jul 2023 13:13:56 -0600 Subject: [PATCH 22/38] Switching helicsport (not sure if needed?) --- .../06_simple_yaw_control/hercules_input_longsim.yaml | 2 +- .../06_simple_yaw_control/hercules_input_shortsim.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml b/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml index 0360ec6a..750c86a4 100644 --- a/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml +++ b/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml @@ -30,7 +30,7 @@ hercules_comms: subscription_topics: [status] publication_topics: [control] endpoints: [] - helicsport: 23405 + helicsport : 32000 publication_interval: 1 endpoint_interval: 1 starttime: 0 diff --git a/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml b/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml index 5a2bf7f3..0d45a06a 100644 --- a/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml +++ b/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml @@ -30,7 +30,7 @@ hercules_comms: subscription_topics: [status] publication_topics: [control] endpoints: [] - helicsport: 23405 + helicsport : 32000 publication_interval: 1 endpoint_interval: 1 starttime: 0 From e7364cc538b44151d09301037faaf1ff04aa3bb5 Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 26 Jul 2023 12:20:14 -0600 Subject: [PATCH 23/38] Updates to read in time step. --- hercules/dummy_amr_wind.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/hercules/dummy_amr_wind.py b/hercules/dummy_amr_wind.py index e85c386f..e51eb814 100644 --- a/hercules/dummy_amr_wind.py +++ b/hercules/dummy_amr_wind.py @@ -56,6 +56,11 @@ def read_amr_wind_input(amr_wind_input): with open(amr_wind_input) as fp: Lines = fp.readlines() + # Get the simulation time step + for line in Lines: + if 'time.fixed_dt' in line: + dt = float(line.split()[2]) + # Find the actuators for line in Lines: if 'Actuator.labels' in line: @@ -77,6 +82,7 @@ def read_amr_wind_input(amr_wind_input): turbine_locations.append(locations) return_dict = { + 'dt':dt, 'num_turbines': num_turbines, 'turbine_labels': turbine_labels, 'rotor_diameter': D, @@ -103,6 +109,10 @@ def __init__(self, config_dict ,amr_wind_input): self.amr_wind_input = amr_wind_input self.amr_wind_input_dict = read_amr_wind_input(self.amr_wind_input) + # Get the simulation time step + self.dt = self.amr_wind_input_dict['dt'] + self.config_dict['helics']['deltat'] = self.dt + # Get the number of turbines self.num_turbines = self.amr_wind_input_dict['num_turbines'] @@ -114,6 +124,7 @@ def run(self): # Initialize the values turbine_powers = np.zeros(self.num_turbines) sim_time_s = 0. # initialize time to 0 + time_step = 0 # Initialize time step counter to 0 amr_wind_speed = 8.0 amr_wind_direction = 240.0 @@ -170,12 +181,15 @@ def run(self): turbine_wind_directions = list( amr_wind_direction + 5.*np.random.randn(self.num_turbines) ) + turbine_wind_directions = [sim_time_s+0.01, sim_time_s+0.02] + + amr_wind_direction = sim_time_s # ================================================================ # Communicate with control center # Send the turbine powers for this time step and get wind speed and wind direction for the # nex time step - logger.info('Time step: %d' % sim_time_s) + logger.info('Time step: %d' % time_step) logger.info("** Communicating with control center") message_from_client_array = [ sim_time_s, amr_wind_speed, amr_wind_direction @@ -201,8 +215,9 @@ def run(self): # Note dummy doesn't currently use received info for anything - # Advance simulation time - sim_time_s += 1 + # Advance simulation time and time step counter + sim_time_s += self.dt + time_step += 1 self.sync_time_helics(self.absolute_helics_time + self.deltat) # TODO cleanup code to move publish and subscribe here. @@ -228,7 +243,7 @@ def launch_dummy_amr_wind(amr_input_file): "gridpack": { }, "helics": { - "deltat": 1, + "deltat": 1, # Will be overridden by input file value "subscription_topics": [ "control" From 98e93b1f2ffa99e10fc7157e5525d12edc96cd28 Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 26 Jul 2023 13:28:34 -0600 Subject: [PATCH 24/38] Reorganizing emulator somewhat to allow clearer execution of main loop and swap send and receive to AMRwind. Removes KAFKA pieces (were not being used?) --- hercules/emulator.py | 210 +++++++++++++++++++------------------------ 1 file changed, 90 insertions(+), 120 deletions(-) diff --git a/hercules/emulator.py b/hercules/emulator.py index 5735765e..62b487b9 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -57,18 +57,6 @@ def __init__(self, controller, py_sims, input_dict): # TODO: Store other things self.use_dash_frontend = self.helics_config_dict["use_dash_frontend"] - self.KAFKA = self.helics_config_dict["KAFKA"] - - # TODO Copied direct from control_center.py but not actually ready yet - if self.KAFKA: - from dav_kafka_python.configuration import Configuration - from dav_kafka_python.producer import PythonProducer - # Kafka topic : - self.topic = self.helics_config_dict["KAFKA_TOPIC"] - print("KAFKA topic", self.topic) - config = Configuration(env_path='./.env') - self.python_producer = PythonProducer(config) - self.python_producer.connect() # AMR wind files # Grab py sim details @@ -109,6 +97,9 @@ def __init__(self, controller, py_sims, input_dict): self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = [0.]*self.num_turbines + self.wind_speed = 0 + self.wind_direction = 0 + # TODO Could set up logging here # TODO Set interface comms to either dash or kenny's front end @@ -145,128 +136,94 @@ def run(self): continue # Update controller and py sims + #self.main_dict = self.controller.step(self.main_dict) self.main_dict['controller'] = self.controller.get_controller_dict() self.py_sims.step(self.main_dict) self.main_dict['py_sims'] = self.py_sims.get_py_sim_dict() - # TODO: usage differs a little here between controller and py_sims. - # controller returns an altered input_dict directly (and alters - # fields outside of input_dict['controller'], whereas py_sims.step - # returns None and then the 'py_sims' field of input_dict is populated - # using self.py_sims.get_py_sim_dict(). This is consistent with - # the sims (and amr_wind) running as individual systems, with the - # controller coordinating them. As well as handling controllable - # inputs, the controller should then also pass other signals between - # amr-wind/py_sims (such as the turbines' powers). - - # Print the input dict - # print(self.main_dict) - - # Subscribe to helics messages: - incoming_messages = self.helics_connector.get_all_waiting_messages() - if incoming_messages != {}: - subscription_value = self.process_subscription_messages( - incoming_messages) - else: - print( - "Emulator: Did not receive subscription from AMRWind, setting everyhthing to 0.") - subscription_value = [ - 0, 0, 0] + [0 for t in range(self.num_turbines)] + [0 for t in range(self.num_turbines)] - - # TODO Parse returns from AMRWind - sim_time_s_amr_wind, wind_speed_amr_wind, wind_direction_amr_wind = subscription_value[ - :3] - turbine_power_array = subscription_value[3:3+self.num_turbines] - turbine_wd_array = subscription_value[3+self.num_turbines:] - self.wind_speed = wind_speed_amr_wind - self.wind_direction = wind_direction_amr_wind - - # Assign Py_sim outputs - if self.main_dict['py_sims']: - self.main_dict['py_sims']['inputs']['available_power'] = sum(turbine_power_array) - - - ## TODO add other parameters that need to be logged to csv here. - # Write turbine power and turbine wind direction to csv logfile. - aa = [str(xx) for xx in turbine_power_array] - xyz = ",".join(aa) - bb = [str(xx) for xx in turbine_wd_array] - zyx = ",".join(bb) - with open(f'{LOGFILE}.csv', 'a') as filex: - filex.write(str(self.absolute_helics_time) + ',' + str(sim_time_s_amr_wind) + ',' + str( - wind_speed_amr_wind) + ',' + str(wind_direction_amr_wind) + ',' + xyz + ',' + zyx + os.linesep) - - - # TODO F-Strings - print("=======================================") - print("AMRWindTime:", sim_time_s_amr_wind) - print("AMRWindSpeed:", wind_speed_amr_wind) - print("AMRWindDirection:", wind_direction_amr_wind) - print("AMRWindTurbinePowers:", turbine_power_array) - print("AMRWIND number of turbines here: ", self.num_turbines) - print("AMRWindTurbineWD:", turbine_wd_array) - print("=======================================") - - # Process periocdic functions. - self.process_periodic_publication() - - # Send control center values through Kafka if enabled: - if self.KAFKA: - key = json.dumps({"key": "wind_tower"}) - value = json.dumps({"helics_time": self.absolute_helics_time, "bucket": "wind_tower", "AMRWind_speed": wind_speed_amr_wind, - "AMRWind_direction": wind_direction_amr_wind, "AMRWind_time": sim_time_s_amr_wind}) - self.python_producer.write(key=key, value=value, - topic=self.topic, token='test-token') - - # Store turbine powers back to the dict - # TODO hard-coded for now assuming only one AMR-WIND - self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_powers'] = turbine_power_array - self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_wind_directions'] = turbine_wd_array - self.turbine_power_array = turbine_power_array - self.amr_wind_dict[self.amr_wind_names[0] - ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind - # TODO: write these to the hercules_comms object, too? - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['turbine_powers'] = turbine_power_array - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['turbine_wind_directions'] = turbine_wd_array - - - # Send turbine powers through Kafka if enabled: - if self.KAFKA: - if len(turbine_power_array) == 0: - turbine_power_array = np.ones(self.num_turbines)*-1.0 - for t in range(self.num_turbines): - - keyname = f"wind_turbine_{t}" - key = json.dumps({"key": keyname}) - value = json.dumps({"helics_time": self.currenttime, "bucket": keyname, "turbine_wd_direction": turbine_wd_array[t], "power": turbine_power_array[ - t], "AMRWind_speed": wind_speed_amr_wind, "AMRWind_direction": wind_direction_amr_wind, "AMRWind_time": sim_time_s_amr_wind}) - self.python_producer.write( - key=key, value=value, topic=self.topic, token='test-token') - - self.sync_time_helics(self.absolute_helics_time + self.deltat) + # Send inputs (initiates the AMRWind step) + self.send_data_to_amrwind() - # Log the input dict + # Log the current state self.log_main_dict() + # Update time to next time step (TODO: check logging for pysims?) + self.sync_time_helics(self.absolute_helics_time + self.deltat) + + # Receive outputs back (for next time step) + self.receive_amrwind_data() + # If this is first iteration print the input dict # And turn off the first iteration flag if self.first_iteration: print(self.main_dict) + self.save_main_dict_as_text() self.first_iteration = False - # Echo the dictionary to a seperate file in case it is helpful - # to see full dictionary in interpreting log - - original_stdout = sys.stdout - with open('main_dict.echo', 'w') as f_i: - sys.stdout = f_i # Change the standard output to the file we created. - print(self.main_dict) - sys.stdout = original_stdout # Reset the standard output to its original value + def receive_amrwind_data(self): + + # Subscribe to helics messages: + incoming_messages = self.helics_connector.get_all_waiting_messages() + if incoming_messages != {}: + subscription_value = self.process_subscription_messages( + incoming_messages) + else: + print( + "Emulator: Did not receive subscription from AMRWind, setting everyhthing to 0.") + subscription_value = [ + 0, 0, 0] + [0 for t in range(self.num_turbines)] + [0 for t in range(self.num_turbines)] + + # TODO Parse returns from AMRWind + sim_time_s_amr_wind, wind_speed_amr_wind, wind_direction_amr_wind = subscription_value[ + :3] + turbine_power_array = subscription_value[3:3+self.num_turbines] + turbine_wd_array = subscription_value[3+self.num_turbines:] + self.wind_speed = wind_speed_amr_wind + self.wind_direction = wind_direction_amr_wind + + # Assign Py_sim outputs + if self.main_dict['py_sims']: + self.main_dict['py_sims']['inputs']['available_power'] = sum(turbine_power_array) + + ## TODO add other parameters that need to be logged to csv here. + # Write turbine power and turbine wind direction to csv logfile. + # TODO: should this be in this method, or its own method? + aa = [str(xx) for xx in turbine_power_array] + xyz = ",".join(aa) + bb = [str(xx) for xx in turbine_wd_array] + zyx = ",".join(bb) + with open(f'{LOGFILE}.csv', 'a') as filex: + filex.write(str(self.absolute_helics_time) + ',' + str(sim_time_s_amr_wind) + ',' + str( + wind_speed_amr_wind) + ',' + str(wind_direction_amr_wind) + ',' + xyz + ',' + zyx + os.linesep) + + # Printouts related to message received from AMRWind + print("=======================================") + print("AMRWindTime:", sim_time_s_amr_wind) + print("AMRWindSpeed:", wind_speed_amr_wind) + print("AMRWindDirection:", wind_direction_amr_wind) + print("AMRWindTurbinePowers:", turbine_power_array) + print("AMRWIND number of turbines here: ", self.num_turbines) + print("AMRWindTurbineWD:", turbine_wd_array) + print("=======================================") + + # Store turbine powers back to the dict + # TODO hard-coded for now assuming only one AMR-WIND + self.amr_wind_dict[self.amr_wind_names[0] + ]['turbine_powers'] = turbine_power_array + self.amr_wind_dict[self.amr_wind_names[0] + ]['turbine_wind_directions'] = turbine_wd_array + self.turbine_power_array = turbine_power_array + self.amr_wind_dict[self.amr_wind_names[0] + ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind + # TODO: write these to the hercules_comms object, too? + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['turbine_powers'] = turbine_power_array + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['turbine_wind_directions'] = turbine_wd_array + + + return None def recursive_flatten_main_dict(self, nested_dict, prefix = ''): @@ -315,6 +272,16 @@ def log_main_dict(self): with open(self.output_file, 'a') as filex: filex.write(','.join([str(v) for v in values]) + os.linesep) + def save_main_dict_as_text(self): + # Echo the dictionary to a seperate file in case it is helpful + # to see full dictionary in interpreting log + + original_stdout = sys.stdout + with open('main_dict.echo', 'w') as f_i: + sys.stdout = f_i # Change the standard output to the file we created. + print(self.main_dict) + sys.stdout = original_stdout # Reset the standard output to its original value + def parse_input_yaml(self, filename): @@ -330,6 +297,9 @@ def process_subscription_messages(self, msg): print(f"Subscription error: {e} , returning 0s ", flush=True) return [0, 0, 0] + [0 for t in range(self.num_turbines)] + [0 for t in range(self.num_turbines)] + def send_data_to_amrwind(self): + self.process_periodic_publication() + def process_periodic_publication(self): # Periodically publish data to the surrogate From 3d0b8fa9941872d3f071303e8c79ed7837cc6438 Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 6 Dec 2023 13:54:29 -0700 Subject: [PATCH 25/38] Commiting to change branch. --- hercules/controllers/controller_base.py | 21 ++++++++++++++++++- hercules/emulator.py | 27 ++++++++++++++----------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/hercules/controllers/controller_base.py b/hercules/controllers/controller_base.py index 31d2a0a4..bcafee01 100644 --- a/hercules/controllers/controller_base.py +++ b/hercules/controllers/controller_base.py @@ -3,12 +3,31 @@ class Controller(): def __init__(self, input_dict): + + # Get wind farm information (assumes only one wind farm!) + self.wf_name = list(input_dict["hercules_comms"]["amr_wind"].keys())[0] + pass @abstractmethod def step(self, input_dict): - pass + num_turbines = input_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.wf_name]\ + ["num_turbines"] + + # Set turbine yaw angles based on current AMR-Wind wind direction + wd = input_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.wf_name]\ + ["wind_direction"] + input_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.wf_name]\ + ["turbine_yaw_angles"] = num_turbines*[wd] + + return input_dict @abstractmethod def get_controller_dict(self): diff --git a/hercules/emulator.py b/hercules/emulator.py index 62b487b9..d9a7a4d1 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -96,6 +96,8 @@ def __init__(self, controller, py_sims, input_dict): ['turbine_powers'] = [0.]*self.num_turbines self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = [0.]*self.num_turbines + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['wind_direction'] = 0 self.wind_speed = 0 self.wind_direction = 0 @@ -136,8 +138,7 @@ def run(self): continue # Update controller and py sims - #self.main_dict = - self.controller.step(self.main_dict) + self.main_dict = self.controller.step(self.main_dict) self.main_dict['controller'] = self.controller.get_controller_dict() self.py_sims.step(self.main_dict) self.main_dict['py_sims'] = self.py_sims.get_py_sim_dict() @@ -221,6 +222,8 @@ def receive_amrwind_data(self): ['turbine_powers'] = turbine_power_array self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ ['turbine_wind_directions'] = turbine_wd_array + self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ + ['wind_direction'] = wind_direction_amr_wind return None @@ -304,20 +307,20 @@ def process_periodic_publication(self): # Periodically publish data to the surrogate # Hard coded to single wind farm for the moment - if "turbine_yaw_angles" in self.main_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.amr_wind_names[0]]: - yaw_angles = self.main_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.amr_wind_names[0]]\ - ["turbine_yaw_angles"] - else: # set yaw_angles based on self.wind_direction - yaw_angles = [self.wind_direction]*self.num_turbines + # if "turbine_yaw_angles" in self.main_dict["hercules_comms"]\ + # ["amr_wind"]\ + # [self.amr_wind_names[0]]: + yaw_angles = self.main_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.amr_wind_names[0]]\ + ["turbine_yaw_angles"] + # else: # set yaw_angles based on self.wind_direction + # yaw_angles = [self.wind_direction]*self.num_turbines # Send timing and yaw information to AMRWind via helics # publish on topic: control tmp = np.array([self.absolute_helics_time, self.wind_speed, - self.wind_direction] + yaw_angles).tolist() + self.wind_direction] + yaw_angles).tolist() self.send_via_helics("control", str(tmp)) From 8c3e9113a9d731e1760fd3000347ab89a70fbf28 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 8 Dec 2023 11:04:45 -0700 Subject: [PATCH 26/38] Removing unneded controllers in favor of whoc. --- .../06_simple_yaw_control/amr_input.inp | 173 ------------------ .../batch_script_amrwind.sh | 31 ---- .../batch_script_dummy.sh | 19 -- .../hercules_input_longsim.yaml | 66 ------- .../hercules_input_shortsim.yaml | 66 ------- .../hercules_runscript.py | 20 -- .../hercules_runscript_dummy_amr.py | 14 -- .../06_simple_yaw_control/readme.txt | 2 - hercules/controllers/simple_yaw_controller.py | 58 ------ 9 files changed, 449 deletions(-) delete mode 100755 example_case_folders/06_simple_yaw_control/amr_input.inp delete mode 100755 example_case_folders/06_simple_yaw_control/batch_script_amrwind.sh delete mode 100755 example_case_folders/06_simple_yaw_control/batch_script_dummy.sh delete mode 100644 example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml delete mode 100644 example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml delete mode 100644 example_case_folders/06_simple_yaw_control/hercules_runscript.py delete mode 100644 example_case_folders/06_simple_yaw_control/hercules_runscript_dummy_amr.py delete mode 100644 example_case_folders/06_simple_yaw_control/readme.txt delete mode 100644 hercules/controllers/simple_yaw_controller.py diff --git a/example_case_folders/06_simple_yaw_control/amr_input.inp b/example_case_folders/06_simple_yaw_control/amr_input.inp deleted file mode 100755 index 6670155e..00000000 --- a/example_case_folders/06_simple_yaw_control/amr_input.inp +++ /dev/null @@ -1,173 +0,0 @@ -#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# -# SIMULATION STOP # -#.......................................# -time.stop_time = 10800.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 #Input mode -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 # -#.......................................# - -# 2.3 MW Turbine inputs - -Actuator.labels = T00 T01 -Actuator.type = JoukowskyDisk -Actuator.JoukowskyDisk.rotor_diameter = 116.0 -Actuator.JoukowskyDisk.hub_height = 90.0 -Actuator.JoukowskyDisk.output_frequency = 10 -Actuator.JoukowskyDisk.diameters_to_sample = 2.5 -Actuator.JoukowskyDisk.num_points_r = 40 -Actuator.JoukowskyDisk.num_points_t = 5 -Actuator.JoukowskyDisk.num_blades = 3 -Actuator.JoukowskyDisk.use_tip_correction = true -Actuator.JoukowskyDisk.use_root_correction = true -Actuator.JoukowskyDisk.epsilon = 5.0 -Actuator.JoukowskyDisk.vortex_core_size = 13.0 - -Actuator.JoukowskyDisk.wind_speed = 3.0 3.889649963239854 4.684006996752303 5.377830233987229 5.966542092267928 6.44625847394617 6.8138143922059236 7.066784852446481 7.203500851477444 7.22306038896904 7.320786359429763 7.535153078939617 7.864746237154081 8.30739130337076 8.860167873258558 9.519428936578247 10.280824938773394 10.589724312062877 11.13933247768231 12.08928744604103 13.12442240111568 14.237907914913496 15.422397632159566 16.670076738763772 17.972713521001396 19.321713675239476 20.708177009893884 22.122956165519163 23.556716965618207 25.0 -Actuator.JoukowskyDisk.rpm = 5.500000000399841 5.7503730274604 6.924733009169061 7.950466035772244 8.820804709636782 9.530006866155707 10.073393458373337 10.447379427563192 10.649497356086282 10.678413746477254 10.82288967359941 11.139804720841314 11.627068002870239 12.28146474128283 13.098677474494233 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 13.141137992834643 -Actuator.JoukowskyDisk.thrust_coeff = 0.795419507524108 0.8163759621542088 0.8163759621542089 0.8163759621542088 0.8163759621542088 0.8163759621542093 0.8163759621542093 0.816375962154209 0.8163759621542089 0.816375962154209 0.8163759621542089 0.8163759621542088 0.8163759621542088 0.7816497292837605 0.6881024487256834 0.5960935792514858 0.5110705883010171 0.48169007343985104 0.3992418445397665 0.301726241148816 0.23137184415660814 0.17948146484830918 0.1408250546606467 0.11178366795199553 0.08975645587417404 0.07310080594700819 0.060299514659720915 0.0504505646442757 0.04286514300370846 0.03692885272686376 - - -Actuator.JoukowskyDisk.yaw = 240.0 - -Actuator.T00.base_position = 2000.0 2000.0 0.0 -Actuator.T01.base_position = 2500.0 2500.0 0.0 diff --git a/example_case_folders/06_simple_yaw_control/batch_script_amrwind.sh b/example_case_folders/06_simple_yaw_control/batch_script_amrwind.sh deleted file mode 100755 index ec4c7242..00000000 --- a/example_case_folders/06_simple_yaw_control/batch_script_amrwind.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -#SBATCH --job-name=hercules -#SBATCH --time=1:00:00 -#SBATCH --nodes=2 -#SBATCH --ntasks-per-node=36 -#SBATCH --account=ssc -# #SBATCH --qos=high - -# A lot of modules and conda stuff -source /nopt/nrel/apps/anaconda/5.3/etc/profile.d/conda.sh -module use /not/nrel/apps/modules/default/modulefiles -module purge -module load conda -export PREFIX=~/.conda-envs/hercules -export PATH=$PREFIX/bin:$PATH -export FI_PROVIDER_PATH=$PREFIX/lib/libfabric/prov -export LD_LIBRARY_PATH=$PREFIX/lib/libfabric:$PREFIX/lib/release_mt:$LD_LIBRARY_PATH -source activate hercules -module load helics/helics-3.1.0_openmpi -module load netcdf-c/4.7.3/gcc-mpi - -export HELICS_PORT=32000 - -# Set up the helics broker -helics_broker -t zmq -f 2 --loglevel="debug" --local_port=$HELICS_PORT & - -# Need to set this to your hercules folder -python hercules_runscript.py hercules_input_longsim.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file - -# Now go back to scratch folder and launch the job -mpirun -n 72 /home/msinner/emu_moa_dev/amr-wind-2/build/amr_wind amr_input.inp >> logamr 2>&1 \ No newline at end of file diff --git a/example_case_folders/06_simple_yaw_control/batch_script_dummy.sh b/example_case_folders/06_simple_yaw_control/batch_script_dummy.sh deleted file mode 100755 index 10c6d221..00000000 --- a/example_case_folders/06_simple_yaw_control/batch_script_dummy.sh +++ /dev/null @@ -1,19 +0,0 @@ -# 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 up the helics broker -helics_broker -t zmq -f 2 --loglevel="debug" & - -# Need to set this to your emu_python folder -# cd /home/pfleming/emu_python/emu_python -python hercules_runscript.py hercules_input_shortsim.yaml >> loghercules 2>&1 & # Start the controller center and pass in input file - - -python hercules_runscript_dummy_amr.py amr_input.inp >> logdummy 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 \ No newline at end of file diff --git a/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml b/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml deleted file mode 100644 index 750c86a4..00000000 --- a/example_case_folders/06_simple_yaw_control/hercules_input_longsim.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# Input YAML for emy_python - -# Name -name: example_000 - -### -# Describe this emulator setup -description: Just a solar plant - -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 - yaw_simulator_name: yaw_system_0 # can also use "none" (without quotes) - - 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: 36000 - - Agent: ControlCenter - -py_sims: - - solar_farm_0: # The name of py_sim object 1 - - py_sim_type: SimpleSolar - capacity: 50 # MW - efficiency: 0.5 #Fraction - - initial_conditions: - - power: 25 # MW - irradiance: 1000 - -controller: - - controller_type: SimpleYawController # This may not be needed - num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind - initial_conditions: - yaw: 270. # degrees (same for all turbines) (will this work?) - - - - - - - diff --git a/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml b/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml deleted file mode 100644 index 0d45a06a..00000000 --- a/example_case_folders/06_simple_yaw_control/hercules_input_shortsim.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# Input YAML for emy_python - -# Name -name: example_000 - -### -# Describe this emulator setup -description: Just a solar plant - -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 - yaw_simulator_name: yaw_system_0 # can also use "none" (without quotes) - - 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: - - solar_farm_0: # The name of py_sim object 1 - - py_sim_type: SimpleSolar - capacity: 50 # MW - efficiency: 0.5 #Fraction - - initial_conditions: - - power: 25 # MW - irradiance: 1000 - -controller: - - controller_type: SimpleYawController # This may not be needed - num_turbines: 2 # Should match AMR-Wind! Ideally, would come from AMR-wind - initial_conditions: - yaw: 270. # degrees (same for all turbines) (will this work?) - - - - - - - diff --git a/example_case_folders/06_simple_yaw_control/hercules_runscript.py b/example_case_folders/06_simple_yaw_control/hercules_runscript.py deleted file mode 100644 index 87a222fe..00000000 --- a/example_case_folders/06_simple_yaw_control/hercules_runscript.py +++ /dev/null @@ -1,20 +0,0 @@ -from hercules.emulator import Emulator -from hercules.controllers.simple_yaw_controller import SimpleYawController -from hercules.py_sims import PySims -from hercules.utilities import load_yaml - -import sys - - - -input_dict = load_yaml(sys.argv[1]) - -controller = SimpleYawController(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=[[]]) - -print("runscript complete.") diff --git a/example_case_folders/06_simple_yaw_control/hercules_runscript_dummy_amr.py b/example_case_folders/06_simple_yaw_control/hercules_runscript_dummy_amr.py deleted file mode 100644 index 8321fa9a..00000000 --- a/example_case_folders/06_simple_yaw_control/hercules_runscript_dummy_amr.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys -from hercules.dummy_amr_wind import launch_dummy_amr_wind - -# Check that one command line argument was given -if len(sys.argv) != 2: - raise Exception("Usage: python emu_runscript_dummy_amr.py ") - -# # Get the first command line argument -# This is the name of the file to read -amr_input_file = sys.argv[1] -print(f"Running AMR-Wind dummy with input file: {amr_input_file}") - - -launch_dummy_amr_wind(amr_input_file) \ No newline at end of file diff --git a/example_case_folders/06_simple_yaw_control/readme.txt b/example_case_folders/06_simple_yaw_control/readme.txt deleted file mode 100644 index f12eec32..00000000 --- a/example_case_folders/06_simple_yaw_control/readme.txt +++ /dev/null @@ -1,2 +0,0 @@ -This example demonstrates the use of SimpleYawController, either using the -dummy or the real AMR-Wind. \ No newline at end of file diff --git a/hercules/controllers/simple_yaw_controller.py b/hercules/controllers/simple_yaw_controller.py deleted file mode 100644 index 64daa2c9..00000000 --- a/hercules/controllers/simple_yaw_controller.py +++ /dev/null @@ -1,58 +0,0 @@ -from abc import abstractmethod -from hercules.controllers.controller_base import Controller - -class SimpleYawController(Controller): - - def __init__(self, input_dict): - - # Perhaps these should inherit from an abstract class that - # requires a certain input structure. - - self.dt = input_dict["dt"] # Won't be needed here, but generally good to have - self.n_turbines = input_dict["controller"]["num_turbines"] - self.turbines = range(self.n_turbines) - - # Set initial conditions - yaw_IC = input_dict["controller"]["initial_conditions"]["yaw"] - if hasattr(yaw_IC, '__len__'): - if len(yaw_IC) == self.n_turbines: - self.yaw_angles = yaw_IC - else: - raise TypeError("yaw initial condition should be a float or "+\ - "a list of floats of length num_turbines.") - else: - self.yaw_angles = [yaw_IC]*self.n_turbines - - def return_outputs(self, input_dict): - - return input_dict - - - def step(self, input_dict): - - # In this case, the controller doesn't need memory (yaw instantaneously). - # Still, to demonstrate, we can save the current wind directions. - - # Grab name of wind farm (assumes there is only one!) - wf_name = list(input_dict["hercules_comms"]["amr_wind"].keys())[0] - - # How would we do this part, if not saved in hercules_comms? might be though? - self.wind_directions = input_dict["hercules_comms"]\ - ["amr_wind"]\ - [wf_name]\ - ["turbine_wind_directions"] - - # Now, set the amr-wind yaw angles - yaw_angles = self.wind_directions # Yaws instantaneously - input_dict["hercules_comms"]\ - ["amr_wind"]\ - [wf_name]\ - ["turbine_yaw_angles"] = yaw_angles - - return self.return_outputs(input_dict) - - - @abstractmethod - def get_controller_dict(self): - - return {} \ No newline at end of file From 8078d977ff55dbd634712a195f18538f40af0479 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 8 Dec 2023 11:05:39 -0700 Subject: [PATCH 27/38] Emulator controller set up as desired; still tbd where time should come from --- hercules/emulator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hercules/emulator.py b/hercules/emulator.py index d9a7a4d1..9c4a5d55 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -36,7 +36,6 @@ def __init__(self, controller, py_sims, input_dict): self.py_sims = py_sims # Update the input dict components - self.main_dict['controller'] = self.controller.get_controller_dict() self.main_dict['py_sims'] = self.py_sims.get_py_sim_dict() # HELICS dicts @@ -138,8 +137,9 @@ def run(self): continue # Update controller and py sims + # TODO: Update this to be the AMR-Wind time, rather than helics time + self.main_dict['time'] = self.absolute_helics_time self.main_dict = self.controller.step(self.main_dict) - self.main_dict['controller'] = self.controller.get_controller_dict() self.py_sims.step(self.main_dict) self.main_dict['py_sims'] = self.py_sims.get_py_sim_dict() From 03ff765faddb2be23c8bea3e36376f38f13aa932 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 8 Dec 2023 11:09:09 -0700 Subject: [PATCH 28/38] Adding back a pass-through controller in case WHOC not available. --- hercules/controller.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 hercules/controller.py diff --git a/hercules/controller.py b/hercules/controller.py new file mode 100644 index 00000000..7630247a --- /dev/null +++ b/hercules/controller.py @@ -0,0 +1,14 @@ + + + +class Controller(): + + def __init__(self, input_dict): + pass + + def step(self, main_dict): + + # TODO: does there need to be a seperate "controller" dict? + # Might make understanding the log easier? + return main_dict + From a999f891390c82754d45cb8ea887bcb184fb00d3 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 8 Dec 2023 11:19:17 -0700 Subject: [PATCH 29/38] Replacing controllers/control_base with controller_standin. --- .../01_amr_wind_only/hercules_runscript.py | 4 +- .../hercules_runscript.py | 4 +- .../hercules_runscript.py | 4 +- .../hercules_runscript.py | 4 +- .../hercules_runscript.py | 4 +- hercules/controller.py | 14 ------- hercules/controller_standin.py | 21 ++++++++++ hercules/controllers/controller_base.py | 38 ------------------- 8 files changed, 31 insertions(+), 62 deletions(-) delete mode 100644 hercules/controller.py create mode 100644 hercules/controller_standin.py delete mode 100644 hercules/controllers/controller_base.py diff --git a/example_case_folders/01_amr_wind_only/hercules_runscript.py b/example_case_folders/01_amr_wind_only/hercules_runscript.py index b8f73ad2..b8366189 100644 --- a/example_case_folders/01_amr_wind_only/hercules_runscript.py +++ b/example_case_folders/01_amr_wind_only/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controllers.controller_base import Controller +from hercules.controller_standin import ControllerStandin from hercules.py_sims import PySims from hercules.utilities import load_yaml @@ -9,7 +9,7 @@ input_dict = load_yaml(sys.argv[1]) -controller = Controller(input_dict) +controller = ControllerStandin(input_dict) py_sims = PySims(input_dict) diff --git a/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py b/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py index 6113a5e9..f373929f 100644 --- a/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py +++ b/example_case_folders/02_amr_wind_dummy_only/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controllers.controller_base import Controller +from hercules.controller_standin import ControllerStandin from hercules.py_sims import PySims from hercules.utilities import load_yaml @@ -10,7 +10,7 @@ input_dict = load_yaml(sys.argv[1]) -controller = Controller(input_dict) +controller = ControllerStandin(input_dict) py_sims = PySims(input_dict) diff --git a/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py b/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py index b8f73ad2..b8366189 100644 --- a/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py +++ b/example_case_folders/03_amr_wind_and_solar/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controllers.controller_base import Controller +from hercules.controller_standin import ControllerStandin from hercules.py_sims import PySims from hercules.utilities import load_yaml @@ -9,7 +9,7 @@ input_dict = load_yaml(sys.argv[1]) -controller = Controller(input_dict) +controller = ControllerStandin(input_dict) py_sims = PySims(input_dict) diff --git a/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py b/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py index b8f73ad2..b8366189 100644 --- a/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py +++ b/example_case_folders/04_amr_wind_and_solar_and_battery/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controllers.controller_base import Controller +from hercules.controller_standin import ControllerStandin from hercules.py_sims import PySims from hercules.utilities import load_yaml @@ -9,7 +9,7 @@ input_dict = load_yaml(sys.argv[1]) -controller = Controller(input_dict) +controller = ControllerStandin(input_dict) py_sims = PySims(input_dict) diff --git a/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py b/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py index 222457a2..e87f599f 100644 --- a/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py +++ b/example_case_folders/05_amr_wind_dummy_and_electrolzyer/hercules_runscript.py @@ -1,5 +1,5 @@ from hercules.emulator import Emulator -from hercules.controllers.controller_base import Controller +from hercules.controller_standin import ControllerStandin from hercules.py_sims import PySims from hercules.utilities import load_yaml @@ -10,7 +10,7 @@ input_dict = load_yaml(sys.argv[1]) -controller = Controller(input_dict) +controller = ControllerStandin(input_dict) py_sims = PySims(input_dict) diff --git a/hercules/controller.py b/hercules/controller.py deleted file mode 100644 index 7630247a..00000000 --- a/hercules/controller.py +++ /dev/null @@ -1,14 +0,0 @@ - - - -class Controller(): - - def __init__(self, input_dict): - pass - - def step(self, main_dict): - - # TODO: does there need to be a seperate "controller" dict? - # Might make understanding the log easier? - return main_dict - diff --git a/hercules/controller_standin.py b/hercules/controller_standin.py new file mode 100644 index 00000000..2b0c1cce --- /dev/null +++ b/hercules/controller_standin.py @@ -0,0 +1,21 @@ +from abc import abstractmethod + +class ControllerStandin(): + """ + This class is a pass-through stand-in for a plant-level controller. + Actual controllers should be implemented in WHOC + (https://github.com/NREL/wind-hybrid-open-controller). However, this + has been left in to allow users to run Hercules without plant-level + control, if desired. + """ + + def __init__(self, input_dict): + pass + + @abstractmethod + def step(self, main_dict): + + # TODO: does there need to be a seperate "controller" dict? + # Might make understanding the log easier? + return main_dict + diff --git a/hercules/controllers/controller_base.py b/hercules/controllers/controller_base.py deleted file mode 100644 index bcafee01..00000000 --- a/hercules/controllers/controller_base.py +++ /dev/null @@ -1,38 +0,0 @@ -from abc import abstractmethod - -class Controller(): - - def __init__(self, input_dict): - - # Get wind farm information (assumes only one wind farm!) - self.wf_name = list(input_dict["hercules_comms"]["amr_wind"].keys())[0] - - pass - - @abstractmethod - def step(self, input_dict): - - num_turbines = input_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.wf_name]\ - ["num_turbines"] - - # Set turbine yaw angles based on current AMR-Wind wind direction - wd = input_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.wf_name]\ - ["wind_direction"] - input_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.wf_name]\ - ["turbine_yaw_angles"] = num_turbines*[wd] - - return input_dict - - @abstractmethod - def get_controller_dict(self): - # This method may not be needed, if the controller sets the inputs - # to amr-wind and the py_sims directly. Still, could be used for - # recording the controllers internal state for logging purposes. - - return {} \ No newline at end of file From 5d38604b9d0f919ee121e2c28a3cf954c8920563 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 8 Dec 2023 12:03:12 -0700 Subject: [PATCH 30/38] Adding comment about time issue. --- hercules/emulator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hercules/emulator.py b/hercules/emulator.py index 9c4a5d55..9583fbdd 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -137,7 +137,8 @@ def run(self): continue # Update controller and py sims - # TODO: Update this to be the AMR-Wind time, rather than helics time + # TODO: Should 'time' in the main dict be AMR-wind time or + # helics time? Why aren't they the same? self.main_dict['time'] = self.absolute_helics_time self.main_dict = self.controller.step(self.main_dict) self.py_sims.step(self.main_dict) From 23f5c3f8024d81944e852c8d1382470bc399f242 Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 8 Dec 2023 12:37:13 -0700 Subject: [PATCH 31/38] Reinstating nominal yaw control in pass-through. --- hercules/controller_standin.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/hercules/controller_standin.py b/hercules/controller_standin.py index 2b0c1cce..65708c60 100644 --- a/hercules/controller_standin.py +++ b/hercules/controller_standin.py @@ -10,11 +10,28 @@ class ControllerStandin(): """ def __init__(self, input_dict): - pass + + # Get wind farm information (assumes exactly one wind farm) + self.wf_name = list(input_dict["hercules_comms"]["amr_wind"].keys())[0] @abstractmethod def step(self, main_dict): + num_turbines = main_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.wf_name]\ + ["num_turbines"] + + # Set turbine yaw angles based on current AMR-Wind wind direction + wd = main_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.wf_name]\ + ["wind_direction"] + main_dict["hercules_comms"]\ + ["amr_wind"]\ + [self.wf_name]\ + ["turbine_yaw_angles"] = num_turbines*[wd] + # TODO: does there need to be a seperate "controller" dict? # Might make understanding the log easier? return main_dict From 5f64aab2dde2629499440a482d85eb6aaa0631f3 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 12 Dec 2023 12:43:20 -0700 Subject: [PATCH 32/38] Add comments to explain temporary AD connection. --- hercules/controller_standin.py | 4 ++++ hercules/emulator.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/hercules/controller_standin.py b/hercules/controller_standin.py index 65708c60..b4e7f824 100644 --- a/hercules/controller_standin.py +++ b/hercules/controller_standin.py @@ -7,6 +7,10 @@ class ControllerStandin(): (https://github.com/NREL/wind-hybrid-open-controller). However, this has been left in to allow users to run Hercules without plant-level control, if desired. + + This assumes Hercules is running with actuator disk turbine models, and + will be updated (to be simply a pass-through) when the ROSCO/FAST turbine + models are incorporated. """ def __init__(self, input_dict): diff --git a/hercules/emulator.py b/hercules/emulator.py index 9583fbdd..78fa006f 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -311,6 +311,9 @@ def process_periodic_publication(self): # if "turbine_yaw_angles" in self.main_dict["hercules_comms"]\ # ["amr_wind"]\ # [self.amr_wind_names[0]]: + + # Stop-gap to implement controls on actuator disk models, until we + # have ROSCO/FAST connected and can implement controls via ROSCO. yaw_angles = self.main_dict["hercules_comms"]\ ["amr_wind"]\ [self.amr_wind_names[0]]\ From a36307b52c3fcb9e2b434bb42df21569e8ddc3cd Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 12 Dec 2023 12:52:26 -0700 Subject: [PATCH 33/38] Copying in old controller code for possible later use. --- hercules/controller_standin.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hercules/controller_standin.py b/hercules/controller_standin.py index b4e7f824..faa51fa7 100644 --- a/hercules/controller_standin.py +++ b/hercules/controller_standin.py @@ -40,3 +40,19 @@ def step(self, main_dict): # Might make understanding the log easier? return main_dict + +# Can uncomment the below and work on once the ROSCO/FAST connection is +# in place and we are no longer using actuator disks. + +# class Controller(): + +# def __init__(self, input_dict): +# pass + +# def step(self, main_dict): + +# pass + +# def get_controller_dict(self): + +# return {} From 486e5f5c02ab7685beabedb5f3916956ea1237a7 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 12 Dec 2023 12:55:30 -0700 Subject: [PATCH 34/38] Aligning with develop branch. --- .../amr_input.inp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/example_case_folders/05_amr_wind_dummy_and_electrolzyer/amr_input.inp b/example_case_folders/05_amr_wind_dummy_and_electrolzyer/amr_input.inp index 7323a79d..3815a2b2 100644 --- a/example_case_folders/05_amr_wind_dummy_and_electrolzyer/amr_input.inp +++ b/example_case_folders/05_amr_wind_dummy_and_electrolzyer/amr_input.inp @@ -158,7 +158,8 @@ Actuator.UniformCtDisk.num_points_r = 20 Actuator.UniformCtDisk.num_points_t = 5 -Actuator.labels = T00 T01 T02 T03 T04 T05 T06 T07 +Actuator.labels = T00 T01 +# T02 T03 T04 T05 T06 T07 Actuator.JoukowskyDisk.thrust_coeff = 8.1672e-01 7.9044e-01 7.8393e-01 7.8624e-01 7.8824e-01 7.8942e-01 7.8902e-01 7.8740e-01 7.8503e-01 7.8237e-01 7.7955e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.7583e-01 7.6922e-01 7.4270e-01 5.5949e-01 4.6163e-01 3.8786e-01 3.2901e-01 2.8093e-01 2.4114e-01 2.0795e-01 1.8010e-01 1.5663e-01 1.3679e-01 1.1995e-01 1.0562e-01 9.3384e-02 8.2908e-02 7.3910e-02 6.6159e-02 5.9463e-02 5.3662e-02 4.8622e-02 4.4230e-02 Actuator.JoukowskyDisk.wind_speed = 3.0000e+00 3.5495e+00 4.0679e+00 4.5539e+00 5.0064e+00 5.4244e+00 5.8069e+00 6.1530e+00 6.4619e+00 6.7330e+00 6.9655e+00 7.1589e+00 7.3128e+00 7.4269e+00 7.5009e+00 7.5345e+00 7.5412e+00 7.5883e+00 7.6757e+00 7.8031e+00 7.9702e+00 8.1767e+00 8.4221e+00 8.7059e+00 9.0273e+00 9.3856e+00 9.7800e+00 1.0210e+01 1.0659e+01 1.0673e+01 1.1170e+01 1.1699e+01 1.2259e+01 1.2848e+01 1.3465e+01 1.4109e+01 1.4778e+01 1.5471e+01 1.6185e+01 1.6921e+01 1.7674e+01 1.8445e+01 1.9231e+01 2.0030e+01 2.0841e+01 2.1661e+01 2.2489e+01 2.3323e+01 2.4160e+01 2.5000e+01 Actuator.JoukowskyDisk.rpm = 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0000e+00 5.0861e+00 5.1954e+00 5.2765e+00 5.3290e+00 5.3529e+00 5.3577e+00 5.3912e+00 5.4532e+00 5.5437e+00 5.6625e+00 5.8092e+00 5.9836e+00 6.1851e+00 6.4135e+00 6.6681e+00 6.9483e+00 7.2535e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 7.4992e+00 @@ -174,13 +175,7 @@ Actuator.JoukowskyDisk.use_root_correction = true Actuator.JoukowskyDisk.epsilon = 5.0 Actuator.JoukowskyDisk.vortex_core_size = 24.0 -Actuator.JoukowskyDisk.yaw = 240.0 +Actuator.UniformCtDisk.yaw = 240.0 -Actuator.T00.base_position               = 2000    1000    0 -Actuator.T01.base_position               = 1321.17749    1678.82251    0 -Actuator.T02.base_position               = 642.35498    2357.64502    0 -Actuator.T03.base_position               = 2509.116882    2187.939392    0 -Actuator.T04.base_position               = 1830.294372    2866.761902    0 -Actuator.T05.base_position               = 3697.056274    2697.056274    0 -Actuator.T06.base_position               = 3018.233764    3375.878784    0 -Actuator.T07.base_position               = 2339.411254    4054.701294    0 +Actuator.T00.base_position = 2000.0 2000.0 0.0 +Actuator.T01.base_position = 2500.0 2500.0 0.0 From 5eab3a11edb0d4771dd9e76b35e0c9c517aacb03 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 12 Dec 2023 13:33:32 -0700 Subject: [PATCH 35/38] Add KAFKA code back in, commented out, in case needed later. --- hercules/emulator.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hercules/emulator.py b/hercules/emulator.py index 78fa006f..1e5480db 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -56,6 +56,18 @@ def __init__(self, controller, py_sims, input_dict): # TODO: Store other things self.use_dash_frontend = self.helics_config_dict["use_dash_frontend"] + # self.KAFKA = self.helics_config_dict["KAFKA"] + + # # TODO Copied direct from control_center.py but not actually ready yet + # if self.KAFKA: + # from dav_kafka_python.configuration import Configuration + # from dav_kafka_python.producer import PythonProducer + # # Kafka topic : + # self.topic = self.helics_config_dict["KAFKA_TOPIC"] + # print("KAFKA topic", self.topic) + # config = Configuration(env_path='./.env') + # self.python_producer = PythonProducer(config) + # self.python_producer.connect() # AMR wind files # Grab py sim details From 8c789fff8815a827a8e53402931372d5eeb42612 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 12 Dec 2023 14:25:00 -0700 Subject: [PATCH 36/38] Changing helicsport to sidestep bug with not connecting on correct port. --- .../02_amr_wind_dummy_only/amr_input.inp | 2 +- .../02_amr_wind_dummy_only/bash_script.sh | 5 +++-- hercules/dummy_amr_wind.py | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) mode change 100644 => 100755 example_case_folders/02_amr_wind_dummy_only/bash_script.sh diff --git a/example_case_folders/02_amr_wind_dummy_only/amr_input.inp b/example_case_folders/02_amr_wind_dummy_only/amr_input.inp index 3815a2b2..27fe02fd 100755 --- a/example_case_folders/02_amr_wind_dummy_only/amr_input.inp +++ b/example_case_folders/02_amr_wind_dummy_only/amr_input.inp @@ -76,7 +76,7 @@ ABL.stats_output_format = netcdf # Whether to use helics helics.activated = true -helics.broker_port =32000 +helics.broker_port = 32000 #¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨# # ADAPTIVE MESH REFINEMENT # diff --git a/example_case_folders/02_amr_wind_dummy_only/bash_script.sh b/example_case_folders/02_amr_wind_dummy_only/bash_script.sh old mode 100644 new mode 100755 index 953181e9..219fe6f1 --- a/example_case_folders/02_amr_wind_dummy_only/bash_script.sh +++ b/example_case_folders/02_amr_wind_dummy_only/bash_script.sh @@ -5,12 +5,13 @@ conda activate hercules # Set the helics port to use: -export HELICS_PORT=32000 +export HELICS_PORT=23404 #make sure you use the same port number in the amr_input.inp and hercules_input_000.yaml files. # Set up the helics broker -helics_broker -f 2 --consoleloglevel=trace --loglevel=debug --local_port=$HELICS_PORT & +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 diff --git a/hercules/dummy_amr_wind.py b/hercules/dummy_amr_wind.py index e51eb814..30bf9ac8 100644 --- a/hercules/dummy_amr_wind.py +++ b/hercules/dummy_amr_wind.py @@ -81,12 +81,18 @@ def read_amr_wind_input(amr_wind_input): for f in line.split()[-3:-1]]) turbine_locations.append(locations) + # Get the helics port + for line in Lines: + if 'helics.broker_port' in line: + broker_port = int(line.split()[2]) + return_dict = { 'dt':dt, 'num_turbines': num_turbines, 'turbine_labels': turbine_labels, 'rotor_diameter': D, - 'turbine_locations': turbine_locations + 'turbine_locations': turbine_locations, + 'helics_port': broker_port, } @@ -238,6 +244,9 @@ def process_subscription_messages(self, msg): def launch_dummy_amr_wind(amr_input_file): + temp = read_amr_wind_input(amr_input_file) + print(temp["helics_port"]) + config = { "name": "dummy_amr_wind", "gridpack": { @@ -253,7 +262,8 @@ def launch_dummy_amr_wind(amr_input_file): ], "endpoints": [ - ] + ], + "helicsport":temp["helics_port"], }, "publication_interval": 1, From a0d48cada67cf053537bd14abdb9f7eee87c3910 Mon Sep 17 00:00:00 2001 From: Zachary Date: Tue, 12 Dec 2023 14:43:58 -0700 Subject: [PATCH 37/38] put sim_tim_s_amr_wind in emulator main_dict in init --- hercules/emulator.py | 306 +++++++++++++++++++++++++------------------ 1 file changed, 180 insertions(+), 126 deletions(-) diff --git a/hercules/emulator.py b/hercules/emulator.py index 1e5480db..29173c6d 100644 --- a/hercules/emulator.py +++ b/hercules/emulator.py @@ -11,14 +11,11 @@ from SEAS.federate_agent import FederateAgent -LOGFILE = str(dt.datetime.now()).replace( - ":", "_").replace(" ", "_").replace(".", "_") +LOGFILE = str(dt.datetime.now()).replace(":", "_").replace(" ", "_").replace(".", "_") class Emulator(FederateAgent): - def __init__(self, controller, py_sims, input_dict): - # Save the input dict to main dict self.main_dict = input_dict @@ -26,32 +23,32 @@ def __init__(self, controller, py_sims, input_dict): self.main_dict_flat = {} # Initialize the output file - self.output_file = 'hercules_output.csv' + self.output_file = "hercules_output.csv" # Save timt step - self.dt = input_dict['dt'] + self.dt = input_dict["dt"] # Initialize components self.controller = controller self.py_sims = py_sims # Update the input dict components - self.main_dict['py_sims'] = self.py_sims.get_py_sim_dict() + self.main_dict["py_sims"] = self.py_sims.get_py_sim_dict() # HELICS dicts - self.hercules_comms_dict = input_dict['hercules_comms'] - self.hercules_helics_dict = self.hercules_comms_dict['helics'] - self.helics_config_dict = self.hercules_comms_dict['helics']['config'] + self.hercules_comms_dict = input_dict["hercules_comms"] + self.hercules_helics_dict = self.hercules_comms_dict["helics"] + self.helics_config_dict = self.hercules_comms_dict["helics"]["config"] # Write the time step into helics config dict - self.helics_config_dict['helics']['deltat'] = self.dt + self.helics_config_dict["helics"]["deltat"] = self.dt # Initialize the Federate class for HELICS communitation super(Emulator, self).__init__( - name=self.helics_config_dict['name'], - starttime=self.helics_config_dict['starttime'], - endtime=self.helics_config_dict['stoptime'], - config_dict=self.helics_config_dict + name=self.helics_config_dict["name"], + starttime=self.helics_config_dict["starttime"], + endtime=self.helics_config_dict["stoptime"], + config_dict=self.helics_config_dict, ) # TODO: Store other things @@ -71,7 +68,7 @@ def __init__(self, controller, py_sims, input_dict): # AMR wind files # Grab py sim details - self.amr_wind_dict = self.hercules_comms_dict['amr_wind'] + self.amr_wind_dict = self.hercules_comms_dict["amr_wind"] self.n_amr_wind = len(self.amr_wind_dict) self.amr_wind_names = list(self.amr_wind_dict.keys()) @@ -80,35 +77,45 @@ def __init__(self, controller, py_sims, input_dict): for amr_wind_name in self.amr_wind_names: self.amr_wind_dict[amr_wind_name].update( self.read_amr_wind_input( - self.amr_wind_dict[amr_wind_name]['amr_wind_input_file'] + self.amr_wind_dict[amr_wind_name]["amr_wind_input_file"] ) ) # TODO For now, need to assume for simplicity there is one and only # one AMR_Wind simualtion - self.num_turbines = self.amr_wind_dict[self.amr_wind_names[0] - ]['num_turbines'] - self.rotor_diameter = self.amr_wind_dict[self.amr_wind_names[0] - ]['rotor_diameter'] - self.turbine_locations = self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_locations'] - self.turbine_labels = self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_labels'] + self.num_turbines = self.amr_wind_dict[self.amr_wind_names[0]]["num_turbines"] + self.rotor_diameter = self.amr_wind_dict[self.amr_wind_names[0]][ + "rotor_diameter" + ] + self.turbine_locations = self.amr_wind_dict[self.amr_wind_names[0]][ + "turbine_locations" + ] + self.turbine_labels = self.amr_wind_dict[self.amr_wind_names[0]][ + "turbine_labels" + ] # TODO In fugure could cover multiple farms # Initialize the turbine power array self.turbine_power_array = np.zeros(self.num_turbines) - self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_powers'] = np.zeros(self.num_turbines) - self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_wind_directions'] = [0.]*self.num_turbines + self.amr_wind_dict[self.amr_wind_names[0]]["turbine_powers"] = np.zeros( + self.num_turbines + ) + self.amr_wind_dict[self.amr_wind_names[0]]["turbine_wind_directions"] = [ + 0.0 + ] * self.num_turbines # Write to hercules_comms so that controller can access - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['turbine_powers'] = [0.]*self.num_turbines - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['turbine_wind_directions'] = [0.]*self.num_turbines - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['wind_direction'] = 0 + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "turbine_powers" + ] = [0.0] * self.num_turbines + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "turbine_wind_directions" + ] = [0.0] * self.num_turbines + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "wind_direction" + ] = 0 + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "sim_time_s_amr_wind" + ] = 0 self.wind_speed = 0 self.wind_direction = 0 @@ -130,7 +137,6 @@ def __init__(self, controller, py_sims, input_dict): # self.logger.info(" #### Entering main loop #### ") def run(self): - # TODO In future code that doesnt insist on AMRWInd can make this optional print("... waiting for initial connection from AMRWind") # Send initial connection signal to AMRWind @@ -143,18 +149,17 @@ def run(self): # Run simulation till endtime while self.absolute_helics_time < self.endtime: - # Loop till we reach simulation startime. - if (self.absolute_helics_time < self.starttime): + if self.absolute_helics_time < self.starttime: continue # Update controller and py sims - # TODO: Should 'time' in the main dict be AMR-wind time or + # TODO: Should 'time' in the main dict be AMR-wind time or # helics time? Why aren't they the same? - self.main_dict['time'] = self.absolute_helics_time + self.main_dict["time"] = self.absolute_helics_time self.main_dict = self.controller.step(self.main_dict) self.py_sims.step(self.main_dict) - self.main_dict['py_sims'] = self.py_sims.get_py_sim_dict() + self.main_dict["py_sims"] = self.py_sims.get_py_sim_dict() # Send inputs (initiates the AMRWind step) self.send_data_to_amrwind() @@ -164,7 +169,7 @@ def run(self): # Update time to next time step (TODO: check logging for pysims?) self.sync_time_helics(self.absolute_helics_time + self.deltat) - + # Receive outputs back (for next time step) self.receive_amrwind_data() @@ -176,40 +181,59 @@ def run(self): self.first_iteration = False def receive_amrwind_data(self): - # Subscribe to helics messages: incoming_messages = self.helics_connector.get_all_waiting_messages() if incoming_messages != {}: - subscription_value = self.process_subscription_messages( - incoming_messages) + subscription_value = self.process_subscription_messages(incoming_messages) else: print( - "Emulator: Did not receive subscription from AMRWind, setting everyhthing to 0.") - subscription_value = [ - 0, 0, 0] + [0 for t in range(self.num_turbines)] + [0 for t in range(self.num_turbines)] + "Emulator: Did not receive subscription from AMRWind, setting everyhthing to 0." + ) + subscription_value = ( + [0, 0, 0] + + [0 for t in range(self.num_turbines)] + + [0 for t in range(self.num_turbines)] + ) # TODO Parse returns from AMRWind - sim_time_s_amr_wind, wind_speed_amr_wind, wind_direction_amr_wind = subscription_value[ - :3] - turbine_power_array = subscription_value[3:3+self.num_turbines] - turbine_wd_array = subscription_value[3+self.num_turbines:] + ( + sim_time_s_amr_wind, + wind_speed_amr_wind, + wind_direction_amr_wind, + ) = subscription_value[:3] + turbine_power_array = subscription_value[3 : 3 + self.num_turbines] + turbine_wd_array = subscription_value[3 + self.num_turbines :] self.wind_speed = wind_speed_amr_wind self.wind_direction = wind_direction_amr_wind # Assign Py_sim outputs - if self.main_dict['py_sims']: - self.main_dict['py_sims']['inputs']['available_power'] = sum(turbine_power_array) + if self.main_dict["py_sims"]: + self.main_dict["py_sims"]["inputs"]["available_power"] = sum( + turbine_power_array + ) - ## TODO add other parameters that need to be logged to csv here. - # Write turbine power and turbine wind direction to csv logfile. + ## TODO add other parameters that need to be logged to csv here. + # Write turbine power and turbine wind direction to csv logfile. # TODO: should this be in this method, or its own method? aa = [str(xx) for xx in turbine_power_array] xyz = ",".join(aa) bb = [str(xx) for xx in turbine_wd_array] zyx = ",".join(bb) - with open(f'{LOGFILE}.csv', 'a') as filex: - filex.write(str(self.absolute_helics_time) + ',' + str(sim_time_s_amr_wind) + ',' + str( - wind_speed_amr_wind) + ',' + str(wind_direction_amr_wind) + ',' + xyz + ',' + zyx + os.linesep) + with open(f"{LOGFILE}.csv", "a") as filex: + filex.write( + str(self.absolute_helics_time) + + "," + + str(sim_time_s_amr_wind) + + "," + + str(wind_speed_amr_wind) + + "," + + str(wind_direction_amr_wind) + + "," + + xyz + + "," + + zyx + + os.linesep + ) # Printouts related to message received from AMRWind print("=======================================") @@ -223,49 +247,51 @@ def receive_amrwind_data(self): # Store turbine powers back to the dict # TODO hard-coded for now assuming only one AMR-WIND - self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_powers'] = turbine_power_array - self.amr_wind_dict[self.amr_wind_names[0] - ]['turbine_wind_directions'] = turbine_wd_array + self.amr_wind_dict[self.amr_wind_names[0]][ + "turbine_powers" + ] = turbine_power_array + self.amr_wind_dict[self.amr_wind_names[0]][ + "turbine_wind_directions" + ] = turbine_wd_array self.turbine_power_array = turbine_power_array - self.amr_wind_dict[self.amr_wind_names[0] - ]['sim_time_s_amr_wind'] = sim_time_s_amr_wind + self.amr_wind_dict[self.amr_wind_names[0]][ + "sim_time_s_amr_wind" + ] = sim_time_s_amr_wind # TODO: write these to the hercules_comms object, too? - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['turbine_powers'] = turbine_power_array - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['turbine_wind_directions'] = turbine_wd_array - self.main_dict['hercules_comms']['amr_wind'][self.amr_wind_names[0]]\ - ['wind_direction'] = wind_direction_amr_wind - + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "turbine_powers" + ] = turbine_power_array + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "turbine_wind_directions" + ] = turbine_wd_array + self.main_dict["hercules_comms"]["amr_wind"][self.amr_wind_names[0]][ + "wind_direction" + ] = wind_direction_amr_wind return None - def recursive_flatten_main_dict(self, nested_dict, prefix = ''): - + def recursive_flatten_main_dict(self, nested_dict, prefix=""): # Recursively flatten the input dict for k, v in nested_dict.items(): if isinstance(v, dict): - self.recursive_flatten_main_dict(v, prefix + k + '.') + self.recursive_flatten_main_dict(v, prefix + k + ".") else: # If v is a list or np.array, enter each element seperately if isinstance(v, (list, np.ndarray)): for i, vi in enumerate(v): if isinstance(vi, (int, float)): - self.main_dict_flat[prefix + k + '.%03d' % i] = vi + self.main_dict_flat[prefix + k + ".%03d" % i] = vi # If v is a string, int, or float, enter it directly if isinstance(v, (int, float)): self.main_dict_flat[prefix + k] = v - def log_main_dict(self): - # Update the flattened input dict self.recursive_flatten_main_dict(self.main_dict) # Add the current time - self.main_dict_flat['clock_time'] = dt.datetime.now() + self.main_dict_flat["clock_time"] = dt.datetime.now() # The keys and values as two lists keys = list(self.main_dict_flat.keys()) @@ -273,32 +299,34 @@ def log_main_dict(self): # If this is first iteration, write the keys as csv header if self.first_iteration: - with open(self.output_file, 'w') as filex: - filex.write(','.join(keys) + os.linesep) + with open(self.output_file, "w") as filex: + filex.write(",".join(keys) + os.linesep) # Load the csv header and check if it matches the current keys - with open(self.output_file, 'r') as filex: - header = filex.readline().strip().split(',') + with open(self.output_file, "r") as filex: + header = filex.readline().strip().split(",") if header != keys: - print("WARNING: Input dict keys have changed since first iteration.\ - Not writing to csv file.") + print( + "WARNING: Input dict keys have changed since first iteration.\ + Not writing to csv file." + ) return # Append the values to the csv file - with open(self.output_file, 'a') as filex: - filex.write(','.join([str(v) for v in values]) + os.linesep) + with open(self.output_file, "a") as filex: + filex.write(",".join([str(v) for v in values]) + os.linesep) def save_main_dict_as_text(self): - # Echo the dictionary to a seperate file in case it is helpful + # Echo the dictionary to a seperate file in case it is helpful # to see full dictionary in interpreting log - + original_stdout = sys.stdout - with open('main_dict.echo', 'w') as f_i: - sys.stdout = f_i # Change the standard output to the file we created. + with open("main_dict.echo", "w") as f_i: + sys.stdout = f_i # Change the standard output to the file we created. print(self.main_dict) - sys.stdout = original_stdout # Reset the standard output to its original value - - + sys.stdout = ( + original_stdout # Reset the standard output to its original value + ) def parse_input_yaml(self, filename): pass @@ -306,16 +334,22 @@ def parse_input_yaml(self, filename): def process_subscription_messages(self, msg): # process data from HELICS subscription print( - f"{self.name}, {self.absolute_helics_time} subscribed to message {msg}", flush=True) + f"{self.name}, {self.absolute_helics_time} subscribed to message {msg}", + flush=True, + ) try: return list(ast.literal_eval(str(msg["status"]["message"]))) except Exception as e: print(f"Subscription error: {e} , returning 0s ", flush=True) - return [0, 0, 0] + [0 for t in range(self.num_turbines)] + [0 for t in range(self.num_turbines)] + return ( + [0, 0, 0] + + [0 for t in range(self.num_turbines)] + + [0 for t in range(self.num_turbines)] + ) def send_data_to_amrwind(self): self.process_periodic_publication() - + def process_periodic_publication(self): # Periodically publish data to the surrogate @@ -324,19 +358,20 @@ def process_periodic_publication(self): # ["amr_wind"]\ # [self.amr_wind_names[0]]: - # Stop-gap to implement controls on actuator disk models, until we + # Stop-gap to implement controls on actuator disk models, until we # have ROSCO/FAST connected and can implement controls via ROSCO. - yaw_angles = self.main_dict["hercules_comms"]\ - ["amr_wind"]\ - [self.amr_wind_names[0]]\ - ["turbine_yaw_angles"] + yaw_angles = self.main_dict["hercules_comms"]["amr_wind"][ + self.amr_wind_names[0] + ]["turbine_yaw_angles"] # else: # set yaw_angles based on self.wind_direction # yaw_angles = [self.wind_direction]*self.num_turbines # Send timing and yaw information to AMRWind via helics # publish on topic: control - tmp = np.array([self.absolute_helics_time, self.wind_speed, - self.wind_direction] + yaw_angles).tolist() + tmp = np.array( + [self.absolute_helics_time, self.wind_speed, self.wind_direction] + + yaw_angles + ).tolist() self.send_via_helics("control", str(tmp)) @@ -347,7 +382,6 @@ def process_periodic_endpoint(self): pass def read_amr_wind_input(self, amr_wind_input): - # TODO this function is ugly and uncommented # TODO Initialize to empty in case doesn't run @@ -359,7 +393,7 @@ def read_amr_wind_input(self, amr_wind_input): # Find the actuators for line in Lines: - if 'Actuator.labels' in line: + if "Actuator.labels" in line: turbine_labels = line.split()[2:] num_turbines = len(turbine_labels) @@ -368,45 +402,65 @@ def read_amr_wind_input(self, amr_wind_input): aa = [f"power_{i}" for i in range(num_turbines)] xyz = ",".join(aa) - bb = [f"turbine_wd_direction_{i}" for i in range( - num_turbines)] + bb = [f"turbine_wd_direction_{i}" for i in range(num_turbines)] zyx = ",".join(bb) - with open(f'{LOGFILE}.csv', 'a') as filex: - filex.write('helics_time' + ',' + 'AMRwind_time' + ',' + - 'AMRWind_speed' + ',' + 'AMRWind_direction' + ',' + xyz + ',' + zyx + os.linesep) + with open(f"{LOGFILE}.csv", "a") as filex: + filex.write( + "helics_time" + + "," + + "AMRwind_time" + + "," + + "AMRWind_speed" + + "," + + "AMRWind_direction" + + "," + + xyz + + "," + + zyx + + os.linesep + ) # Find the diameter for line in Lines: - if 'rotor_diameter' in line: + if "rotor_diameter" in line: D = float(line.split()[-1]) # Get the turbine locations turbine_locations = [] for label in turbine_labels: for line in Lines: - if 'Actuator.%s.base_position' % label in line: - locations = tuple([float(f) - for f in line.split()[-3:-1]]) + if "Actuator.%s.base_position" % label in line: + locations = tuple([float(f) for f in line.split()[-3:-1]]) turbine_locations.append(locations) return_dict = { - 'num_turbines': num_turbines, - 'turbine_labels': turbine_labels, - 'rotor_diameter': D, - 'turbine_locations': turbine_locations + "num_turbines": num_turbines, + "turbine_labels": turbine_labels, + "rotor_diameter": D, + "turbine_locations": turbine_locations, } print(return_dict) - # Write header for logfile: + # Write header for logfile: aa = [f"power_{i}" for i in range(self.num_turbines)] xyz = ",".join(aa) - bb = [f"turbine_wd_direction_{i}" for i in range( - self.num_turbines)] + bb = [f"turbine_wd_direction_{i}" for i in range(self.num_turbines)] zyx = ",".join(bb) - with open(f'{LOGFILE}.csv', 'a') as filex: - filex.write('helics_time' + ',' + 'AMRwind_time' + ',' + - 'AMRWind_speed' + ',' + 'AMRWind_direction' + ',' + xyz + ',' + zyx + os.linesep) - + with open(f"{LOGFILE}.csv", "a") as filex: + filex.write( + "helics_time" + + "," + + "AMRwind_time" + + "," + + "AMRWind_speed" + + "," + + "AMRWind_direction" + + "," + + xyz + + "," + + zyx + + os.linesep + ) return return_dict From e3861e68ac0c497d2378174ed5fd4a0939ed4d58 Mon Sep 17 00:00:00 2001 From: misha Date: Tue, 12 Dec 2023 14:52:03 -0700 Subject: [PATCH 38/38] Change back to 32000. --- example_case_folders/02_amr_wind_dummy_only/bash_script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_case_folders/02_amr_wind_dummy_only/bash_script.sh b/example_case_folders/02_amr_wind_dummy_only/bash_script.sh index 219fe6f1..594308c1 100755 --- a/example_case_folders/02_amr_wind_dummy_only/bash_script.sh +++ b/example_case_folders/02_amr_wind_dummy_only/bash_script.sh @@ -5,7 +5,7 @@ conda activate hercules # Set the helics port to use: -export HELICS_PORT=23404 +export HELICS_PORT=32000 #make sure you use the same port number in the amr_input.inp and hercules_input_000.yaml files.