Skip to content

Commit

Permalink
Saving current state
Browse files Browse the repository at this point in the history
  • Loading branch information
abhineet-gupta committed Nov 12, 2023
1 parent c04386b commit 61d5287
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 121 deletions.
37 changes: 23 additions & 14 deletions Examples/17b_zeromq_multi_openfast.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import platform
import types
import os
import matplotlib.pyplot as plt
from ROSCO_toolbox.inputs.validation import load_rosco_yaml
Expand All @@ -10,24 +11,35 @@
from ROSCO_toolbox import controller as ROSCO_controller
from ROSCO_toolbox.ofTools.case_gen import CaseLibrary as cl
import numpy as np
import zmq
import multiprocessing as mp
from ROSCO_toolbox.ofTools.case_gen.run_FAST import run_FAST_ROSCO

this_dir = os.path.dirname(os.path.abspath(__file__))
example_out_dir = os.path.join(this_dir,'examples_out')
os.makedirs(example_out_dir,exist_ok=True)

def controller_logic(current_time):
def mycontrollerlogic(self):
id = 0
connections = {}
current_time = connections.measurements[id]['Time']
if current_time <= 10.0:
yaw_setpoints = [0,0]
yaw_setpoint = 0.0
else:
yaw_setpoints = [-10,10]
return yaw_setpoints
if id == 1:
yaw_setpoint = -10.0
else:
yaw_setpoint = 10

def run_zmq():
s = wfc_zmq_server(network_address="tcp://*:5555", timeout=100000.0, verbose=True)
s_process = s.startserver()
s_process.join()
network_address = "tcp://*:5555"
server = wfc_zmq_server(network_address, timeout=100000.0, verbose=False)
server.connect()
server.runserver()
# server.controller_logic = types.MethodType(mycontrollerlogic,server)
# server.controller_logic = mycontrollerlogic

pass

def sim_openfast_1():
# fstfile = '/Users/agupta/Projects/Tools/AG_ROSCO/Examples/IEA-3.4-130-RWT/openfast/IEA-3.4-130-RWT.fst'
Expand All @@ -50,8 +62,6 @@ def sim_openfast_1():
r.save_dir = run_dir
r.run_FAST()



def sim_openfast_2():
r = run_FAST_ROSCO()
r.tuning_yaml = 'NREL5MW.yaml'
Expand All @@ -69,14 +79,13 @@ def sim_openfast_2():
r.controller_params['DISCON']['ZMQ_ID'] = 2
r.run_FAST()


if __name__ == "__main__":
run_zmq()
p1 = mp.Process(target=sim_openfast_1)
p2 = mp.Process(target=sim_openfast_2)
# p2 = mp.Process(target=sim_openfast_2)
p1.start()
p2.start()
# p2.start()
run_zmq()
p1.join()
p2.join()
# p2.join()


