Imposed force does not affect the motion of a turbine

Hello!

I am trying to run a simulation where the force imposed on a wind turbine is taken from a pipe created in Windows that is read by the libdiscon.dll controller.
I modified the FORTRAN code in the background to show in the console what the inputs are:

IF (PipeOpen) THEN
READ(pipe_unit, *, IOSTAT=ios) t, fx, fy, fz, mx, my, mz

IF (ios == 0) THEN
    ! Got fresh data
    LocalVar%Time         = t
    LocalVar%StC_Force_X  = fx
    LocalVar%StC_Force_Y  = fy
    LocalVar%StC_Force_Z  = fz
    LocalVar%StC_Moment_X = mx
    LocalVar%StC_Moment_Y = my
    LocalVar%StC_Moment_Z = mz
    
    IF (MOD(NINT(t*10),100) == 0) THEN
       WRITE(*,'(F10.4, 1X, 6(ES12.4,1X))') t, fx, fy, fz, mx, my, mz
    END IF

ELSE
    ! Reader saw EOF or broken pipe. Close and mark for reconnect.
    WRITE(*,*) 'Pipe read failed, IOSTAT=', ios, ' — will attempt to reconnect.'
    CLOSE(pipe_unit, IOSTAT=ios)
    PipeOpen = .FALSE.
    ! Keep last valid values to avoid NaNs; next call will reconnect.
END IF

END IF

The line

WRITE(*,‘(F10.4, 1X, 6(ES12.4,1X))’) t, fx, fy, fz, mx, my, mz

is the one that, during simulations, shows me that what the simulation sees is the same as what I send in the background (it is printed in the console).

My problem is that the results do not change when I change the force applied. My ServoDyn is like this:

