NREL / ROSCO

A Reference Open Source Controller for Wind Turbines
https://rosco.readthedocs.io/en/latest/
Apache License 2.0
106 stars 92 forks source link

Feature: Change wfc_controller from function to a class to allow access to all turbine signals and memory #354

Closed Bartdoekemeijer closed 3 months ago

Bartdoekemeijer commented 3 months ago

Description and Purpose

See #353. Currently, the ZeroMQ wfc_controller() function is called once for each turbine, and the only available information passed to this function are that turbine's measurements at the current time. For the wind farm control logic, there is no way to access measurements from the past (e.g., the last 10 seconds) and there is no way to access measurements from the other turbines.

To resolve this, the wfc_controller function can be turned into a Python class. That allows it to have internal variables, e.g. for memory, and thereby also allows measurements from different turbines to be accessed.

Type of change

What types of change is it? Select the appropriate type(s) that describe this PR

TODO Items General:

TODO Items API Change:

Github issues addressed, if one exists

Issue #353.

Examples/Testing, if applicable

Examples 17a and 17b have been updated and work as expected.

Bartdoekemeijer commented 3 months ago

Usage of the proposed changes are demonstrated in a FAST.Farm simulation here: https://github.com/SUDOCO-EU/windfarmcontroller_zeromq_demo

abhineet-gupta commented 3 months ago

Hi Bart, Thanks for testing this feature and I'm glad to see that it is working well for your application.

We did consider the fact that we are only sending the measurement of a single turbine for a single time-step. We were envisioning that ROSCO communicate the lowest level of information needed for a controller and the users can write their own wrapper as they see fit (e.g. to store into memory the past measurements, low pass filters etc.)

If I understand correctly, the approach you have suggested basically does that but in a more user friendly way than using a wfc_controller as a class rather than a single function.

Btw, am I correct in assuming that with with this approach, the zeromq still communicates the setpoints and measurements of a single turbine 'for a single time step' at a time?

Bartdoekemeijer commented 3 months ago

If I understand correctly, the approach you have suggested basically does that but in a more user friendly way than using a wfc_controller as a class rather than a single function.

Yes indeed.

Btw, am I correct in assuming that with with this approach, the zeromq still communicates the setpoints and measurements of a single turbine 'for a single time step' at a time?

Yes indeed, the ZeroMQ communication has not changed in this PR. The only thing that changed is how the setpoints are prepared in Python. Basically meaning if you are calling the function to calculate the setpoints of T2, you also have access to the measurements of T1.

We did consider the fact that we are only sending the measurement of a single turbine for a single time-step. We were envisioning that ROSCO communicate the lowest level of information needed for a controller and the users can write their own wrapper as they see fit (e.g. to store into memory the past measurements, low pass filters etc.)

In my understanding, it seems very difficult to implement a lowpass filter or wind farm control where you need access to all turbine signals simultaneously in the current wfc_zmq_server class. Since you can only assign a simple Python function for wfc_controller, and the function only receives the local time, turbine ID and that turbine's measurements, how are you supposed to pull in measurements from the other turbines into the function? And how are you supposed to conserve the states of your lowpass filter between multiple calls of wfc_controller()?

Please take a look at the example repository that I mentioned: https://github.com/SUDOCO-EU/windfarmcontroller_zeromq_demo/blob/main/wind_farm_controller/wake_steering_controller.py

This shows a typical wake steering controller implementation that uses FLORIS in the loop for real-time optimization.