Adding new variables to .fst and .out files

Hi

I’m currently working with a controller through BladedDllInterface. All is going well. I’d like to add some functionality to FAST that isn’t currently there though. I’d like to add some safety chain simulation in the BladedDLLInterface (things like pitch out of position, overspeed, sudden shock detection, overpower…). There is space in there to do this but, I’d like to be able to record which safety chain is active in my .out file by writing Bladed swap array elements 97 and 98 (iSafetySystemThatHasBeenActivated (from FAST to DISCON) and iSafetySystemToActivate (from DISCON to FAST))to the .out file. Also, being able to log other info like the user defined variables (array elements 120 to 129) would be very useful for being able to help troubleshoot what the controller is up to.

Also, I’d like to be able to add some variables to my .fst file. I plan to simulate a couple of faults with the turbine instrumentation and would like to batch different faults with different external conditions. Adding a ‘Fault to simulate’ input to the .fst file feels like the neatest way to do this.

I have read the FAST manual, but haven’t spent very much time looking through the code. I was hoping that you might be able to give me some advice on which bits of code to look at or 5 minutes of generic advice before I get stuck in.

Thanks

Alec

Dear Alec,

I suggest you look at how other input parameters are processed by FAST to see what changes would be required to add your own inputs. (E.g., search for how “PCMode” is used in the source code.)

Likewise, look at how other output parameters are processed by FAST to see what changes would be required to add your own outputs. (E.g., search for how “GenTq” is used in the source code.)

Best regards,

Thanks Jason

Below is what I found out. I discovered a few little rules along the way, could you have a quick look to see if I’ve missed any?

Also, there is a slightly unrelated question at the end.

So, for outputs you need to add your variable name to the list in the MODULE Output of FAST_Mods:

...
INTEGER(4), PARAMETER        :: Wave9Axi             = 531
INTEGER(4), PARAMETER        :: Wave9Ayi             = 532
INTEGER(4), PARAMETER        :: Wave9Azi             = 533

INTEGER(4), PARAMETER        :: FUVar1               = 544

FUVar1 is short for FAST User Variable 1. There is a 10 character limit on these variable names, and they shouldn’t begin with a ‘-’, ‘_’, ‘m’ or ‘M’ (or they’ll be made negative and the first character would be dropped from the name).
This defines the location of FUVar1 in the AllOuts array of reals. All your array element numbers are sequential, so if I wanted to add something about Blade 2 Tip motions (array elements 40 to 51) I’d need to renumber about 482 variables or play about with excel to help a little. (actually excel makes it fairly easy)

The size of AllOuts () needs to be tweaked a little to cater for my new variable. There is a note elsewhere not to exceed 1000.

...
!jmj   general purpose, quasi-static solution based on the analytical catenary
!jmj   cable equations with seabed interaction:
!remove6.02bREAL(ReKi)                   :: AllOuts  (0:389)                                ! An array holding the value of all of the calculated (not selected) output channels.

REAL(ReKi)                   :: AllOuts  (0:544)                                ! An array holding the value of all of the calculated (not selected) output channels.

!jmj End of proposed change.  v6.02b-jmj  15-Nov-2006.
!jmj End of proposed change.  v6.02a-jmj  25-Aug-2006.
...
! SEE NOTE ABOVE FOR SIZE (DIMENSION) OF THE PREVIOUS VARIABLE:

Next in FAST_IO in Subroutine ChckOutLst:

   CASE ( 'FUVAR1' )
      OutInd  (I)       = FUVar1
      OutParam(I)%Units = '(user set)'

The string of the CASE seems to need to be in uppercase, even though my variable name isn’t. A quick hunt on google didn’t reveal any reason why. The case variable needs to match the text in the .fst file under the OUTPUT section, and you’d be nuts not to make it the same as your variable name.

I wanted to have FUVar1 calculated in BladedDLLInterface, but AllOuts() would need to be added under the declaration as INTENT (out), so it will be worthwhile passing it through an intermediate variable.