------- SERVODYN v1.05.* INPUT FILE ------------------------------------------------------------------ SIMULATION CONTROL --------------------------------------False Echo - Echo input data to .ech (flag)“default” DT - Communication interval for controllers (s) (or “default”)---------------------- PITCH CONTROL -------------------------------------------5 PCMode - Pitch control mode {0: none, 3: user-defined from routine PitchCntrl, 4: user-defined from Simulink/Labview, 5: user-defined from Bladed-style DLL} (switch)0.0 TPCOn - Time to enable active pitch control (s) [unused when PCMode=0]9999.9 TPitManS(1) - Time to start override pitch maneuver for blade 1 and end standard pitch control (s)9999.9 TPitManS(2) - Time to start override pitch maneuver for blade 2 and end standard pitch control (s)9999.9 TPitManS(3) - Time to start override pitch maneuver for blade 3 and end standard pitch control (s) [unused for 2 blades]2.0 PitManRat(1) - Pitch rate at which override pitch maneuver heads toward final pitch angle for blade 1 (deg/s)2.0 PitManRat(2) - Pitch rate at which override pitch maneuver heads toward final pitch angle for blade 2 (deg/s)2.0 PitManRat(3) - Pitch rate at which override pitch maneuver heads toward final pitch angle for blade 3 (deg/s) [unused for 2 blades]0.0 BlPitchF(1) - Blade 1 final pitch for pitch maneuvers (degrees)0.0 BlPitchF(2) - Blade 2 final pitch for pitch maneuvers (degrees)0.0 BlPitchF(3) - Blade 3 final pitch for pitch maneuvers (degrees) [unused for 2 blades]---------------------- GENERATOR AND TORQUE CONTROL ----------------------------5 VSContrl - Variable-speed control mode {0: none, 1: simple VS, 3: user-defined from routine UserVSCont, 4: user-defined from Simulink/Labview, 5: user-defined from Bladed-style DLL} (switch)1 GenModel - Generator model {1: simple, 2: Thevenin, 3: user-defined from routine UserGen} (switch) [used only when VSContrl=0]95.756 GenEff - Generator efficiency [ignored by the Thevenin and user-defined generator models] (%)True GenTiStr - Method to start the generator {T: timed using TimGenOn, F: generator speed using SpdGenOn} (flag)True GenTiStp - Method to stop the generator {T: timed using TimGenOf, F: when generator power = 0} (flag)9999.9 SpdGenOn - Generator speed to turn on the generator for a startup (HSS speed) (rpm) [used only when GenTiStr=False]0.0 TimGenOn - Time to turn on the generator for a startup (s) [used only when GenTiStr=True]9999.9 TimGenOf - Time to turn off the generator (s) [used only when GenTiStp=True]---------------------- SIMPLE VARIABLE-SPEED TORQUE CONTROL --------------------7.559987120819503 VS_RtGnSp - Rated generator speed for simple variable-speed generator control (HSS side) (rpm) [used only when VSContrl=1]19624046.66639 VS_RtTq - Rated generator torque/constant generator torque in Region 3 for simple variable-speed generator control (HSS side) (N-m) [used only when VSContrl=1]343357.4355671095 VS_Rgn2K - Generator torque constant in Region 2 for simple variable-speed generator control (HSS side) (N-m/rpm^2) [used only when VSContrl=1]2. VS_SlPc - Rated generator slip percentage in Region 2 1/2 for simple variable-speed generator control (%) [used only when VSContrl=1]---------------------- SIMPLE INDUCTION GENERATOR ------------------------------9999.9 SIG_SlPc - Rated generator slip percentage (%) [used only when VSContrl=0 and GenModel=1]9999.9 SIG_SySp - Synchronous (zero-torque) generator speed (rpm) [used only when VSContrl=0 and GenModel=1]9999.9 SIG_RtTq - Rated torque (N-m) [used only when VSContrl=0 and GenModel=1]9999.9 SIG_PORt - Pull-out ratio (Tpullout/Trated) (-) [used only when VSContrl=0 and GenModel=1]---------------------- THEVENIN-EQUIVALENT INDUCTION GENERATOR -----------------9999.9 TEC_Freq - Line frequency [50 or 60] (Hz) [used only when VSContrl=0 and GenModel=2]100 TEC_NPol - Number of poles [even integer > 0] (-) [used only when VSContrl=0 and GenModel=2]9999.9 TEC_SRes - Stator resistance (ohms) [used only when VSContrl=0 and GenModel=2]9999.9 TEC_RRes - Rotor resistance (ohms) [used only when VSContrl=0 and GenModel=2]9999.9 TEC_VLL - Line-to-line RMS voltage (volts) [used only when VSContrl=0 and GenModel=2]9999.9 TEC_SLR - Stator leakage reactance (ohms) [used only when VSContrl=0 and GenModel=2]9999.9 TEC_RLR - Rotor leakage reactance (ohms) [used only when VSContrl=0 and GenModel=2]9999.9 TEC_MR - Magnetizing reactance (ohms) [used only when VSContrl=0 and GenModel=2]---------------------- HIGH-SPEED SHAFT BRAKE ----------------------------------0 HSSBrMode - HSS brake model {0: none, 1: simple, 3: user-defined from routine UserHSSBr, 4: user-defined from Simulink/Labview, 5: user-defined from Bladed-style DLL} (switch)9999.9 THSSBrDp - Time to initiate deployment of the HSS brake (s)9999.9 HSSBrDT - Time for HSS-brake to reach full deployment once initiated (sec) [used only when HSSBrMode=1]9999.9 HSSBrTqF - Fully deployed HSS-brake torque (N-m)---------------------- NACELLE-YAW CONTROL -------------------------------------0 YCMode - Yaw control mode {0: none, 3: user-defined from routine UserYawCont, 4: user-defined from Simulink/Labview, 5: user-defined from Bladed-style DLL} (switch)9999.9 TYCOn - Time to enable active yaw control (s) [unused when YCMode=0]0.0 YawNeut - Neutral yaw position–yaw spring force is zero at this yaw (degrees)6009291301.0 YawSpr - Nacelle-yaw spring constant (N-m/rad)4811254.0 YawDamp - Nacelle-yaw damping constant (N-m/(rad/s))9999.9 TYawManS - Time to start override yaw maneuver and end standard yaw control (s)0.25 YawManRat - Yaw maneuver rate (in absolute value) (deg/s)0.0 NacYawF - Final yaw angle for override yaw maneuvers (degrees)---------------------- Aerodynamic Flow Control -------------------------------------0 AfCmode - Airfoil control mode {0- none, 1- cosine wave cycle, 4- user-defined from Simulink/Labview, 5- user-defined from Bladed-style DLL}0.0 AfC_Mean - Mean level for sinusoidal cycling or steady value (-) [used only with AfCmode==1]0.0 AfC_Amp - Amplitude for for cosine cycling of flap signal (AfC = AfC_Amp*cos(Azimuth+phase)+AfC_mean) (-) [used only with AfCmode==1]0.0 AfC_phase - Phase relative to the blade azimuth (0 is vertical) for for cosine cycling of flap signal (deg) [used only with AfCmode==1]---------------------- STRUCTURAL CONTROL ---------------------------------------0 NumBStC - Number of blade structural controllers (integer)“unused” BStCfiles - Name of the file for blade tuned mass damper (quoted string) [unused when CompNTMD is false]0 NumNStC - Number of nacelle structural controllers (integer)“unused” NStCfiles - Name of the file for nacelle tuned mass damper (quoted string) [unused when CompNTMD is false]1 NumTStC - Number of tower structural controllers (integer)“STC_Ctrl_Timestamp_5_Force_sin_Tower_Top_Definition.dat” TStCfiles - Name of the file for tower tuned mass damper (quoted string) [unused when CompNTMD is false]0 NumSStC - Number of sbustructure structural controllers (integer)“unsutugiorasddasdasded” SStCfiles - Name of the file for sbustructure tuned mass damper (quoted string) [unused when CompNTMD is false]---------------------- CABLE CONTROL ----------------------------------------0 CCmode - Cable control mode {0- none, 4- user-defined from Simulink/Labview, 5- user-defineAfC_phased from Bladed-style DLL}---------------------- BLADED INTERFACE ---------------------------------------- [used only with Bladed Interface]“ServoData/libdiscon_pipe.dll” DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface]“BaseLine_Case_DoF_Surge_True_DISCON.IN” DLL_InFile - Name of input file sent to the DLL (-) [used only with Bladed Interface]“DISCON” DLL_ProcName - Name of procedure in DLL to be called (-) [case sensitive; used only with DLL Interface]“default” DLL_DT - Communication interval for dynamic library (s) (or “default”) [used only with Bladed Interface]False DLL_Ramp - Whether a linear ramp should be used between DLL_DT time steps [introduces time shift when true] (flag) [used only with Bladed Interface]9999.9 BPCutoff - Cuttoff frequency for low-pass filter on blade pitch from DLL (Hz) [used only with Bladed Interface]0.0 NacYaw_North - Reference yaw angle of the nacelle when the upwind end points due North (deg) [used only with Bladed Interface]0 Ptch_Cntrl - Record 28: Use individual pitch control {0: collective pitch; 1: individual pitch control} (switch) [used only with Bladed Interface]0.0 Ptch_SetPnt - Record 5: Below-rated pitch angle set-point (deg) [used only with Bladed Interface]0.0 Ptch_Min - Record 6: Minimum pitch angle (deg) [used only with Bladed Interface]0.0 Ptch_Max - Record 7: Maximum pitch angle (deg) [used only with Bladed Interface]0.0 PtchRate_Min - Record 8: Minimum pitch rate (most negative value allowed) (deg/s) [used only with Bladed Interface]0.0 PtchRate_Max - Record 9: Maximum pitch rate (deg/s) [used only with Bladed Interface]0.0 Gain_OM - Record 16: Optimal mode gain (Nm/(rad/s)^2) [used only with Bladed Interface]0.0 GenSpd_MinOM - Record 17: Minimum generator speed (rpm) [used only with Bladed Interface]0.0 GenSpd_MaxOM - Record 18: Optimal mode maximum speed (rpm) [used only with Bladed Interface]0.0 GenSpd_Dem - Record 19: Demanded generator speed above rated (rpm) [used only with Bladed Interface]0.0 GenTrq_Dem - Record 22: Demanded generator torque above rated (Nm) [used only with Bladed Interface]0.0 GenPwr_Dem - Record 13: Demanded power (W) [used only with Bladed Interface]---------------------- BLADED INTERFACE TORQUE-SPEED LOOK-UP TABLE -------------0 DLL_NumTrq - Record 26: No. of points in torque-speed look-up table {0 = none and use the optimal mode parameters; nonzero = ignore the optimal mode PARAMETERs by setting Record 16 to 0.0} (-) [used only with Bladed Interface]GenSpd_TLU GenTrq_TLU(rpm) (Nm)---------------------- OUTPUT --------------------------------------------------False SumPrint - Print summary data to .sum (flag) (currently unused)1 OutFile - Switch to determine where output will be placed: {1: in module output file only; 2: in glue code output file only; 3: both} (currently unused)True TabDelim - Use tab delimiters in text tabular output file? (flag) (currently unused)“ES10.3E2” OutFmt - Format used for text tabular output (except time). Resulting field should be 10 characters. (quoted string) (currently unused)0.0 TStart - Time to begin tabular output (s) (currently unused)OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-)“GenPwr”“GenTq”END of input file (the word “END” must appear in the first 3 columns of this last OutList line)

