Coupling AeroDyn and ElastoDyn (standalones)

Hello there,

I would like to couple AeroDyn and ElastoDyn + BeamDyn in their standalone mode, but I am not able to find an example in the openFAST repo.
In particular, I’d like to compute the aerodynamic loads given some pitch and torque commands (and wind flow). Then, passing this information to ElastoDyn and BeamDyn to compute the deflections, position changes, etc, then passing this information back to AeroDyn for the computation of the aerodynamic loads at the next time step.

Is it possible, in the first place?
I am a bit puzzled about the coupling of different input files for the different modules.

Many thanks for your help,
Lorenzo Schena

Dear Lorenzo,

You mention standalone AeroDyn, ElastoDyn, and BeamDyn, but then you mention two-way aero-elastic coupling. The latter is available in OpenFAST, not through the standalone modules.

The source code and compile scripts for the standalone AeroDyn and BeamDyn drivers are available in the OpenFAST GitHub repository. NREL has not developed a standalone driver for ElastoDyn.

Best regards,

Dear Jason,

Thanks for the prompt response and clarification.
Then, to use an external controller (without a DLL) while exploiting the two-ways coupling, one should modify the ServoDyn file each time the commands change and then re-launch the simulation in fast?

I am sorry for these questions but I am struggling to find them someplace else.

Kind regards,

Dear @Lorenzo.Schena,

If you are simulating with a user-defined controller that does not make use of an input file, then you must recompile that controller every time you change it. If you are using a Fortran subroutine linked within OpenFAST (rather than a controller compiled as a dynamic library that OpenFAST would call or Simulink), then you when you recompile the controller, you must also recompile OpenFAST.

Best regards,

Dear Jason,

Thanks for the response. Now I am reading carefully the ROSCO documentation, as I am not really able to find a comprehensive guide on ServoDyn, for instance in your readthedocs. If, by chance, you have something more comprehensive in that regard please don’t hesitate to share it with me, it would be much appreciated.

What I’d like to do is to set up a controller in Python and then use it in OpenFAST, exploiting the aeroelastic capabilities (two-way coupling) of the solver. Up to now, to my limited understanding, it appears I have three choices to do so, namely:

  1. I use the ServoDyn standalone mode, handling the .txt files, but this requires a call to such a configuration each time I want to change the command acting on the turbine, which looks not very computationally efficient
  2. Modify the BldPtchCntrl.f90 routine in Fortran to call a Python script at run time. However, this also looks pretty challenging, especially if the Python script to be run is somehow dynamic - i.e. requires an allocation in memory to do its computations (e.g. MPC controller).
  3. Employ a ROSCO package, which could allow me to optimize a controller. The problem is, that this enforces a structure on the controller itself, PI/PID, which I would like to avoid.

My understanding of the OpenFAST suite is still in its first steps, thus I assume that I might be missing something.
If you have some suggestions on how to proceed with (1), (2), or (3); or if you faced/heard a similar problem in the past, I am all ears.

Many thanks again for your help,

Dear @Lorenzo.Schena,

The ServoDyn documentation in OpenFAST readthedocs is limited. There is some description–though a bit outdated–of the various features of ServoDyn in the old FAST User’s Guide: https://openfast.readthedocs.io/en/main/_downloads/d8bd014121d6505cb25cf49bee5eaa80/Old_FAST6_UsersGuide.pdf. But ServoDyn is also quite a simple module–mostly just selecting between various control features and interfacing to user-defined control options–so, it may be easiest to just review the source code.

If you want to implement your own user-defined controller in source code, really options 3 (SUBROUTINE PitchCntrl for pitch control) or 5 (Bladed-style dynamic library) are the two options. But neither of these supports a direct interface to python, so, you’ll have to develop an interface between Fortran and python to enable that–similar to the existing wrappers that have recently been implemented in some of the OpenFAST modules so that python code can call the OpenFAST modules directly.

Regardless, using ROSCO is likely the most straightforward option. ROSCO already has python scripts to work with and you can customize the ROSCO source code if you need control features that are not currently supported.

Best regards,

Dear @Jason.Jonkman,

Many thanks for the insights.
I have a last question on this subject. Is it possible to control the wind turbine(s) model “on-line”?
Where by that I mean essentially launching one OpenFast simulation, taking as an input the wind flow and the wind turbine model and then make it point to some config .txt file where I can, for instance, decide to change the pitch and/or torque while also output some intermediate results of the simulation? A similar approach to run single modules in a standalone way, such as AeroDyn, for instance.

Kind regards,

Dear @Lorenzo.Schena,

OpenFAST is not really set-up to make changes to the simulation during run time. I’m sure it is possible to change OpenFAST to achieve this functionality, but it will require a source code change.

Best regards,

Dear @Jason.Jonkman ,

I see. I don’t know Fortran very well, so my next question might be a silly one.
As a first step towards achieving this real-time capability, I am modifying the UserSubs.f90 routine, as follows:

 
module UserSubs
contains
   
!=======================================================================
SUBROUTINE PitchCntrl ( BlPitch, ElecPwr, LSS_Spd, TwrAccel, NumBl, ZTime, DT, DirRoot, BlPitchCom )


!   ! This is a dummy routine for holding the place of a user-specified
!   ! blade pitch control model (either independent or rotor-collective).
!   ! Modify this code to create your own model.


USE                             Precision
!use forpy_mod
!implicit none 
IMPLICIT                        NONE


!   ! Passed variables:

INTEGER(4), INTENT(IN )      :: NumBl                                           ! Number of blades, (-).