For making a new input:
You need to declare it with all the other inputs in FAST_Mods, a good place would be MODULE TurbCont, but I made a new one, MODULE Faults:

Module Faults

USE Precision

INTEGER(4)                   :: FltCndtn                                        ! The fault condition to be simulated

END MODULE Faults

In FAST_IO, SUBROUTINE GetPrimary, there is code that makes a list that matches the variables in the .fst file. Find where you want to add your new variable in the .fst file and add your code in a similar location. Here’s mine:

   ! FltCndtn - Fault Condition to be simulated.

CALL ReadIVar ( UnIn, PriFile, FltCndtn, 'FltCndtn', 'Fault Condition to be applied' )

IF ( ( Cmpl4SFun ) .AND. ( VSContrl /= 0 ) )  THEN
   CALL ProgAbort ( ' FltCndtn can only equal 0 (no fault) when FAST is interfaced with Simulink.' )
ENDIF

If I was feeling fancy I could do something like you did in AeroSubs.f90 with the tower shadow:

!bjj: this is a hack job to allow both the new tower influence and the old tower wake models to be used
!rm   CALL ReadStr( UnIn, AeroInFile, Line, 'TwrShad', 'Tower shadow deficit', ErrStat )
   CALL ReadStr( UnIn, AeroInFile, Line, 'NewTowerModel?', 'Check for tower influence model', ErrStat )
   IF ( ErrStat /= 0 ) RETURN

      ! Check if this is the "special string" to indicate the new tower influence model
   CALL Conv2UC( Line )
   IF ( INDEX(Line, "NEWTOWER" ) > 0 ) THEN

      !----------------------------------------------------------------------------------------------
      ! New tower influence model, as implemented by PJM
      !----------------------------------------------------------------------------------------------
      PJM_Version = .TRUE.


         ! Read in the tower potential flow switch
      CALL ReadVar( UnIn, AeroInFile, TwrPotent, 'TwrPotent', 'Tower influence model', ErrStat)
      IF ( ErrStat /= 0 ) RETURN

         ! Read in the tower shadow switch
      CALL ReadVar( UnIn, AeroInFile, TwrShadow, 'TwrShadow', 'Tower shadow model', ErrStat)
      IF ( ErrStat /= 0 ) RETURN

         ! Read in the tower drag file name
      CALL ReadVar( UnIn, AeroInFile, TwrFile, 'TwrFile', 'Tower data file name', ErrStat)
      IF ( ErrStat /= 0 ) RETURN

   ELSE
      !----------------------------------------------------------------------------------------------
      ! Old tower influence model, read TwrShad from Line for now
      !----------------------------------------------------------------------------------------------
      PJM_Version = .FALSE.

      TwrPotent = .FALSE.     ! We don't want to read the tower file!
      TwrShadow = .FALSE.     ! We don't want to read the tower file!