What am I doing wrong and how can I make the force affect my simulation?

Dear @Tudor.Istrate,

Can you provide further clarification on what you are doing? Is the Fortran code you shared included in the source code of the DISCON library, Structural Control (StC) submodel of ServoDyn, or somewhere else? How is your tower StC file, STC_Ctrl_Timestamp_5_Force_sin_Tower_Top_Definition.dat set up?

Best regards,

Dear @Jason.Jonkman,

Thank you for your answer!

Sorry for not making myself completely clear:

The FORTRAN code I used in the DISCON controller source code (in ReadSetParameters.f90, inside the ReadAvrSWAP subroutine). After I implemented the pipe ( Pipes (Interprocess Communications) - Win32 apps | Microsoft Learn ), I wasn’t sure the forces I wanted to stress the turbine with were properly read by the simulation. Now, after I start OpenFAST in the console, I print the time at which a certain force is imposed, as well as the forces and the moments. Everything is printed as I expected (it matches what I send from the other end of the pipe), so I know the code works and OpenFAST reads the correct values for the force timeseries.

Regarding STC_Ctrl_Timestamp_5_Force_sin_Tower_Top_Definition.dat, I am using StC_DOF_MODE = 5, so most of the file does not come into play, since it is for other values of StC_DOF_MODE. Below is the first part of the file:

