Software in Loop (SIL) Overview

The Software in Loop interface in QBlade provides an easy way of controlling the whole simulation loop of a wind turbine and enable cosimulation within other software frameworks or scripting languages, such as Python or Matlab. To enable this functionality QBlade is compiled as a Dynamic Link Library (.dll, Windows) or as a Shared Object (.so, Unix) and the relevant functionality is exported into an interface.

Through the different functions that are exported the user can explicitly import QBlade projects (.qpr) or Simulation Definition Files (.sim) and then progress the simulation incrementally in time by calling the advanceTurbineSimulation() function. At every timestep it is possible to inquire any variable of the simulation and control various aspects of the simulation in response, such as changing the inflow conditions, changing the position and orientation of the turbine or controlling the various control actuators (pitch, yaw, generator torque) of the wind turbine. A possible application for the SIL interface is a cosimulation, where the turbine floater can be modeled within a specialized software that is coupled with QBlade through force/position intercommunication. Instead of modeling the floater, the cosimulation could also model the drivetrain, controller or generator in a more sophisticated way. Another application is controller development, where the controller can run in a scripting language (such as Simulink), receiving custom signals from the simulation and controlling the turbine actuators in response. When running a multi-turbine simulation within the SIL interface the user may control each simulated turbine individually, enabling the modeling of global wind park controllers.

In general, the high level overview of the SIL interface and the simulation loop, when running the SIL in an external language, looks as follows:

loadLibrary()
createInstance()
loadProject()
initializeSimulation()

#start of the simulation loop

for i in range(end):

        ...do something...
        advanceTurbineSimulation()

#end of the simulation loop

storeProject()
closeInstance()

Quick Start with the SIL Interface in Python

To test the SIL interface in Python you can simply start the python script sampleScript.py, which you find in the folder PythonInterface of the QBlade directory. This script imports the QBlade library, loads a turbine and simulation definition by from a QBlade project file (.qpr) and then runs a simulation loop for 500 timesteps while printing out some results and finally saving the finished simulation as a new project file. This samples are just meant as a quick demo on how to interface with QBlade in Python and does not serve any other particular purpose. Adapt as needed.

Python Examples:

Python Example: Running the QBlade Library. Python Example: Definition of the QBladeLibrary Class.

Matlab Examples:

Matlab Example: Running the QBlade Library. Matlab Example: Definition of the QBladeLibrary Class.

Interface Function Definitions

Listing 88 : QBladeLibInclude.h
 1#all variables and return values are c data types
 2
 3void setLibraryPath(char *str);
 4
 5void createInstance(int clDevice, int groupSize);
 6void loadProject(char *str);
 7void loadSimDefinition(char *str);
 8void initializeSimulation();
 9void runFullSimulation();
10
11void advanceController_at_num(double *vars, int num);
12void advanceTurbineSimulation();
13
14void storeProject(char *str);
15void setLogFile(char *str);
16void closeInstance();
17
18void loadTurbulentWindBinary(char *str);
19void addTurbulentWind(double windspeed, double refheight, double hubheight, double dimensions, int gridPoints, double length, double dT, char *turbulenceClass, char *turbulenceType, int seed, double vertInf, double horInf, bool removeFiles);
20
21void setPowerLawWind(double windspeed, double horAngle, double vertAngle, double shearExponent, double referenceHeight);
22void setDebugInfo(bool isDebug);
23void setGranularDebug(bool dStr, bool dSim, bool dTurb, bool dCont, bool dSer);
24void setTimestepSize(double timestep);
25void setRPMPrescribeType_at_num(int type, int num);
26void setRampupTime(double time);
27void setInitialConditions_at_num(double yaw, double pitch, double azimuth, double rpm, int num);
28void setTurbinePosition_at_num(double x, double y, double z, double rotx, double roty, double rotz, int num);
29void setControlVars_at_num(double *vars, int num);
30void setExternalAction(char *action, char *id, double val, double pos, char *dir, bool isLocal, int num);
31
32void getWindspeed(double posx, double posy, double posz, double *velocity);
33void getWindspeedArray(double *posx, double *posy, double *posz, double *velx, double *vely, double *velz, int arraySize);
34void getTowerBottomLoads_at_num(double *loads, int num);
35void getTurbineOperation_at_num(double *vars, int num);
36double getCustomData_at_num(char *str, double pos, int num);
37double getCustomSimulationData(char *str);

Interface Function Documentation

In the following, the functionality that is exported from the QBlade dll or shared object is described and the function arguments and return types are given. ALl functions with the appendix _at_num affect the turbine specified by the argument num - this has only an effect for multi turbine simulations.

void setLibraryPath(char *atr)

This function sets the location of the QBlade dll or shared object so that the QBlade instance knows about its location. This function must be called first so that the QBlade instance knows about the location of associated binaries (XFoil, TurbSim) and possibly license files.

void createInstance(int clDevice = 0, int groupSize = 32)

This function creates a new instance of QBlade. The OpenCL device and the OpenCL group-size can both be specified in the arguments. Calling this function is mandatory!

void loadProject(char *str)

