Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/develop' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
dzalkind committed Jul 11, 2023
2 parents 4983dac + c701f67 commit 108440f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 14 deletions.
54 changes: 45 additions & 9 deletions Examples/23_structural_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@
import numpy as np
from ROSCO_toolbox.ofTools.fast_io.FAST_reader import InputReader_OpenFAST
from ROSCO_toolbox.inputs.validation import load_rosco_yaml
from ROSCO_toolbox.controller import OpenLoopControl


'''
ROSCO currently supports user-defined hooks for structural control control actuation, if StC_Mode = 1.
The control logic can be determined in Controllers.f90 with the StructrualControl subroutine.
In the DISCON input, users must specify StC_GroupIndex relating to the control ChannelID.
These indices can be found in the ServoDyn summary file (*SrvD.sum)
In the example below (and hard-coded in ROSCO) a step change of -4e5 N on the first structural controller
is applied at 50 sec.
In the example below, we implement a smooth step change mimicing the exchange of ballast from the
upwind column to the down wind columns
The develop branch (as of Mar 3, 2023) of OpenFAST (v3.5.0, upcoming) is required to run this example
OpenFAST v3.5.0 is required to run this example
'''


Expand Down Expand Up @@ -65,6 +67,41 @@ def main():
for StC_file in reader.fst_vt['ServoDyn']['SStCfiles']:
reader.fst_vt['SStC'].append(reader.read_StC(StC_file))

# Set up open loop inputs to ROSCO
t_trans = 60
t_sigma = 80
t_max = 200

applied_force = [-2e6, 1e6, 1e6]

olc = OpenLoopControl(t_max=t_max)
olc.interp_timeseries(
'struct_control_1',
[0,t_trans,t_trans+t_sigma],
[0,0,applied_force[0]] ,
'sigma'
)

olc.interp_timeseries(
'struct_control_2',
[0,t_trans,t_trans+t_sigma],
[0,0,applied_force[1]] ,
'sigma'
)

olc.interp_timeseries(
'struct_control_3',
[0,t_trans,t_trans+t_sigma],
[0,0,applied_force[2]] ,
'sigma'
)


ol_params = olc.write_input(os.path.join(run_dir,'open_loop_ballast.dat'))

controller_params = {}
controller_params['open_loop'] = ol_params
controller_params['StC_Mode'] = 2

# simulation set up
r = run_FAST_ROSCO()
Expand All @@ -73,16 +110,15 @@ def main():
r.wind_case_fcn = cl.power_curve
r.wind_case_opts = {
'U': [9],
'T_max': 100,
'TMax': t_max,
}
r.case_inputs = {}
r.fst_vt = reader.fst_vt
r.save_dir = run_dir
r.rosco_dir = rosco_dir

r.fst_vt = reader.fst_vt
r.save_dir = run_dir
r.rosco_dir = rosco_dir
r.controller_params = controller_params
r.run_FAST()



if __name__=="__main__":
main()
1 change: 1 addition & 0 deletions Examples/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
'20_active_wake_control',
'21_optional_inputs',
'22_cable_control',
'23_structural_control',
'update_rosco_discons',
]

Expand Down
34 changes: 31 additions & 3 deletions ROSCO/src/Controllers.f90
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,8 @@ SUBROUTINE CableControl(avrSWAP, CntrPar, LocalVar, objInst, ErrVar)

! Internal Variables
Integer(IntKi) :: I_GROUP
CHARACTER(*), PARAMETER :: RoutineName = 'StructuralControl'



IF (CntrPar%CC_Mode == 1) THEN
Expand Down Expand Up @@ -756,31 +758,40 @@ SUBROUTINE CableControl(avrSWAP, CntrPar, LocalVar, objInst, ErrVar)

END DO

! Add RoutineName to error message
IF (ErrVar%aviFAIL < 0) THEN
ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg)
ENDIF

END SUBROUTINE CableControl