------- STRUCTURAL CONTROL (StC) INPUT FILE ----------------------------
Input file for AOC YawFriction Module Testing (FWTC)
---------------------- SIMULATION CONTROL --------------------------------------
FALSE Echo - Echo input data to .ech (flag)
---------------------- StC DEGREES OF FREEDOM ----------------------------------
5 StC_DOF_MODE - DOF mode (switch) {0: No StC or TLCD DOF; 1: StC_X_DOF, StC_Y_DOF, and/or StC_Z_DOF (three independent StC DOFs); 2: StC_XY_DOF (Omni-Directional StC); 3: TLCD; 4: Prescribed force/moment time series; 5: Force determined by external DLL}
FALSE StC_X_DOF - DOF on or off for StC X (flag) [Used only when StC_DOF_MODE=1]
true StC_Y_DOF - DOF on or off for StC Y (flag) [Used only when StC_DOF_MODE=1]
FALSE StC_Z_DOF - DOF on or off for StC Z (flag) [Used only when StC_DOF_MODE=1]
---------------------- StC LOCATION ------------------------------------------- [relative to the reference origin of component attached to]
0 StC_P_X - At rest X position of StC (m)
0 StC_P_Y - At rest Y position of StC (m)
100 StC_P_Z - At rest Z position of StC (m)

I used 5 because “Force determined by external DLL” is exactly what I was trying to achieve (my DLL to get the values of the force from a pipe that I set up).

As I said, however, when I change the values of the force I send, the results stay the same. What I am trying to figure out is how to make the simulation respond to the force I impose.

Best regards,
Tudor Istrate

Dear @Tudor.Istrate,

How are you getting the forces from the external DLL to OpenFAST? Are you using avrSWAP(2816-1818) to pass StC_ForceX through StC_ForceZ? Or are you transferring the data from the external DLL to the OpenFAST StC submodel also through pipes? I’m not too familiar with the StC_DOF_MODE = 5 option, but as as I can tell from my quick look through the source code, only forces (not moments) are supported and the data transfer from the external DLL to StC seems to be tied in with the StC_CMODE option.

Best regards,

Dear @Jason.Jonkman,