This function loads a simulation definition from a QBlade project (.qpr) into the QBlade instance. The file location has to be passed as a char pointer. File names can be passed as absolute or as relative paths. If the QBlade project contains one or more simulation definitions, the first simulation definition of the project file (in alphabetic order) is loaded into the SIL interface.

void loadSimDefinition(char *str)

This function loads a simulation definition (.sim) file into the QBlade instance. The (.sim) files are ASCII files and any aspect of the simulation can be changed by modifying or preprocessing (.sim) files. The file location has to be passed as a char pointer. File names can be passed as absolute or as relative paths.

void initializeSimulation()

This function initializes the simulation, e.g. the simulation is reset and structural ramp-up is carried out.

void runFullSimulation()

This function runs all timesteps for all turbines of the simulation as defined in the simulation object. This is equivalent to pressing the Start Simulation button in QBlade`s GUI. This function needs to be called after void initializeSimulation(). When calling this function it is not possible to interact with the simulation before it is finished. To interact with the simulation you need to create your own simulation loop and call the functions void advanceController_at_num() and void advanceTurbineSimulation() at every timestep.

void advanceController_at_num(double *vars, int num = 0)

This function advances the controller shared library that is assigned to the selected turbine (argument num). When calling this function the controller outputs (gen. torque, blade pitch, etc.) are automatically applied to the turbine (no need to call void setControlVars_at_num(double *vars, int num = 0)). The controller outputs are also returned in the vars array, and can be processed further:

  • vars[0] = generator torque [Nm]

  • vars[1] = yaw angle [deg]

  • vars[2] = pitch blade 1 [deg]

  • vars[3] = pitch blade 2 [deg]

  • vars[4] = pitch blade 3 [deg]

void advanceTurbineSimulation()

This function advances the turbine simulation for all turbines and finishes the timestep.

void storeProject(char *str)

This functions stores a project file. The file location has to be passed as a char pointer. File names can be passed as absolute or as relative paths.

void setLogFile(char *str)

This functions sets the path to a log file that will be created to store the dobug output. This is helpful when accessing the SIL interface from a tool that does not display standard output.

void closeInstance()

This function closes the instance of QBlade and frees the memory.

void loadTurbulentWindBinary(char *str)

This function allows to load a turbulent windfield that is stored in binary format. The file location has to be passed as a char pointer. File names can be passed as absolute or as relative paths.

void addTurbulentWind(double windspeed, double refheight, double hubheight, double dimensions, int gridPoints,double length, double dT, char *turbulenceClass, char *turbulenceType, int seed, double vertInf, double horInf, bool removeFiles = false)

This function allows to define and add a turbulent windfield (using TurbSim) to the simulation. If a turbulent windfield is used the function setPowerLawWind() has no effect. It uses the following parameters:

  • windspeed: the mean windspeed at the reference height [m/s]

  • refheight: the reference height [m]

  • hubheight: the hubheight, more specifically the height of the windfield center [m]

  • dimensions: the y- and z- dimensions of the windfield in meters [m]

  • length: the simulated length of the windfield in seconds [s]

  • dT: the temporal resolution of the windfield [s]

  • turbulenceClass: the turbulence class, can be “A”, “B” or “C”

  • turbulenceType: the turbulence type, can be “NTM”, “ETM”, “xEWM1” or “xEWM50” - where x is the turbine class (1,2 or 3)

  • seed: the random seed for the turbulent windfield

  • vertInf: vertical inflow angle in degrees [deg]

  • horInf: horizontal inflow angle in degrees [deg]

void setPowerLawWind(double windspeed, double horAngle, double vertAngle, double shearExponent, double referenceHeight)

This function can be called before or at any time after the simulation has been initialized with initializeSimulation() to statically or dynamically change the inflow conditions. It defines a power law wind profile (https://en.wikipedia.org/wiki/Wind_profile_power_law) and its inflow direction. The arguments for this function are:

  • windspeed: constant windspeed in m/s [m/s]

  • horAngle: the horizontal inflow angle in degrees [deg]

  • vertAngle: the vertical inflow angle in degrees [deg]

  • shearExponent: this is the exponent for the power law boundary layer profile, if this is set to 0 the windspeed is constant with height [-]

  • referenceHeight: this is the height at which the velocity in the boundary layer is the defined windspeed, usually set to the hubheight [m]

  • exemplary call: addTurbulentWind(12,115,115,220,20,60,0.1,”A”,”NTM”,1000000,0,0);

void setDebugInfo(bool isDebug)

This function enables the debug output if set to true.

void setGranularDebug(bool dStr, bool dSim, bool dTurb, bool dCont, bool dSer)

This function enables a granular debug output.

  • dStr: enable structural model debug info

  • dSim: enable simulation debug info

  • dTurb: enable turbine debug info

  • dCont: enable controller debug info

  • dSer: enable serializer debug info

void setTimestepSize(double timestep)

This function can be used to set the timestep size (in [s]) if the user wants to change this value from the project or simulation definition file. It needs to be called before initializeSimulation().

void setRPMPrescribeType_at_num(int type, int num = 0)

This function can be used to change the rpm prescribe type. It needs to be called before initializeSimulation().

  • 0 - RPM prescribed during ramp-up only

  • 1 - RPM prescribed for the whole simulation

  • 3 - no prescribed RPM

void setRampupTime(double time)

This function can be used to change the ramp-up time from the value specified in the project or simulation file, call before initializeSimulation().

void setInitialConditions_at_num(double yaw, double pitch, double azimuth, double rpm, int num = 0)

This function may be used to set the turbine initial yaw [deg], collective pitch [deg], azimuthal angle [deg] and initial rotSpeed [rpm] to a value different than specified in the QBlade project or simulation input file. It needs to be called before initializeSimulation().

void setTurbinePosition_at_num(double x, double y, double z, double rotx, double roty, double rotz, int num = 0)

This function sets the turbine tower bottom x, y and z position [m], and xrot, yrot zrot rotation [deg]. It can be called before initializeSimulation() if the turbine position should be offset initially or during the simulation loop if it should be changed dynamically, for example during cosimulation with a hydrodynamics software that models the floater.

void setControlVars_at_num(double *vars, int num = 0)

This function applies the control actions to the selected turbine (argument num) for torque, pitch and yaw angle. If it is called after the function advanceController() the control actions from the controller are overwritten (in this way the controll actions can also be modified). The following data needs to be passed in the array vars.

  • vars[0] = generator torque [Nm];

  • vars[1] = yaw angle [deg];

  • vars[2] = pitch blade 1 [deg];

  • vars[3] = pitch blade 2 [deg];

  • vars[4] = pitch blade 3 [deg];

void setExternalAction(char *action, char *id, double val, double pos, char *dir, bool isLocal, int num)

This is a general purpose function that can be used to apply an external action to the simulated turbine.

The action can be of different types, defined by the parameter action. All of these actions are not accumulated and are reset at every timestep, or in other words if, for example, a constant mass should be assigned to the turbine it needs to be assigned with this function at every timestep or it will automatically be reset to zero. The different types are:

  • ADDMASS: adds mass to a location, in [kg]

  • ADDFORCE: adds a force to a location, in [N]

  • ADDTORQUE: adds a torque to a location, in [Nm]

  • SETLENGTH: sets the delta Length of a cable, in [m]

  • SETAFC: sets the state of an AFC element [-]

  • SETTORQUE: sets the generator torque, in [Nm]

  • SETYAW: sets the yaw angle, in [deg]

  • SETPITCH: sets the pitch angle for BLD_X, in [deg]

  • SETBRAKE: sets the brake modulation [0-1]

Some actions are applied to a certain location ID, indicated by the parameter id, the different locations are:

  • CAB_<X>: applies the action to the guycable with ID <X>. Actions on cables are: SETLENGTH, ADDMASS, ADDFORCE

  • MOO_<X>: applies the action to the mooring line with ID <X>. Actions on moorings are: SETLENGTH, ADDMASS, ADDFORCE

  • SMOO_<X>: applies the action to the shared mooring line with ID <X>. Actions on moorings are: SETLENGTH, ADDMASS, ADDFORCE

  • TRQ: applies the action to the torquetube. Actions on the torquetube are: ADDFORCE, ADDTORQUE, ADDMASS

  • BLD_<X>: applies the action to blade <X>. Actions on the blades are: ADDFORCE, ADDTORQUE, ADDMASS

  • STR_<X>_<Y>: applies the action to strut <X> of blade <Y>. Actions on the struts are: ADDFORCE, ADDTORQUE, ADDMASS

  • AFC_<X>_<Y>: applies the action to AFC <X> of blade <Y>. Actions on the AFC elements are: SETAFC

  • SUB_<X>: applies the action to the substructure element with ID <X>. Actions on the substructure elements are: ADDFORCE, ADDTORQUE, ADDMASS

  • JNT_<X>: applies the action to the substructure joint with ID <X>. Actions on the substructure joints are: ADDFORCE, ADDTORQUE, ADDMASS

  • HUB: applies the action to the free LSS hub node. Actions on the hub node are: ADDFORCE, ADDTORQUE, ADDMASS

  • HUBFIXED: applies the action to the fixed non-rotating hub node. Actions on the hub node are: DDFORCE, ADDTORQUE, ADDMASS

The remaining parameters are used to further define the action that is applied, their coordinate systems, etc.

  • The parameter val specifies the mass [kg], torque [Nm], force [N], delta length [m] or AFC state [-].

  • The parameter pos sets the normalized position [0-1] at which the mass, force or torque is applied. Only has an effect on elements, not on nodes.

  • The parameter dir specifies the direction along which the force or torque is applied, options are “X”, “Y”, “Z”.

  • The parameter isLocal specifies sets whether the direction is defined in global or local (element or node) coordinates.

  • The parameter num specifies the turbine instance to which the action is applied.

void getWindspeed(double x, double y, double z, double *velocity)

This function can be called to get the current windspeed at the chosen position (x,y,z), returns the windspeed vector in the double pointer velocity.

  • velocity[0] = x-component [m/s];

  • velocity[1] = y-component [m/s];

  • velocity[2] = z-component [m/s];

void getWindspeedArray(double *posx, double *posy, double *posz, double *velx, double *vely, double *velz, int arraySize)

This function can be called to get the current windspeed for an array of positions

  • posx = double array of position x-components;

  • posy = double array of position y-components;

  • posz = double array of position z-components;

  • velx = double array of velocity x-components evaluated at the pos array;

  • vely = double array of velocity y-components evaluated at the pos array;

  • velz = double array of velocity z-components evaluated at the pos array;

  • arraySize = the size of the pos and velocity arrays;

void getTowerBottomLoads_at_num(double *loads, int num)

This function can be used to obtain the loads at the bottom of the tower. The main purpose of this is to be used in conjunction with the setTurbinePosition_at_num() function for force/position cosimilation with a hydrodynamics solver that is modeling the floater.

void getTurbineOperation_at_num(double *vars, int num = 0)

This function returns useful turbine operational parameters from the selected turbine (argument num). Typically, this data is used to feed the logic of a supervisory wind turbine controller. The data is returned in the array vars which has the following content:

  • vars[0] = rotational speed [rad/s]

  • vars[1] = power [W]

  • vars[2] = Abs HH wind velocity [m/s]

  • vars[3] = yaw angle [deg]

  • vars[4] = pitch blade 1 [deg]

  • vars[5] = pitch blade 2 [deg]

  • vars[6] = pitch blade 3 [deg]

  • vars[7] = oop blade root bending moment blade 1 [Nm]

  • vars[8] = oop blade root bending moment blade 2 [Nm]

  • vars[9] = oop blade root bending moment blade 3 [Nm]

  • vars[10] = ip blade root bending moment blade 1 [Nm]

  • vars[11] = ip blade root bending moment blade 2 [Nm]

  • vars[12] = ip blade root bending moment blade 3 [Nm]

  • vars[13] = tor blade root bending moment blade 1 [Nm]

  • vars[14] = tor blade root bending moment blade 2 [Nm]

  • vars[15] = tor blade root bending moment blade 3 [Nm]

  • vars[16] = oop tip deflection blade 1 [m]

  • vars[17] = oop tip deflection blade 2 [m]

  • vars[18] = oop tip deflection blade 3 [m]

  • vars[19] = ip tip deflection blade 1 [m]

  • vars[20] = ip tip deflection blade 2 [m]

  • vars[21] = ip tip deflection blade 3 [m]

  • vars[22] = tower top acceleration in global X [m/s^2]

  • vars[23] = tower top acceleration in global Y [m/s^2]

  • vars[24] = tower top acceleration in global Z [m/s^2]

  • vars[25] = tower top fore aft acceleration [m/s^2]

  • vars[26] = tower top side side acceleration [m/s^2]

  • vars[27] = tower top X position [m]

  • vars[28] = tower top Y position [m]

  • vars[29] = tower bottom force along global X [Nm]

  • vars[30] = tower bottom force along global Y [Nm]

  • vars[31] = tower bottom force along global Z [Nm]

  • vars[32] = tower bottom bending moment along global X [Nm]

  • vars[33] = tower bottom bending moment along global Y [Nm]

  • vars[34] = tower bottom bending moment along global Z [Nm]

  • vars[35] = current time [s]

  • vars[36] = azimuthal position of the LSS [deg]

  • vars[37] = azimuthal position of the HSS [deg]

  • vars[38] = HSS torque [Nm]

  • vars[39] = wind speed at hub height [m/s]

  • vars[40] = HH wind velocity x [m/s]

  • vars[41] = HH wind velocity y [m/s]

  • vars[42] = HH wind velocity z [m/s]

double getCustomData_at_num(char *str, double pos = 0, int num = 0)

This function can be used to access the current value from an arbitrary turbine simulation variable in QBlade. Specify the data name as is would appear in any QBlade graph as a char pointer. If you are requesting an aerodynamic ‘at section’ variable, for instance ‘Angle of Attack at 0.25c (at section) Blade 1 [deg]’ you can specify the normalized position along the blade length using the ‘pos’ variable. As an example, to get the AoA at 85% blade length from turbine 0, you would call the function the following way: getCustomData_at_num("Angle of Attack at 0.25c (at section) Blade 1 [deg]", 0.85,0).

double getCustomSimulationData(char *str)

This function can be used to access the current value from an arbitrary simulation time graph variable in QBlade.

Python Example: Running the QBlade Library

The following code example (sampleScript.py) is an example for a light weight Python script that utilizes the QBlade SIL interface. There are many ways to improve this, e.g. the library could be loaded into multiple separate processes for parallelization and sophisticated algorithms could be implemented instead of using a standard controller. This exemplary script only uses a small amount of the functionality that is exported by the QBlade library for purely illustrative purposes.

In this Python example script the library is loaded by calling creating an object of the class QBladeLibrary which handles the library import. After the object QBLIB of the class QBladeLibrary has been created any function of the QBlade library can be accessed by calling QBLIB.function_XYZ(). All lines of code that are needed to load the QBlade library into python are highlighted in the example below.

After the QBlade library has been loaded a simulation object is imported and a simulation is started over 500 timesteps. During the simulation loop different data is obtained from the turbine simulation. The turbine controller that is defined in the simulation object is advanced and its signals are passed to the turbine actuators. After the simulation loop has finished the simulation is stored into a project file, for later inspection, and the library is unloaded from python.

Listing 89 : sampleScript.py
 1from ctypes import *
 2from QBladeLibrary import QBladeLibrary
 3
 4#loading the QBlade library from the folder below the location of sampleScript.py, if calling this script not from the script folder directly you need to use an absolute path instead!
 5QBLIB = QBladeLibrary("../QBladeCE_2.0.6.dll")
 6
 7#creation of a QBlade instance from the library
 8QBLIB.createInstance(1,32)
 9
10#loading a project or sim-file, in this case the DTU_10MW_Demo project or simulation definition file
11#QBLIB.loadSimDefinition(b"./DTU_10MW_Demo.sim") #uncomment this line to load a simulation definition file
12QBLIB.loadProject(b"./NREL_5MW_Sample.qpr")
13
14#initializing the sim and ramp-up phase, call before starting the simulation loop
15QBLIB.initializeSimulation()
16
17#we will run the simulation for 500 steps before storing the results
18number_of_timesteps = 500
19
20#start of the simulation loop
21for i in range(number_of_timesteps):
22
23        #advance the simulation
24        QBLIB.advanceTurbineSimulation()
25
26        #assign the c-type double array 'loads' with length [6], initialized with zeros
27        loads = (c_double * 6)(0,0,0,0,0,0)
28        #retrieve the tower loads and store the in the array 'loads' by calling the function getTowerBottomLoads_at_num()
29        QBLIB.getTowerBottomLoads_at_num(loads,0)
30
31        #uncomment the next line to try changing the position of the turbine dynamically
32        #QBLIB.setTurbinePosition_at_num(-0.2*i,0,0,0,i*0.1,i*0.1,0)
33
34        #example how to extract a variable by name from the simulation, call as often as needed with different variable names, extracting rpm and time in the lines below
35        rpm = QBLIB.getCustomData_at_num(b"Rotational Speed [rpm]",0,0)
36        time = QBLIB.getCustomData_at_num(b"Time [s]",0,0) #example how to extract the variable 'Time' by name from the simulation
37        AoA = QBLIB.getCustomData_at_num(b"Angle of Attack at 0.25c (at section) Blade 1 [deg]",0.85,0) #example how to extract the variable 'Angle of Attack' by name at 85% blade length from the simulation
38
39        #example how to extract a 3 length double array with the x,y,z windspeed components at a global position of x=-50,Y=0,Z=100m from the simulation
40        windspeed = (c_double * 3)(0,0,0)
41        QBLIB.getWindspeed(-50,0,100,windspeed)
42
43        #assign the c-type double array 'ctr_vars' with length [5], initialized with zeros
44        ctr_vars = (c_double * 5)(0);
45        #advance the turbine controller and store the controller signals in the array 'ctr_vars'
46        QBLIB.advanceController_at_num(ctr_vars,0)
47
48        #pass the controller signals in 'ctr_vars' to the turbine by calling setControlVars_at_num(ctr_vars,0)
49        QBLIB.setControlVars_at_num(ctr_vars,0)
50
51        #print out a few of the recorded data, in this case torque, tower bottom force along z (weight force) and rpm
52        print("Time:","{:3.2f}".format(time),"   Windspeed:","{:2.2f}".format(windspeed[0]),"  Torque:","{:1.4e}".format(ctr_vars[0]),"    RPM:","{:2.2f}".format(rpm),"   Pitch:","{:2.2f}".format(ctr_vars[2]),"   AoA at 85%:","{:2.2f}".format(AoA))
53
54
55
56#the simulation loop ends here after all 'number_of_timesteps have been evaluated
57
58#storing the finished simulation in a project as DTU_10MW_Demo_finished.qpr, you can open this file to view the results of the simulation inside QBlade's GUI
59QBLIB.storeProject(b"./NREL_5MW_Sample_completed.qpr")
60
61#closing the QBlade instance to free memory
62QBLIB.closeInstance()
63
64#unloading the QBlade library
65del QBLIB.lib

Python Example: Definition of the QBladeLibrary Class

The script QBladeLibrary.py defines the class QBladeLibrary and loads the shared object. This script is just a suggestion on how to interface with the QBlade Library in Python and certainly there are more efficient ways of how to do this.

Listing 90 : QBladeLibrary.py
  1from ctypes import *
  2from sys import platform
  3
  4class QBladeLibrary:
  5
  6    def __init__(self, shared_lib_path):
  7
  8        try:
  9            self.lib = CDLL(shared_lib_path)
 10            print("Successfully loaded ", shared_lib_path)
 11        except Exception as e:
 12            print("Could not load the file ", shared_lib_path)
 13            print(e)
 14            return
 15
 16        #setting the library Path, so that the Library knows about its location!
 17        self.lib.setLibraryPath(shared_lib_path.encode('utf-8')) #setting the library Path, so that the DLL knows about its location!
 18
 19        #here the imported functions are defined
 20
 21        self.loadProject = self.lib.loadProject
 22        self.loadProject.argtype = c_char_p
 23        self.loadProject.restype = c_void_p
 24
 25        self.loadSimDefinition = self.lib.loadSimDefinition
 26        self.loadSimDefinition.argtype = c_char_p
 27        self.loadSimDefinition.restype = c_void_p
 28
 29        self.getCustomData_at_num = self.lib.getCustomData_at_num
 30        self.getCustomData_at_num.argtypes = [c_char_p, c_double, c_int]
 31        self.getCustomData_at_num.restype = c_double
 32
 33        self.getCustomSimulationData = self.lib.getCustomSimulationData
 34        self.getCustomSimulationData.argtype = c_char_p
 35        self.getCustomSimulationData.restype = c_double
 36
 37        self.getWindspeed = self.lib.getWindspeed
 38        self.getWindspeed.argtypes = [c_double, c_double, c_double, c_double * 3]
 39        self.getWindspeed.restype = c_void_p
 40
 41        self.getWindspeedArray = self.lib.getWindspeedArray
 42        self.getWindspeedArray.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_int]
 43        self.getWindspeedArray.restype = c_void_p
 44
 45        self.storeProject = self.lib.storeProject
 46        self.storeProject.argtype = c_char_p
 47        self.storeProject.restype = c_void_p
 48
 49        self.setLibraryPath = self.lib.createInstance
 50        self.setLibraryPath.argtype = c_char_p
 51        self.setLibraryPath.restype = c_void_p
 52
 53        self.setLogFile = self.lib.setLogFile
 54        self.setLogFile.argtype = c_char_p
 55        self.setLogFile.restype = c_void_p
 56
 57        self.createInstance = self.lib.createInstance
 58        self.createInstance.argtypes = [c_int, c_int]
 59        self.createInstance.restype = c_void_p
 60
 61        self.closeInstance = self.lib.closeInstance
 62        self.closeInstance.restype = c_void_p
 63
 64        self.addTurbulentWind = self.lib.addTurbulentWind
 65        self.addTurbulentWind.argtypes = [c_double, c_double, c_double, c_double, c_int, c_double, c_double, c_char_p, c_char_p, c_int, c_double, c_double, c_bool]
 66        self.addTurbulentWind.restype = c_void_p
 67
 68        self.setExternalAction = self.lib.setExternalAction
 69        self.setExternalAction.argtypes = [c_char_p, c_char_p, c_double, c_double, c_char_p, c_bool, c_int]
 70        self.setExternalAction.restype = c_void_p
 71
 72        self.loadTurbulentWindBinary = self.lib.loadTurbulentWindBinary
 73        self.loadTurbulentWindBinary.argtype = c_char_p
 74        self.loadTurbulentWindBinary.restype = c_void_p
 75
 76        self.setTimestepSize = self.lib.setTimestepSize
 77        self.setTimestepSize.argtype = c_double
 78        self.setTimestepSize.restype = c_void_p
 79
 80        self.setInitialConditions_at_num = self.lib.setInitialConditions_at_num
 81        self.setInitialConditions_at_num.argtypes = [c_double, c_double, c_double, c_double, c_int]
 82        self.setInitialConditions_at_num.restype = c_void_p
 83
 84        self.setRPMPrescribeType_at_num = self.lib.setRPMPrescribeType_at_num
 85        self.setRPMPrescribeType_at_num.argtypes = [c_int, c_int]
 86        self.setRPMPrescribeType_at_num.restype = c_void_p
 87
 88        self.setRampupTime = self.lib.setRampupTime
 89        self.setRampupTime.argtype = c_double
 90        self.setRampupTime.restype = c_void_p
 91
 92        self.setTurbinePosition_at_num = self.lib.setTurbinePosition_at_num
 93        self.setTurbinePosition_at_num.argtypes = [c_double, c_double, c_double, c_double, c_double, c_double, c_int]
 94        self.setTurbinePosition_at_num.restype = c_void_p
 95
 96        self.getTowerBottomLoads_at_num = self.lib.getTowerBottomLoads_at_num
 97        self.getTowerBottomLoads_at_num.argtypes = [c_double * 6, c_int]
 98        self.getTowerBottomLoads_at_num.restype = c_void_p
 99
100        self.initializeSimulation = self.lib.initializeSimulation
101        self.initializeSimulation.restype = c_void_p
102
103        self.advanceTurbineSimulation = self.lib.advanceTurbineSimulation
104        self.advanceTurbineSimulation.restype = c_void_p
105
106        self.advanceController_at_num = self.lib.advanceController_at_num
107        self.advanceController_at_num.argtypes = [c_double * 5, c_int]
108        self.advanceController_at_num.restype = c_void_p
109
110        self.setDebugInfo = self.lib.setDebugInfo
111        self.setDebugInfo.argtype = c_bool
112        self.setDebugInfo.restype = c_void_p
113
114        self.setGranularDebug = self.lib.setGranularDebug
115        self.setGranularDebug.argtypes = [c_bool, c_bool, c_bool, c_bool, c_bool]
116        self.setGranularDebug.restype = c_void_p
117
118        self.setControlVars_at_num = self.lib.setControlVars_at_num
119        self.setControlVars_at_num.argtypes = [c_double * 5, c_int]
120        self.setControlVars_at_num.restype = c_void_p
121
122        self.getTurbineOperation_at_num = self.lib.getTurbineOperation_at_num
123        self.getTurbineOperation_at_num.argtypes = [c_double * 41, c_int]
124        self.getTurbineOperation_at_num.restype = c_void_p
125
126        self.setPowerLawWind = self.lib.setPowerLawWind
127        self.setPowerLawWind.argtypes = [c_double, c_double, c_double, c_double, c_double]
128        self.setPowerLawWind.restype = c_void_p
129
130        self.runFullSimulation = self.lib.runFullSimulation
131        self.runFullSimulation.restype = c_void_p

Matlab Example: Running the QBlade Library

This is an example for using the QBlade library within Matlab. It reproduces the Python example above. An object of the class QBladeLibrary, that contains the library interface is created and a simple simulation loop is started.

Listing 91 : sampleScript.m
 1clear all
 2close all
 3clc
 4
 5% create an object of the class 'QBladeLibrary' that contains all interface functions
 6QBLIB = QBladeLibrary('../QBladeCE_2.0.6.dll');
 7
 8QBLIB.createInstance(1,32);
 9
10% since matlab is unable to display the console output from the library, we store the output in a log file
11QBLIB.setLogFile('./LogFile.txt')
12
13QBLIB.loadProject('NREL_5MW_Sample.qpr')
14
15QBLIB.initializeSimulation()
16
17number_of_timesteps = 500;
18
19f = waitbar(0,'Initializing Simulation') ;
20
21for i = 1:1:number_of_timesteps
22
23        %advance the simulation
24        QBLIB.advanceTurbineSimulation()
25
26        %assign the c-type double array 'loads' with length [6], initialized with zeros
27        loads = libpointer('doublePtr',zeros(6,1));
28        %retrieve the tower loads and store the in the array 'loads' by calling the function getTowerBottomLoads_at_num()
29        QBLIB.getTowerBottomLoads_at_num(loads,0);
30        %dereferencing the 'loads' pointer and accessing its first value
31        loads.Value(1);
32
33        %uncomment the next line to try changing the position of the turbine dynamically
34        %QBLIB.setTurbinePosition_at_num(-0.2*i,0,0,0,i*0.1,i*0.1,0)
35
36        %example how to extract a variable by name from the simulation, call as often as needed with different variable names, extracting rpm and time in the lines below
37        rpm = QBLIB.getCustomData_at_num('Rotational Speed [rpm]',0,0);
38        t = QBLIB.getCustomData_at_num('Time [s]',0,0);  %example how to extract the variable 'Time' by name from the simulation
39        AoA = QBLIB.getCustomData_at_num('Angle of Attack at 0.25c (at section) Blade 1 [deg]',0.85,0); %example how to extract the variable 'Angle of Attack' by name at 85% blade length from the simulation
40
41        %example how to extract a 3 length double array with the x,y,z windspeed components at a global position of x=-50,Y=0,Z=100m from the simulation
42        windspeed = libpointer('doublePtr',zeros(3,1));
43        QBLIB.getWindspeed(-50,0,100,windspeed);
44
45        %assign the c-type double array 'ctr_vars' with length [5], initialized with zeros
46        ctr_vars = libpointer('doublePtr',zeros(5,1));
47        %advance the turbine controller and store the controller signals in the array 'ctr_vars'
48        QBLIB.advanceController_at_num(ctr_vars,0)
49
50        %passthe controller signals in 'ctr_vars' to the turbine by calling setControlVars_at_num(ctr_vars,0)
51        QBLIB.setControlVars_at_num(ctr_vars,0)
52
53        fprintf('Time: %3.2f    Windspeed: %2.2f    Torque: %1.4e       RPM: %2.2f      Pitch: %2.2f    AoA at 85%%: %2.2f\n',t,windspeed.Value(1),ctr_vars.Value(1),rpm,ctr_vars.Value(3),AoA);
54
55        waitbar(i/number_of_timesteps,f,'QBlade Simulation Running')
56
57end
58
59close(f)
60
61QBLIB.storeProject('./NREL_5MW_Sample_completed.qpr')
62
63QBLIB.closeInstance()
64
65QBLIB.unload()

Matlab Example: Definition of the QBladeLibrary Class

This code shows how the class QBladeLibrary is defined in the Matlab environment. To load the library, a header file QBladeLibInclude.h is required that contains the C-type Interface Function Definitions of the QBlade shared object.

Listing 92 : QBladeLibrary.m
  1classdef QBladeLibrary
  2    properties
  3        lib % DLL handle
  4    end
  5
  6    methods
  7        % Constructor
  8        function obj = QBladeLibrary(dllPath)
  9            % Load DLL
 10            obj.lib = loadlibrary(dllPath,'QBladeLibInclude.h','alias','QBLIB');
 11            calllib('QBLIB','setLibraryPath',dllPath)
 12        end
 13
 14        % Destructor
 15        function unload(obj)
 16            % Unload Library
 17            if libisloaded('QBLIB')
 18                unloadlibrary 'QBLIB'
 19            end;
 20        end
 21
 22        % Function to call library function
 23        function createInstance(obj,clDevice,groupSize)
 24            calllib('QBLIB', 'createInstance', clDevice, groupSize);
 25        end
 26
 27        function loadProject(obj,str)
 28            calllib('QBLIB', 'loadProject', str);
 29        end
 30
 31        function loadSimDefinition(obj,str)
 32            calllib('QBLIB', 'loadSimDefinition', str);
 33        end
 34
 35        function initializeSimulation(obj)
 36            calllib('QBLIB', 'initializeSimulation');
 37        end
 38
 39        function runFullSimulation(obj)
 40            calllib('QBLIB', 'runFullSimulation');
 41        end
 42
 43        function advanceController_at_num(obj,vars,num)
 44            calllib('QBLIB', 'advanceController_at_num', vars, num);
 45        end
 46
 47        function advanceTurbineSimulation(obj)
 48            calllib('QBLIB', 'advanceTurbineSimulation');
 49        end
 50
 51        function storeProject(obj,str)
 52            calllib('QBLIB', 'storeProject',str);
 53        end
 54
 55        function closeInstance(obj)
 56            calllib('QBLIB', 'closeInstance');
 57        end
 58
 59        function setLogFile(obj,str)
 60            calllib('QBLIB', 'setLogFile',str);
 61        end
 62
 63        function loadTurbulentWindBinary(obj,str)
 64            calllib('QBLIB', 'loadTurbulentWindBinary', str);
 65        end
 66
 67        function addTurbulentWind(obj,windspeed, refheight, hubheight, dimensions, gridPoints, length, dT, turbulenceClass, turbulenceType, seed, vertInf, horInf, removeFiles)
 68            calllib('QBLIB', 'addTurbulentWind', windspeed, refheight, hubheight, dimensions, gridPoints, length, dT, turbulenceClass, turbulenceType, seed, vertInf, horInf, removeFiles);
 69        end
 70
 71        function setPowerLawWind(obj,windspeed,horAngle,vertAngle,shearExponent,referenceHeight)
 72            calllib('QBLIB', 'setPowerLawWind',windspeed,horAngle,vertAngle,shearExponent,referenceHeight);
 73        end
 74
 75        function setDebugInfo(obj,isDebug)
 76            calllib('QBLIB', 'setDebugInfo', isDebug);
 77        end
 78
 79        function setGranularDebug(obj,dStr,dSim,dTurb,dCont,dSer)
 80        calllib('QBLIB', 'setGranularDebug',dStr,dSim,dTurb,dCont,dSer);
 81        end
 82
 83        function setTimestepSize(obj,timestep)
 84            calllib('QBLIB', 'setTimestepSize', timestep);
 85        end
 86
 87        function setRPMPrescribeType_at_num(obj,type,num)
 88            calllib('QBLIB', 'setRPMPrescribeType_at_num',type,num);
 89        end
 90
 91        function setRampupTime(obj,time)
 92            calllib('QBLIB', 'setRampupTime',time);
 93        end
 94
 95        function setInitialConditions_at_num(obj,yaw,pitch,azimuth,rpm,num)
 96            calllib('QBLIB', 'setInitialConditions_at_num',yaw,pitch,azimuth,rpm,num);
 97        end
 98
 99        function setTurbinePosition_at_num(obj,x,y,z,xrot,yrot,zrot,num)
100            calllib('QBLIB', 'setTurbinePosition_at_num',x,y,z,xrot,yrot,zrot,num);
101        end
102
103        function setControlVars_at_num(obj,vars,num)
104            calllib('QBLIB', 'setControlVars_at_num',vars,num);
105        end
106
107        function setExternalAction(obj,action,id,val,pos,dir,isLocal,num)
108            calllib('QBLIB', 'setExternalAction',action,id,val,pos,dir,isLocal,num);
109        end
110
111        function getWindspeed(obj,x,y,z,velocity)
112            calllib('QBLIB', 'getWindspeed',x,y,z,velocity);
113        end
114
115        function getWindspeedArray(obj,posx,posy,posz,velx,vely,velz,arraySize)
116            calllib('QBLIB', 'getWindspeedArray',posx,posy,posz,velx,vely,velz,arraySize);
117        end
118
119        function getTowerBottomLoads_at_num(obj,loads,num)
120            calllib('QBLIB', 'getTowerBottomLoads_at_num',loads,num);
121        end
122
123        function getTurbineOperation_at_num(obj,vars,num)
124            calllib('QBLIB', 'getTurbineOperation_at_num',vars,num);
125        end
126
127        function output = getCustomData_at_num(obj,var,i,j)
128            output = calllib('QBLIB','getCustomData_at_num',var,i,j);
129        end
130
131        function output = getCustomSimulationData(obj,var)
132            output = calllib('QBLIB','getCustomSimulationData',var);
133        end
134
135    end
136end