Close loop simulation using python

Dear all,
Up to now i’ve been using the Simulink FAST_SFunc to perform software in the loop simulations. I was wondering if this can also be done using python. I see the OpenFAST.py and openfast_library.py and it looks to me that the FastLibAPI class is intended to start and run a whole simulation, but i would like to call the simulation in steps so that i can interact with it on every step.
Is that possible?
If so, is there any example available?

Thanks in advance and kind regards
Roberto

1 Like

Hi Roberto,

Yes, you can use the openfast_library module to do this. The FastLibAPI class provides these methods (note there are more that I’ve left out because they’re meant to be used only internally to the class):

class FastLibAPI:
    func fast_init
    func fast_sim
    func fast_deinit
    func fast_run
    func total_time_steps
    func total_output_steps
    func get_hub_position

fast_init creates all the data structures that OpenFAST needs and sizes them appropriately, fast_sim does the time stepping, and fast_deinit does clean up and frees memory. fast_run simply calls init, sim, and deinit.

In your case, you could either modify this Python module or create your own wrapper to it to split the steps that are currently included in fast_sim. Instead of looping through each time step, you could add a function to run FastLibAPI.FAST_Update() and handle moving forward in time by your calling code.

Let me know how this works for you.

Thanks,
Rafael

Hello Rafael!
It looks afordable, I will post an example to help others if i succeed.

Kind regards
Roberto

1 Like

Hello Rafael and thanks again for your help,

yesterday i was struggling with the openfast compilation and more specifically with the openfast library compilation. I compiled the whole thing using CygWin64 as specified in docs, so i get a dll and i am able to create the FastLibAPI object:

library_path = "X:/Wind/OpenFAST/glue-codes/python/cygopenfastlib.dll"
input_file_name =  "X:/Wind/OpenFAST/reg_tests/r-test/glue-codes/openfast/AOC_YFix_WSt/AOC_YFix_WSt.fst"

openfastlib = openfast_library.FastLibAPI(library_path, input_file_name)

After this, i call the fast_init() procedure but it fails here, it may happen that it gets stuck and never ends or it just returns without any error. I have added some print messages for debugging purporses in the openfast_library.py fast_init function and i realized that the issue comes in the starting point with allocation of the turbines:

        self.FAST_AllocateTurbines(
                byref(self.n_turbines),
                byref(_error_status),
                _error_message
            )

Do you have any clue why this may be happening? I’m thinking on adding some debug messages in the library code but i’m not big expert in Fortran so any help will be really helpful.

Kind regards

Roberto

Hi Roberto,

Thanks for trying it out and reporting back. It sounds like your script is hanging as soon as Python tries to interact with the dll. Can you verify that the dll file has the appropriate permissions?

Unfortunately, I don’t have access to a Windows computer to recreate your setup, and I’m also not very familiar with Windows systems, in general, as I use Mac and Linux. If you’re comfortable with Linux, then you could use the Windows Subsystem for Linux (WSL), and that will be closer to my system so we could debug this together.

If you wanted to see what’s happening in the library itself, I would start with adding a print statement to this line just to verify that it’s running at all. Maybe print the value of nTurbines, something like:

print *, "ROBERTOROBERTOROBERTOROBERTO"
! or
print *, "### nTurbines: ", nTurbines

The FAST_Library interface itself (the Fortran) is fairly robust and the Python interface has not been used extensively, so it’s more likely there’s an issue on the Python side. You could also add a print statement above this line to check that value of the error status and error message:

print("error status: ", _error_status)
print("error message: ", _error_message)

Please let me know what you find.

Thanks,
Rafael

Hello Rafael and thanks for the advise on using WSL… i just recompiled there (probably 10 times faster than cygwin and probably 100 times faster than VS :wink: ) and i got it working on the first try.

Once i connect all the pieces i am needing for my project i will dedicate some time to try find out what happens with the windows dll by adding some debug messages to the code.

Thanks again and regards

Roberto

Hello again,

As mentioned in the previous message… i got it working! And I was able to run it step by step and read the outputs on every step.

But in order to modify the inputs, I’m not sure which kind of generator and pitch system should i define in the servodyn file. When using the simulink library i was setting the “4: user-defined from Simulink/Labview” option, but this is not working here and only option 0 allows me run the simulation.

I’m not sure if i’m in the correct direction, but i have started by modifying ServoDyn.f90 so that i avoid the error if i am using ControlMode_EXTERN (4: user-defined from simulink) and at least i can run simulation, but i don’t see that has any effect on the simulation…

In any case… i found that at some point in the code:

CASE ( ControlMode_EXTERN )                             ! User-defined variable-speed control from Simulink or LabVIEW.
    GenTrq  = u%ExternalGenTrq
    ElecPwr = u%ExternalElecPwr

but i do not see any other reference to ExternalGenTrq apart from the initial initialization…

   u%ExternalGenTrq = 0.
   u%ExternalElecPwr = 0.

Any advice here?

thanks again and Kind regards

Hi Roberto,

I would avoid trying to modify the code to support your Simulink controller when you’re using any pitch control that isn’t option 4. Instead, you can use option 5 and your own Bladed-style controller function or the ROSCO controller. For this mode, the controller must be compiled into a shared library (.dll for windows, .so for linux) and the path to the library passed in the DLL_FileName input. You can see this case as an example.

I don’t know the details of how OpenFAST is coupled to Simulink, but I think it would be difficult to drive the simulation from Python and call a Simulink function. If you need to use your controller in Simulink, then maybe someone else can provide some insight there.

For the NREL 5MW turbine, there are some controller libraries included with the OpenFAST repository. Let me know if you’re using that turbine, and we can help you get those libraries.

I hope it helps!

Rafael

Hello Rafael,

I don’t want to link it with simulink, that was my first option and it was quite straightforward to implement thanks to some another forum message. What i want now is to remove matlab/simulink for simplicity of the project.

What i’m trying to do is to implement the controller directly in python, in such a way that i am able to read the outputs after each iteration and calculate new inputs (torque and pitch) to be injected for the next iteration.

Kind regards

Roberto