!-------------------------------------------------------------------------------------------------------------------------------
SUBROUTINE StructuralControl(avrSWAP, CntrPar, LocalVar, objInst)
SUBROUTINE StructuralControl(avrSWAP, CntrPar, LocalVar, objInst, ErrVar)
! Cable controller
! StC_Mode = 0, No cable control, this code not executed
! StC_Mode = 1, User-defined cable control
! StC_Mode = 2, Ballast-like control, not yet implemented
!
! Note that LocalVar%StC_Input() has a fixed max size of 12, which can be increased in rosco_types.yaml
!
USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances
USE ROSCO_Types, ONLY : ControlParameters, LocalVariables, ObjectInstances, ErrorVariables

REAL(ReKi), INTENT(INOUT) :: avrSWAP(*) ! The swap array, used to pass data to, and receive data from, the DLL controller.

TYPE(ControlParameters), INTENT(INOUT) :: CntrPar
TYPE(LocalVariables), INTENT(INOUT) :: LocalVar
TYPE(ObjectInstances), INTENT(INOUT) :: objInst
TYPE(ErrorVariables), INTENT(INOUT) :: ErrVar


! Internal Variables
Integer(IntKi) :: I_GROUP
CHARACTER(*), PARAMETER :: RoutineName = 'StructuralControl'



IF (CntrPar%StC_Mode == 1) THEN
! User defined control
! User defined control, step example

IF (LocalVar%Time > 500) THEN
! Step change in input of -4500 N
Expand All @@ -791,6 +802,18 @@ SUBROUTINE StructuralControl(avrSWAP, CntrPar, LocalVar, objInst)
END IF


ELSEIF (CntrPar%StC_Mode == 2) THEN


DO I_GROUP = 1,CntrPar%StC_Group_N
IF (CntrPar%Ind_StructControl(I_GROUP) > 0) THEN
LocalVar%StC_Input(I_GROUP) = interp1d(CntrPar%OL_Breakpoints, &
CntrPar%OL_StructControl(I_GROUP,:), &
LocalVar%Time,ErrVar)
ENDIF
ENDDO



END IF

Expand All @@ -800,6 +823,11 @@ SUBROUTINE StructuralControl(avrSWAP, CntrPar, LocalVar, objInst)
avrSWAP(CntrPar%StC_GroupIndex(I_GROUP)) = LocalVar%StC_Input(I_GROUP)
END DO

! Add RoutineName to error message
IF (ErrVar%aviFAIL < 0) THEN
ErrVar%ErrMsg = RoutineName//':'//TRIM(ErrVar%ErrMsg)
ENDIF

END SUBROUTINE StructuralControl
!-------------------------------------------------------------------------------------------------------------------------------
END MODULE Controllers
2 changes: 1 addition & 1 deletion ROSCO/src/DISCON.F90
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ SUBROUTINE DISCON(avrSWAP, aviFAIL, accINFILE, avcOUTNAME, avcMSG) BIND (C, NAME

! Structural control
IF (CntrPar%StC_Mode > 0) THEN
CALL StructuralControl(avrSWAP,CntrPar,LocalVar, objInst)
CALL StructuralControl(avrSWAP,CntrPar,LocalVar, objInst, ErrVar)
END IF

IF ( CntrPar%LoggingLevel > 0 ) THEN
Expand Down
5 changes: 4 additions & 1 deletion ROSCO_toolbox/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from wisdem.inputs import load_yaml

from wisdem.inputs import load_yaml
from ROSCO_toolbox.ofTools.util.FileTools import remove_numpy

# Some useful constants
now = datetime.datetime.now()
Expand Down Expand Up @@ -296,7 +297,7 @@ def read_DISCON(DISCON_filename):
if (line.split()[1] != '!'): # Array valued entries
array_length = line.split().index('!')
param = line.split()[array_length+1]
values = np.array( [float(x) for x in line.split()[:array_length]] )
values = [float(x) for x in line.split()[:array_length]]
DISCON_in[param] = values
else: # All other entries
param = line.split()[2]
Expand Down Expand Up @@ -587,6 +588,8 @@ def DISCON_dict(turbine, controller, txt_filename=None):
for param, value in controller.controller_params['DISCON'].items():
DISCON_dict[param] = value

# Make all lists, not numpy
DISCON_dict = remove_numpy(DISCON_dict)

return DISCON_dict

Expand Down

0 comments on commit 108440f

Please sign in to comment.