REAL(ReKi), INTENT(IN )      :: BlPitch   (NumBl)                               ! Current values of the blade pitch angles, rad.
REAL(DbKi), INTENT(IN )      :: DT                                              ! Integration time step, sec.
REAL(ReKi), INTENT(IN )      :: ElecPwr                                         ! Electrical power, watts.
REAL(ReKi), INTENT(IN )      :: LSS_Spd                                         ! LSS speed (rad/s)
REAL(ReKi), INTENT(OUT)      :: BlPitchCom(NumBl)                               ! Commanded blade pitch angles (demand pitch angles), rad.
REAL(ReKi), INTENT(IN )      :: TwrAccel                                        ! Tower Acceleration, m/s^2.
REAL(DbKi), INTENT(IN )      :: ZTime                                           ! Current simulation time, sec.
!
CHARACTER(1024), INTENT(IN ) :: DirRoot                                         ! The name of the root file including the full path to the current working directory.  This may be useful if you want this routine to write a permanent record of what it does to be stored with the simulation results: the results should be stored in a file whose name (including path) is generated by appending any suitable extension to DirRoot.
!

BlPitchCom(1) = 1.0
BlPitchCom(2) = 2.0
BlPitchCom(3) = 3.0

RETURN 
END SUBROUTINE PitchCntrl

So basically I should see a constant command applied to the blades, respectively of one, two and three degrees.

As a next step, I proceed with re-compiling a new build (I am compiling in Linux/Ubuntu).

cmake ..

runs fine, but when I launch:

sudo make install

I get the following error:

   23 |    USE ServoDyn_Types
      |       2
......
   41 |    INTEGER(IntKi), PARAMETER      :: OutStrLenM1 = ChanLen - 1
      |                                                1
Error: Symbol ‘outstrlenm1’ at (1) conflicts with symbol from module ‘nwtc_base’, use-associated at (2)
/home/yoda/Workspace/OpenFAST/modules/servodyn/src/ServoDyn_IO.f90:1377:115:

 1377 |      InputFileData%NumOuts, 'OutList', "List of user-requested output channels", ErrStat2, ErrMsg2, UnEcho )
      |                                                                                                            1

Error: Type mismatch in argument ‘errstat’ at (1); passed CHARACTER(7) to INTEGER(4)
make[2]: *** [modules/servodyn/CMakeFiles/servodynlib.dir/build.make:140: modules/servodyn/CMakeFiles/servodynlib.dir/src/ServoDyn_IO.f90.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:944: modules/servodyn/CMakeFiles/servodynlib.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

As the “code” should not contain any errors - hopefully, since I am just assigning some values - I must doing something wrong in the procedure.

Maybe you can easily spot what is wrong?

Also, in OpenFAST/modules/servodyn/CMakeLists.txt I have commeted out PitchCntrl.f90 as the pitch command will be provided now from the UserSubs.f90 routine.

[...]
set(SrvD_SOURCES
  src/BladedInterface.f90
  src/UserSubs.f90
  #src/PitchCntrl_ACH.f90 commenting since we are using UserSubs.f90 (custom defined)
  src/StrucCtrl.f90
  src/UserVSCont_KP.f90
  src/ServoDyn.f90
  src/ServoDyn_IO.f90
  src/StrucCtrl_Types.f90
  src/ServoDyn_Types.f90
)

Many thanks for you precious help,

Dear @Lorenzo.Schena,

I’m not quite understanding. Did you change anything in the source code other than PitchCntrl() in UserSubs.f90?

Best regards,

Hello @Jason.Jonkman ,

No, in this example I’ve just imposed a constant blade pitch as written above in UserSubs.f90. Then I launched again cmake and make install commenting out the Pitch Control routine from the Cmake list and the compiler threw this error.

Many thanks,

Dear @Lorenzo.Schena,

Were you able to compile OpenFAST using cmake before making any changes to the source code?

Best regards,

Dear @Jason.Jonkman,

Yes, I compiled successfully before such modification, without any errors.

Aside modifying the UserSubs.f90 file is any other step required from my side to plug this in openfast?

Thanks,

Blockquote
Also, in OpenFAST/modules/servodyn/CMakeLists.txt I have commeted out PitchCntrl.f90 as the pitch command will be provided now from the UserSubs.f90 routine.

Hi @Lorenzo.Schena did you also remove the USE PitchCntrl_ACH line in ServoDyn.f90?

Dear @Lorenzo.Schena,

Comenting out the PitchCntrl_ACH.f90 file in CMakeLists.txt will also require removing the dependency on the PitchCntrl module in ServoDyn.90 (line 30). Otherwise cmake/make will not be able to build due to missing dependencies.

However, it appears you have some additional issues going on with your build. It appears you have different versions of the _Types.f90 files than the rest of the source code. Can you clean your build directory and rerun cmake and make again?

Also, I’m a bit surprised you need to use sudo – I don’t believe this should be necessary (I haven’t checked the install step on linux recently though).

Regards,
Andy

Sudo may be required since he’s doing make install if CMAKE_INSTALL_PREFIX is set to /usr/local.

1 Like

Dears @Jason.Jonkman, @Andy.Platt, and @Rafael.Mudafort thank you for the follow-up.

With a clean build of OpenFAST, I am indeed able to recompile successfully.

Many thanks all for the interesting discussion, for now, we can close.

If interested, we could talk about this real-time communication fast capability once I have developed the thought deeper.

Best,
Lorenzo

1 Like