I had only tried to impose forces, the moments I’ve always kept at zero. I did not use the avrSWAP, but I unpacked the information sent by the pipe inside the ReadAvrSWAP subroutine:

IF (PipeOpen) THEN
READ(pipe_unit, *, IOSTAT=ios) t, fx, fy, fz, mx, my, mz

IF (ios == 0) THEN
    ! Got fresh data
    LocalVar%Time         = t
    LocalVar%StC_Force_X  = fx
    LocalVar%StC_Force_Y  = fy
    LocalVar%StC_Force_Z  = fz
    LocalVar%StC_Moment_X = mx
    LocalVar%StC_Moment_Y = my
    LocalVar%StC_Moment_Z = mz
    
    IF (MOD(NINT(t*10),100) == 0 .AND. ABS(t - t_last) > 1_ReKi) THEN
       WRITE(*,'(F10.4, 1X, 6(ES12.4,1X))') t, fx, fy, fz, mx, my, mz
       t_last = t
    END IF

So at each step, I read what the pipe is sending and update the values with that.
Since what I see in the console was what I was sending, I did not think I should modify it. I will try to use the avrSWAP for the forces, as you suggested.

Best regards,
Tudor Istrate

Dear @Jason.Jonkman,

I did use the avrSWAP in my DLL code. The results of the simulation do not change, so something is wrong with the way the force is transmitted. I made a GitHub repository with the simulation I am using and with the modified DLL code (based on ROSCO): GitHub - tudor-gsp/Externally_imposed_motion: OpenFast simulation to impose the force from an external DLL .

I am thinking that the problem is with the DLL, but I am not sure. Could you please check if that is really the reason?

Best regards,
Tudor Istrate

Dear @Jason.Jonkman,

I changed the reading of the forces as below:

IF (PipeOpen) THEN
READ(pipe_unit, *, IOSTAT=ios) t, fx, fy, fz, mx, my, mz

IF (ios == 0) THEN
    ! Got fresh data
    LocalVar%Time         = t
    LocalVar%StC_Force_X  = fx
    LocalVar%StC_Force_Y  = fy
    LocalVar%StC_Force_Z  = fz
    LocalVar%StC_Moment_X = mx
    LocalVar%StC_Moment_Y = my
    LocalVar%StC_Moment_Z = mz

    avrSWAP(2816) = fx
    avrSWAP(2817) = fy
    avrSWAP(2818) = fz

    IF (MOD(NINT(t*10),100) == 0 .AND. ABS(t - t_last) > 1_ReKi) THEN
       WRITE(*,'(F10.4, 1X, 6(ES12.4,1X))') t, fx, fy, fz, mx, my, mz
       t_last = t
    END IF

END IF

END IF

My console shows the received values, which are consistent with what I send. However, I do not see any results changing in the simulation.
Do you know what else I could try to actually use the forces I send?

Best regards,
Tudor Istrate

Dear @Tudor.Istrate,

I know @Andy.Platt is reviewing the implementation within StC, where I expect a bug to exist. From my quick review of the source code, it looks like only forces (not moments) are supported and the option seems to be tied to StC_CMODE, but that input is only supposed to be used with StC_DOF_MODE = 1 or 2.

Best regards,

Dear @Jason.Jonkman and @Andy.Platt,

This is what I don’t understand:
For StC_DOF_MODE, it says that value 5 is Force determined by external DLL (what I am trying to achieve - a modified DLL with forces taken from an external source).

If I only try to impose forces, should I, after all, use StC_DOF_MODE = 1 or 2?

Best regards,

Tudor Istrate

Dear @Tudor.Istrate,

StC_DOF_MODE = 1 or 2 are for adding TMDs to your model. I agree that StC_DOF_MODE = 5 is meant for an applied force determined by an external DLL, but from my skim of the StC source code, I believe there is a bug in the implementation. I know that @Andy.Platt intends to look into this and correct when he can.

Best regards,

Dear @Jason.Jonkman and @Andy.Platt,

Thank you for the clarifications!
The bug will be fixed as an addition to the current version of OpenFAST or in a future version? If so, do you know when it is going to roll out?

Best regards,
Tudor Istrate

Dear @Tudor.Istrate,

I’ll let @Andy.Platt answer this one…

Best regards,