2 changes: 1 addition & 1 deletion ROSCO/src/zmq_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ int zmq_client (
char string_to_ssc[char_buffer_size_array];
char string_from_ssc[char_buffer_size_array];

int verbose = 0; // Variable to define verbose
int verbose = 1; // Variable to define verbose

if (verbose == 1) {
printf ("Connecting to ZeroMQ server at %s...\n", zmq_address);
Expand Down
143 changes: 37 additions & 106 deletions ROSCO_toolbox/control_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,99 +281,6 @@ def null_free_dll(*spam): # pragma: no cover
del self.discon


class farm_zmq_server():
def __init__(self, network_addresses=["tcp://*:5555", "tcp://*:5556"],
identifiers=None, timeout=600.0, verbose=False):
"""Python implementation for communicating with multiple instances
of the ROSCO ZeroMQ interface. This is useful for SOWFA and FAST.Farm
simulations in which multiple turbines are running in real time.
Args:
network_addresses (str, optional): List with the network addresses
used to communicate with the desired instances of ROSCO.
identifiers (iteratible, optional): List of strings denoting the
turbine identification string, e.g., ["WTG-01", "WTG-02"].
If left unspecified, will simple name the turbines "0" to
nturbs - 1.
timeout (float, optional): Seconds to wait for a message from
the ZeroMQ server before timing out. Defaults to 600.0.
verbose (bool, optional): Print to console. Defaults to False.
"""
self.network_addresses = network_addresses
self.verbose = verbose
self.nturbs = len(self.network_addresses)

if identifiers is None:
identifiers = ["%d" % i for i in range(self.nturbs)]

# Initialize ZeroMQ servers
self.zmq_servers = [None for _ in range(self.nturbs)]
for ti, address in enumerate(self.network_addresses):
self.zmq_servers[ti] = wfc_zmq_server(
network_address=address,
identifier=identifiers[ti],
timeout=timeout,
verbose=verbose)

def get_measurements(self):
'''
Get measurements from zmq servers
'''
measurements = [None for _ in range(self.nturbs)]
for ti in range(self.nturbs):
measurements[ti] = self.zmq_servers[ti].get_measurements()
return measurements

def send_setpoints(self, genTorques=None, nacelleHeadings=None,
bladePitchAngles=None):

'''
Send setpoints to DLL via zmq server for farm level controls
Parameters:
-----------
genTorques: List
List of generator torques of length self.nturbs
nacelleHeadings: List
List of nacelle headings of length self.nturbs
bladePitchAngles: List
List of blade pitch angles of length self.nturbs
'''
# Default choices if unspecified
if genTorques is None:
genTorques = [0.0] * self.nturbs
if nacelleHeadings is None:
nacelleHeadings = [0.0] * self.nturbs
if bladePitchAngles is None:
bladePitchAngles = [[0.0, 0.0, 0.0]] * self.nturbs

# Send setpoints
for ti in range(self.nturbs):
self.zmq_servers[ti].send_setpoints(
genTorque=genTorques[ti],
nacelleHeading=nacelleHeadings[ti],
bladePitch=bladePitchAngles[ti]
)

class wfc_zmq_connections():
connected = {}

def __init__(self,wfc_interface):
self.wfc_interface = wfc_interface
self.setpoints = {}
self.measurements = {}

def add_unique(self,id):
if id not in wfc_zmq_connections.connected.keys():
wfc_zmq_connections.connected.update({id:True})
self.setpoints.update({id:{s: 0.0 for s in self.wfc_interface['setpoints']}}) # init setpoints with zeros
self.measurements.update({id:{s: 0.0 for s in self.wfc_interface['measurements']}}) # init measurements with zeros

def update_measurements(self,id,measurements):
self.measurements.update({id:measurements})




class wfc_zmq_server():
interface_file = os.path.realpath(os.path.join(os.path.dirname(__file__),'../ROSCO/rosco_registry/wfc_interface.yaml'))
wfc_interface = load_yaml(interface_file)
Expand Down Expand Up @@ -401,24 +308,21 @@ def __init__(self, network_address="tcp://*:5555",timeout=600.0, verbose=False):
self.network_address = network_address
self.timeout = timeout
self.verbose = verbose
self.context = zmq.Context()
self.socket = self.context.socket(zmq.REP)
self.connections = wfc_zmq_connections(self.wfc_interface)
self._connect()
self.socket.setsockopt(zmq.LINGER, 0)


def _connect(self):
def connect(self):
'''
Connect to zmq server
'''
address = self.network_address

# Connect socket
context = zmq.Context()
self.socket = context.socket(zmq.REP)
self.socket.setsockopt(zmq.LINGER, 0)
self.socket.bind(address)


self.socket.bind(self.network_address)
if self.verbose:
print("[%s] Successfully established connection")
# Connect socket


def _disconnect(self):
'''
Expand All @@ -428,7 +332,10 @@ def _disconnect(self):
context = zmq.Context()
context.term()

def startserver(self):
def runserver(self):
# self._connect()
# self.socket.bind(self.network_address)

server_process = mp.Process(target = wfc_zmq_server._runserver,args=(self,))
server_process.start()
return server_process
Expand All @@ -449,13 +356,14 @@ def _runserver(self):
# Add unize
self.connections.add_unique(id)
self.connections.update_measurements(id,measurements)
setpoints = self.controller_logic()
self.check_for_disconnect()

def check_for_disconnect(self):
num_connected = sum(self.connections.connected.values())
if num_connected>0:
if self.verbose:
print('Still connected to ' + num_connected + ' ROSCO clients')
print('Still connected to ', num_connected, ' ROSCO clients')
else:
self._disconnect()

Expand All @@ -466,6 +374,7 @@ def get_measurements(self):
if self.verbose:
print("[%s] Waiting to receive measurements from ROSCO...")

message_in = self.socket.recv_string()
# Initialize a poller for timeouts
poller = zmq.Poller()
poller.register(self.socket, zmq.POLLIN)
Expand Down Expand Up @@ -501,6 +410,7 @@ def send_setpoints(self, id):
self.setpoints (dict) corresponding to ZMQ_* LocalVars, defined by wfc_interface
id (int) id of turbine that setpoints are sent to
'''
# setpoints =
setpoints_id = self.setpoints[id-1]

# Re-order setpoints again (to be sure) as ordered in wfc_interface.yaml
Expand All @@ -518,3 +428,24 @@ def send_setpoints(self, id):

if self.verbose:
print("[%s] Setpoints sent successfully." % self.identifier)

# def controller_logic(self):
# raise NotImplementedError('Controller logic needs to be provided by the user')

class wfc_zmq_connections():
connected = {}

def __init__(self,wfc_interface):
self.wfc_interface = wfc_interface
self.setpoints = {}
self.measurements = {}

def add_unique(self,id):
if id not in wfc_zmq_connections.connected.keys():
wfc_zmq_connections.connected.update({id:True})
self.setpoints.update({id:{s: 0.0 for s in self.wfc_interface['setpoints']}}) # init setpoints with zeros
self.measurements.update({id:{s: 0.0 for s in self.wfc_interface['measurements']}}) # init measurements with zeros

def update_measurements(self,id,measurements):
self.measurements.update({id:measurements})

0 comments on commit 61d5287

Please sign in to comment.