!
!         ! Read in the tower shadow deficit
!      CALL ReadVar( UnIn, AeroInFile, TwrShad, 'TwrShad', 'Tower shadow deficit', ErrStat)
!      IF ( ErrStat /= 0 ) RETURN

         ! Read in the tower shadow deficit
      IF ( INDEX( 'FTft', Line(:1) ) > 0 )  THEN
         CALL ProgWarn( ' Invalid numeric input. "'//TRIM( Line )//'" found when trying to read TwrShad.' )

         ErrStat = 1
         RETURN
      ELSE
         READ (Line,*,IOSTAT=ErrStat)  TwrShad
         CALL CheckIOS ( ErrStat, AeroInFile, 'TwrShad', NumType, .TRUE. )

         IF ( Echo )  THEN
            WRITE (UnEc,"( 2X, ES11.4e2, 2X, A, T30, ' - ', A )")  TwrShad, "TwrShad", 'Tower shadow deficit'
         END IF

      END IF
!----------------

      IF ( TwrShad >= 1.0 ) THEN
         CALL ProgWarn( ' Tower shadow deficit cannot be >= 1.  Setting default deficit = 0.3' )
         TwrShad = 0.3
      END IF


         ! Read in the tower shadow width
      CALL ReadVar( UnIn, AeroInFile, ShadHWid, 'ShadHWid', 'Tower shadow half width', ErrStat)
      IF ( ErrStat /= 0 ) RETURN

      IF ( ShadHWid <= 0.0 ) THEN
         CALL ProgWarn( ' Tower shadow width cannot be <= zero.  Setting default half width = 1.0' )
         ShadHWid = 1.0
      END IF


         ! Read in the tower shadow reference point (distance from yaw axis to hub)
      CALL ReadVar( UnIn, AeroInFile, T_Shad_Refpt, 'T_Shad_Refpt', 'Tower shadow reference point', ErrStat)
      IF ( ErrStat /= 0 ) RETURN

         ! Constants used in tower shadow calculations
      TShadC1 = ShadHWid / SQRT ( ABS( T_Shad_Refpt ) )
      TShadC2 = TwrShad  * SQRT ( ABS( T_Shad_Refpt ) )

   END IF

!bjj end

But I’m not. Do you think it would be worth getting the FAST executable version number to be an input in the .fst file to check for this sort of issue? It could give a warning if the .fst file didn’t match the version of the .exe…

Thanks for your help.

Alec

Alec,

The call to ReadIVar takes care of all the I/O error checking for you for stuff such as invalid numeric input. The next version of AeroDyn will no longer do that and will call ReadVar instead. You will still need to sanity-check your data. For instance, tower shadow should never be negative.

We have more-recently added the overloaded routine ReadVar, which checks for the argument type to determine which actual routine to execute. So, if the third argument in the call is an integer, it will execute ReadIVar for you. This works the same way built-in functions work. For instance, ABS will take the absolute value of any argument type.

I’ll let Jason respond to the other, more FAST-related questions.

Hi Alec,

It looks like you are using code before FAST v7.01.00a-bjj. If you use FAST v7.01.00a-bjj, we have a Matlab script that reads the “OutListParameters.xlsx” file included in the FAST archive and creates the code for output variables in the Output MODULE in FAST_Mods and SUBROUTINE ChckOutLst() in FAST_IO.f90. However, I have made some comments below on the pre-v7.01.00a-bjj code:

Correct. For a complete list of the rules for output names, see the “Instructions” worksheet in the “OutListParameters.xlsx” file included in the FAST archive.

Not necessarially. It would probably look nicer to have the Blade 2 Tip motions all defined together and may be more efficient computationally, but because you will (should) access the array element using the parameter names, it doesn’t really matter what the parameter values are.

Yes; the AllOuts array needs to be dimensioned the correct size for the outputs being stored. The reason for the limit of 1000 has to do with the FAST2ADAMS preprocessor. (FAST v7.01.00a-bjj modifies the variable MaxOutPts when new outputs are added.)

The code you show has been replaced in FAST v7.01.00a-bjj.

The strings from the input file are converted to upper case using the Conv2UC() function. Fortran distinguishes between upper/lower case when you compare strings (ASCII characters), so, we convert everything to uppercase before comparing values.

The remaining comments apply to FAST 7.00.00a-bjj and later versions:

As Marshall suggested, I would use ReadVar instead of ReadIVar. I assume you intended to use FltCndtn instead of VSContrl in the IF statement in your code. Is there a reason FltCndtn must be 0 with Simulink?

In general, I don’t recommend copying features that I’ve commented with “this is a hack job.” Besides, Marshall really doesn’t like this particular feature. :stuck_out_tongue:

We’ve considered this before, but it can get tricky to implement. For now, we leave it up to the user to check (you can usually figure this out pretty quickly if the program aborts!). We try to keep up-to-date version numbers in the certification tests, but you don’t want to require people to change version numbers in all of their input files if the input file hasn’t changed.

Make sure you change the version number of the code you are modifying. When your new code has changes to the input file, you should modify the number between the two decimals in the version number.

Hi Bonnie and Marshall

Thanks for the thorough responses.

I’ll check out the overloaded ReadVar routine.

Yes, I did mean to change from VSControl to FltCndtn.

I plan to implement the faults in BladedDLLInterface for now, and I suspect they will be ignored if FAST is compiled for simulink due to different external communication methods.

Alec

Hi Bonnie

I hope you are well.

You said

Do you have a document that I could look through that describes a FAST version release process? We’ve made a few tweaks and we ought to go through a release process before we start using FAST with projects. I’m generally lazy and like to copy other peoples work rather than do something myself so I thought I’d ask.

Thanks

Alec

Dear Alec,

We are working to publish a programmer’s handbook for FAST and other software. This handbook describes the version release process and a lot more. For more information, please see my August 24, 2012 post in the forum topic found here: http://forums.nrel.gov/t/modeling-workshops/523/1.

Best regards,

In a parallel thread, I have mentioned that I am writing new code to implement tower wind loads due to aerodynamic drag.

http://forums.nrel.gov/t/normal-aerodynamic-forces-on-the-tower/379/1

I need to output all the external loads applied to the tower. I propose to add internal time-dependent signals to FAST.OUT.

WIth respect to replies on this thread to Alec’s questions about adding new output signals, I would like to run the MATLAB script, but cannot find it in the FAST release. Please can you (1) tell me the name of the file, (2) tell me where it is in the FAST release or (2) send me a copy? Thanks.

Kind Regards,

Mark

Here are all the MATLAB .m files in the FAST archive.

Hi, Mark.

Here is a link to the Matlab script that generates Fortran code for the FAST and HydroDyn outputs: wind.nrel.gov/public/bjonkman/De … ckOutLst.m

It is not documented incredibly well, but the only thing you should need to change is the location of the OutListParameters.xls file for FAST (near the top of the script). The script then reads the Excel file (with additional outputs that you’ve specified in it) and creates two f90 files. The code in one file (Mods_MatlabGen.f90) will replace part of the Output module in FAST_Mods.f90, and the other (ChckOutLst_MatlabGen.f90) replaces part of subroutine ChckOutLst in FAST_IO.f90. Search for “Matlab” to see the start/end of what needs to be replaced. I think I ended up replacing some of the checks at the end of the auto-generated ChckOutLst code to make it more efficient, so you will probably want to just delete that part of the auto-generated code when you paste it into FAST_IO.f90. Take a look at the source code to see what I’m talking about.

Thanks a lot for the MATLAB script. I am still waiting for MATLAB to be installed on my local machine so that I can start using it!

I want to add output for all the locations at which externally-applied tower wind (aerodynamic drag) loads are calculated. There will be TwrNodes channels for every simulation. I don’t just want one channel per output gauge. I want these data so that I can apply identical loads to a parallel model of the structural dynamics in SACS.

Can I use a single switch in the FST file, to activate or suppress the output of these data without actually specifying all the locations separately at which the external loads are to be output?

Ideally, I want to make space in AllOuts for all the channels as a single matrix (TwrNodes × num_time_steps). This matrix would be converted into TwrNodes separate time series in the output file. If I have to make additional spaces in AllOuts for every new time series, there will be an additional TwrNodes new rows (there is no upper limit on the number of tower nodes allowed in the model).

Kind Regards,

Mark

Hi, Mark.

The code doesn’t currently let you specify multiple channels with a single switch, though we have talked about adding a similar feature. You’d have to either specify them all by name, or you could modify the code so that if it saw something like ChanName* in the input file it would automatically select all the ChanName1, ChanName2, ChanName3, … output channels. It would be easiest to set some limit on how large TwrNodes can be, but with some coding effort, you could allow it to be arbitrary size.

If you don’t want to use the tower gage nodes, you will just have to make sure you use TwrNodes instead of NTwGages for the DO loop and don’t use TwrGagNd as an array index when you set the AllOuts(ChanName*) values.