diff --git a/CMakeLists.txt b/CMakeLists.txt index 736de6c9b3..257aa06029 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,7 @@ set(OPENFAST_MODULES inflowwind extloads aerodyn + aerodisk servodyn elastodyn beamdyn @@ -209,6 +210,7 @@ set(OPENFAST_MODULES supercontroller externalinflow openfast-library + simple-elastodyn ) set(OPENFAST_REGISTRY_INCLUDES "" CACHE INTERNAL "Registry includes paths") diff --git a/docs/OtherSupporting/AeroDisk/AeroDisk_Plan_Rev2.doc b/docs/OtherSupporting/AeroDisk/AeroDisk_Plan_Rev2.doc new file mode 100644 index 0000000000..4c0cb580b8 Binary files /dev/null and b/docs/OtherSupporting/AeroDisk/AeroDisk_Plan_Rev2.doc differ diff --git a/docs/OtherSupporting/OutListParameters.xlsx b/docs/OtherSupporting/OutListParameters.xlsx index 20c6204683..8d622a50d7 100644 Binary files a/docs/OtherSupporting/OutListParameters.xlsx and b/docs/OtherSupporting/OutListParameters.xlsx differ diff --git a/docs/source/user/aerodisk/index.rst b/docs/source/user/aerodisk/index.rst new file mode 100644 index 0000000000..816a0cd27b --- /dev/null +++ b/docs/source/user/aerodisk/index.rst @@ -0,0 +1,19 @@ +.. _ADsk: + +AeroDisk +======== + + +This document describes the AeroDisk (ADsk) module. + + +.. only:: html + + +.. toctree:: + :maxdepth: 2 + + input_files.rst + +.. + zrefs.rst diff --git a/docs/source/user/aerodisk/input_files.rst b/docs/source/user/aerodisk/input_files.rst new file mode 100644 index 0000000000..7cd1593c0c --- /dev/null +++ b/docs/source/user/aerodisk/input_files.rst @@ -0,0 +1,165 @@ +.. _adsk_input-files: + +Input and Output Files +====================== + + +Units +----- + +AeroDisk uses the SI system (kg, m, s, N). + +.. _adsk_input-file: + +Input file +---------- + +The AeroDisk input file defines the general inputs required for the actuator +disk calculations. The following inputs may be changed by the user to achieve +the desired behaviour. + +Simulation Control +~~~~~~~~~~~~~~~~~~ + +**echo** [switch] + + Write the input file contents to a file .ADsk.ech. This is useful + for diagnosing errors reported about the input file. + +**DT** [seconds] + + Integration time step for AeroDisk to use, or _"default"_ to use the glue code + time step. + +Environmental Conditions +~~~~~~~~~~~~~~~~~~~~~~~~ + +**AirDens** [kg/m^3] + + Air density, or _"default"_ to use the air density from the glue code + +Actuator Disk Properties +~~~~~~~~~~~~~~~~~~~~~~~~ + +**RotorRad** [m] + + Radius of the rotor, or _"default"_ to use the value passed from the glue + code + +The lookup table for the disk actuator forces and moments follows. The data in +this table is flexible as it allows for a very simple lookup based on a single +variable (**TSR** for example), or up to four variables. The last six columns +of the table must include the six force and moment coefficients that correspond +to a set of conditions given in the first set of columns. + +**InColNames** [-] + + Comma separated List of column names corresponding to the variable columns in + the input file. See below for options. + +**InColDims** [-] + + Comma separted list of the number unique entries for each of the named + variable column names. The number of rows in the table must be equal to the + product of all numbers given. Must be the same number of entries as given in + **InColNames** + + +For the input variable columns in the table, at least one column must be given, +with a maximum of four of the five listed below (**TSR** and **RtSpd** are +mutually exclusive). + +**TSR** [-] + + Tip Speed Ratio, cannot be used with _RtSpd_ + +**RtSpd** [rpm] + + Rotor speed, cannot be used with _TSR_ + +**VRel** [m/s] + + Relative velocity of wind normal to rotor + +**Pitch** [deg] + + Collective blade pitch + +**Skew** [deg] + + Skew angle of inflow. If this is not provided, the affect of skew is modeled + as :math:`(cos(\chi))^2` + + +The remaining six columns of the table must contain the force and moment +coefficents. See the example table below. + + + +Sample input file +~~~~~~~~~~~~~~~~~ + +Note that the table given below is for illustration of the format and does not +represent any particular turbine. + +.. code:: + + --- AERO DISK INPUT FILE ------- + Sample actuator disk input file + --- SIMULATION CONTROL --------- + FALSE echo - Echo input data to ".ADsk.ech" (flag) + "default" DT - Integration time step (s) + --- ENVIRONMENTAL CONDITIONS --- + 1.225 AirDens - Air density (kg/m^3) (or "default") + --- ACTUATOR DISK PROPERTIES --- + 63.0 RotorRad - Rotor radius (m) (or "default") + "RtSpd,VRel" InColNames - Input column headers (string) {may include a combination of "TSR, RtSpd, VRel, Pitch, Skew"} (up to 4 columns) [choose TSR or RtSpd,VRel; if Skew is absent, Skew is modeled as (COS(Skew))^2] + 9,2 InColDims - Number of unique values in each column (-) (must have same number of columns as InColName) [each >=2] + RtSpd VRel C_Fx C_Fy C_Fz C_Mx C_My C_Mz + (rpm) (m/s) (-) (-) (-) (-) (-) (-) + 3.0 9.0 0.2347 0.0 0.0 0.0306 0.0 0.0 + 4.0 9.0 0.2349 0.0 0.0 0.0314 0.0 0.0 + 5.0 9.0 0.2350 0.0 0.0 0.0322 0.0 0.0 + 6.0 9.0 0.2351 0.0 0.0 0.0330 0.0 0.0 + 7.0 9.0 0.2352 0.0 0.0 0.0338 0.0 0.0 + 8.0 9.0 0.2352 0.0 0.0 0.0346 0.0 0.0 + 9.0 9.0 0.2351 0.0 0.0 0.0353 0.0 0.0 + 10.0 9.0 0.2350 0.0 0.0 0.0361 0.0 0.0 + 11.0 9.0 0.2349 0.0 0.0 0.0368 0.0 0.0 + 3.0 12.0 0.7837 0.0 0.0 0.0663 0.0 0.0 + 4.0 12.0 0.7733 0.0 0.0 0.0663 0.0 0.0 + 5.0 12.0 0.7628 0.0 0.0 0.0663 0.0 0.0 + 6.0 12.0 0.7520 0.0 0.0 0.0662 0.0 0.0 + 7.0 12.0 0.7409 0.0 0.0 0.0660 0.0 0.0 + 8.0 12.0 0.7297 0.0 0.0 0.0658 0.0 0.0 + 9.0 12.0 0.7182 0.0 0.0 0.0656 0.0 0.0 + 10.0 12.0 0.7066 0.0 0.0 0.0653 0.0 0.0 + 11.0 12.0 0.6947 0.0 0.0 0.0649 0.0 0.0 + --- OUTPUTS -------------------- + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) + END of input file (the word "END" must appear in the first 3 columns of this last OutList line) + -------------------------------- + + + + + + +.. _adsk_outputs: + +Outputs +------- + +The write outputs are: + - "ADSpeed": Actuator disk rotational speed (rpm) + - "ADTSR": Actuator disk tip-speed ratio (-) + - "ADPitch": Actuator-disk collective blade-pitch angle (deg) + - "ADVWindx, ADVWindy, ADVWindz": Actuator-disk-averaged wind velocity in the local coordinate system (m/s) + - "ADSTVx, ADSTVy, ADSTVz": Actuator-disk structural translational velocity in the local coordinate system (m/s) + - "ADVRel": Actuator-disk-averaged relative wind speed (m/s) + - "ADSkew": Actuator-disk inflow-skew angle (deg) + - "ADCp, ADCt, ADCq": Actuator-disk power, thrust, and torque coefficients (-) + - "ADFx, ADFy, ADFz": Actuator disk aerodynamic force loads in the local coordinate system (N) + - "ADMx, ADMy, ADMz": Actuator disk aerodynamic moment loads in the local coordinate system (N-m) + - "ADPower": Actuator disk power (W) + diff --git a/docs/source/user/aerodyn/examples/ad_primary_example.dat b/docs/source/user/aerodyn/examples/ad_primary_example.dat index 8e76523d9c..6505544349 100644 --- a/docs/source/user/aerodyn/examples/ad_primary_example.dat +++ b/docs/source/user/aerodyn/examples/ad_primary_example.dat @@ -49,8 +49,8 @@ True AoA34 - Sample the angle of attack (AoA) at the 3/4 c 3 UA_Mod - Unsteady Aero Model Switch (switch) {0=Quasi-steady (no UA), 2=B-L Gonzalez, 3=B-L Minnema/Pierce, 4=B-L HGM 4-states, 5=B-L HGM+vortex 5 states, 6=Oye, 7=Boeing-Vertol} True FLookup - Flag to indicate whether a lookup for f' will be calculated (TRUE) or whether best-fit exponential equations will be used (FALSE); if FALSE S1-S4 must be provided in airfoil input files (flag) [used only when UA_Mod=2 or UA_Mod=3] 3 IntegrationMethod - Switch to indicate which integration method UA uses (1=RK4, 2=AB4, 3=ABM4, 4=BDF2) - 0 UAStartRad - Starting radius for dynamic stall (fraction of rotor radius [0.0,1.0]) [used only when UA_Mod=2; if line is missing UAStartRad=0] - 1 UAEndRad - Ending radius for dynamic stall (fraction of rotor radius [0.0,1.0]) [used only when UA_Mod=2; if line is missing UAEndRad=1] + 0 UAStartRad - Starting radius for dynamic stall (fraction of rotor radius [0.0,1.0]) [used only when UA_Mod>0; if line is missing UAStartRad=0] + 1 UAEndRad - Ending radius for dynamic stall (fraction of rotor radius [0.0,1.0]) [used only when UA_Mod>0; if line is missing UAEndRad=1] ====== Airfoil Information ========================================================================= 1 AFTabMod - Interpolation method for multiple airfoil tables {1=1D interpolation on AoA (first table only); 2=2D interpolation on AoA and Re; 3=2D interpolation on AoA and UserProp} (-) 1 InCol_Alfa - The column in the airfoil tables that contains the angle of attack (-) diff --git a/docs/source/user/api_change.rst b/docs/source/user/api_change.rst index 22371f694f..b9ce706d29 100644 --- a/docs/source/user/api_change.rst +++ b/docs/source/user/api_change.rst @@ -10,6 +10,7 @@ The line number corresponds to the resulting line number after all changes are i Thus, be sure to implement each in order so that subsequent line numbers are correct. + OpenFAST v3.5.3 to OpenFAST dev ---------------------------------- @@ -17,12 +18,15 @@ The HydroDyn module was split into HydroDyn and SeaState. This results in a completely new input file for SeaState, and complete revision of the HydroDyn input file. See examples in the regression tests for the new formats. +New modules AeroDisk (see :numref:`ADsk`) and Simplified-ElastoDyn (see :numref:`SED`). + ============================================= ======= ==================== ======================================================================================================================================================================================================== Modified in OpenFAST `dev` ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Module Line Flag Name Example Value ============================================= ======= ==================== ======================================================================================================================================================================================================== -OpenFAST 15 CompAero\** 2 CompAero - Compute aerodynamic loads (switch) {0=None; 2=AeroDyn v15} +OpenFAST 15 CompAero\** 2 CompAero - Compute aerodynamic loads (switch) {0=None; 1=AeroDisk; 2=AeroDyn; 3=ExtLoads} +OpenFAST 13 CompElast 3 CompElast - Compute structural dynamics (switch) {1=ElastoDyn; 2=ElastoDyn + BeamDyn for blades; 3=Simplified ElastoDyn} AeroDyn 40 IntegrationMethod 3 IntegrationMethod - Switch to indicate which integration method UA uses (1=RK4, 2=AB4, 3=ABM4, 4=BDF2) AeroDyn 140\* BldNd_BlOutNd "All" BldNd_BlOutNd - Specify a portion of the nodes to output. {"ALL", "Tip", "Root", or a list of node numbers} (-) ElastoDyn blade file 15 Removal of the `PitchAxis` input column @@ -51,7 +55,14 @@ AeroDyn 82\* NacDragAC 0, 0, \*Exact line number depends on number of entries in various preceeding tables. -\*\* The AeroDyn 14 module has been removed. AeroDyn15 renamed to AeroDyn +\*\* The AeroDyn 14 module has been removed and replaced with AeroDisk. AeroDyn15 renamed to AeroDyn + +New Modules +~~~~~~~~~~~ + +- AeroDisk -- reduced order actuator disk model (see :numref:`ADsk`) +- Simplified ElastoDyn -- a reduced order structural model with only yaw and rotor speed degrees of freedom (see :numref:`SED`) +- SeaState -- wave dynamics calculations (previously part of HydroDyn) .. _api_change_ad4x: diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index d67e9a2648..f2d22f797d 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -17,6 +17,7 @@ This section contains documentation for the OpenFAST module-coupling environment AeroDyn OLAF Aeroacoustics + AeroDisk BeamDyn SubDyn ExtPtfm @@ -26,6 +27,7 @@ This section contains documentation for the OpenFAST module-coupling environment InflowWind MoorDyn ServoDyn + Simplified ElastoDyn Structural Control TurbSim FAST.Farm diff --git a/docs/source/user/simplified_elastodyn/_implementation.rst.unused b/docs/source/user/simplified_elastodyn/_implementation.rst.unused new file mode 100644 index 0000000000..84a9ef4f05 --- /dev/null +++ b/docs/source/user/simplified_elastodyn/_implementation.rst.unused @@ -0,0 +1,175 @@ + +Implementation +============== + + +Datatypes +---------- + +**InitInput**: + +.. code:: + + CHARACTER(1024) InputFile - - - "Name of the input file" - + CHARACTER(1024) RootName - - - "RootName for writing output files" + + +**InitOutput**: + +The module returns init outputs needed for AeroDyn and ServoDyn, respecting the conventions of ElastoDyn. + +.. code:: + + CHARACTER(ChanLen) WriteOutputHdr {:} - - "Names of the output-to-file channels" - + CHARACTER(ChanLen) WriteOutputUnt {:} - - "Units of the output-to-file channels" - + IntKi NumBl - - - "Number of blades on the turbine" - + ReKi BlPitch - - - "Initial blade pitch angle" rad + ReKi TowerHeight - - - "Tower Height" m + ReKi HubHt - - - "Height of the hub" m + ReKi PlatformPos {6} - - "Initial platform position (6 DOFs)" + ReKi HubRad - - - "Preconed hub radius (distance from the rotor apex to the blade root)" m + ReKi BladeLength - - - "Blade length (for AeroDyn)" m + ReKi RotSpeed - - - "Initial or fixed rotor speed" rad/s + LOGICAL isFixed_GenDOF - - - "Whether the generator is fixed or free" - + + + + +**Inputs**: + +Note: the yaw rate is only used to setup the velocities on the meshes. + +.. code:: + + ReKi AerTrq - - - "Aerodynamic torque" N-m + ReKi HSSBrTrqC - - - "High speed side brake torque" N-m + ReKi GenTrq - - - "Electrical generator torque on HSS" N-m + ReKi BlPitchCom {:} - 2pi "Commanded blade pitch angles" rad + ReKi Yaw - - - "Yaw angle" rad + ReKi YawRate - - - "Yaw rate" rad/s + + +**Outputs**: + + +The module returns outputs needed for AeroDyn and ServoDyn, respecting the conventions of ElastoDyn and using the proper units. +Towerline, nacelle and hub are needed for AeroDyn. Platform mesh is for possible future implementation with HydroDyn. + +Note: LSSTipPxa is obtained from the state, but modulo 2 pi + +Note: additional scalar outputs may be needed by ServoDyn. + +.. code:: + + MeshType BladeRootMotion {:} - - "For AeroDyn/BeamDyn: motions at the blade roots" + MeshType HubPtMotion - - - "For AeroDyn and Lidar(InflowWind): motions of the hub" + MeshType NacelleMotion - - - "For AeroDyn14 & ServoDyn/TMD: motions of the nacelle." + MeshType TowerLn2Mesh - - - "Tower line2 mesh with positions/orientations/velocities/accelerations" - + MeshType PlatformPtMesh - - - "Platform reference point positions/orientations/velocities/accelerations" - + ReKi LSSTipPxa - - 2pi "Rotor azimuth angle (position)" radians + ReKi RotSpeed - - - "Rotor azimuth angular speed" rad/s + ReKi WriteOutput {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" + ReKi HSS_Spd - - - "High-speed shaft (HSS) speed" rad/s + + +**States**: + +.. code:: + + R8Ki QT {1} - - "Current estimate of Q (displacement) for each degree of freedom" - + R8Ki QDT {1} - - "Current estimate of QD (velocity) for each degree of freedom" + + +**Misc**: + +None anticipated at the moment. + +**Parameters**: + +.. code:: + + + R8Ki DeltaT - - - "Time step for module time integration" s + IntKi IntMethod - - - "Integration method {1: RK4, 2: AB4, or 3: ABM4}" - + ReKi J_DT - - - "Drivetrain inertia (blades+hub+shaft+generator) kgm^2 + ReKi PtfmPitch - - - "Static platform tilt angle" rad + MeshMapType mapPtf2Twr - - - "Mesh mapping from Ptfm to Tower line" - + MeshMapType mapTwr2Nac - - - "Mesh mapping from Tower to Nacelle" - + MeshMapType mapNac2Hub - - - "Mesh mapping from Nacelle to Hub" - + LOGICAL isFixed_GenDOF - - - "whether the generator is fixed or free" - + +.. + ReKi GBoxEff - - - "Gear box efficiency" - + +**InputFileInput** + +.. code:: + + R8Ki DeltaT - - - "Time step for module time integration" s + IntKi IntMethod - - - "Integration method {1: RK4, 2: AB4, or 3: ABM4}" - + LOGICAL GenDOF - - - "whether the generator is fixed or free" - + R8Ki Azimuth - - - "Initial azimuth angle for blade 1" deg + ReKi BlPitch - - - "Initial blade pitch angles" radians + ReKi RotSpeed - - - "Initial or fixed rotor speed" RPM + ReKi PtfmPitch - - - "Initial platform position (6 DOFs)" + IntKi NumBl - - - "Number of blades on the turbine" - + ReKi TipRad - - - "Preconed blade-tip radius (distance from the rotor apex to the blade tip)" m + ReKi HubRad - - - "Preconed hub radius (distance from the rotor apex to the blade root)" m + ReKi PreCone - - - "Rotor precone angles" deg + ReKi OverHang - - - "Distance from yaw axis to rotor apex or teeter pin" m + ReKi ShftTilt - - - "Rotor shaft tilt angle" deg + ReKi Twr2Shft - - - "Vertical distance from the tower-top to the rotor shaft" m + ReKi TowerHt - - - "Height of tower above ground level [onshore] or MSL [offshore]" m + ReKi RotIner - - - "Hub inertia about teeter axis (2-blader) or rotor axis (3-blader)" "kg m^2" + ReKi GenIner - - - "Generator inertia about HSS" "kg m^2" + LOGICAL SumPrint - - - "Print summary data to .sum" - + ReKi GBRatio - - - "Gearbox ratio" - + +.. + ReKi GBoxEff - - - "Gearbox efficiency" % + + +Workflow of main routines +------------------------- + +Init +~~~~ + +- Read input file +- Transfer InputFile data to parameters +- Set reference positions of meshes + - Set `PlatformPtMesh` (at (0,0,0)) + - Set `TowerL2Mesh` using two nodes at `PlatformPtMesh` and at the `TowerHt` + - Set `NacellePtMotion`, based on `NacYaw` and last point of `TowerL2Mesh` + - Set `HubPtMotion` based on geometry (`Twr2Shft`, `Tilt`, `OverHang`, and zero azimuth) + - Set `BladeRootMotion`, distributing the blades azimuthally based on the number of blades. +- Define mesh mappings: + - Set mesh mapping between `PlatformPtMesh` and `TowerL2Mesh` + - Set mesh mapping between `TowerL2Mesh` and `NacellePtMotion` + - Set mesh mapping between `NacellePtMotion` and `HubPtMotion` + - Set mesh mapping between `HubPtMotion` and `BladeRootMotion` + +- Return quantities needed by AeroDyn and ServoDyn + + +UpdateStates +~~~~~~~~~~~~ + +If the generator degrees of freedom is on (`isFixed_GenDOF`) , this routine calls one of the time integration method, based on `IntMethod`, each calling the function `CalcConStateDerivative`. +Otherwise, compute states based on Eq. :eq:`sed_stateEqGenDOF`. + +CalcConStateDerivative +~~~~~~~~~~~~~~~~~~~~~~ + +Returns derivative of states according to Eq. :eq:`sed_stateEq`. + + + +CalcOutput +~~~~~~~~~~ + +- Set relative motions and successively update meshes using mapping: + - Set relative motion of `NacellePtMotion` based on yaw and yawrate + - Set relative motion of `HubPtMotion` using :math:`\psi` & :math:`\dot{\psi}` + - Set relative motion of `BladeRootMotion` based on `BlPitchCom` +- Compute `Outputs` and `WriteOutputs` ("Azimuth", "RotSpeed", "RotAcc", "GenSpeed", "GenAcc") from states and inputs. diff --git a/docs/source/user/simplified_elastodyn/index.rst b/docs/source/user/simplified_elastodyn/index.rst new file mode 100644 index 0000000000..49e2238c2a --- /dev/null +++ b/docs/source/user/simplified_elastodyn/index.rst @@ -0,0 +1,30 @@ +.. _SED: + +Simplified ElastoDyn +====================================== + + +This document describes the Simplified ElastoDyn (SED) module. +This module has the features the following: + +- The rotor is rigid +- One degree of freedom is used, representing the variable rotor speed +- No calculation of structural loads +- Possibility to be run with large time step +- A constant platform tilt angle +- Compatibility with AeroDyn and AeroDisk +- Compatibility with active control of torque and yaw (and pitch) + + + +.. only:: html + + +.. toctree:: + :maxdepth: 2 + + theory.rst + input_files.rst + +.. + zrefs.rst diff --git a/docs/source/user/simplified_elastodyn/input_files.rst b/docs/source/user/simplified_elastodyn/input_files.rst new file mode 100644 index 0000000000..f664fb8356 --- /dev/null +++ b/docs/source/user/simplified_elastodyn/input_files.rst @@ -0,0 +1,71 @@ +.. _sed_input-files: + +Input and Output Files +====================== + + +Units +----- + +SED uses the SI system (kg, m, s, N). + +.. _sed_input-file: + +Input file +---------- + + +.. code:: + + ------- SIMPLIFIED ELASTODYN INPUT FILE ---------------------------------------- + Comment + ---------------------- SIMULATION CONTROL -------------------------------------- + False Echo - Echo input data to ".ech" (flag) + 3 Method - Integration method: {1: RK4, 2: AB4, or 3: ABM4} (-) + "default" DT - Integration time step (s) + ---------------------- DEGREES OF FREEDOM -------------------------------------- + True GenDOF - Generator DOF (flag) + ---------------------- INITIAL CONDITIONS -------------------------------------- + 0 Azimuth - Initial azimuth angle for blades (degrees) + 0 BlPitch - Blades initial pitch (degrees) + 0.0 RotSpeed - Initial or fixed rotor speed (rpm) + 0 NacYaw - Initial or fixed nacelle-yaw angle (degrees) + 0 PtfmPitch - Fixed pitch tilt rotational displacement of platform (degrees) + ---------------------- TURBINE CONFIGURATION ----------------------------------- + 3 NumBl - Number of blades (-) + 63 TipRad - The distance from the rotor apex to the blade tip (meters) + 1.5 HubRad - The distance from the rotor apex to the blade root (meters) + -2.5 PreCone - Blades cone angle (degrees) + -5.0191 OverHang - Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades] (meters) + -5 ShftTilt - Rotor shaft tilt angle (degrees) + 1.96256 Twr2Shft - Vertical distance from the tower-top to the rotor shaft (meters) + 87.6 TowerHt - Height of tower above ground level [onshore] or MSL [offshore] (meters) + ---------------------- MASS AND INERTIA ---------------------------------------- + 115926 RotIner - Rot inertia about rotor axis [blades + hub] (kg m^2) + 534.116 GenIner - Generator inertia about HSS (kg m^2) + ---------------------- DRIVETRAIN ---------------------------------------------- + 100 GBoxEff - Gearbox efficiency (%) + 97 GBRatio - Gearbox ratio (-) + ---------------------- OUTPUT -------------------------------------------------- + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) + "Azimuth" - Blades azimuth angle + "RotSpeed" - Low-speed shaft rotational speed + "RotAcc" - Low-speed shaft rotational acceleration + END of input file (the word "END" must appear in the first 3 columns of this last OutList line) + ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + + + + +.. _sed_outputs: + +Outputs +------- + +The write outputs are: + - "Azimuth" : Blades azimuth angle (deg) between 0 and 2pi + - "RotSpeed": Low-speed shaft rotational speed (rpm) + - "RotAcc": Low-speed shaft rotational acceleration (rad/s^2) + - "GenSpeed": High-speed shaft rotational speed (rpm) + - "GenAcc": High-speed shaft rotational acceleration (rad/s^2) diff --git a/docs/source/user/simplified_elastodyn/theory.rst b/docs/source/user/simplified_elastodyn/theory.rst new file mode 100644 index 0000000000..806cbbfa55 --- /dev/null +++ b/docs/source/user/simplified_elastodyn/theory.rst @@ -0,0 +1,67 @@ +.. _sed-theory: + +Theory +============= + +In this module, the rotor is represented by a rigid disk. + + +The module has two states :math:`\psi` and :math:`\dot{\psi}`, corresponding to the azimuthal angle and the rotor speed. (Note: introducing the azimuthal angle as a state is optional, but convenient for coupling with AeroDyn. Such introduction should not have any influence on the time-step required since both equations are effictively decoupled.). +The state-space equations are: + +.. math:: :label: sed_stateEq + + \begin{aligned} + \dot{\psi} & = \dot{\psi} \\ + \ddot{\psi} & = \frac{1}{J_\text{DT}}\left( Q_g - Q_a + Q_b\right) + \end{aligned} + +where :math:`J_{DT}` is the total inertia of the drivetrain (blades+hub+generator), :math:`Q_g`, :math:`Q_a` and :math:`Q_b` are the generator, aerodynamic and brake torque respectively, all expressed on the low-speed-shaft (LSS) side. +The total inertia of the drivetrain is obtained as: + +.. math:: :label: sed_JDT + + J_\text{DT} = J_r + n_g^2 J_{g,HSS} + +where :math:`J_r` is the inertia of the rotor (blades+hub+"shaft"), +:math:`n_g` is the gear ratio of the gearbox +and :math:`J_{g,HSS}` is the inertia of the generator on the high-speed-shaft (HSS). +It is noted that OpenFAST considers the inertia of the shaft to be included in the "hub" (i.e. rotor). +The generator and brake torques on the LSS is obtained from the HSS as follows: + +.. math:: :label: QgLSS + + Q_g = n_g Q_{g,HSS} + ,\quad + Q_b = n_g Q_{b,HSS} + +.. + where :math:`\eta_{DT}` is the efficiency of the drivetrain. + Q_g = \frac{n_g}{\eta_{DT}} Q_{g,HSS} + +The initial conditions associated with equation :eq:`sed_stateEq` are: + +.. math:: :label: sed_stateInit + + \begin{aligned} + \psi & = \psi_0 \\ + \dot{\psi} & = \Omega_0 + \end{aligned} + +where :math:`\psi_0` is the initial azimuthal angle in rad and :math:`\Omega_0` is the initial rotor speed in rad/s. + + + +If the generator degrees of freedom is off, then the states are simply determined as follows: + + +.. math:: :label: sed_stateEqGenDOF + + + \begin{aligned} + \psi & = \psi_0 + \int_{0}^t \dot{\psi} dt = \psi_0 + \Omega_0 n \Delta t \\ + \dot{\psi} & = \Omega_0 + \end{aligned} + +where :math:`n` is the time step index and :math:`\Delta t` is the time step of the module. + diff --git a/glue-codes/fast-farm/src/FASTWrapper.f90 b/glue-codes/fast-farm/src/FASTWrapper.f90 index 3b72c45a53..db5a1181ec 100644 --- a/glue-codes/fast-farm/src/FASTWrapper.f90 +++ b/glue-codes/fast-farm/src/FASTWrapper.f90 @@ -145,15 +145,15 @@ SUBROUTINE FWrap_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init ExternInitData%windGrid_pZero = InitInp%p_ref_high - InitInp%p_ref_Turbine ExternInitData%windGrid_data => InitInp%Vdist_High - - + + CALL FAST_InitializeAll_T( t_initial, InitInp%TurbNum, m%Turbine, ErrStat2, ErrMsg2, InitInp%FASTInFile, ExternInitData ) ; if (Failed()) return; - + !................. ! Check that we've set up FAST properly: !................. - if (m%Turbine%p_FAST%CompAero /= MODULE_AD) then - call SetErrStat(ErrID_Fatal,"AeroDyn (v15) must be used in each instance of FAST for FAST.Farm.",ErrStat,ErrMsg,RoutineName) + if (m%Turbine%p_FAST%CompAero /= MODULE_AD .and. m%Turbine%p_FAST%CompAero /= MODULE_ADsk) then + call SetErrStat(ErrID_Fatal,"AeroDyn or AeroDisk must be used in each instance of FAST for FAST.Farm.",ErrStat,ErrMsg,RoutineName) call cleanup() return end if @@ -182,57 +182,59 @@ SUBROUTINE FWrap_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init call AllocAry(y%toSC, InitInp%NumCtrl2SC, 'y%toSC (turbine controller outputs to Super Controller)', ErrStat2, ErrMsg2); if (Failed()) return; end if - nb = size(m%Turbine%AD%y%rotors(1)%BladeLoad) - Allocate( m%ADRotorDisk(nb), m%TempDisp(nb), m%TempLoads(nb), m%AD_L2L(nb), STAT=ErrStat2 ); if (Failed0("ADRotorDisk meshes.")) return; + if (m%Turbine%p_FAST%CompAero == MODULE_AD) then + nb = size(m%Turbine%AD%y%rotors(1)%BladeLoad) + allocate( m%ADRotorDisk(nb), m%TempDisp(nb), m%TempLoads(nb), m%AD_L2L(nb), STAT=ErrStat2 ); if (Failed0("ADRotorDisk meshes.")) return; - do k=1,nb - - call meshCopy( SrcMesh = m%Turbine%AD%y%rotors(1)%BladeLoad(k) & - , DestMesh = m%TempDisp(k) & - , CtrlCode = MESH_COUSIN & ! Like a sibling, except using new memory for position/refOrientation and elements - , Orientation = .TRUE. & ! set automatically to identity - , TranslationDisp = .TRUE. & ! set automatically to 0 - , ErrStat = ErrStat2 & - , ErrMess = ErrMsg2 ) - if (Failed()) return; - - call meshCopy( SrcMesh = m%TempDisp(k) & - , DestMesh = m%TempLoads(k) & - , CtrlCode = MESH_SIBLING & - , Force = .true. & - , Moment = .true. & - , ErrStat = ErrStat2 & - , ErrMess = ErrMsg2 ) - if (Failed()) return; - + do k=1,nb - call MeshCreate ( BlankMesh = m%ADRotorDisk(k) & - ,IOS = COMPONENT_OUTPUT & - ,Nnodes = p%nr & - ,ErrStat = ErrStat2 & - ,ErrMess = ErrMsg2 & - ,Force = .true. & - ,Moment = .true. & - ,TranslationDisp = .true. & ! only for loads transfer - ,Orientation = .true. & ! only for loads transfer - ) - if (Failed()) return; - - ! set node initial position/orientation - ! shortcut for - ! call MeshPositionNode(m%ADRotorDisk(k), j, [0,0,r(j)], errStat2, errMsg2) - m%ADRotorDisk(k)%Position(3,:) = p%r ! this will get overwritten later, but we check that we have no zero-length elements in MeshCommit() - m%ADRotorDisk(k)%TranslationDisp = 0.0_R8Ki ! this happens by default, anyway.... - - ! create line2 elements - do j=1,p%nr-1 - call MeshConstructElement( m%ADRotorDisk(k), ELEMENT_LINE2, errStat2, errMsg2, p1=j, p2=j+1 ); if (Failed()) return; - end do !j - - call MeshCommit(m%ADRotorDisk(k), errStat2, errMsg2 ); if (Failed()) return; + call meshCopy( SrcMesh = m%Turbine%AD%y%rotors(1)%BladeLoad(k) & + , DestMesh = m%TempDisp(k) & + , CtrlCode = MESH_COUSIN & ! Like a sibling, except using new memory for position/refOrientation and elements + , Orientation = .TRUE. & ! set automatically to identity + , TranslationDisp = .TRUE. & ! set automatically to 0 + , ErrStat = ErrStat2 & + , ErrMess = ErrMsg2 ) + if (Failed()) return; + + call meshCopy( SrcMesh = m%TempDisp(k) & + , DestMesh = m%TempLoads(k) & + , CtrlCode = MESH_SIBLING & + , Force = .true. & + , Moment = .true. & + , ErrStat = ErrStat2 & + , ErrMess = ErrMsg2 ) + if (Failed()) return; + + + call MeshCreate ( BlankMesh = m%ADRotorDisk(k) & + ,IOS = COMPONENT_OUTPUT & + ,Nnodes = p%nr & + ,ErrStat = ErrStat2 & + ,ErrMess = ErrMsg2 & + ,Force = .true. & + ,Moment = .true. & + ,TranslationDisp = .true. & ! only for loads transfer + ,Orientation = .true. & ! only for loads transfer + ) + if (Failed()) return; + + ! set node initial position/orientation + ! shortcut for + ! call MeshPositionNode(m%ADRotorDisk(k), j, [0,0,r(j)], errStat2, errMsg2) + m%ADRotorDisk(k)%Position(3,:) = p%r ! this will get overwritten later, but we check that we have no zero-length elements in MeshCommit() + m%ADRotorDisk(k)%TranslationDisp = 0.0_R8Ki ! this happens by default, anyway.... - call MeshMapCreate(m%TempLoads(k), m%ADRotorDisk(k), m%AD_L2L(k), ErrStat2, ErrMsg2); if (Failed()) return; ! this is going to transfer the motions as well as the loads, which is overkill - end do + ! create line2 elements + do j=1,p%nr-1 + call MeshConstructElement( m%ADRotorDisk(k), ELEMENT_LINE2, errStat2, errMsg2, p1=j, p2=j+1 ); if (Failed()) return; + end do !j + + call MeshCommit(m%ADRotorDisk(k), errStat2, errMsg2 ); if (Failed()) return; + + call MeshMapCreate(m%TempLoads(k), m%ADRotorDisk(k), m%AD_L2L(k), ErrStat2, ErrMsg2); if (Failed()) return; ! this is going to transfer the motions as well as the loads, which is overkill + end do + endif call cleanup() @@ -506,143 +508,199 @@ SUBROUTINE FWrap_CalcOutput(p, u, y, m, ErrStat, ErrMsg) end if - ! ....... outputs from AeroDyn v15 ............ - - ! note that anything that uses m%Turbine%AD%Input(1) assumes we have not updated these inputs after calling AD_CalcOutput in FAST. - - ! Orientation of rotor centerline, normal to disk: - y%xHat_Disk = m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Orientation(1,:,1) !actually also x_hat_disk and x_hat_hub - - - ! Nacelle-yaw error i.e. the angle about positive Z^ from the rotor centerline to the rotor-disk-averaged relative wind - ! velocity (ambients + deficits + motion), both projected onto the horizontal plane, rad - - ! if the orientation of the rotor centerline or rotor-disk-averaged relative wind speed is directed vertically upward or downward (+/-Z^) - ! the nacelle-yaw error is undefined - if ( EqualRealNos(m%Turbine%AD%m%rotors(1)%V_DiskAvg(1), 0.0_ReKi) .and. EqualRealNos(m%Turbine%AD%m%rotors(1)%V_DiskAvg(2), 0.0_ReKi) ) then - call SetErrStat(ErrID_Fatal,"Nacelle-yaw error is undefined because the rotor-disk-averaged relative wind speed "// & - "is directed vertically", ErrStat,ErrMsg,RoutineName) - elseif ( EqualRealNos(y%xHat_Disk(1), 0.0_ReKi) .and. EqualRealNos(y%xHat_Disk(2), 0.0_ReKi) ) then - call SetErrStat(ErrID_Fatal,"Nacelle-yaw error is undefined because the rotor centerline "// & - "is directed vertically", ErrStat,ErrMsg,RoutineName) - else - vy = m%Turbine%AD%m%rotors(1)%V_DiskAvg(2) * y%xHat_Disk(1) - m%Turbine%AD%m%rotors(1)%V_DiskAvg(1) * y%xHat_Disk(2) - vx = m%Turbine%AD%m%rotors(1)%V_DiskAvg(1) * y%xHat_Disk(1) + m%Turbine%AD%m%rotors(1)%V_DiskAvg(2) * y%xHat_Disk(2) + if (m%Turbine%p_FAST%CompAero == MODULE_AD) then + ! ....... outputs from AeroDyn v15 ............ - y%YawErr = atan2(vy, vx) - end if - + ! note that anything that uses m%Turbine%AD%Input(1) assumes we have not updated these inputs after calling AD_CalcOutput in FAST. - ! Center position of hub, m - p0 = m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Position(:,1) + m%Turbine%AD%Input(1)%rotors(1)%HubMotion%TranslationDisp(:,1) - y%p_hub = p%p_ref_Turbine + p0 - - ! Rotor diameter, m - y%D_rotor = 2.0_ReKi * maxval(m%Turbine%AD%m%rotors(1)%BEMT_u(indx)%rLocal) ! BEMT_u(indx) is calculated on inputs that were passed INTO AD_CalcOutput; Input(1) is calculated from values passed out of ED_CalcOutput AFTER AD_CalcOutput - - if ( y%D_rotor > p%r(p%nr) ) then - call SetErrStat(ErrID_Fatal,"The radius of the wake planes is not large relative to the rotor diameter.", ErrStat,ErrMsg,RoutineName) - end if - - ! Rotor-disk-averaged relative wind speed (ambient + deficits + motion), normal to disk, m/s - y%DiskAvg_Vx_Rel = m%Turbine%AD%m%rotors(1)%V_dot_x - - ! Azimuthally averaged thrust force coefficient (normal to disk), distributed radially - theta = 0.0_ReKi - do k=1,size(m%ADRotorDisk) ! loop on blades - - m%TempDisp(k)%RefOrientation = m%Turbine%AD%Input(1)%rotors(1)%BladeMotion(k)%Orientation - m%TempDisp(k)%Position = m%Turbine%AD%Input(1)%rotors(1)%BladeMotion(k)%Position + m%Turbine%AD%Input(1)%rotors(1)%BladeMotion(k)%TranslationDisp - !m%TempDisp(k)%TranslationDisp = 0.0_R8Ki - m%TempLoads(k)%Force = m%Turbine%AD%y%rotors(1)%BladeLoad(k)%Force - m%TempLoads(k)%Moment = m%Turbine%AD%y%rotors(1)%BladeLoad(k)%Moment + ! Orientation of rotor centerline, normal to disk: + y%xHat_Disk = m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Orientation(1,:,1) !actually also x_hat_disk and x_hat_hub - theta(1) = m%Turbine%AD%m%rotors(1)%hub_theta_x_root(k) - orientation = EulerConstruct( theta ) - m%ADRotorDisk(k)%RefOrientation(:,:,1) = matmul(orientation, m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Orientation(:,:,1) ) - do j=1,p%nr - m%ADRotorDisk(k)%RefOrientation(:,:,j) = m%ADRotorDisk(k)%RefOrientation(:,:,1) - m%ADRotorDisk(k)%Position(:,j) = p0 + p%r(j)*m%ADRotorDisk(k)%RefOrientation(3,:,1) - end do - !m%ADRotorDisk(k)%TranslationDisp = 0.0_ReKi - m%ADRotorDisk(k)%RemapFlag = .true. - - call transfer_line2_to_line2(m%TempLoads(k), m%ADRotorDisk(k), m%AD_L2L(k), ErrStat2, ErrMsg2, m%TempDisp(k), m%ADRotorDisk(k)) - call setErrStat(ErrStat2,ErrMsg2,ErrStat2,ErrMsg,RoutineName) - if (ErrStat >= AbortErrLev) return - end do + + ! Nacelle-yaw error i.e. the angle about positive Z^ from the rotor centerline to the rotor-disk-averaged relative wind + ! velocity (ambients + deficits + motion), both projected onto the horizontal plane, rad + + ! if the orientation of the rotor centerline or rotor-disk-averaged relative wind speed is directed vertically upward or downward (+/-Z^) + ! the nacelle-yaw error is undefined + if ( EqualRealNos(m%Turbine%AD%m%rotors(1)%V_DiskAvg(1), 0.0_ReKi) .and. EqualRealNos(m%Turbine%AD%m%rotors(1)%V_DiskAvg(2), 0.0_ReKi) ) then + call SetErrStat(ErrID_Fatal,"Nacelle-yaw error is undefined because the rotor-disk-averaged relative wind speed "// & + "is directed vertically", ErrStat,ErrMsg,RoutineName) + elseif ( EqualRealNos(y%xHat_Disk(1), 0.0_ReKi) .and. EqualRealNos(y%xHat_Disk(2), 0.0_ReKi) ) then + call SetErrStat(ErrID_Fatal,"Nacelle-yaw error is undefined because the rotor centerline "// & + "is directed vertically", ErrStat,ErrMsg,RoutineName) + else + vy = m%Turbine%AD%m%rotors(1)%V_DiskAvg(2) * y%xHat_Disk(1) - m%Turbine%AD%m%rotors(1)%V_DiskAvg(1) * y%xHat_Disk(2) + vx = m%Turbine%AD%m%rotors(1)%V_DiskAvg(1) * y%xHat_Disk(1) + m%Turbine%AD%m%rotors(1)%V_DiskAvg(2) * y%xHat_Disk(2) - ! --- Ct and Cq on polar grid (goes beyond rotor radius) - if (EqualRealNos(y%DiskAvg_Vx_Rel,0.0_ReKi)) then - y%AzimAvg_Ct = 0.0_ReKi - y%AzimAvg_Cq = 0.0_ReKi - else - y%AzimAvg_Ct(1) = 0.0_ReKi - y%AzimAvg_Cq(1) = 0.0_ReKi + y%YawErr = atan2(vy, vx) + end if + + + ! Center position of hub, m + p0 = m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Position(:,1) + m%Turbine%AD%Input(1)%rotors(1)%HubMotion%TranslationDisp(:,1) + y%p_hub = p%p_ref_Turbine + p0 - do j=2,p%nr + ! Rotor diameter, m + y%D_rotor = 2.0_ReKi * maxval(m%Turbine%AD%m%rotors(1)%BEMT_u(indx)%rLocal) ! BEMT_u(indx) is calculated on inputs that were passed INTO AD_CalcOutput; Input(1) is calculated from values passed out of ED_CalcOutput AFTER AD_CalcOutput + + if ( y%D_rotor > p%r(p%nr) ) then + call SetErrStat(ErrID_Fatal,"The radius of the wake planes is not large relative to the rotor diameter.", ErrStat,ErrMsg,RoutineName) + end if + + ! Rotor-disk-averaged relative wind speed (ambient + deficits + motion), normal to disk, m/s + y%DiskAvg_Vx_Rel = m%Turbine%AD%m%rotors(1)%V_dot_x + + ! Azimuthally averaged thrust force coefficient (normal to disk), distributed radially + theta = 0.0_ReKi + do k=1,size(m%ADRotorDisk) ! loop on blades + + m%TempDisp(k)%RefOrientation = m%Turbine%AD%Input(1)%rotors(1)%BladeMotion(k)%Orientation + m%TempDisp(k)%Position = m%Turbine%AD%Input(1)%rotors(1)%BladeMotion(k)%Position + m%Turbine%AD%Input(1)%rotors(1)%BladeMotion(k)%TranslationDisp + !m%TempDisp(k)%TranslationDisp = 0.0_R8Ki + m%TempLoads(k)%Force = m%Turbine%AD%y%rotors(1)%BladeLoad(k)%Force + m%TempLoads(k)%Moment = m%Turbine%AD%y%rotors(1)%BladeLoad(k)%Moment - denom = m%Turbine%AD%p%rotors(1)%AirDens * pi * p%r(j) * y%DiskAvg_Vx_Rel**2 - - ! Thrust coefficient - ! Ct(r) = dT/dr / (1/2 rho pi r U_rel^2 ), with dT/dr = sum_iB dFn/dr - num = 0.0_ReKi - do k=1,size(m%ADRotorDisk) ! loop on blades force contribution - num = num + dot_product( y%xHat_Disk, m%ADRotorDisk(k)%Force(:,j) ) - end do - y%AzimAvg_Ct(j) = num / denom - - ! Torque coefficient - ! Cq = dQ/dr / (1/2 rho pi r^2 U_rel^2) dQ/dr = sum_iB r dFt/dr - num = 0.0_ReKi - do k=1,size(m%ADRotorDisk) ! loop on blades force contribution - num = num - p%r(j)*dot_product(m%ADRotorDisk(k)%RefOrientation(2,:,1), m%ADRotorDisk(k)%Force(:,j) ) + dot_product(y%xHat_Disk, m%ADRotorDisk(k)%Moment(:,j) ) + theta(1) = m%Turbine%AD%m%rotors(1)%hub_theta_x_root(k) + orientation = EulerConstruct( theta ) + m%ADRotorDisk(k)%RefOrientation(:,:,1) = matmul(orientation, m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Orientation(:,:,1) ) + do j=1,p%nr + m%ADRotorDisk(k)%RefOrientation(:,:,j) = m%ADRotorDisk(k)%RefOrientation(:,:,1) + m%ADRotorDisk(k)%Position(:,j) = p0 + p%r(j)*m%ADRotorDisk(k)%RefOrientation(3,:,1) end do - y%AzimAvg_Cq(j) = num / (denom * p%r(j) ) + !m%ADRotorDisk(k)%TranslationDisp = 0.0_ReKi + m%ADRotorDisk(k)%RemapFlag = .true. + + call transfer_line2_to_line2(m%TempLoads(k), m%ADRotorDisk(k), m%AD_L2L(k), ErrStat2, ErrMsg2, m%TempDisp(k), m%ADRotorDisk(k)) + call setErrStat(ErrStat2,ErrMsg2,ErrStat2,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return end do - end if + ! --- Ct and Cq on polar grid (goes beyond rotor radius) + if (EqualRealNos(y%DiskAvg_Vx_Rel,0.0_ReKi)) then + y%AzimAvg_Ct = 0.0_ReKi + y%AzimAvg_Cq = 0.0_ReKi + else + y%AzimAvg_Ct(1) = 0.0_ReKi + y%AzimAvg_Cq(1) = 0.0_ReKi - ! --- Variables needed to orient wake planes in "skew" coordinate system - ! chi_skew and psi_skew - y%chi_skew = Calc_Chi0(m%Turbine%AD%m%rotors(1)%V_diskAvg, m%turbine%AD%m%rotors(1)%V_dot_x) ! AeroDyn_IO - - ! TODO place me in an AeroDyn Function like Calc_Chi0 - ! Construct y_hat, orthogonal to x_hat when its z component is neglected (in a projected horizontal plane) - yHat_plane(1:3) = (/ -y%xHat_Disk(2), y%xHat_Disk(1), 0.0_ReKi /) - yHat_plane(1:3) = yHat_plane/TwoNorm(yHat_plane) - ! Construct z_hat - zHat_plane(1) = -y%xHat_Disk(1)*y%xHat_Disk(3) - zHat_plane(2) = -y%xHat_Disk(2)*y%xHat_Disk(3) - zHat_plane(3) = y%xHat_Disk(1)*y%xHat_Disk(1) + y%xHat_Disk(2)*y%xHat_Disk(2) - zHat_plane(1:3) = zHat_plane/TwoNorm(zHat_plane) + do j=2,p%nr + + denom = m%Turbine%AD%p%rotors(1)%AirDens * pi * p%r(j) * y%DiskAvg_Vx_Rel**2 + + ! Thrust coefficient + ! Ct(r) = dT/dr / (1/2 rho pi r U_rel^2 ), with dT/dr = sum_iB dFn/dr + num = 0.0_ReKi + do k=1,size(m%ADRotorDisk) ! loop on blades force contribution + num = num + dot_product( y%xHat_Disk, m%ADRotorDisk(k)%Force(:,j) ) + end do + y%AzimAvg_Ct(j) = num / denom + + ! Torque coefficient + ! Cq = dQ/dr / (1/2 rho pi r^2 U_rel^2) dQ/dr = sum_iB r dFt/dr + num = 0.0_ReKi + do k=1,size(m%ADRotorDisk) ! loop on blades force contribution + num = num - p%r(j)*dot_product(m%ADRotorDisk(k)%RefOrientation(2,:,1), m%ADRotorDisk(k)%Force(:,j) ) + dot_product(y%xHat_Disk, m%ADRotorDisk(k)%Moment(:,j) ) + end do + y%AzimAvg_Cq(j) = num / (denom * p%r(j) ) + end do + + end if + + ! --- Variables needed to orient wake planes in "skew" coordinate system + ! chi_skew and psi_skew + y%chi_skew = Calc_Chi0(m%Turbine%AD%m%rotors(1)%V_diskAvg, m%turbine%AD%m%rotors(1)%V_dot_x) ! AeroDyn_IO + + ! TODO place me in an AeroDyn Function like Calc_Chi0 + ! Construct y_hat, orthogonal to x_hat when its z component is neglected (in a projected horizontal plane) + yHat_plane(1:3) = (/ -y%xHat_Disk(2), y%xHat_Disk(1), 0.0_ReKi /) + yHat_plane(1:3) = yHat_plane/TwoNorm(yHat_plane) + ! Construct z_hat + zHat_plane(1) = -y%xHat_Disk(1)*y%xHat_Disk(3) + zHat_plane(2) = -y%xHat_Disk(2)*y%xHat_Disk(3) + zHat_plane(3) = y%xHat_Disk(1)*y%xHat_Disk(1) + y%xHat_Disk(2)*y%xHat_Disk(2) + zHat_plane(1:3) = zHat_plane/TwoNorm(zHat_plane) !~ zHat_Disk = m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Orientation(3,:,1) ! TODO TODO, shoudn't rotate - ! Skew system (y and z are in disk plane, x is normal to disk, y is in the cross-flow direction formed by the diskavg velocity) - xSkew = y%xHat_Disk - ySkew = y%xHat_Disk - m%Turbine%AD%m%rotors(1)%V_diskAvg - denom = TwoNorm(ySkew) - if (EqualRealNos(denom, 0.0_ReKi)) then - ! There is no skew - ySkew = yHat_plane - zSkew = zHat_plane - else - ySkew = ySkew / denom - zSkew(1) = xSkew(2) * ySkew(3) - xSkew(3) * ySkew(2) - zSkew(2) = xSkew(3) * ySkew(1) - xSkew(1) * ySkew(3) - zSkew(3) = xSkew(1) * ySkew(2) - xSkew(2) * ySkew(1) - endif - zHat_Disk = zSkew + ! Skew system (y and z are in disk plane, x is normal to disk, y is in the cross-flow direction formed by the diskavg velocity) + xSkew = y%xHat_Disk + ySkew = y%xHat_Disk - m%Turbine%AD%m%rotors(1)%V_diskAvg + denom = TwoNorm(ySkew) + if (EqualRealNos(denom, 0.0_ReKi)) then + ! There is no skew + ySkew = yHat_plane + zSkew = zHat_plane + else + ySkew = ySkew / denom + zSkew(1) = xSkew(2) * ySkew(3) - xSkew(3) * ySkew(2) + zSkew(2) = xSkew(3) * ySkew(1) - xSkew(1) * ySkew(3) + zSkew(3) = xSkew(1) * ySkew(2) - xSkew(2) * ySkew(1) + endif + zHat_Disk = zSkew + + tmp_sz_y = -1.0_ReKi * dot_product(zHat_Disk,yHat_plane) + tmp_sz_z = dot_product(zHat_Disk,zHat_plane) + if ( EqualRealNos(tmp_sz_y,0.0_ReKi) .and. EqualRealNos(tmp_sz_z,0.0_ReKi) ) then + y%psi_skew = 0.0_ReKi + else + y%psi_skew = atan2( tmp_sz_y, tmp_sz_z ) + end if - tmp_sz_y = -1.0_ReKi * dot_product(zHat_Disk,yHat_plane) - tmp_sz_z = dot_product(zHat_Disk,zHat_plane) - if ( EqualRealNos(tmp_sz_y,0.0_ReKi) .and. EqualRealNos(tmp_sz_z,0.0_ReKi) ) then - y%psi_skew = 0.0_ReKi - else - y%psi_skew = atan2( tmp_sz_y, tmp_sz_z ) - end if - + elseif (m%Turbine%p_FAST%CompAero == MODULE_ADsk) then + ! ....... outputs from AeroDisk ............... + ! Orientation of rotor centerline, normal to disk: + y%xHat_Disk = m%Turbine%ADsk%Input(1)%HubMotion%Orientation(1,:,1) !actually also x_hat_disk and x_hat_hub + + ! Nacelle-yaw error i.e. the angle about positive Z^ from the rotor centerline to the rotor-disk-averaged relative wind + ! velocity (ambients + deficits + motion), both projected onto the horizontal plane, rad + ! if the orientation of the rotor centerline or rotor-disk-averaged relative wind speed is directed vertically upward or downward (+/-Z^) + ! the nacelle-yaw error is undefined (this is handled inside of AeroDisk) + y%YawErr = m%Turbine%ADsk%y%YawErr + + ! Center position of hub, m + p0 = m%Turbine%ADsk%Input(1)%HubMotion%Position(:,1) + m%Turbine%ADsk%Input(1)%HubMotion%TranslationDisp(:,1) + y%p_hub = p%p_ref_Turbine + p0 + + ! Rotor diameter, m + y%D_rotor = 2.0_ReKi * m%Turbine%ADsk%p%RotorRad + + if ( y%D_rotor > p%r(p%nr) ) then + call SetErrStat(ErrID_Fatal,"The radius of the wake planes is not large relative to the rotor diameter.", ErrStat,ErrMsg,RoutineName) + end if + + ! Rotor-disk-averaged relative wind speed (ambient + deficits + motion), normal to disk, m/s + y%DiskAvg_Vx_Rel = m%Turbine%ADsk%y%VRel + + ! Thrust coefficients + if (EqualRealNos(y%DiskAvg_Vx_Rel,0.0_ReKi)) then + y%AzimAvg_Ct = 0.0_ReKi + else + y%AzimAvg_Ct = 0.0_ReKi + do j=1,p%nr + if (p%r(j) <= m%Turbine%ADsk%p%RotorRad) then + y%AzimAvg_Ct(1:j) = m%Turbine%ADsk%y%Ct + endif + enddo + endif + + ! Torque coefficients + if (EqualRealNos(y%DiskAvg_Vx_Rel,0.0_ReKi)) then + y%AzimAvg_Cq = 0.0_ReKi + else + y%AzimAvg_Cq(1) = 0.0_ReKi + do j=2,p%nr + if (p%r(j) <= m%Turbine%ADsk%p%RotorRad) then + y%AzimAvg_Cq(j) = m%Turbine%ADsk%y%Cq * m%Turbine%ADsk%p%RotorRad / p%r(j) ! \f$ C_q(r) = C_Q \frac{R}{r} \f$ + endif + enddo + endif + + ! Skew angles + y%psi_skew = m%Turbine%ADsk%y%PsiSkew + y%chi_skew = m%Turbine%ADsk%y%ChiSkew + + endif + END SUBROUTINE FWrap_CalcOutput !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets the inputs needed before calling an instance of FAST diff --git a/glue-codes/simulink/CMakeLists.txt b/glue-codes/simulink/CMakeLists.txt index a4ca557ef5..c331ac1f66 100644 --- a/glue-codes/simulink/CMakeLists.txt +++ b/glue-codes/simulink/CMakeLists.txt @@ -41,6 +41,8 @@ set(MEX_LIBS $ $ # MATLAB Specific $ + $ + $ ) # Build the matlab shared library (mex) using the current toolchain. diff --git a/modules/aerodisk/CMakeLists.txt b/modules/aerodisk/CMakeLists.txt new file mode 100644 index 0000000000..fa7ee11fba --- /dev/null +++ b/modules/aerodisk/CMakeLists.txt @@ -0,0 +1,41 @@ +# +# Copyright 2024 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if (GENERATE_TYPES) + generate_f90_types(src/AeroDisk_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/AeroDisk_Types.f90) +endif() + +add_library(aerodisklib + src/AeroDisk_Types.f90 + src/AeroDisk_Output_Params.f90 + src/AeroDisk_IO.f90 + src/AeroDisk.f90 +) +target_link_libraries(aerodisklib ifwlib nwtclibs) + +add_executable(aerodisk_driver + src/driver/AeroDisk_Driver_Types.f90 + src/driver/AeroDisk_Driver_Subs.f90 + src/driver/AeroDisk_Driver.f90 +) +target_link_libraries(aerodisk_driver aerodisklib versioninfolib ${CMAKE_DL_LIBS}) + +install(TARGETS aerodisklib aerodisk_driver + EXPORT "${CMAKE_PROJECT_NAME}Libraries" + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/modules/aerodisk/src/AeroDisk.f90 b/modules/aerodisk/src/AeroDisk.f90 new file mode 100644 index 0000000000..efd0e40ac8 --- /dev/null +++ b/modules/aerodisk/src/AeroDisk.f90 @@ -0,0 +1,876 @@ +!********************************************************************************************************************************** +!> ## AeroDisk +!! The AeroDisk module solves a quasi-steady actuator disk representation of the rotor to calculate the 3 forces and 3 moments of +!! the rotor dependent on the tip-speed ratio (TSR), rotor speed (RotSpeed), relative wind velocity vector (VRel), and the rotor- +!! collective blade-pitch (BlPitch). +!! +! .................................................................................................................................. +!! ## LICENSING +!! Copyright (C) 2024 National Renewable Energy Laboratory +!! +!! This file is part of AeroDisk. +!! +!! Licensed under the Apache License, Version 2.0 (the "License"); +!! you may not use this file except in compliance with the License. +!! You may obtain a copy of the License at +!! +!! http://www.apache.org/licenses/LICENSE-2.0 +!! +!! Unless required by applicable law or agreed to in writing, software +!! distributed under the License is distributed on an "AS IS" BASIS, +!! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +!! See the License for the specific language governing permissions and +!! limitations under the License. +!********************************************************************************************************************************** +MODULE AeroDisk + + USE AeroDisk_Types + USE AeroDisk_IO + USE NWTC_Library + use IfW_FlowField, only: IfW_FlowField_GetVelAcc + + + implicit none + private + type(ProgDesc), parameter :: ADsk_Ver = ProgDesc( 'AeroDisk', '', '' ) + + public :: ADsk_Init + public :: ADsk_End + public :: ADsk_UpdateStates + public :: ADsk_CalcOutput + public :: ADsk_CalcContStateDeriv + + ! Linearization is not supported by this module, so the following routines are omitted + !public :: ADsk_CalcConstrStateResidual + !public :: ADsk_UpdateDiscState + !public :: ADsk_JacobianPInput + !public :: ADsk_JacobianPContState + !public :: ADsk_JacobianPDiscState + !public :: ADsk_JacobianPConstrState + !public :: ADsk_GetOP + +CONTAINS + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Initialize the AeroDisk module: +!! - load settings (passed or from file) +!! - setup meshes +!! - initialize outputs and other data storage +SUBROUTINE ADsk_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) + type(ADsk_InitInputType), intent(in ) :: InitInp !< Input data for initialization routine + type(ADsk_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined + type(ADsk_ParameterType), intent( out) :: p !< Parameters + type(ADsk_ContinuousStateType), intent( out) :: x !< Initial continuous states + type(ADsk_DiscreteStateType), intent( out) :: xd !< Initial discrete states + type(ADsk_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states + type(ADsk_OtherStateType), intent( out) :: OtherState !< Initial other states (logical, etc) + type(ADsk_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated) + type(ADsk_MiscVarType), intent( out) :: m !< Misc variables for optimization (not copied in glue code) + real(DbKi), intent(inout) :: Interval !< Coupling interval in seconds + type(ADsk_InitOutputType), intent( out) :: InitOut !< Output for initialization routine + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + type(ADsk_InputFile) :: InputFileData !< Data from input file as a string array + type(FileInfoType) :: FileInfo_In !< The derived type for holding the full input file for parsing -- we may pass this in the future + integer(IntKi) :: UnEc ! unit number for the echo file (-1 for not in use) + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'ADsk_Init' + + ! Initialize variables + ErrStat = ErrID_None + ErrMsg = "" + + ! Initialize the NWTC Subroutine Library + call NWTC_Init( ) + + ! Display the module information + call DispNVD( ADsk_Ver ) + + ! set rootname + p%RootName = trim(InitInp%RootName)//".ADsk" + + ! Get primary input file + if ( InitInp%UseInputFile ) then + CALL ProcessComFile( InitInp%InputFile, FileInfo_In, ErrStat2, ErrMsg2 ) + else + CALL NWTC_Library_CopyFileInfoType( InitInp%PassedFileData, FileInfo_In, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + endif + if (Failed()) return + + ! For diagnostic purposes, the following can be used to display the contents + ! of the FileInfo_In data structure. + !call Print_FileInfo_Struct( CU, FileInfo_In ) ! CU is the screen -- different number on different systems. + + ! Parse all ADsk-related input and populate the InputFileData structure + call ADsk_ParsePrimaryFileData( InitInp, p%RootName, Interval, FileInfo_In, InputFileData, UnEc, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + ! Verify all the necessary initialization and input file data + CALL ADskInput_ValidateInput( InitInp, InputFileData, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + ! Set parameters + CALL ADskInput_SetParameters( InitInp, Interval, InputFileData, p, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + ! For diagnostic purposes. If we add a summary file, use this to write table + !call WriteAeroTab(p%AeroTable,Cu) + + + ! Set pointer to FlowField data + if (associated(InitInp%FlowField)) then + p%FlowField => InitInp%FlowField + call SetDiskAvgPoints(ErrStat2,ErrMsg2); if (Failed()) return + else + ErrStat2 = ErrID_Fatal + ErrMsg2 = "No flow field data available. AeroDisk cannot continue." + if (Failed()) return + endif + + + ! Set inputs + call Init_U(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set outputs + call Init_Y(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set InitOutputs + call Init_InitY(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set some other stuff that the framework requires + call Init_OtherStuff(ErrStat2,ErrMsg2); if (Failed()) return + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + + subroutine Cleanup() + if (UnEc > 0_IntKi) close (UnEc) + end subroutine Cleanup + + !> Setup points for disk average velocity calculations + subroutine SetDiskAvgPoints(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + integer(IntKi) :: i + real(ReKi) :: R,theta + ! positions relative to hub + call AllocAry(p%DiskWindPosRel,3,ADsk_NumPtsDiskAvg,'ADsk_NumPtsDiskAvg',ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + ! absolute point positions for call to GetWindVelAcc + call AllocAry(m%DiskWindPosAbs,3,ADsk_NumPtsDiskAvg,'ADsk_NumPtsDiskAvg',ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + ! wind velocity at all requested points + call AllocAry(m%DiskWindVel ,3,ADsk_NumPtsDiskAvg,'DiskWindVel' ,ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + ! Calculate relative points on disk (do this once up front to save computational time). + ! NOTE: this is in the XY plane, and will be multiplied by the hub orientation vector + R = real(p%RotorRad,ReKi) * 0.7_reKi !70% radius + do i=1,ADsk_NumPtsDiskAvg + theta = pi +(i-1)*TwoPi/ADsk_NumPtsDiskAvg + p%DiskWindPosRel(1,i) = R*cos(theta) + p%DiskWindPosRel(2,i) = R*sin(theta) + p%DiskWindPosRel(3,i) = 0.0_ReKi + end do + end subroutine SetDiskAvgPoints + + !> Initialize the inputs in u + subroutine Init_U(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + ! HubMotion mesh + call MeshCreate ( BlankMesh = u%HubMotion & + ,IOS = COMPONENT_INPUT & + ,Nnodes = 1 & + ,ErrStat = ErrStat3 & + ,ErrMess = ErrMsg3 & + ,Orientation = .true. & + ,TranslationDisp = .true. & + ,RotationVel = .true. & + ) + if (errStat3 >= AbortErrLev) return + call MeshPositionNode(u%HubMotion, 1, InitInp%HubPosition, errStat3, errMsg3, InitInp%HubOrientation); if (errStat3 >= AbortErrLev) return + call MeshConstructElement( u%HubMotion, ELEMENT_POINT, errStat3, errMsg3, p1=1 ); if (errStat3 >= AbortErrLev) return + call MeshCommit(u%HubMotion, errStat3, errMsg3 ); if (errStat3 >= AbortErrLev) return + u%HubMotion%Orientation = u%HubMotion%RefOrientation + u%HubMotion%TranslationDisp = 0.0_R8Ki + u%HubMotion%RotationVel = 0.0_ReKi + return + end subroutine Init_U + + !> Initialize the outputs in Y + subroutine Init_Y(ErrStat3,ErrMSg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + ! Set output loads mesh + call MeshCopy ( SrcMesh = u%HubMotion & + , DestMesh = y%AeroLoads & + , CtrlCode = MESH_SIBLING & + , IOS = COMPONENT_OUTPUT & + , force = .TRUE. & + , moment = .TRUE. & + , ErrStat = ErrStat3 & + , ErrMess = ErrMsg3 ) + if (ErrStat3 >= AbortErrLev) return + + ! Initialize all outputs to zero (will be set by CalcOutput) + y%YawErr = 0.0_ReKi + y%PsiSkew = 0.0_ReKi + y%ChiSkew = 0.0_ReKi + y%VRel = 0.0_ReKi + y%Ct = 0.0_ReKi + y%Cq = 0.0_ReKi + call AllocAry(y%WriteOutput,p%NumOuts,'WriteOutput',Errstat3,ErrMsg3); if (ErrStat3 >= AbortErrLev) return + y%WriteOutput = 0.0_ReKi + end subroutine Init_Y + + !> Initialize other stuff that the framework requires, but isn't used here + subroutine Init_OtherStuff(ErrStat3,ErRMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + ErrStat3 = ErrID_None + ErrMsg3 = "" + x%DummyContState = 0.0_ReKi + xd%DummyDiscreteState = 0.0_ReKi + z%DummyConstrState = 0.0_ReKi + OtherState%DummyOtherState = 0_IntKi + m%idx_last = 1_IntKi ! Aerotable lookup indice + if (allocated(m%AllOuts)) deallocate(m%AllOuts) + allocate(m%AllOuts(0:MaxOutPts),STAT=ErrStat3) + if (ErrStat3 /= 0) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = "Cannot allocate m%AllOuts" + return + endif + m%AllOuts = 0.0_SiKi + end subroutine Init_OtherStuff + + !> Initialize the InitOutput + subroutine Init_InitY(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + integer(IntKi) :: i + call AllocAry(InitOut%WriteOutputHdr,p%NumOuts,'WriteOutputHdr',Errstat3,ErrMsg3); if (ErrStat3 >= AbortErrLev) return + call AllocAry(InitOut%WriteOutputUnt,p%NumOuts,'WriteOutputUnt',Errstat3,ErrMsg3); if (ErrStat3 >= AbortErrLev) return + do i=1,p%NumOuts + InitOut%WriteOutputHdr(i) = p%OutParam(i)%Name + InitOut%WriteOutputUnt(i) = p%OutParam(i)%Units + end do + ! Version + InitOut%Ver = ADsk_Ver + end subroutine Init_InitY +END SUBROUTINE ADsk_Init + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine is called at the end of the simulation. +SUBROUTINE ADsk_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) + type(ADsk_InputType), intent(inout) :: u !< System inputs + type(ADsk_ParameterType), intent(inout) :: p !< Parameters + type(ADsk_ContinuousStateType), intent(inout) :: x !< Continuous states + type(ADsk_DiscreteStateType), intent(inout) :: xd !< Discrete states + type(ADsk_ConstraintStateType), intent(inout) :: z !< Constraint states + type(ADsk_OtherStateType), intent(inout) :: OtherState !< Other states + type(ADsk_OutputType), intent(inout) :: y !< System outputs + type(ADsk_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'ADsk_End' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + !! Place any last minute operations or calculations here: + + !! Close files here (but because of checkpoint-restart capability, it is not recommended to have files open during the simulation): + + ! Destroy the input data: + call ADsk_DestroyInput( u, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the parameter data: + call ADsk_DestroyParam( p, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the state data: + call ADsk_DestroyContState( x, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call ADsk_DestroyDiscState( xd, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call ADsk_DestroyConstrState( z, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call ADsk_DestroyOtherState( OtherState, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the output data: + call ADsk_DestroyOutput( y, ErrStat2, ErrMsg2 ); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the misc data: + call ADsk_DestroyMisc( m, ErrStat2, ErrMsg2 ); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +END SUBROUTINE ADsk_End + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a routine for computing outputs, used in both loose and tight coupling. +!! It calculates the forces and moments on the rotor disk given an orientation, motion, blade pitch, rotor speed, and wind velocity. +!! The calculations are based on an interpolation into input table values. Since the table is stored as single kind and this method +!! makes several simplifying assumptions that introduuce error, all calculations here are performed in single precision. +SUBROUTINE ADsk_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg, NeedWriteOutput ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(ADsk_InputType), intent(in ) :: u !< Inputs at t + type(ADsk_ParameterType), intent(in ) :: p !< Parameters + type(ADsk_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(ADsk_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(ADsk_ConstraintStateType), intent(in ) :: z !< Constraint states at t + type(ADsk_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(ADsk_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + type(ADsk_OutputType), intent(inout) :: y !< Outputs computed at t (Input only for mesh) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + logical, optional, intent(in ) :: NeedWriteOutput !< Flag to determine if WriteOutput values need to be calculated in this call + + ! local variables + real(ReKi), allocatable :: NoAcc(:,:) ! Placeholder array not used when accelerations not required. + real(SiKi) :: x_hatDisk(3) ! X direction unit vector of rotor disk (global) + real(SiKi) :: y_hatDisk(3) ! Y direction unit vector of rotor disk (global) + real(SiKi) :: z_hatDisk(3) ! Z direction unit vector of rotor disk (global) + real(SiKi) :: VRel_vec(3) ! relative velocity of wind in moving rotor disk frame + real(SiKi) :: tmp1,tmp3(3) ! temporary variables for calculations + integer(IntKi) :: i ! generic counter + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'ADsk_CalcOutput' + logical :: CalcWriteOutput + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + m%AllOuts = 0.0_SiKi + + if (present(NeedWriteOutput)) then + CalcWriteOutput = NeedWriteOutput + else + CalcWriteOutput = .true. ! by default, calculate WriteOutput unless told that we do not need it + end if + + + !-------------------------------- + !> Disk average wind speed + call CalcDiskAvgVel(ErrStat2,ErrMsg2); if (FAiled()) return + + !-------------------------------- + !> Disk vectors (in global frame) + !! + !! | Vector | Description | Name | + !! | :-------------------------- | :------------------------------------------------------------------------------------------ | :-------- | + !! | \f$\hat{x}_\textrm{disk}\f$ | normal to disk | x_hatDisk | + !! | \f$\hat{y}_\textrm{disk}\f$ | perpendicular to \f$\hat{x}_\textrm{disk}\f$ in the \f$x-y\f$ plane | y_hatDisk | + !! | \f$\hat{z}_\textrm{disk}\f$ | right handed coordinate axis to \f$\hat{x}_\textrm{disk}\f$ and \f$\hat{y}_\textrm{disk}\f$ | z_hatDisk | + ! Normal to disk + x_hatDisk(1:3) = real(u%HubMotion%Orientation(1:3,1,1), SiKi) + ! unit vector of disk normal projected to XY plane (calculate from cross_product(Z_global, x_hatDisk) ) + y_hatDisk(1) = real(-x_hatDisk(2),SiKi) + y_hatDisk(2) = real(x_hatDisk(1),SiKi) + y_hatDisk(3) = 0.0_SiKi + y_hatDisk = y_hatDisk / TwoNorm( y_hatDisk ) ! normalize + ! unit vector normal to x_hatDisk and y_hatDisk -- cross_product(x_hatDisk, y_hatDisk) + z_hatDisk(1) = real(-x_hatDisk(1)*x_hatDisk(3),SiKi) + z_hatDisk(2) = real(-x_hatDisk(2)*x_hatDisk(3),SiKi) + z_hatDisk(3) = real(x_hatDisk(1)*x_hatDisk(1) + x_hatDisk(2)*x_hatDisk(2),SiKi) + + + !------------- + ! Error checks + !------------- + ! Verify rotor and wind orientation are not pointed vertically + if ((EqualRealNos(m%DiskAvgVel(1), 0.0_ReKi) .and. EqualRealNos(m%DiskAvgVel(2), 0.0_ReKi)) .and. (.not. EqualRealNos(m%DiskAvgVel(3), 0.0_ReKi))) then + ErrStat2 = ErrID_Fatal; ErrMsg2 = "AeroDisk cannot calculate aero loads with wind in the vertical direction. Nacelle yaw-error undefined." + if (Failed()) return + endif + if (EqualRealNos(x_hatDisk(1), 0.0_SiKi) .and. EqualRealNos(x_hatDisk(2), 0.0_SiKi)) then + ErrStat2 = ErrID_Fatal; ErrMsg2 = "AeroDisk cannot calculate aero loads with rotor pointed in vertical direction. Nacelle yaw-error undefined." + if (Failed()) return + endif + + !------------------------- + !> Calculate some constants + !! - \f$\vec{V}_\textrm{rel} = \vec{V}_\textrm{wind} - \vec{v}_\textrm{rotor}\f$ + !! - \f$V_\textrm{rel} = \left\| \vec{V}_\textrm{rel} \right\|_2\f$ + !! - \f$V_\textrm{rel,x-disk} = \vec{V}_\textrm{rel_g} \bullet \hat{x}_\textrm{disk}\f$ + !------------------------- + ! Calculate relative wind velocity (global) + VRel_vec(1:3) = real(m%DiskAvgVel(1:3) - u%HubMotion%TranslationVel(1:3,1), SiKi) + ! Magnitude of relative wind velocity + m%VRel = TwoNorm(VRel_vec) + ! relative wind velocity along disk normal + m%VRel_xd = abs(dot_product(VRel_vec, x_hatDisk)) + ! set output + y%VRel = m%VRel + + !> calculate Lambda (TSR) and ChiSkew (inflow skew angle) + !! - \f$\lambda = \left\{ \begin{matrix} + !! 0 &\textrm{for}& \vec{V}_\textrm{rel,x-disk} = 0 \\ + !! \frac{\Omega R}{\vec{V}_\textrm{rel,x-disk}} &\textrm{otherwise}& + !! \end{matrix} \right.\f$ + !! - \f$ \chi = \left\{ \begin{matrix} + !! 0 &\textrm{for}& V_\textrm{rel} = 0 \\ + !! \text{ACOS}\left(\frac{\vec{V}_\textrm{rel}\bullet\hat{x}_\textrm{disk}}{V_\textrm{rel}}\right) &\textrm{otherwise}& + !! \end{matrix} \right.\f$ + !! + if (EqualRealNos(m%VRel_xd,0.0_SiKi)) then + m%lambda = 0.0_SiKi + else + m%lambda = real((u%RotSpeed * p%RotorRad),SiKi) / abs(m%VRel_xd) + endif + if (EqualRealNos(m%VRel,0.0_SiKi)) then + m%Chi = 0.0_SiKi + else + m%Chi = acos( m%VRel_xd / m%VRel ) + endif + y%ChiSkew = m%Chi + + !-------------- + !> x,y,z vectors -- convert global coordinates to disk coordinates + !! - \f$ A_\textrm{tmp} = \left\| \left( \vec{V}_\textrm{rel} \bullet \hat{x}_\textrm{disk} \right) \hat{x}_\textrm{disk} - \vec{V}_\textrm{rel} \right\|_2 \f$ + !! - \f$ \hat{x} = \hat{x}_\textrm{disk}\f$ + !! - \f$ \hat{y} = \left\{ \begin{matrix} + !! \hat{y}_\textrm{disk} = \hat{y}_\textrm{disk} &\textrm{for}& A_\textrm{tmp} = 0\\ + !! \hat{y}_\textrm{disk} = \frac{\left(\vec{V}_\textrm{rel} \bullet \hat{x}_\textrm{disk}\right) \hat{x}_\textrm{disk} - \vec{V}_\textrm{rel}}{A_\textrm{tmp}} &\textrm{otherwise}& + !! \end{matrix}\right. \f$ + !! - \f$ \hat{z} = \left\{ \begin{matrix} + !! \hat{z}_\textrm{disk} = \hat{z}_\textrm{disk} &\textrm{for}& A_\textrm{tmp} = 0\\ + !! \hat{z}_\textrm{disk} = \frac{\vec{V}_\textrm{rel} \times \hat{x}_\textrm{disk}}{A_\textrm{tmp}} &\textrm{otherwise}& + !! \end{matrix}\right.\f$ + tmp3 = dot_product(VRel_vec, x_hatDisk) * x_hatDisk - VRel_vec + tmp1 = TwoNorm(tmp3) + if (EqualRealNos(tmp1, 0.0_SiKi)) then + m%x_hat = x_hatDisk + m%y_hat = y_hatDisk + m%z_hat = z_hatDisk + else + m%x_hat = x_hatDisk + m%y_hat = tmp3 / tmp1 + m%z_hat = cross_product( VRel_vec, x_hatDisk ) / tmp1 + endif + + !--------------- + !> YawErr and Skew + !! - YawErr: \f$ \gamma_\textrm{YawErr} = \text{ATAN2}\left( + !! \vec{V}_\textrm{rel}[2] \hat{x}_\textrm{disk}[1] - \vec{V}_\textrm{rel}[1] \hat{x}_\textrm{disk}[2], + !! \vec{V}_\textrm{rel}[1] \hat{x}_\textrm{disk}[1] + \vec{V}_\textrm{rel}[2] \hat{x}_\textrm{disk}[2] \right) \f$ + !! - PsiSkew: \f$ \Psi_\textrm{skew} = \text{ATAN2}\left( \hat{z} \bullet \hat{y}_\textrm{disk}, -\hat{z}\bullet \hat{z}_\textrm{disk} \right) \f$ + y%YawErr = atan2( VRel_vec(2)*x_hatDisk(1) - VRel_vec(1)*x_hatDisk(2), VRel_vec(1)*x_hatDisk(1) + VRel_vec(2)*x_hatDisk(2) ) + y%PsiSkew = atan2( -1.0_ReKi * real( dot_product(m%z_hat,y_hatDisk), ReKi), real( dot_product(m%z_hat, z_hatDisk), ReKi) ) + + + !------------------------------------------- + !> Interpolate Force and Moment coefficients + call ADskTableInterp(p%AeroTable, p%UseTSR, m%lambda, real(u%RotSpeed,SiKi), m%VRel_xd, real(u%BlPitch,SiKi), m%Chi, m%idx_last, m%C_F, m%C_M, ErrStat2, ErrMsg2) + if (Failed()) return + + !> Apply skew if not in table + !! - \f$ \vec{F} = \vec{F} \left( \cos(\chi) \right)^2 \f$ + !! - \f$ \vec{M} = \vec{M} \left( \cos(\chi) \right)^2 \f$ + if (p%AeroTable%N_Skew <= 0_IntKi) then + tmp1 = cos(m%Chi) * cos(m%Chi) + m%C_F(1:3) = m%C_F(1:3) * tmp1 + m%C_M(1:3) = m%C_M(1:3) * tmp1 + endif + + !------------------------------------------- + !> Calculate forces using force coefficients (disk coordinates) + !! - \f$ F_x = \frac{1}{2} \rho A \left( V_\textrm{rel,x} \right)^2 * C_\textrm{F,x}\left(\text{TSR}@\lambda,\text{RtSpd}@\Omega,\text{V}_\text{rel}@V_\textrm{rel},\text{Pitch}@\theta,\text{Skew}@\chi\right) \f$ + !! - \f$ F_y = \frac{1}{2} \rho A \left( V_\textrm{rel,x} \right)^2 * C_\textrm{F,y}\left(\text{TSR}@\lambda,\text{RtSpd}@\Omega,\text{V}_\text{rel}@V_\textrm{rel},\text{Pitch}@\theta,\text{Skew}@\chi\right) \f$ + !! - \f$ F_z = \frac{1}{2} \rho A \left( V_\textrm{rel,x} \right)^2 * C_\textrm{F,z}\left(\text{TSR}@\lambda,\text{RtSpd}@\Omega,\text{V}_\text{rel}@V_\textrm{rel},\text{Pitch}@\theta,\text{Skew}@\chi\right) \f$ + !! - \f$ M_x = \frac{1}{2} \rho A \left( V_\textrm{rel,x} \right)^2 * C_\textrm{M,x}\left(\text{TSR}@\lambda,\text{RtSpd}@\Omega,\text{V}_\text{rel}@V_\textrm{rel},\text{Pitch}@\theta,\text{Skew}@\chi\right) \f$ + !! - \f$ M_y = \frac{1}{2} \rho A \left( V_\textrm{rel,x} \right)^2 * C_\textrm{M,y}\left(\text{TSR}@\lambda,\text{RtSpd}@\Omega,\text{V}_\text{rel}@V_\textrm{rel},\text{Pitch}@\theta,\text{Skew}@\chi\right) \f$ + !! - \f$ M_z = \frac{1}{2} \rho A \left( V_\textrm{rel,x} \right)^2 * C_\textrm{M,z}\left(\text{TSR}@\lambda,\text{RtSpd}@\Omega,\text{V}_\text{rel}@V_\textrm{rel},\text{Pitch}@\theta,\text{Skew}@\chi\right) \f$ + tmp1 = real(p%halfRhoA,SiKi) * m%VRel_xd * m%VRel_xd + m%Force(1:3) = tmp1 * m%C_F(1:3) + m%Moment(1:3) = tmp1 * real(p%RotorRad,SiKi) * m%C_M(1:3) + + + + !------------- + !> Set outputs + !! - Thrust force: \f$ C_t = C_\textrm{F,x} \f$ + !! - Torque coefficient: \f$ C_t = C_\textrm{M,x} \f$ + !! - \f$ \vec{F} = F_\textrm{x,disk} \hat{x} + F_\textrm{y,disk} \hat{y} + F_\textrm{z,disk} \hat{z} \f$ + !! - \f$ \vec{M} = M_\textrm{x,disk} \hat{x} + M_\textrm{y,disk} \hat{y} + M_\textrm{z,disk} \hat{z} \f$ + y%Ct = m%C_F(1) ! Fx in disk reference frame + y%Cq = m%C_M(1) ! Mx in disk reference frame + + ! AeroLoads in global coordinates + y%AeroLoads%Force( 1:3,1) = m%Force( 1)*m%x_hat + m%Force( 2)*m%y_hat + m%Force( 3)*m%z_hat + y%AeroLoads%Moment(1:3,1) = m%Moment(1)*m%x_hat + m%Moment(2)*m%y_hat + m%Moment(3)*m%z_hat + + + !---------------------------- + !> Set requested WriteOutputs + call Calc_WriteOutput( u, p, y, m, ErrStat2, ErrMsg2, CalcWriteOutput ) + + if (CalcWriteOutput) then + ! Place the selected output channels into the WriteOutput(:) + do i = 1,p%NumOuts ! Loop through all selected output channels + y%WriteOutput(i) = p%OutParam(i)%SignM * m%AllOuts( p%OutParam(i)%Indx ) + end do + endif + + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed + subroutine CalcDiskAvgVel(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + integer(IntKi) :: i + integer(IntKi), parameter :: StartNode = 1 ! index to start returning wind info from external flow field (note: this will not work with ExtInflow) + do i=1,ADsk_NumPtsDiskAvg + m%DiskWindPosAbs(:,i) = real(u%HubMotion%Position(1:3,1)+u%HubMotion%TranslationDisp(1:3,1),ReKi) + matmul(real(u%HubMotion%Orientation(1:3,1:3,1),ReKi),p%DiskWindPosRel(:,i)) + end do + call IfW_FlowField_GetVelAcc(p%FlowField, StartNode, t, m%DiskWindPosAbs, m%DiskWindVel, NoAcc, ErrStat3, ErrMsg3) + if (ErrStat2 >= AbortErrLev) return + ! calculate average + m%DiskAvgVel = sum(m%DiskWindVel, dim=2) / REAL(ADsk_NumPtsDiskAvg,SiKi) + end subroutine CalcDiskAvgVel +END SUBROUTINE ADsk_CalcOutput + +subroutine ADskTableInterp(ATab, UseTSR, lambda, RotSpeed, VRel, BlPitch, Chi, idx_last, C_F, C_M, ErrStat, ErrMsg) + type(ADsk_AeroTable), intent(in ) :: ATab !< AeroTable + logical, intent(in ) :: UseTSR !< flag to use TSR instead of RotSpeed and VRel + real(SiKi), intent(in ) :: lambda !< TSR - tip speed ratio + real(SiKi), intent(in ) :: RotSpeed !< Rotor Speed (rad/s) + real(SiKi), intent(in ) :: VRel !< relative wind velocity along disk normal + real(SiKi), intent(in ) :: BlPitch !< Blade pitch (collective) + real(SiKi), intent(in ) :: Chi !< Inflow skew angle + integer(IntKi), intent(inout) :: idx_last(5) !< m%idx_last -- for slight speedup in searching + real(SiKi), intent( out) :: C_F(3) !< Interpolated force coefficient + real(SiKi), intent( out) :: C_M(3) !< Interpolated moment coefficient + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! Local variables + real(ReKi),parameter :: Tol = 1.0E-4 ! a tolerance for determining if two reals are the same (for interpolation) + real(SiKi) :: r_TSR ! Location between bouding indices into TSR dimension + real(SiKi) :: r_RtSpd ! Location between bounding indices into RtSpd dimension + real(SiKi) :: r_VRel ! Location between bounding indices into VRel dimension + real(SiKi) :: r_Pitch ! Location between bounding indices into Pitch dimension + real(SiKi) :: r_Skew ! Location between bounding indices into Skew dimension + integer(IntKi) :: i_TSR(2) ! Bounding indices into TSR dimension + integer(IntKi) :: i_RtSpd(2) ! Bounding indices into RtSpd dimension + integer(IntKi) :: i_VRel(2) ! Bounding indices into VRel dimension + integer(IntKi) :: i_Pitch(2) ! Bounding indices into Pitch dimension + integer(IntKi) :: i_Skew(2) ! Bounding indices into Skew dimension + real(SiKi) :: N3D( 8) ! interpolation coefficients for 3D interp -- size 2^n + real(SiKi) :: U3D( 8) ! interpolation values for 3D interp -- size 2^n + real(SiKi) :: N4D(16) ! interpolation coefficients for 4D interp -- size 2^n + real(SiKi) :: U4D(16) ! interpolation values for 4D interp -- size 2^n + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'ADskTableInterp' + + ! Initialize variables + ErrStat = ErrID_None + ErrMsg = "" + + !--------------------------------------------- + ! Check that we can interpolate into the table + if (UseTSR) then + if ((lambda < ATab%TSR(1)-Tol) .or. (lambda > ATab%TSR(ATab%N_TSR)+Tol)) then + ErrMsg2 = " TSR value of "//trim(Num2LStr(lambda))//" is outside bounds of aero table ["// & + trim(Num2LStr(ATab%TSR(1)))//":"//trim(Num2LStr(ATab%TSR(ATab%N_TSR)))//"]" + call SetErrStat(ErrID_Fatal,ErrMsg2,ErrStat,ErrMsg,RoutineName) + endif + else + if ((RotSpeed < ATab%RtSpd(1)-Tol) .or. (RotSpeed > ATab%RtSpd(ATab%N_RtSpd)+Tol)) then + ErrMsg2 = " Rotor Speed value of "//trim(Num2LStr(RotSpeed))//" is outside bounds of aero table ["// & + trim(Num2LStr(ATab%RtSpd(1)))//":"//trim(Num2LStr(ATab%RtSpd(ATab%N_RtSpd)))//"]" + call SetErrStat(ErrID_Fatal,ErrMsg2,ErrStat,ErrMsg,RoutineName) + endif + if ((VRel < ATab%VRel(1)-Tol) .or. (VRel > ATab%VRel(ATab%N_VRel)+Tol)) then + ErrMsg2 = " VRel value of "//trim(Num2LStr(VRel))//" is outside bounds of aero table ["// & + trim(Num2LStr(ATab%VRel(1)))//":"//trim(Num2LStr(ATab%VRel(ATab%N_VRel)))//"]" + call SetErrStat(ErrID_Fatal,ErrMsg2,ErrStat,ErrMsg,RoutineName) + endif + endif + if (ATab%N_Pitch > 0_IntKi) then + if ((BlPitch < ATab%Pitch(1)-Tol) .or. (BlPitch > ATab%Pitch(ATab%N_Pitch)+Tol)) then + ErrMsg2 = " Blade pitch value of "//trim(Num2LStr(BlPitch))//" is outside bounds of aero table ["// & + trim(Num2LStr(ATab%Pitch(1)))//":"//trim(Num2LStr(ATab%Pitch(ATab%N_Pitch)))//"]" + call SetErrStat(ErrID_Fatal,ErrMsg2,ErrStat,ErrMsg,RoutineName) + endif + endif + if (ATab%N_Skew > 0_IntKi) then + if ((Chi < ATab%Skew(1)-Tol) .or. (Chi > ATab%Skew(ATab%N_Skew)+Tol)) then + ErrMsg2 = " Skew value of "//trim(Num2LStr(Chi))//" is outside bounds of aero table ["// & + trim(Num2LStr(ATab%Skew(1)))//":"//trim(Num2LStr(ATab%Skew(ATab%N_Skew)))//"]" + call SetErrStat(ErrID_Fatal,ErrMsg2,ErrStat,ErrMsg,RoutineName) + endif + endif + if (ErrStat >= AbortErrLev) return + + + !------------------------------- + ! Find indices for interpolation + + ! Initialize all indices to 1. If a column is not used, both indices will be 1 + i_TSR = 1_IntKi + i_RtSpd = 1_IntKi + i_VRel = 1_IntKi + i_Pitch = 1_IntKi + i_Skew = 1_IntKi + ! Set the ratios for all indices to 0 (centered between indices) + r_TSR = 0.0_SiKi + r_RtSpd = 0.0_SiKi + r_VRel = 0.0_SiKi + r_Pitch = 0.0_SiKi + r_Skew = 0.0_SiKi + + ! Find indices to TSR or RtSpd + VRel entries + if (UseTSR) then ! use TSR entry + if (ATab%N_TSR > 1_IntKi) then + ! Find lambda in the table (idx_last(1) is for TSR) + CALL LocateStp( lambda, ATab%TSR, idx_last(1), ATab%N_TSR ) + i_TSR(1) = idx_last(1) ! point before our point of interest + i_TSR(2) = min(idx_last(1)+1,ATab%N_TSR) ! next point, but not out of bounds + call CalcIsoparCoords( lambda, ATab%TSR(i_TSR(1)), ATab%TSR(i_TSR(2)), r_TSR ) + endif + else ! Use RtSpd and VRel instead + if (ATab%N_RtSpd > 1_IntKi) then + ! Find RotSpeed in the table (idx_last(2) is for RtSpd) + CALL LocateStp( RotSpeed, ATab%RtSpd, idx_last(2), ATab%N_RtSpd ) + i_RtSpd(1) = idx_last(2) + i_RtSpd(2) = min(idx_last(2)+1,ATab%N_RtSpd) + call CalcIsoparCoords( RotSpeed, ATab%RtSpd(i_RtSpd(1)), ATab%RtSpd(i_RtSpd(2)), r_RtSpd ) + endif + if (ATab%N_VRel > 1_IntKi) then + ! Find VRel in the table (idx_last(3) is for VRel) + CALL LocateStp( VRel, ATab%VRel, idx_last(3), ATab%N_VRel ) + i_VRel(1) = idx_last(3) + i_VRel(2) = min(idx_last(3)+1,ATab%N_VRel) + call CalcIsoparCoords( VRel, ATab%VRel(i_VRel(1)), ATab%VRel(i_VRel(2)), r_VRel ) + endif + endif + + ! Find indices to pitch + if (ATab%N_Pitch > 1_IntKi) then + ! Find Pitch in the table (idx_last(4) is for Pitch) + CALL LocateStp( BlPitch, ATab%Pitch, idx_last(4), ATab%N_Pitch ) + i_Pitch(1) = idx_last(4) + i_Pitch(2) = min(idx_last(4)+1,ATab%N_Pitch) + call CalcIsoparCoords( BlPitch, ATab%Pitch(i_Pitch(1)), ATab%Pitch(i_Pitch(2)), r_Pitch ) + endif + + ! Find indices to Skew + if (ATab%N_Skew > 1_IntKi) then + ! Find Chi in the table (idx_last(5) is for Skew) + CALL LocateStp( Chi, ATab%Skew, idx_last(5), ATab%N_Skew ) + i_Skew(1) = idx_last(5) + i_Skew(2) = min(idx_last(5)+1,ATab%N_Skew) + call CalcIsoparCoords( Chi, ATab%Skew(i_Skew(1)), ATab%Skew(i_Skew(2)), r_Skew ) + endif + + + !------------------------------------------------ + ! Interpolate values from the coefficients tables + ! For speed, the TSR and RtSpd + VRel cases are + ! handled separately. The table could be + ! interpolated using a 5D interpolation, but + ! since it is known that some indices will not + ! be needed (one ignored indice for RtSpd + VRel + ! case, or two indices for TSR case), simpler + ! interpolations can be used. + + if (UseTSR) then ! use TSR entry + ! Coefficients -- same for all calculations + N3D = getN3D() + + ! Force coefficients + U3D = getU3D(ATab%C_Fx); C_F(1) = sum(N3D * U3D) + U3D = getU3D(ATab%C_Fy); C_F(2) = sum(N3D * U3D) + U3D = getU3D(ATab%C_Fz); C_F(3) = sum(N3D * U3D) + + ! Moment coefficients + U3D = getU3D(ATab%C_Mx); C_M(1) = sum(N3D * U3D) + U3D = getU3D(ATab%C_My); C_M(2) = sum(N3D * U3D) + U3D = getU3D(ATab%C_Mz); C_M(3) = sum(N3D * U3D) + else ! Use RtSpd and VRel instead + ! Coefficients -- same for all calculations + N4D = getN4D() + + ! Force coefficients + U4D = getU4D(ATab%C_Fx); C_F(1) = sum(N4D * U4D) + U4D = getU4D(ATab%C_Fy); C_F(2) = sum(N4D * U4D) + U4D = getU4D(ATab%C_Fz); C_F(3) = sum(N4D * U4D) + + ! Moment coefficients + U4D = getU4D(ATab%C_Mx); C_M(1) = sum(N4D * U4D) + U4D = getU4D(ATab%C_My); C_M(2) = sum(N4D * U4D) + U4D = getU4D(ATab%C_Mz); C_M(3) = sum(N4D * U4D) + endif + + return + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed + function getN3D() result(Nr) ! For when TSR is given + real(SiKi) :: Nr(8) + Nr( 1) = ( 1.0_SiKi - r_TSR ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 2) = ( 1.0_SiKi - r_TSR ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 3) = ( 1.0_SiKi - r_TSR ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 4) = ( 1.0_SiKi - r_TSR ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 5) = ( 1.0_SiKi + r_TSR ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 6) = ( 1.0_SiKi + r_TSR ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 7) = ( 1.0_SiKi + r_TSR ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 8) = ( 1.0_SiKi + r_TSR ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr = Nr/ REAL( SIZE(Nr), SiKi ) ! normalize + end function getN3D + function getN4D() result(Nr) ! For when TSR is not given + real(SiKi) :: Nr(16) + Nr( 1) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 2) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 3) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 4) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 5) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 6) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 7) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr( 8) = ( 1.0_SiKi - r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr( 9) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr(10) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr(11) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr(12) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi - r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr(13) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr(14) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi - r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr(15) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi - r_Skew ) + Nr(16) = ( 1.0_SiKi + r_RtSpd ) * ( 1.0_SiKi + r_VRel ) * ( 1.0_SiKi + r_Pitch ) * ( 1.0_SiKi + r_Skew ) + Nr = Nr / REAL( SIZE(Nr), SiKi ) ! normalize + end function getN4D + function getU3D(CT) result(Ur) ! For when TSR is given (i_RtSpd(1)=i_VRel(1)=1) + real(SiKi), intent(in) :: CT(:,:,:,:,:) ! Coefficient table + real(SiKi) :: Ur(8) + Ur( 1) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(1), i_Skew(1) ) + Ur( 2) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(1), i_Skew(2) ) + Ur( 3) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(2), i_Skew(1) ) + Ur( 4) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(2), i_Skew(2) ) + Ur( 5) = CT( i_TSR(2), i_RtSpd(1), i_VRel(1), i_Pitch(1), i_Skew(1) ) + Ur( 6) = CT( i_TSR(2), i_RtSpd(1), i_VRel(1), i_Pitch(1), i_Skew(2) ) + Ur( 7) = CT( i_TSR(2), i_RtSpd(1), i_VRel(1), i_Pitch(2), i_Skew(1) ) + Ur( 8) = CT( i_TSR(2), i_RtSpd(1), i_VRel(1), i_Pitch(2), i_Skew(2) ) + end function getU3D + function getU4D(CT) result(Ur) ! For when TSR is not given (i_TSR(1)=1) + real(SiKi), intent(in) :: CT(:,:,:,:,:) ! Coefficient table + real(SiKi) :: Ur(16) + Ur( 1) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(1), i_Skew(1) ) + Ur( 2) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(1), i_Skew(2) ) + Ur( 3) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(2), i_Skew(1) ) + Ur( 4) = CT( i_TSR(1), i_RtSpd(1), i_VRel(1), i_Pitch(2), i_Skew(2) ) + Ur( 5) = CT( i_TSR(1), i_RtSpd(1), i_VRel(2), i_Pitch(1), i_Skew(1) ) + Ur( 6) = CT( i_TSR(1), i_RtSpd(1), i_VRel(2), i_Pitch(1), i_Skew(2) ) + Ur( 7) = CT( i_TSR(1), i_RtSpd(1), i_VRel(2), i_Pitch(2), i_Skew(1) ) + Ur( 8) = CT( i_TSR(1), i_RtSpd(1), i_VRel(2), i_Pitch(2), i_Skew(2) ) + Ur( 9) = CT( i_TSR(1), i_RtSpd(2), i_VRel(1), i_Pitch(1), i_Skew(1) ) + Ur(10) = CT( i_TSR(1), i_RtSpd(2), i_VRel(1), i_Pitch(1), i_Skew(2) ) + Ur(11) = CT( i_TSR(1), i_RtSpd(2), i_VRel(1), i_Pitch(2), i_Skew(1) ) + Ur(12) = CT( i_TSR(1), i_RtSpd(2), i_VRel(1), i_Pitch(2), i_Skew(2) ) + Ur(13) = CT( i_TSR(1), i_RtSpd(2), i_VRel(2), i_Pitch(1), i_Skew(1) ) + Ur(14) = CT( i_TSR(1), i_RtSpd(2), i_VRel(2), i_Pitch(1), i_Skew(2) ) + Ur(15) = CT( i_TSR(1), i_RtSpd(2), i_VRel(2), i_Pitch(2), i_Skew(1) ) + Ur(16) = CT( i_TSR(1), i_RtSpd(2), i_VRel(2), i_Pitch(2), i_Skew(2) ) + end function getU4D +end subroutine ADskTableInterp +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine calculates the iosparametric coordinates, isopc, which is a value between -1 and 1 (for each dimension of a dataset) +!! indicating where InCoord falls between posLo and posHi. +!! This routine is copied from WAMIT_Interp.f90 +subroutine CalcIsoparCoords( InCoord, posLo, posHi, isopc ) + real(SiKi), intent(in ) :: InCoord !< + real(SiKi), intent(in ) :: posLo !< coordinate values associated with Indx_Lo + real(SiKi), intent(in ) :: posHi !< coordinate values associated with Indx_Hi + real(SiKi), intent( out) :: isopc !< isoparametric coordinates + ! local variables + real(SiKi) :: dx ! difference between high and low coordinates in the bounding "box" + dx = posHi - posLo + if (EqualRealNos(dx, 0.0_SiKi)) then + isopc = 1.0_SiKi + else + isopc = ( 2.0_SiKi*InCoord - posLo - posHi ) / dx + ! to verify that we don't extrapolate, make sure this is bound between -1 and 1 (effectively nearest neighbor) + isopc = min( 1.0_SiKi, isopc ) + isopc = max(-1.0_SiKi, isopc ) + end if +end subroutine CalcIsoparCoords + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a loose coupling routine for solving constraint states, integrating continuous states, and updating discrete and other +!! states. Continuous, constraint, discrete, and other states are updated to values at t + Interval. +!! NOTE: there are no states in AeroDisk +SUBROUTINE ADsk_UpdateStates( t, n, Inputs, InputTimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< Current step of the simulation: t = n*Interval + type(ADsk_InputType), intent(inout) :: Inputs(:) !< Inputs at InputTimes (output for mesh connect) + real(DbKi), intent(in ) :: InputTimes(:) !< Times in seconds associated with Inputs + type(ADsk_ParameterType), intent(in ) :: p !< Parameters + type(ADsk_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; + type(ADsk_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; + type(ADsk_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; + type(ADsk_OtherStateType), intent(inout) :: OtherState !< Other states: Other states at t; + type(ADsk_MiscVarType), intent(inout) :: m !< Misc variables for optimization + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! Local variables + character(*), parameter :: RoutineName = 'ADsk_UpdateStates' + + ! Initialize variables + ErrStat = ErrID_None + ErrMsg = "" + + ! There are no states. + x%DummyContState = 0.0_ReKi + +end subroutine ADsk_UpdateStates + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a tight coupling routine for computing derivatives of continuous states. +!! NOTE: there are no states in AeroDisk +SUBROUTINE ADsk_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(ADsk_InputType), intent(in ) :: u !< Inputs at t + type(ADsk_ParameterType), intent(in ) :: p !< Parameters + type(ADsk_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(ADsk_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(ADsk_ConstraintStateType), intent(in ) :: z !< Constraint states at t + type(ADsk_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(ADsk_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + type(ADsk_ContinuousStateType), intent( out) :: dxdt !< Continuous state derivatives at t + INTEGER(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + character(*), parameter :: RoutineName = 'ADsk_CalcContStateDeriv' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! There are no states + dxdt%DummyContState = 0.0_ReKi + +END SUBROUTINE ADsk_CalcContStateDeriv + + +END MODULE AeroDisk +!********************************************************************************************************************************** diff --git a/modules/aerodisk/src/AeroDisk_IO.f90 b/modules/aerodisk/src/AeroDisk_IO.f90 new file mode 100644 index 0000000000..997edf8f0c --- /dev/null +++ b/modules/aerodisk/src/AeroDisk_IO.f90 @@ -0,0 +1,1106 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2024 National Renewable Energy Laboratory +! +! This file is part of AeroDisk +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +MODULE AeroDisk_IO + + USE AeroDisk_Types + USE AeroDisk_Output_Params + USE NWTC_Library + + implicit none + + ! data storage for reading/parsing table + type :: TableIndexType + integer(IntKi) :: ColTSR !< Column number for Tip-Speed Ratio + integer(IntKi) :: ColRtSpd !< Column number for Rotor Speed + integer(IntKi) :: ColVRel !< Column number for VRel + integer(IntKi) :: ColSkew !< Column number for Skew + integer(IntKi) :: ColPitch !< Column number for Pitch + integer(IntKi) :: NumColNamesGiven !< total number of column names given + end type TableIndexType + +contains + +!--------------------------------------------------------------- +!> Parse the input in the InFileInfo (FileInfo_Type data structure): +subroutine ADsk_ParsePrimaryFileData( InitInp, RootName, interval, FileInfo_In, InputFileData, UnEc, ErrStat, ErrMsg ) + type(ADsk_InitInputType), intent(in ) :: InitInp !< Input data for initialization routine + character(1024), intent(in ) :: RootName !< root name for summary file + real(DBKi), intent(in ) :: interval !< timestep + type(FileInfoType), intent(in ) :: FileInfo_In !< The input file stored in a data structure + type(ADsk_InputFile), intent(inout) :: InputFileData !< The data for initialization + integer(IntKi), intent( out) :: UnEc !< The local unit number for this module's echo file + integer(IntKi), intent( out) :: ErrStat !< Error status from this subroutine + character(*), intent( out) :: ErrMsg !< Error message from this subroutine + + ! local vars + integer(IntKi) :: CurLine !< current entry in FileInfo_In%Lines array + integer(IntKi) :: i !< generic counter + type(TableIndexType) :: TabIdx !< indices for table columnns, for simplifying data parsing/passing + real(SiKi) :: TmpRe(10) !< temporary 10 number array for reading values in from table + integer(IntKi) :: ErrStat2 !< Temporary error status for subroutine and function calls + character(ErrMsgLen) :: ErrMsg2 !< Temporary error message for subroutine and function calls + character(*), parameter :: RoutineName="ADsk_ParsePrimaryFileData" + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + UnEc = -1 ! No file + + + CALL AllocAry( InputFileData%OutList, MaxOutPts, "Outlist", ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + !====== General ==================================================================================== + CurLine = 4 ! Skip the first three lines as they are known to be header lines and separators + call ParseVar( FileInfo_In, CurLine, 'Echo', InputFileData%Echo, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + if ( InputFileData%Echo ) then + CALL OpenEcho ( UnEc, TRIM(RootName)//'.ech', ErrStat2, ErrMsg2 ) + if (Failed()) return; + WRITE(UnEc, '(A)') 'Echo file for AeroDisk primary input file: '//trim(InitInp%InputFile) + ! Write the first three lines into the echo file + WRITE(UnEc, '(A)') FileInfo_In%Lines(1) + WRITE(UnEc, '(A)') FileInfo_In%Lines(2) + WRITE(UnEc, '(A)') FileInfo_In%Lines(3) + + CurLine = 4 + call ParseVar( FileInfo_In, CurLine, 'Echo', InputFileData%Echo, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + endif + + + ! DeltaT - Time interval for aerodynamic calculations {or default} (s): + call ParseVarWDefault ( FileInfo_In, CurLine, "DT", InputFileData%DT, interval, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + !====== Environmental Conditions =================================================================== + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + ! AirDens - Air density {or default} (kg/m^3) + call ParseVarWDefault( FileInfo_In, CurLine, "AirDens", InputFileData%AirDens, InitInp%defAirDens, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + !====== Actuator Disk Properties =================================================================== + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + ! RotorRad - Rotor radius (m) (or "default") + call ParseVarWDefault( FileInfo_In, CurLine, "RotorRad", InputFileData%RotorRad, InitInp%RotorRad, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! InColNames - names for the input columns for the table. + call Get_InColNames( FileInfo_In, CurLine, TabIdx, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! InColDims - Number of values in each column (-) (must have same number of columns as InColName) [each >=2] + call Get_InColDims( FileInfo_In, CurLine, TabIdx, InputFileData%AeroTable, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! Column headers + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) + CurLine = CurLine + 1 + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) + CurLine = CurLine + 1 + + ! Read table + call Get_RtAeroTableData( FileInfo_In, CurLine, TabIdx, InputFileData%AeroTable, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + + !====== Outputs ==================================================================================== + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 +! ! SumPrint - Generate a summary file listing input options and interpolated properties to ".AD.sum"? (flag) +! call ParseVar( FileInfo_In, CurLine, "SumPrint", InputFileData%SumPrint, ErrStat2, ErrMsg2, UnEc ) +! if (Failed()) return + + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + call ReadOutputListFromFileInfo( FileInfo_In, CurLine, InputFileData%OutList, & + InputFileData%NumOuts, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return; + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine Cleanup() + ! Only do this on a fault. Leave open for calling routine in case we want to write anything else. + if (UnEc > 0_IntKi) close(UnEc) + end subroutine Cleanup + subroutine GetVarNamePos(FileName,LineNo,ChAry,NameToCheck,ColNum,ErrStat3,ErrMsg3) + character(*), intent(in ) :: FileName + integer(IntKi), intent(in ) :: LineNo + character(*), intent(in ) :: ChAry(:) + character(*), intent(in ) :: NameToCheck + integer(IntKi), intent( out) :: ColNum + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + character(len(ChAry(1))) :: TmpNm + character(len=len(NameToCheck)) :: TmpNmCk + integer(IntKi) :: WordNum + logical :: TmpFlag + ErrStat3 = ErrID_None + ErrMsg3 = "" + TmpFlag = .false. + ColNum = 0_IntKi ! if not present + TmpNmCk = NameToCheck; call Conv2UC(TmpNmCk) + do WordNum=1,size(ChAry) + TmpNm = ChAry(WordNum); call Conv2UC(TmpNm) + if (trim(TmpNm) == TmpNmCk) then + if (TmpFlag) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = NewLine//' >> A fatal error occurred when parsing data from '// & + trim(FileName)//'.'//NewLine// & + ' >> The column name '//trim(NameToCheck)//' occurs more than once on line '// & + trim(Num2LStr(LineNo))//'.' + return + endif + TmpFlag = .true. + ColNum = WordNum + endif + enddo + end subroutine GetVarNamePos + subroutine Get_InColNames(Info,LineNo,Idx,ErrStat3,ErrMsg3,UnEc) + ! A custom routine is used here rather than using the ParseChAry as ParseChAry does not + ! handle an unknown number of quoted strings well (i.e: "RtSpd,VRel,Skew,Pitch") + type(FileInfoType), intent(in ) :: Info + integer(IntKi), intent(inout) :: LineNo + type(TableIndexType), intent(inout) :: Idx + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + integer(IntKi), intent(in ) :: UnEc + integer(IntKi) :: WordPos + character(1024) :: thisFile ! Simplify some error management + integer(IntKi) :: thisLine ! Simplify some error management + character(20) :: TmpChAry(6) + ErrStat3 = ErrID_None + ErrMsg3 = "" + ! Echo if we have a file + if ( UnEc > 0 ) write (UnEc,'(A)') TRIM( Info%Lines(LineNo) ) + + ! Parse out words from the line + call GetWords( Info%Lines(LineNo), TmpChAry, size(TmpChAry)) + + ! file info for error handling -- note that this could be in a different file than the above!!!! + thisFile =trim(Info%FileList(Info%FileIndx(LineNo))) + thisLine = Info%FileLine(LineNo) + + ! Check that this is the right line in the file and InColNames exists in first 5 words of line + call GetVarNamePos(thisFile, thisLine, TmpChAry, "InColNames", WordPos, ErrStat3, ErrMsg3) ! ignore duplicate entry error here + ! Error handling if wrong row + if (WordPos <= 0_IntKi) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = NewLine//' >> A fatal error occurred when parsing data from '// & + trim(thisFile)//'.'//NewLine// & + ' >> The variable "InColNames" was not found on line '//trim(Num2LStr(thisLine))//'.' + return + endif + + ! Get order of the other columns (returns 0 if column name not in file) + call GetVarNamePos(thisFile, thisLine, TmpChAry, "TSR", Idx%ColTSR, ErrStat3,ErrMsg3); if (ErrStat3 >= ErrID_Fatal) return + call GetVarNamePos(thisFile, thisLine, TmpChAry, "RtSpd", Idx%ColRtSpd, ErrStat3,ErrMsg3); if (ErrStat3 >= ErrID_Fatal) return + call GetVarNamePos(thisFile, thisLine, TmpChAry, "VRel", Idx%ColVRel, ErrStat3,ErrMsg3); if (ErrStat3 >= ErrID_Fatal) return + call GetVarNamePos(thisFile, thisLine, TmpChAry, "Skew", Idx%ColSkew, ErrStat3,ErrMsg3); if (ErrStat3 >= ErrID_Fatal) return + call GetVarNamePos(thisFile, thisLine, TmpChAry, "Pitch", Idx%ColPitch, ErrStat3,ErrMsg3); if (ErrStat3 >= ErrID_Fatal) return + + ! total number of columns specified + Idx%NumColNamesGiven = maxval( (/ Idx%ColTSR, Idx%ColRtSpd, Idx%ColVRel, Idx%ColSkew, Idx%ColPitch /) ) + + ! make sure have have column names + if ((Idx%ColTSR + Idx%ColRtSpd + Idx%ColVRel + Idx%ColSkew + Idx%ColPitch) <= 0_IntKi) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = NewLine//' >> A fatal error occurred when parsing data from '// & + trim(thisFile)//'.'//NewLine// & + ' >> At least one column of data named "TSR", "RtSpd", "VRel", "Skew", or "Pitch" must exist'// & + ' in the table header, but none were found'//trim(Num2LStr(thisLine))//'.' + return + endif + LineNo = LineNo + 1 ! Picked up column names, so increment to next line and return + return + end subroutine Get_InColNames + subroutine Get_InColDims(Info,LineNo,Idx,AeroTable,ErrStat3,ErrMsg3,UnEc) + ! A custom routine is used here rather than using the ParseChAry as ParseChAry does not + ! handle an unknown number of quoted strings well (i.e: "RtSpd,VRel,Skew,Pitch") + type(FileInfoType), intent(in ) :: Info + integer(IntKi), intent(inout) :: LineNo + type(TableIndexType), intent(in ) :: Idx + type(ADsk_AeroTable), intent(inout) :: AeroTable + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + integer(IntKi), intent(in ) :: UnEc + integer(IntKi) :: WordPos + integer(IntKi) :: IOS + character(1024) :: thisFile ! Simplify some error management + integer(IntKi) :: thisLine ! Simplify some error management + character(20) :: TmpChAry(6) ! Assume 5 columns and the name + ErrStat3 = ErrID_None + ErrMsg3 = "" + ! Echo if we have a file + if ( UnEc > 0 ) write (UnEc,'(A)') TRIM( Info%Lines(LineNo) ) + + ! Parse out words from the line + call GetWords( Info%Lines(LineNo), TmpChAry, size(TmpChAry)) + + ! file info for error handling -- note that this could be in a different file than the above!!!! + thisFile =trim(Info%FileList(Info%FileIndx(LineNo))) + thisLine = Info%FileLine(LineNo) + + ! Check that this is the right line in the file and InColDims exists in first 5 words of line + call GetVarNamePos(thisFile, thisLine, TmpChAry, "InColDims", WordPos, ErrStat3, ErrMsg3) ! ignore duplicate entry error here + ! Error handling if wrong row + if (WordPos <= 0_IntKi) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = NewLine//' >> A fatal error occurred when parsing data from '// & + trim(thisFile)//'.'//NewLine// & + ' >> The variable "InColDims" was not found on line '//trim(Num2LStr(thisLine))//'.' + return + endif + + ! set number of indices for each to 0 + AeroTable%N_TSR = 0 + AeroTable%N_RtSpd = 0 + AeroTable%N_VRel = 0 + AeroTable%N_Skew = 0 + AeroTable%N_Pitch = 0 + + ! Read numbers for number of various entries from the tmpChAry + if (Idx%ColTSR > 0_IntKi) then + READ (TmpChAry(Idx%ColTSR ),*,IOSTAT=IOS) AeroTable%N_TSR + if (IOS /= 0) then + call SetErrStat(ErrID_Fatal,'Could not read "N_TSR" from column '// & + trim(Num2LStr(Idx%ColTSR))//' on line '//trim(Num2LStr(thisLine))// & + ' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + return + endif + endif + if (Idx%ColRtSpd > 0_IntKi) then + READ (TmpChAry(Idx%ColRtSpd),*,IOSTAT=IOS) AeroTable%N_RtSpd + if (IOS /= 0) then + call SetErrStat(ErrID_Fatal,'Could not read "N_RtSpd" from column '// & + trim(Num2LStr(Idx%ColRtSpd))//' on line '//trim(Num2LStr(thisLine))// & + ' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + return + endif + endif + if (Idx%ColVRel > 0_IntKi) then + READ (TmpChAry(Idx%ColVRel ),*,IOSTAT=IOS) AeroTable%N_VRel + if (IOS /= 0) then + call SetErrStat(ErrID_Fatal,'Could not read "N_VRel" from column '// & + trim(Num2LStr(Idx%ColVRel))//' on line '//trim(Num2LStr(thisLine))// & + ' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + return + endif + endif + if (Idx%ColSkew > 0_IntKi) then + READ (TmpChAry(Idx%ColSkew ),*,IOSTAT=IOS) AeroTable%N_Skew + if (IOS /= 0) then + call SetErrStat(ErrID_Fatal,'Could not read "N_Skew" from column '// & + trim(Num2LStr(Idx%ColSkew))//' on line '//trim(Num2LStr(thisLine))// & + ' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + return + endif + endif + if (Idx%ColPitch > 0_IntKi) then + READ (TmpChAry(Idx%ColPitch),*,IOSTAT=IOS) AeroTable%N_Pitch + if (IOS /= 0) then + call SetErrStat(ErrID_Fatal,'Could not read "N_Pitch" from column '// & + trim(Num2LStr(Idx%ColPitch))//' on line '//trim(Num2LStr(thisLine))// & + ' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + return + endif + endif + ! make sure all values positive + if (AeroTable%N_TSR < 0_IntKi) call SetErrStat(ErrID_Fatal,'Entry for "N_TSR" must be postive valued from column '// & + trim(Num2LStr(Idx%ColTSR ))//' on line '//trim(Num2LStr(thisLine))//' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + if (AeroTable%N_RtSpd < 0_IntKi) call SetErrStat(ErrID_Fatal,'Entry for "N_RtSpd" must be postive valued from column '// & + trim(Num2LStr(Idx%ColRtSpd))//' on line '//trim(Num2LStr(thisLine))//' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + if (AeroTable%N_VRel < 0_IntKi) call SetErrStat(ErrID_Fatal,'Entry for "N_VRel" must be postive valued from column '// & + trim(Num2LStr(Idx%ColVRel ))//' on line '//trim(Num2LStr(thisLine))//' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + if (AeroTable%N_Skew < 0_IntKi) call SetErrStat(ErrID_Fatal,'Entry for "N_Skew" must be postive valued from column '// & + trim(Num2LStr(Idx%ColSkew ))//' on line '//trim(Num2LStr(thisLine))//' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + if (AeroTable%N_Pitch < 0_IntKi) call SetErrStat(ErrID_Fatal,'Entry for "N_Pitch" must be postive valued from column '// & + trim(Num2LStr(Idx%ColPitch))//' on line '//trim(Num2LStr(thisLine))//' in file '//trim(thisFile)//'.',ErrStat3,ErrMsg3,'') + ! NOTE: we are storing 0 for the dimensions that don't exist in the table. We will + ! modify this later to make table usage simpler + LineNo = LineNo + 1 ! Picked up column names, so increment to next line and return + return + end subroutine Get_InColDims + +end subroutine ADsk_ParsePrimaryFileData + + +subroutine Get_RtAeroTableData(Info,LineNo,Idx,AeroTable,ErrStat,ErrMsg,UnEc) + ! Table entries may not be in order. So sort while reading in. + ! NOTE: Sparse data files are not currently supported + type(FileInfoType), intent(in ) :: Info + integer(IntKi), intent(inout) :: LineNo + type(TableIndexType), intent(in ) :: Idx + type(ADsk_AeroTable), intent(inout) :: AeroTable + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi), intent(in ) :: UnEc + integer(IntKi) :: WordPos + integer(IntKi) :: IOS + character(1024) :: thisFile ! Simplify some error management + integer(IntKi) :: thisLine ! Simplify some error management + integer(IntKi) :: i,j ! Generic counters + integer(IntKi) :: NumCols ! number of columns expected + integer(IntKi) :: NumRows ! number of rows expected + integer(IntKi) :: Sz(5) ! Array size -- for readability of code + real(SiKi), allocatable :: TmpTab(:,:) ! temporary real array of allocatable size -- will read entire table in, then do the sorting + logical, allocatable :: Mask(:,:,:,:,:) ! to make sure we aren't missing any terms + integer(IntKi) :: ErrStat2 !< Temporary error status for subroutine and function calls + character(ErrMsgLen) :: ErrMsg2 !< Temporary error message for subroutine and function calls + character(*), parameter :: RoutineName='Get_RtAeroTableData' + ErrStat = ErrID_None + ErrMsg = "" + + + ! Number of columns in table + Sz = 0_IntKi + if (AeroTable%N_TSR > 0_IntKi) Sz(1) = 1_IntKi + if (AeroTable%N_RtSpd > 0_IntKi) Sz(2) = 1_IntKi + if (AeroTable%N_VRel > 0_IntKi) Sz(3) = 1_IntKi + if (AeroTable%N_Pitch > 0_IntKi) Sz(4) = 1_IntKi + if (AeroTable%N_Skew > 0_IntKi) Sz(5) = 1_IntKi + NumCols = Idx%NumColNamesGiven + 6_IntKi ! Add DOF columns + + ! temporary array for sizing -- note that min dimension size is 1 so we calculate number of rows correctly + Sz(1) = max(AeroTable%N_TSR, 1_IntKi) + Sz(2) = max(AeroTable%N_RtSpd,1_IntKi) + Sz(3) = max(AeroTable%N_VRel, 1_IntKi) + Sz(4) = max(AeroTable%N_Pitch,1_IntKi) + Sz(5) = max(AeroTable%N_Skew, 1_IntKi) + + ! Total number of rows we expect to find in the table + NumRows= Sz(1) * Sz(2) * Sz(3) * Sz(4) * Sz(5) + + ! Allocate arrays for forces/moments + call AllocAry(AeroTable%C_Fx, Sz(1), Sz(2), Sz(3), Sz(4), Sz(5), 'AeroTable%C_Fx', ErrStat2, ErrMsg2); if (Failed()) return; AeroTable%C_Fx = 0.0_SiKi + call AllocAry(AeroTable%C_Fy, Sz(1), Sz(2), Sz(3), Sz(4), Sz(5), 'AeroTable%C_Fy', ErrStat2, ErrMsg2); if (Failed()) return; AeroTable%C_Fy = 0.0_SiKi + call AllocAry(AeroTable%C_Fz, Sz(1), Sz(2), Sz(3), Sz(4), Sz(5), 'AeroTable%C_Fz', ErrStat2, ErrMsg2); if (Failed()) return; AeroTable%C_Fz = 0.0_SiKi + call AllocAry(AeroTable%C_Mx, Sz(1), Sz(2), Sz(3), Sz(4), Sz(5), 'AeroTable%C_Mx', ErrStat2, ErrMsg2); if (Failed()) return; AeroTable%C_Mx = 0.0_SiKi + call AllocAry(AeroTable%C_My, Sz(1), Sz(2), Sz(3), Sz(4), Sz(5), 'AeroTable%C_My', ErrStat2, ErrMsg2); if (Failed()) return; AeroTable%C_My = 0.0_SiKi + call AllocAry(AeroTable%C_Mz, Sz(1), Sz(2), Sz(3), Sz(4), Sz(5), 'AeroTable%C_Mz', ErrStat2, ErrMsg2); if (Failed()) return; AeroTable%C_Mz = 0.0_SiKi + + ! Allocate a mask for data checks + allocate(Mask(Sz(1),Sz(2),Sz(3),Sz(4),Sz(5)),STAT=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal,'Could not allocate array for data mask',ErrStat,ErrMsg,RoutineName) + call Cleanup() + return + endif + Mask = .false. + + ! Slurp the entire table into real array -- order for more efficient searching (bad for reading in) + call AllocAry(TmpTab,NumRows,NumCols,'TemporaryTable',ErrStat2,ErrMsg2); if (Failed()) return + do i=1,NumRows + read (Info%Lines(LineNo),*,iostat=IOS) TmpTab(i,1:NumCols) + if (IOS /= 0_IntKi) then + thisFile =trim(Info%FileList(Info%FileIndx(LineNo))) + thisLine = Info%FileLine(LineNo) + call SetErrStat(ErrID_Fatal,'Could not read "Actuator Disk Properties Table" row '//trim(Num2LStr(i))//' (line '//trim(Num2LStr(thisLine))// & + ' in file '//trim(thisFile)//'). Expecting '//trim(Num2LStr(NumRows))//' rows and '//trim(Num2LStr(NumCols))//' columns in table.',ErrStat,ErrMsg,RoutineName) + call Cleanup() + return + endif + if (UnEc > 0_IntKi) write(UnEc,'(A)') trim(Info%Lines(LineNo)) + LineNo = LineNo + 1 + enddo + + ! Find the unique values in the indexing columns of the table (if name given, otherwise skip) + if (AeroTable%N_TSR > 0_IntKi) call GetTabIndexVals( TmpTab(:,Idx%ColTSR ),'TSR' ,AeroTable%N_TSR ,AeroTable%TSR , ErrStat2, ErrMsg2); if (Failed()) return + if (AeroTable%N_RtSpd > 0_IntKi) call GetTabIndexVals( TmpTab(:,Idx%ColRtSpd),'RtSpd',AeroTable%N_RtSpd,AeroTable%RtSpd, ErrStat2, ErrMsg2); if (Failed()) return + if (AeroTable%N_VRel > 0_IntKi) call GetTabIndexVals( TmpTab(:,Idx%ColVRel ),'VRel' ,AeroTable%N_VRel ,AeroTable%VRel , ErrStat2, ErrMsg2); if (Failed()) return + if (AeroTable%N_Pitch > 0_IntKi) call GetTabIndexVals( TmpTab(:,Idx%ColPitch),'Pitch',AeroTable%N_Pitch,AeroTable%Pitch, ErrStat2, ErrMsg2); if (Failed()) return + if (AeroTable%N_Skew > 0_IntKi) call GetTabIndexVals( TmpTab(:,Idx%ColSkew ),'Skew' ,AeroTable%N_Skew ,AeroTable%Skew , ErrStat2, ErrMsg2); if (Failed()) return + + ! Now populate matrix -- read each line and put in correct table entry location + call PopulateAeroTabs(AeroTable,Mask,Idx,TmpTab,NumRows,NumCols,ErrStat2,ErrMsg2); if (Failed()) return + call CheckAeroTabs(AeroTable,Mask,ErrStat2,ErrMsg2); if (Failed()) return + + ! Now convert RtSpd from rpm to rad/s, and Pitch and Skew from deg to rad + if (AeroTable%N_RtSpd > 0_IntKi) AeroTable%RtSpd = (AeroTable%RtSpd * Pi_S)/30.0_SiKi + if (AeroTable%N_Pitch > 0_IntKi) AeroTable%Pitch = (AeroTable%Pitch * Pi_S)/180.0_SiKi + if (AeroTable%N_Skew > 0_IntKi) AeroTable%Skew = (AeroTable%Skew * Pi_S)/180.0_SiKi + + call Cleanup() + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine Cleanup() + if (allocated(TmpTab)) deallocate(TmpTab) + if (allocated(Mask)) deallocate(Mask) + end subroutine Cleanup + subroutine GetTabIndexVals(TabCol,ColName,NumExpect,UniqueArray,ErrStat3,ErrMsg3) + real(SiKi), intent(in ) :: TabCol(:) + character(*), intent(in ) :: ColName + integer(IntKi), intent(in ) :: NumExpect + real(SiKi), allocatable,intent( out) :: UniqueArray(:) + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + integer(IntKi) :: NumFound + call UniqueRealValues( TabCol, UniqueArray, NumFound, ErrStat3, ErrMsg3 ) + if (NumExpect /= NumFound) then + call SetErrStat(ErrID_Fatal,'Expecting '//trim(Num2LStr(NumExpect))//' unique '//ColName// & + ' entries in "Actuator Disk Properties Table", but found '//trim(Num2LStr(NumFound))//' instead.',ErrStat3,ErrMsg3,'') + endif + end subroutine GetTabIndexVals + subroutine PopulateAeroTabs(Aero,DatMask,TabIdx,Dat,nRow,nCol,ErrStat3,ErrMsg3) + type(ADsk_AeroTable), intent(inout) :: Aero + logical, allocatable,intent(inout) :: DatMask(:,:,:,:,:) + type(TableIndexType), intent(in ) :: TabIdx + real(SiKi), allocatable,intent(in ) :: Dat(:,:) + integer(IntKi), intent(in ) :: nCol ! number of columns in Dat + integer(IntKi), intent(in ) :: nRow ! number of rows in Dat + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + real(SiKi) :: TmpR6(6) + integer(IntKi) :: row + integer(IntKi) :: iTSR,iRtSpd,iVRel,iPitch,iSkew + ErrStat3 = ErrID_None + ErrMsg3 = '' + DatMask = .false. ! Make sure data mask is false. Set to true for entries we populate + ! Step through each row of raw data, and find corresponding data location + ! Set index for placement to 1 in the event that the variable isn't actually used + do row=1,nRow + TmpR6(1:6) = Dat(row,nCol-5:nCol) ! DOF values + ! Find TSR entry LocateStp(XVal, XAry, Ind, AryLen) + iTSR = 1_IntKi + iRtSpd = 1_IntKi + iVRel = 1_IntKi + iPitch = 1_IntKi + iSkew = 1_IntKi + ! if no entries, the corresponding array is unallocated, so skip and leave index at 1 + if (Aero%N_TSR > 0_IntKi) call LocateStp( Dat(row,TabIdx%ColTSR ),Aero%TSR ,iTSR ,Aero%N_TSR ) + if (Aero%N_RtSpd > 0_IntKi) call LocateStp( Dat(row,TabIdx%ColRtSpd),Aero%RtSpd,iRtSpd,Aero%N_RtSpd) + if (Aero%N_VRel > 0_IntKi) call LocateStp( Dat(row,TabIdx%ColVRel ),Aero%VRel ,iVRel ,Aero%N_VRel ) + if (Aero%N_Pitch > 0_IntKi) call LocateStp( Dat(row,TabIdx%ColPitch),Aero%Pitch,iPitch,Aero%N_Pitch) + if (Aero%N_Skew > 0_IntKi) call LocateStp( Dat(row,TabIdx%ColSkew ),Aero%Skew ,iSkew ,Aero%N_Skew ) + Aero%C_Fx(iTSR,iRtSpd,iVRel,iPitch,iSkew) = TmpR6(1) + Aero%C_Fy(iTSR,iRtSpd,iVRel,iPitch,iSkew) = TmpR6(2) + Aero%C_Fz(iTSR,iRtSpd,iVRel,iPitch,iSkew) = TmpR6(3) + Aero%C_Mx(iTSR,iRtSpd,iVRel,iPitch,iSkew) = TmpR6(4) + Aero%C_My(iTSR,iRtSpd,iVRel,iPitch,iSkew) = TmpR6(5) + Aero%C_Mz(iTSR,iRtSpd,iVRel,iPitch,iSkew) = TmpR6(6) + if (DatMask(iTSR,iRtSpd,iVRel,iPitch,iSkew)) then + call SetErrStat(ErrID_Fatal,'Duplicate data entry in "Actuator Disk Properties Table" row '//trim(Num2LStr(row))//'.',ErrStat3,ErrMsg3,'') + else + DatMask(iTSR,iRtSpd,iVRel,iPitch,iSkew) = .true. + endif + enddo + end subroutine PopulateAeroTabs + subroutine CheckAeroTabs(Aero,DatMask,ErrStat3,ErrMsg3) + type(ADsk_AeroTable), intent(in ) :: Aero + logical, allocatable,intent(in ) :: DatMask(:,:,:,:,:) + integer(IntKi), intent( out) :: ErrStat3 + character(ErrMsgLen), intent( out) :: ErrMsg3 + integer(IntKi) :: iTSR,iRtSpd,iVRel,iPitch,iSkew + logical :: DataMiss + logical :: FirstMiss + character(60) :: TmpChar + real(SiKi) :: TmpR5(5) + ErrStat3 = ErrID_None + ErrMsg3 = '' + DataMiss = .false. + FirstMiss = .false. + do iSkew=1,size(DatMask,DIM=5) + do iPitch=1,size(DatMask,DIM=4) + do iVRel=1,size(DatMask,DIM=3) + do iRtSpd=1,size(DatMask,DIM=2) + do iTSR=1,size(DatMask,DIM=1) + if (.not. DatMask(iTSR,iRtSpd,iVRel,iPitch,iSkew)) then + DataMiss = .true. + if (.not. FirstMiss) then + FirstMiss = .true. + ErrStat3 = ErrID_Fatal + ErrMsg3 = NewLine//'Data missing from table!!! (note order may not be same as file)'//NewLine// & + ' TSR RtSpd VRel Pitch Skew '//NewLine + endif + TmpR5 = NaN_S + if (Aero%N_TSR >0_IntKi) TmpR5(1) = Aero%TSR (iTSR ) + if (Aero%N_RtSpd>0_IntKi) TmpR5(2) = Aero%RtSpd(iRtSpd) + if (Aero%N_VRel >0_IntKi) TmpR5(3) = Aero%VRel (iVRel ) + if (Aero%N_Pitch>0_IntKi) TmpR5(4) = Aero%Pitch(iPitch) + if (Aero%N_Skew >0_IntKi) TmpR5(5) = Aero%Skew (iSkew ) + write(TmpChar,'(6(f10.3))') TmpR5(1), TmpR5(2), TmpR5(3), TmpR5(4), TmpR5(5) + ErrMsg3 = ErrMsg3//TmpChar//NewLine + endif + enddo + enddo + enddo + enddo + enddo + end subroutine CheckAeroTabs +end subroutine Get_RtAeroTableData + + +!> This subroutine counts the number of unique values in an array and returns a sorted array of them. +!! NOTE: this routine is found in the WAMIT2.f90 file as well +SUBROUTINE UniqueRealValues( DataArrayIn, DataArrayOut, NumUnique, ErrStat, ErrMsg ) + IMPLICIT NONE + REAL(SiKi), INTENT(IN ) :: DataArrayIn(:) !< Array to search + REAL(SiKi), ALLOCATABLE, INTENT( OUT) :: DataArrayOut(:) !< Array to return results in + INTEGER(IntKi), INTENT( OUT) :: NumUnique !< Number of unique values found + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error Status + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Message about the error + ! Local variables + REAL(SiKi) :: TmpReal !< Temporary real value + INTEGER(IntKi) :: I !< Generic counter + INTEGER(IntKi) :: J !< Generic counter + REAL(SiKi), ALLOCATABLE :: TmpRealArray(:) !< Temporary real array + LOGICAL :: Duplicate !< If there is a duplicate value + ! Error handling + INTEGER(IntKi) :: ErrStatTmp + CHARACTER(2048) :: ErrMsgTmp + CHARACTER(*), PARAMETER :: RoutineName = 'UniqueRealValues' + + ! Initialize things + ErrStat = ErrID_None + ErrStatTmp = ErrID_None + ErrMsg = '' + ErrMsgTmp = '' + + ! Allocate the temporary array + CALL AllocAry( TmpRealArray, SIZE(DataArrayIn,1), 'Temporary array for data sorting', ErrStatTmp, ErrMsgTmp ) + CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL CleanUp + RETURN + ENDIF + + ! Initialize the array with a large negative number. + TmpRealArray = -9.9e9_SiKi + + ! The first point is unique since we haven't compared it to anything yet. + TmpRealArray(1) = DataArrayIn(1) + NumUnique = 1 + + ! Step through the DataArrayIn and put unique values into TmpRealArray. Start at second point + DO I=2,SIZE(DataArrayIn,1) + ! Check the current value against the largest stored value (I-1). If the current value is + ! larger than the last stored one, then it should be stored after it. + IF ( DataArrayIn(I) > TmpRealArray(NumUnique) ) THEN + TmpRealArray(NumUnique + 1) = DataArrayIn(I) + NumUnique = NumUnique + 1 + ELSE + ! Otherwise, if the value should not be put last, then we have to find where it goes. Before + ! searching for the location, first make sure this isn't a duplicate value. + Duplicate = .FALSE. + DO J= NumUnique, 1, -1 + IF ( EqualRealNos( DataArrayIn(I), TmpRealArray(J) )) THEN + Duplicate = .TRUE. + EXIT ! Stop searching + ENDIF + ENDDO + + ! If this is not a duplicate, the location where it goes has to be find. To do this, we will + ! sequentially shift each value one index further as we step backwards through the sorted + ! array. When we find the location between values where this goes, we put the value there. + IF ( .NOT. Duplicate ) THEN + DO J= NumUnique, 1, -1 ! TempRealArray only has NumUnique values. Everything after is junk. + IF ( DataArrayIn(I) < TmpRealArray(J) ) THEN + TmpRealArray(J+1) = TmpRealArray(J) ! Shift this value further along the array + IF ( J == 1 ) THEN ! If we are at the first value, then it goes here. + TmpRealArray(J) = DataArrayIn(I) + NumUnique = NumUnique + 1 + ELSE + IF ( DataArrayIn(I) > TmpRealArray(J-1) ) THEN ! If larger than the preceeding number, it goes here + TmpRealArray(J) = DataArrayIn(I) + NumUnique = NumUnique + 1 + ENDIF + ENDIF + ENDIF + ENDDO + ENDIF + ENDIF + ENDDO + + ! Now that we have the array sorted into unique values, we need to construct an array to return the values in. + CALL AllocAry( DataArrayOut, NumUnique, 'Return array with sorted values', ErrStatTmp, ErrMsgTmp ) + CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL CleanUp + RETURN + ENDIF + + ! Copy the values over + DataArrayOut = TmpRealArray(1:NumUnique) + call Cleanup() +contains + subroutine Cleanup() + if (allocated(TmpRealArray)) deallocate(TmpRealArray) + end subroutine Cleanup +END SUBROUTINE UniqueRealValues + + +!> Check inputdata +subroutine ADskInput_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg ) + type(ADsk_InitInputType), intent(in ) :: InitInp !< Input data for initialization + type(ADsk_InputFile), intent(in ) :: InputFileData !< The data for initialization + integer(IntKi), intent( out) :: ErrStat !< Error status from this subroutine + character(*), intent( out) :: ErrMsg !< Error message from this subroutine + character(*), parameter :: RoutineName="ADskInput_ValidateInput" + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! InitInput checks + if (InputFileData%DT <= 0.0_DbKi) call SetErrStat(ErrID_Fatal,'DT must not be negative.', ErrStat,ErrMsg,RoutineName) + if (InputFileData%AirDens <= 0.0_ReKi) call SetErrStat(ErrID_Fatal,'AirDens must not be negative.',ErrStat,ErrMsg,RoutineName) + if (InputFileData%RotorRad <= 0.0_ReKi) call SetErrStat(ErrID_Fatal,'RotorRad must not be negative.',ErrStat,ErrMsg,RoutineName) + if (InitInp%Linearize) call SetErrStat(ErrID_Fatal,'AeroDisk cannot perform linearization analysis.',ErrStat,ErrMsg,RoutineName) + + ! Some sanity checks AeroTable + if (InputFileData%AeroTable%N_TSR > 0_IntKi) then + if (minval(InputFileData%AeroTable%TSR) <= 0.0_SiKi) then + call SetErrStat(ErrID_Fatal,'All TSR values in table must be postive.',ErrStat,ErrMsg,RoutineName) + endif + endif + if (InputFileData%AeroTable%N_RtSpd > 0_IntKi) then + if (minval(InputFileData%AeroTable%RtSpd) <= 0.0_SiKi) then + call SetErrStat(ErrID_Fatal,'All RtSpd values in table must be postive.',ErrStat,ErrMsg,RoutineName) + endif + endif + if (InputFileData%AeroTable%N_VRel > 0_IntKi) then + if (minval(InputFileData%AeroTable%VRel) < 0.0_SiKi ) then + call SetErrStat(ErrID_Fatal,'All VRel values in table must be postive.',ErrStat,ErrMsg,RoutineName) + endif + endif + if (InputFileData%AeroTable%N_Pitch > 0_IntKi) then ! input table as deg, already converted to rad + if (minval(InputFileData%AeroTable%Pitch) <= -Pi_S .or. maxval(InputFileData%AeroTable%Pitch) >= Pi_S) then + call SetErrStat(ErrID_Fatal,'All Pitch values in table must be between -180 and 180 degrees.',ErrStat,ErrMsg,RoutineName) + endif + endif + if (InputFileData%AeroTable%N_Skew > 0_IntKi) then ! input table as deg, already converted to rad + if (minval(InputFileData%AeroTable%Skew) <= 0.0_SiKi .or. maxval(InputFileData%AeroTable%Skew) >= Pi_S) then + call SetErrStat(ErrID_Fatal,'All Skew values in table must be between 0 and 180 degrees.',ErrStat,ErrMsg,RoutineName) + endif + endif + + ! Either TSR or RtSpd+VRel columns must be provided + if (InputFileData%AeroTable%N_TSR > 1_IntKi) then + if (InputFileData%AeroTable%N_RtSpd > 1_IntKi .or. InputFileData%AeroTable%N_VRel > 1_IntKi) then + call SetErrStat(ErrID_Fatal,'TSR values present in table along with RtSpd or VRel values. '//NewLine// & + ' --> Either RtSpd and VRel values must be in the table, or TSR values may be present.'//NewLine// & + ' To skip columns, you may enter "0" for the column index and leave the table values.',ErrStat,ErrMsg,RoutineName) + return + endif + else + if (InputFileData%AeroTable%N_RtSpd < 2_IntKi .and. InputFileData%AeroTable%N_VRel < 2_IntKi) then + call SetErrStat(ErrID_Fatal,'TSR values NOT present in table, but RtSpd and VRel values are not both present. '//NewLine// & + ' --> Either RtSpd and VRel values must be in the table, or TSR values may be present.'//NewLine// & + ' To skip columns, you may enter "0" for the column index and leave the table values.',ErrStat,ErrMsg,RoutineName) + return + endif + endif +end subroutine ADskInput_ValidateInput + + +!> validate and process input file data (some was done during parsing of input file) +subroutine ADskInput_SetParameters( InitInp, Interval, InputFileData, p, ErrStat, ErrMsg ) + type(ADsk_InitInputType), intent(in ) :: InitInp !< Input data for initialization + real(DbKi), intent(inout) :: Interval !< Coupling interval in seconds + type(ADsk_InputFile), intent(inout) :: InputFileData !< The data for initialization + type(ADsk_ParameterType), intent(inout) :: p !< + integer(IntKi), intent( out) :: ErrStat !< Error status from this subroutine + character(*), intent( out) :: ErrMsg !< Error message from this subroutine + integer(IntKi) :: ErrStat2 !< Temporary error status for subroutine and function calls + character(ErrMsgLen) :: ErrMsg2 !< Temporary error message for subroutine and function calls + character(*), parameter :: RoutineName="ADskInput_SetParameters" + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! Set parameters + p%DT = InputFileData%DT + Interval = p%DT ! Tell glue code what we want for DT + p%numOuts = InputFileData%NumOuts + p%RootName = InitInp%RootName + p%RotorRad = InputFileData%RotorRad + p%AirDens = InputFileData%AirDens + p%UseTSR = .false. ! Reset below if N_TSR>1 + + ! Derived parameter + p%halfRhoA = 0.5_ReKi * p%AirDens * Pi * p%RotorRad*p%RotorRad + + ! Table of values + p%AeroTable%N_TSR = InputFileData%AeroTable%N_TSR + p%AeroTable%N_RtSpd = InputFileData%AeroTable%N_RtSpd + p%AeroTable%N_VRel = InputFileData%AeroTable%N_VRel + p%AeroTable%N_Pitch = InputFileData%AeroTable%N_Pitch + p%AeroTable%N_Skew = InputFileData%AeroTable%N_Skew + if (allocated( InputFileData%AeroTable%TSR )) call move_alloc( InputFileData%AeroTable%TSR, p%AeroTable%TSR ) + if (allocated( InputFileData%AeroTable%RtSpd)) call move_alloc( InputFileData%AeroTable%RtSpd, p%AeroTable%RtSpd ) + if (allocated( InputFileData%AeroTable%VRel )) call move_alloc( InputFileData%AeroTable%VRel, p%AeroTable%VRel ) + if (allocated( InputFileData%AeroTable%Pitch)) call move_alloc( InputFileData%AeroTable%Pitch, p%AeroTable%Pitch ) + if (allocated( InputFileData%AeroTable%Skew )) call move_alloc( InputFileData%AeroTable%Skew, p%AeroTable%Skew ) + if (allocated( InputFileData%AeroTable%C_Fx )) call move_alloc( InputFileData%AeroTable%C_Fx, p%AeroTable%C_Fx ) + if (allocated( InputFileData%AeroTable%C_Fy )) call move_alloc( InputFileData%AeroTable%C_Fy, p%AeroTable%C_Fy ) + if (allocated( InputFileData%AeroTable%C_Fz )) call move_alloc( InputFileData%AeroTable%C_Fz, p%AeroTable%C_Fz ) + if (allocated( InputFileData%AeroTable%C_Mx )) call move_alloc( InputFileData%AeroTable%C_Mx, p%AeroTable%C_Mx ) + if (allocated( InputFileData%AeroTable%C_My )) call move_alloc( InputFileData%AeroTable%C_My, p%AeroTable%C_My ) + if (allocated( InputFileData%AeroTable%C_Mz )) call move_alloc( InputFileData%AeroTable%C_Mz, p%AeroTable%C_Mz ) + + ! Use the TSR values + if (p%AeroTable%N_TSR > 1_IntKi) p%UseTSR = .true. + + ! Set the outputs + call SetOutParam(InputFileData%OutList, p, ErrStat, ErrMsg ) +end subroutine ADskInput_SetParameters + + +!> Write the table out to a whatever UnOut is (as long as > 0). +subroutine WriteAeroTab(Aero, UnOut) + type(ADsk_AeroTable), intent(in ) :: Aero + integer(IntKi), intent(in ) :: UnOut + integer(IntKi) :: i1,i2,i3,i4,i5 !< loop counters + character(*), parameter :: RoutineName="ADskInput_SetParameters" + if (UnOut <= 0_IntKi) return + ! Write header info + write(UnOut,'(A)') '=======================================' + write(UnOut,'(A)') 'AeroDisk Actuator Disk Properties table' + write(UnOut,'(A)') ' NOTE: the units correspond to units used internally within code, not units of input' + if (Aero%N_TSR > 0_IntKi) then + write(UnOut,'(A12,I5,A7)') ' TSR ',Aero%N_TSR,' values' + else + write(UnOut,'(A)') ' TSR ---- unused ----' + endif + if (Aero%N_RtSpd > 0_IntKi) then + write(UnOut,'(A12,I5,A7)') ' RtSpd ',Aero%N_RtSpd,' values' + else + write(UnOut,'(A)') ' RtSpd ---- unused ----' + endif + if (Aero%N_VRel > 0_IntKi) then + write(UnOut,'(A12,I5,A7)') ' VRel ',Aero%N_VRel,' values' + else + write(UnOut,'(A)') ' VRel ---- unused ----' + endif + if (Aero%N_Pitch > 0_IntKi) then + write(UnOut,'(A12,I5,A7)') ' Pitch ',Aero%N_Pitch,' values' + else + write(UnOut,'(A)') ' Pitch ---- unused ----' + endif + if (Aero%N_Skew > 0_IntKi) then + write(UnOut,'(A12,I5,A7)') ' Skew ',Aero%N_Skew,' values' + else + write(UnOut,'(A)') ' Skew ---- unused ----' + endif + ! Table header + if (Aero%N_TSR > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' TSR ' + if (Aero%N_Skew > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' RtSpd ' + if (Aero%N_VRel > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' VRel ' + if (Aero%N_Pitch > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' Pitch ' + if (Aero%N_Skew > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' Skew ' + write(UnOut,'(A15)',ADVANCE='NO') ' C_fx ' + write(UnOut,'(A15)',ADVANCE='NO') ' C_fy ' + write(UnOut,'(A15)',ADVANCE='NO') ' C_fz ' + write(UnOut,'(A15)',ADVANCE='NO') ' C_mx ' + write(UnOut,'(A15)',ADVANCE='NO') ' C_my ' + write(UnOut,'(A15)',ADVANCE='NO') ' C_mz ' + write(UnOut,'(A)') '' + if (Aero%N_TSR > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + if (Aero%N_Skew > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' (rad/s) ' + if (Aero%N_VRel > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' (m/s) ' + if (Aero%N_Pitch > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' (rad) ' + if (Aero%N_Skew > 0_IntKi) write(UnOut,'(A15)',ADVANCE='NO') ' (rad) ' + write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + write(UnOut,'(A15)',ADVANCE='NO') ' (-) ' + write(UnOut,'(A)') '' + ! Table itself + do i1=1,max(1,Aero%N_TSR ) + do i2=1,max(1,Aero%N_Skew ) + do i3=1,max(1,Aero%N_VRel ) + do i4=1,max(1,Aero%N_Pitch) + do i5=1,max(1,Aero%N_Skew ) + if (Aero%N_TSR > 0_IntKi) write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%TSR (i1) + if (Aero%N_Skew > 0_IntKi) write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%Skew (i2) + if (Aero%N_VRel > 0_IntKi) write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%VRel (i3) + if (Aero%N_Pitch> 0_IntKi) write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%Pitch(i4) + if (Aero%N_Skew > 0_IntKi) write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%Skew (i5) + write(UnOut, '(f13.6)',ADVANCE='NO') Aero%C_Fx(i1,i2,i3,i4,i5) + write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%C_Fy(i1,i2,i3,i4,i5) + write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%C_Fz(i1,i2,i3,i4,i5) + write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%C_Mx(i1,i2,i3,i4,i5) + write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%C_My(i1,i2,i3,i4,i5) + write(UnOut,'(2x,f13.6)',ADVANCE='NO') Aero%C_Mz(i1,i2,i3,i4,i5) + write(UnOut,'(A)') '' + enddo + enddo + enddo + enddo + enddo +end subroutine WriteAeroTab + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> this routine fills the AllOuts array, which is used to send data to the glue code to be written to an output file. +!! NOTE: AllOuts is ReKi, but most calculations in this module are in single precision. This requires a bunch of conversions at this +!! stage. +subroutine Calc_WriteOutput( u, p, y, m, ErrStat, ErrMsg, CalcWriteOutput ) + type(ADsk_InputType), intent(in ) :: u !< The inputs at time T + type(ADsk_ParameterType), intent(in ) :: p !< The module parameters + type(ADsk_OutputType), intent(in ) :: y !< outputs + type(ADsk_MiscVarType), intent(inout) :: m !< misc/optimization variables (for computing mesh transfers) + integer(IntKi), intent( out) :: ErrStat !< The error status code + character(*), intent( out) :: ErrMsg !< The error message, if an error occurred + logical, intent(in ) :: CalcWriteOutput !< flag that determines if we need to compute AllOuts (or just the reaction loads that get returned to ServoDyn) + ! local variables + character(*), parameter :: RoutineName = 'Calc_WriteOutput' + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + real(ReKi) :: Tmp3(3) + real(ReKi) :: Rxyz(3,3) !< rotation matrix for x,y,z of local coordinates + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! return if we are not providing outputs + if (.not. CalcWriteOutput) return + + ! rotation matrix +!FIXME: make sure this is actually correct and not the transpose of what we want + Rxyz(1:3,1) = real(m%x_hat, ReKi) + Rxyz(1:3,2) = real(m%y_hat, ReKi) + Rxyz(1:3,3) = real(m%z_hat, ReKi) + + ! Rotspeed etc + m%AllOuts( ADSpeed ) = u%RotSpeed * 30.0_ReKi / Pi + m%AllOuts( ADPitch ) = u%BlPitch * 180.0_ReKi / Pi + + m%AllOuts( ADTSR ) = real(m%lambda, ReKi) ! TSR -- tip speed ratio (-) + m%AllOuts( ADVRel ) = real(m%VRel, ReKi) ! magnitude of VRel vector (m/s) + m%AllOuts( ADSkew ) = real(m%Chi * 180.0_ReKi / Pi, ReKi) + + ! Wind in local frame, inertial frame + Tmp3 = matmul(Rxyz(1:3,1:3), m%DiskAvgVel) + m%AllOuts( ADVWindx ) = Tmp3(1) + m%AllOuts( ADVWindy ) = Tmp3(2) + m%AllOuts( ADVWindz ) = Tmp3(3) + m%AllOuts( ADVWindxi ) = m%DiskAvgVel(1) + m%AllOuts( ADVWindyi ) = m%DiskAvgVel(2) + m%AllOuts( ADVWindzi ) = m%DiskAvgVel(3) + + ! Rotor velocity in local frame, inertial frame + Tmp3 = matmul(Rxyz(1:3,1:3), u%HubMotion%TranslationVel(1:3,1)) + m%AllOuts( ADSTVx ) = Tmp3(1) + m%AllOuts( ADSTVy ) = Tmp3(2) + m%AllOuts( ADSTVz ) = Tmp3(3) + m%AllOuts( ADSTVxi ) = u%HubMotion%TranslationVel(1,1) + m%AllOuts( ADSTVyi ) = u%HubMotion%TranslationVel(2,1) + m%AllOuts( ADSTVzi ) = u%HubMotion%TranslationVel(3,1) + + ! Coefficients + if (EqualRealNos(m%VRel_xd,0.0_SiKi)) then + m%AllOuts( ADCp ) = 0.0_ReKi + else + m%AllOuts( ADCp ) = (real(m%Moment(1),ReKi) * u%RotSpeed) / (p%halfRhoA * real(m%Vrel_xd,ReKi)**3_IntKi ) + endif + m%AllOuts( ADCt ) = real(m%C_F(1),ReKi) + m%AllOuts( ADCq ) = real(m%C_M(1),ReKi) + + ! Power + m%AllOuts( ADPower ) = real(m%Moment(1),ReKi) * u%RotSpeed + + ! Resulting forces + m%AllOuts( ADFx ) = real(m%Force(1), ReKi) + m%AllOuts( ADFy ) = real(m%Force(2), ReKi) + m%AllOuts( ADFz ) = real(m%Force(3), ReKi) + m%AllOuts( ADMx ) = real(m%Moment(1),ReKi) + m%AllOuts( ADMy ) = real(m%Moment(2),ReKi) + m%AllOuts( ADMz ) = real(m%Moment(3),ReKi) + !Tmp3 = m%Force( 1)*m%x_hat + m%Force( 2)*m%y_hat + m%Force( 3)*m%z_hat + Tmp3 = matmul(real(m%Force(1:3),ReKi), Rxyz(1:3,1:3)) + m%AllOuts( ADFxi ) = Tmp3(1) + m%AllOuts( ADFyi ) = Tmp3(2) + m%AllOuts( ADFzi ) = Tmp3(3) + !Tmp3 = m%Moment(1)*m%x_hat + m%Moment(2)*m%y_hat + m%Moment(3)*m%z_hat + Tmp3 = matmul(real(m%Force(1:3),ReKi), Rxyz(1:3,1:3)) + m%AllOuts( ADMxi ) = Tmp3(1) + m%AllOuts( ADMyi ) = Tmp3(2) + m%AllOuts( ADMzi ) = Tmp3(3) + +end subroutine Calc_WriteOutput + + +!********************************************************************************************************************************** +! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m" +! using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these +! lines should be modified in the Matlab script and/or Excel worksheet as necessary. +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine checks to see if any requested output channel names (stored in the OutList(:)) are invalid. It returns a +!! warning if any of the channels are not available outputs from the module. +!! It assigns the settings for OutParam(:) (i.e, the index, name, and units of the output channels, WriteOutput(:)). +!! the sign is set to 0 if the channel is invalid. +!! It sets assumes the value p%NumOuts has been set before this routine has been called, and it sets the values of p%OutParam here. +!! +!! This routine was generated by Write_ChckOutLst.m using the parameters listed in OutListParameters.xlsx at 24-Feb-2022 16:52:56. +SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) +!.................................................................................................................................. + + IMPLICIT NONE + + ! Passed variables + + CHARACTER(ChanLen), INTENT(IN) :: OutList(:) !< The list out user-requested outputs + TYPE(ADsk_ParameterType), INTENT(INOUT) :: p !< The module parameters + INTEGER(IntKi), INTENT(OUT) :: ErrStat !< The error status code + CHARACTER(*), INTENT(OUT) :: ErrMsg !< The error message, if an error occurred + + ! Local variables + + INTEGER :: ErrStat2 ! temporary (local) error status + INTEGER :: I ! Generic loop-counting index + INTEGER :: J ! Generic loop-counting index + INTEGER :: INDX ! Index for valid arrays + + LOGICAL :: CheckOutListAgain ! Flag used to determine if output parameter starting with "M" is valid (or the negative of another parameter) + LOGICAL :: InvalidOutput(0:MaxOutPts) ! This array determines if the output channel is valid for this configuration + CHARACTER(ChanLen) :: OutListTmp ! A string to temporarily hold OutList(I) + CHARACTER(*), PARAMETER :: RoutineName = "SetOutParam" + + CHARACTER(OutStrLenM1), PARAMETER :: ValidParamAry(34) = (/ & ! This lists the names of the allowed parameters, which must be sorted alphabetically + "ADCP ","ADCQ ","ADCT ","ADFX ","ADFXI ","ADFY ","ADFYI ","ADFZ ", & + "ADFZI ","ADMX ","ADMXI ","ADMY ","ADMYI ","ADMZ ","ADMZI ","ADPITCH ", & + "ADPOWER ","ADSKEW ","ADSPEED ","ADSTVX ","ADSTVXI ","ADSTVY ","ADSTVYI ","ADSTVZ ", & + "ADSTVZI ","ADTSR ","ADVREL ","ADVWINDX ","ADVWINDXI","ADVWINDY ","ADVWINDYI","ADVWINDZ ", & + "ADVWINDZI","ADYAWERR "/) + INTEGER(IntKi), PARAMETER :: ParamIndxAry(34) = (/ & ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:) + ADCp , ADCq , ADCt , ADFx , ADFxi , ADFy , ADFyi , ADFz , & + ADFzi , ADMx , ADMxi , ADMy , ADMyi , ADMz , ADMzi , ADPitch , & + ADPower , ADSkew , ADSpeed , ADSTVx , ADSTVxi , ADSTVy , ADSTVyi , ADSTVz , & + ADSTVzi , ADTSR , ADVRel , ADVWindx , ADVWindxi , ADVWindy , ADVWindyi , ADVWindz , & + ADVWindzi , ADYawErr /) + CHARACTER(ChanLen), PARAMETER :: ParamUnitsAry(34) = (/ & ! This lists the units corresponding to the allowed parameters + "(-) ","(-) ","(-) ","(N) ","(N) ","(N) ","(N) ","(N) ", & + "(N) ","(N-m)","(N-m)","(N-m)","(N-m)","(N-m)","(N-m)","(deg)", & + "(W) ","(deg)","(rpm)","(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", & + "(m/s)","(-) ","(m/s)","(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", & + "(m/s)","(deg)"/) + + + ! Initialize values + ErrStat = ErrID_None + ErrMsg = "" + InvalidOutput = .FALSE. + + +! ..... Developer must add checking for invalid inputs here: ..... + +! ................. End of validity checking ................. + + + !------------------------------------------------------------------------------------------------- + ! Allocate and set index, name, and units for the output channels + ! If a selected output channel is not available in this module, set error flag. + !------------------------------------------------------------------------------------------------- + + ALLOCATE ( p%OutParam(0:p%NumOuts) , STAT=ErrStat2 ) + IF ( ErrStat2 /= 0_IntKi ) THEN + CALL SetErrStat( ErrID_Fatal,"Error allocating memory for the AeroDisk OutParam array.", ErrStat, ErrMsg, RoutineName ) + RETURN + ENDIF + + ! Set index, name, and units for the time output channel: + + p%OutParam(0)%Indx = Time + p%OutParam(0)%Name = "Time" ! OutParam(0) is the time channel by default. + p%OutParam(0)%Units = "(s)" + p%OutParam(0)%SignM = 1 + + + ! Set index, name, and units for all of the output channels. + ! If a selected output channel is not available by this module set ErrStat = ErrID_Warn. + + DO I = 1,p%NumOuts + + p%OutParam(I)%Name = OutList(I) + OutListTmp = OutList(I) + + ! Reverse the sign (+/-) of the output channel if the user prefixed the + ! channel name with a "-", "_", "m", or "M" character indicating "minus". + + + CheckOutListAgain = .FALSE. + + IF ( INDEX( "-_", OutListTmp(1:1) ) > 0 ) THEN + p%OutParam(I)%SignM = -1 ! ex, "-TipDxc1" causes the sign of TipDxc1 to be switched. + OutListTmp = OutListTmp(2:) + ELSE IF ( INDEX( "mM", OutListTmp(1:1) ) > 0 ) THEN ! We'll assume this is a variable name for now, (if not, we will check later if OutListTmp(2:) is also a variable name) + CheckOutListAgain = .TRUE. + p%OutParam(I)%SignM = 1 + ELSE + p%OutParam(I)%SignM = 1 + END IF + + CALL Conv2UC( OutListTmp ) ! Convert OutListTmp to upper case + + + Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry ) + + + ! If it started with an "M" (CheckOutListAgain) we didn't find the value in our list (Indx < 1) + + IF ( CheckOutListAgain .AND. Indx < 1 ) THEN ! Let's assume that "M" really meant "minus" and then test again + p%OutParam(I)%SignM = -1 ! ex, "MTipDxc1" causes the sign of TipDxc1 to be switched. + OutListTmp = OutListTmp(2:) + + Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry ) + END IF + + + IF ( Indx > 0 ) THEN ! we found the channel name + IF ( InvalidOutput( ParamIndxAry(Indx) ) ) THEN ! but, it isn't valid for these settings + p%OutParam(I)%Indx = 0 ! pick any valid channel (I just picked "Time=0" here because it's universal) + p%OutParam(I)%Units = "INVALID" + p%OutParam(I)%SignM = 0 + ELSE + p%OutParam(I)%Indx = ParamIndxAry(Indx) + p%OutParam(I)%Units = ParamUnitsAry(Indx) ! it's a valid output + END IF + ELSE ! this channel isn't valid + p%OutParam(I)%Indx = 0 ! pick any valid channel (I just picked "Time=0" here because it's universal) + p%OutParam(I)%Units = "INVALID" + p%OutParam(I)%SignM = 0 ! multiply all results by zero + + CALL SetErrStat(ErrID_Fatal, TRIM(p%OutParam(I)%Name)//" is not an available output channel.",ErrStat,ErrMsg,RoutineName) + END IF + + END DO + + RETURN +END SUBROUTINE SetOutParam +!---------------------------------------------------------------------------------------------------------------------------------- +!End of code generated by Matlab script +!********************************************************************************************************************************** +END MODULE AeroDisk_IO diff --git a/modules/aerodisk/src/AeroDisk_Output_Params.f90 b/modules/aerodisk/src/AeroDisk_Output_Params.f90 new file mode 100644 index 0000000000..77729c170b --- /dev/null +++ b/modules/aerodisk/src/AeroDisk_Output_Params.f90 @@ -0,0 +1,67 @@ +!> The parameters in this code are from the MATLAB autogeneration scripts. Do not manually edit unless also editing the OutListParamters.xls AeroDisk tab. +module AeroDisk_Output_Params + use NWTC_Library + + +! =================================================================================================== +! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m" +! using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these +! lines should be modified in the Matlab script and/or Excel worksheet as necessary. +! =================================================================================================== +! This code was generated by Write_ChckOutLst.m at 24-Feb-2022 18:19:57. + + + ! Indices for computing output channels: + ! NOTES: + ! (1) These parameters are in the order stored in "OutListParameters.xlsx" + ! (2) Array AllOuts() must be dimensioned to the value of the largest output parameter + + ! Time: + + INTEGER(IntKi), PARAMETER :: Time = 0 + + + ! All channels: + + INTEGER(IntKi), PARAMETER :: ADSpeed = 1 + INTEGER(IntKi), PARAMETER :: ADTSR = 2 + INTEGER(IntKi), PARAMETER :: ADPitch = 3 + INTEGER(IntKi), PARAMETER :: ADVWindx = 4 + INTEGER(IntKi), PARAMETER :: ADVWindy = 5 + INTEGER(IntKi), PARAMETER :: ADVWindz = 6 + INTEGER(IntKi), PARAMETER :: ADVWindxi = 7 + INTEGER(IntKi), PARAMETER :: ADVWindyi = 8 + INTEGER(IntKi), PARAMETER :: ADVWindzi = 9 + INTEGER(IntKi), PARAMETER :: ADSTVx = 10 + INTEGER(IntKi), PARAMETER :: ADSTVy = 11 + INTEGER(IntKi), PARAMETER :: ADSTVz = 12 + INTEGER(IntKi), PARAMETER :: ADSTVxi = 13 + INTEGER(IntKi), PARAMETER :: ADSTVyi = 14 + INTEGER(IntKi), PARAMETER :: ADSTVzi = 15 + INTEGER(IntKi), PARAMETER :: ADVRel = 16 + INTEGER(IntKi), PARAMETER :: ADSkew = 17 + INTEGER(IntKi), PARAMETER :: ADYawErr = 18 + INTEGER(IntKi), PARAMETER :: ADCp = 19 + INTEGER(IntKi), PARAMETER :: ADCt = 20 + INTEGER(IntKi), PARAMETER :: ADCq = 21 + INTEGER(IntKi), PARAMETER :: ADFx = 22 + INTEGER(IntKi), PARAMETER :: ADFy = 23 + INTEGER(IntKi), PARAMETER :: ADFz = 24 + INTEGER(IntKi), PARAMETER :: ADFxi = 25 + INTEGER(IntKi), PARAMETER :: ADFyi = 26 + INTEGER(IntKi), PARAMETER :: ADFzi = 27 + INTEGER(IntKi), PARAMETER :: ADMx = 28 + INTEGER(IntKi), PARAMETER :: ADMy = 29 + INTEGER(IntKi), PARAMETER :: ADMz = 30 + INTEGER(IntKi), PARAMETER :: ADMxi = 31 + INTEGER(IntKi), PARAMETER :: ADMyi = 32 + INTEGER(IntKi), PARAMETER :: ADMzi = 33 + INTEGER(IntKi), PARAMETER :: ADPower = 34 + + + ! The maximum number of output channels which can be output by the code. + INTEGER(IntKi), PARAMETER :: MaxOutPts = 34 + +!End of code generated by Matlab script +! =================================================================================================== +end module AeroDisk_Output_Params diff --git a/modules/aerodisk/src/AeroDisk_Registry.txt b/modules/aerodisk/src/AeroDisk_Registry.txt new file mode 100644 index 0000000000..93d0aefa6b --- /dev/null +++ b/modules/aerodisk/src/AeroDisk_Registry.txt @@ -0,0 +1,138 @@ +################################################################################################################################### +# Registry for Simplified ElastoDyn in the FAST Modularization Framework +# This Registry file is used to create MODULE ADsk_Types which contains all of the user-defined types needed in Simplified ElastoDyn. +# It also contains copy, destroy, pack, and unpack routines associated with each defined data types. +# See the NWTC Programmer's Handbook for further information on the format/contents of this file. +# +# Entries are of the form +# +# +# Use ^ as a shortcut for the value in the same column from the previous line. +################################################################################################################################### +# ...... Include files (definitions from NWTC Library) ............................................................................ +include Registry_NWTC_Library.txt +usefrom IfW_FlowField.txt + +# ..... Static Param .............................................................................................................. +param AeroDisk/ADsk - IntKi ADsk_NumPtsDiskAvg - 144 - "Number of points averaged for rotor-average wind speed" - + + +# ..... Table storage ............................................................................................................. +typedef AeroDisk/ADsk ADsk_AeroTable IntKi N_TSR - - - "Number of rotor tip-speed ratios in tables" - +typedef ^ ADsk_AeroTable IntKi N_RtSpd - - - "Number of rotor speeds in tables" - +typedef ^ ADsk_AeroTable IntKi N_VRel - - - "Number of rotor inflow wind speeds in tables" - +typedef ^ ADsk_AeroTable IntKi N_Pitch - - - "Number of rotor-collective blade-pitch angles in tables" - +typedef ^ ADsk_AeroTable IntKi N_Skew - - - "Number of rotor inflow-skew angles in tables" - +typedef ^ ADsk_AeroTable SiKi TSR {:} - - "Rotor TSR values in tables" - +typedef ^ ADsk_AeroTable SiKi RtSpd {:} - - "Rotor speed values in tables" rad/s +typedef ^ ADsk_AeroTable SiKi VRel {:} - - "Rotor inflow wind speeds tables" m/s +typedef ^ ADsk_AeroTable SiKi Pitch {:} - - "Rotor-collective blade-pitch anges in tables" rad +typedef ^ ADsk_AeroTable SiKi Skew {:} - - "Rotor inflow-skew values in tables" rad +typedef ^ ADsk_AeroTable SiKi C_Fx {:}{:}{:}{:}{:} - - "Thrust (x/axial) coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew]" - +typedef ^ ADsk_AeroTable SiKi C_Fy {:}{:}{:}{:}{:} - - "Transverse (y) force coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew]" - +typedef ^ ADsk_AeroTable SiKi C_Fz {:}{:}{:}{:}{:} - - "Transverse (z) force coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew]" - +typedef ^ ADsk_AeroTable SiKi C_Mx {:}{:}{:}{:}{:} - - "Torque (x/axial) coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew]" - +typedef ^ ADsk_AeroTable SiKi C_My {:}{:}{:}{:}{:} - - "Transverse (y) moment coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew]" - +typedef ^ ADsk_AeroTable SiKi C_Mz {:}{:}{:}{:}{:} - - "Transverse (z) moment coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew]" - + + +# ..... Initialization data ....................................................................................................... +# ADsk input file +typedef AeroDisk/ADsk ADsk_InputFile LOGICAL Echo - - - "Echo the input file" - +typedef ^ ADsk_InputFile DBKi DT - - - "Time step for module time integration" s +typedef ^ ADsk_InputFile ReKi AirDens - - - "Air density" "kg/m^3" +typedef ^ ADsk_InputFile ReKi RotorRad - - - "Rotor radius" m +typedef ^ ADsk_InputFile LOGICAL SumPrint - - - "Print summary data to .sum" - +typedef ^ ADsk_InputFile IntKi NumOuts - - - "Number of outputs" - +typedef ^ ADsk_InputFile CHARACTER(ChanLen) OutList : - - "List of user-requested output channels" - +typedef ^ ADsk_InputFile ADsk_AeroTable AeroTable - - - "Data table" - + +# ..... Initialization data ....................................................................................................... +# inputs for initialization: +typedef ^ InitInputType CHARACTER(1024) InputFile - - - "Name of the input file" - +typedef ^ InitInputType CHARACTER(1024) RootName - - - "RootName for writing output files" - +typedef ^ InitInputType ReKi RotorRad - - - "Rotor radius" m +typedef ^ InitInputType ReKi HubPosition {3} - - "Hub position -- center of rotor" m +typedef ^ InitInputType R8Ki HubOrientation {3}{3} - - "Hub orientation" - +typedef ^ InitInputType ReKi defAirDens - - - "Default atmospheric density from the driver; may be overwritten" "kg/m^3" +typedef ^ InitInputType LOGICAL Linearize - .false. - "this module cannot be linearized at present" - +typedef ^ InitInputType LOGICAL UseInputFile - .TRUE. - "Supplied by Driver: .TRUE. if using a input file, .FALSE. if all inputs are being passed in by the caller" - +typedef ^ InitInputType FileInfoType PassedFileData - - - "If we don't use the input file, pass everything through this" - +typedef ^ InitInputType FlowFieldType *FlowField - - - "Pointer of InflowWinds flow field data type" - + + +# outputs from initialization: +typedef ^ InitOutputType CHARACTER(ChanLen) WriteOutputHdr {:} - - "Names of the output-to-file channels" - +typedef ^ InitOutputType CHARACTER(ChanLen) WriteOutputUnt {:} - - "Units of the output-to-file channels" - +typedef ^ InitOutputType ProgDesc Ver - - - "This module's name, version, and date" - + + +# ..... Inputs .................................................................................................................... +# inputs on meshes: NONE +# inputs not on meshes: +typedef ^ InputType MeshType HubMotion - - - "Hub motion" - +typedef ^ InputType ReKi RotSpeed - - - "Rotor speed" "rad/s" +typedef ^ InputType ReKi BlPitch - - 2pi "blade pitch" "rad" + + + +# ..... Outputs ................................................................................................................... +# outputs on meshes: +typedef ^ OutputType MeshType AeroLoads - - - "Mesh containing the forces and moments from the aero loading" - +#TODO: any mesh for visualization of blades/rotor disk? +# outputs not on meshes: +typedef ^ OutputType ReKi YawErr - - - "Nacelle-yaw error, i.e., the angle about positive Z from the rotor centerline to the rotor-disk-averaged relative wind velocity (ambient + rotor motion), both projected onto the horizontal plane" rad +typedef ^ OutputType ReKi PsiSkew - - - "Azimuth angle (from the nominally vertical axis in the disk plane, Z_disk ) to the vector about which the inflow skew angle is defined, i.e., the angle about positive X_disk from Z_disk to the vector about which the positive inflow skew angle is defined " rad +typedef ^ OutputType ReKi ChiSkew - - - "Inflow skew angle" rad +typedef ^ OutputType ReKi VRel - - - "Rotor-disk-averaged relative wind speed (ambient + rotor motion), normal to disk" m/s +typedef ^ OutputType ReKi Ct - - - "Thrust force coefficient (normal to disk)" - +typedef ^ OutputType ReKi Cq - - - "Torque coefficient (normal to disk)" - +typedef ^ OutputType ReKi WriteOutput {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" + + +# ..... States .................................................................................................................... +# continuous (differentiable) states: +typedef ^ ContinuousStateType ReKi DummyContState - - - "" - + +# Define discrete (nondifferentiable) states here: +typedef ^ DiscreteStateType ReKi DummyDiscreteState - - - "" - + +# Define constraint states here: +typedef ^ ConstraintStateType ReKi DummyConstrState - - - "" - + +# any other states +typedef ^ OtherStateType IntKi DummyOtherState - - - "" - + + +# ..... Parameters................................................................................................................. +# unchanging parameters: +typedef ^ ParameterType CHARACTER(1024) RootName - - - "RootName for writing output files" - +typedef ^ ParameterType DBKi DT - - - "Time step for module time integration" s +typedef ^ ParameterType ReKi RotorRad - - - "Rotor radius" m +typedef ^ ParameterType ReKi AirDens - - - "Air density" "kg/m^3" +typedef ^ ParameterType IntKi NumOuts - - - "Number of outputs" - +typedef ^ ParameterType ReKi halfRhoA - - - "half air density times rotor swept area" "kg/m" +typedef ^ ParameterType ADsk_AeroTable AeroTable - - - "Data table" - +typedef ^ ParameterType LOGICAL UseTSR - .false. - "Use TSR values from table instead of VRel + RtSpd" - +typedef ^ ParameterType OutParmType OutParam {:} - - "Names and units (and other characteristics) of all requested output parameters" - +typedef ^ ParameterType FlowFieldType *FlowField - - - "Pointer of InflowWinds flow field data type" - +typedef ^ ParameterType ReKi DiskWindPosRel {:}{:} - - "Disk locations for sampling to get disk avarage velocity (relative to hub)" m + +# ..... Misc/Optimization variables................................................................................................. +typedef ^ MiscVarType IntKi idx_last {5} - - "Last indices used in lookup search" - +typedef ^ MiscVarType ReKi AllOuts {:} - - "Array of all outputs" - +typedef ^ MiscVarType SiKi x_hat 3 - - "Acuator disk X direction unit vector (global)" - +typedef ^ MiscVarType SiKi y_hat 3 - - "Acuator disk Y direction unit vector (global)" - +typedef ^ MiscVarType SiKi z_hat 3 - - "Acuator disk Z direction unit vector (global)" - +typedef ^ MiscVarType SiKi VRel - - - "magnitude of VRel (output as y%VRel)" m/s +typedef ^ MiscVarType SiKi VRel_xd - - - "relative wind velocity along disk normal" m/s +typedef ^ MiscVarType SiKi lambda - - - "TSR - tip speed ratio" - +typedef ^ MiscVarType SiKi Chi - - - "Inflow skew angle" rad +typedef ^ MiscVarType SiKi C_F 3 - - "Force coefficients from table" - +typedef ^ MiscVarType SiKi C_M 3 - - "Moment coefficients from table" - +typedef ^ MiscVarType SiKi Force 3 - - "Force calculated in actuator disk coordinates" N +typedef ^ MiscVarType SiKi Moment 3 - - "Moment calculated in actuator disk coordinates" N-m +typedef ^ MiscVarType ReKi DiskWindPosAbs {:}{:} - - "Disk locations for sampling to get disk avarage velocity (absolute for getting wind)" m +typedef ^ MiscVarType ReKi DiskWindVel {:}{:} - - "Wind speed at disk locations for disk velocity" m/s +typedef ^ MiscVarType ReKi DiskAvgVel 3 - - "Average wind speed across rotor disk" m/s + diff --git a/modules/aerodisk/src/AeroDisk_Types.f90 b/modules/aerodisk/src/AeroDisk_Types.f90 new file mode 100644 index 0000000000..6da764cd80 --- /dev/null +++ b/modules/aerodisk/src/AeroDisk_Types.f90 @@ -0,0 +1,1610 @@ +!STARTOFREGISTRYGENERATEDFILE 'AeroDisk_Types.f90' +! +! WARNING This file is generated automatically by the FAST registry. +! Do not edit. Your changes to this file will be lost. +! +! FAST Registry +!********************************************************************************************************************************* +! AeroDisk_Types +!................................................................................................................................. +! This file is part of AeroDisk. +! +! Copyright (C) 2012-2016 National Renewable Energy Laboratory +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +! +! W A R N I N G : This file was automatically generated from the FAST registry. Changes made to this file may be lost. +! +!********************************************************************************************************************************* +!> This module contains the user-defined types needed in AeroDisk. It also contains copy, destroy, pack, and +!! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. +MODULE AeroDisk_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE IfW_FlowField_Types +USE NWTC_Library +IMPLICIT NONE + INTEGER(IntKi), PUBLIC, PARAMETER :: ADsk_NumPtsDiskAvg = 144 ! Number of points averaged for rotor-average wind speed [-] +! ========= ADsk_AeroTable ======= + TYPE, PUBLIC :: ADsk_AeroTable + INTEGER(IntKi) :: N_TSR = 0_IntKi !< Number of rotor tip-speed ratios in tables [-] + INTEGER(IntKi) :: N_RtSpd = 0_IntKi !< Number of rotor speeds in tables [-] + INTEGER(IntKi) :: N_VRel = 0_IntKi !< Number of rotor inflow wind speeds in tables [-] + INTEGER(IntKi) :: N_Pitch = 0_IntKi !< Number of rotor-collective blade-pitch angles in tables [-] + INTEGER(IntKi) :: N_Skew = 0_IntKi !< Number of rotor inflow-skew angles in tables [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: TSR !< Rotor TSR values in tables [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: RtSpd !< Rotor speed values in tables [rad/s] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: VRel !< Rotor inflow wind speeds tables [m/s] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: Pitch !< Rotor-collective blade-pitch anges in tables [rad] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: Skew !< Rotor inflow-skew values in tables [rad] + REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: C_Fx !< Thrust (x/axial) coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew] [-] + REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: C_Fy !< Transverse (y) force coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew] [-] + REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: C_Fz !< Transverse (z) force coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew] [-] + REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: C_Mx !< Torque (x/axial) coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew] [-] + REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: C_My !< Transverse (y) moment coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew] [-] + REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: C_Mz !< Transverse (z) moment coefficient [N_TSR, N_RtSpd, N_VRel, N_Pitch, N_Skew] [-] + END TYPE ADsk_AeroTable +! ======================= +! ========= ADsk_InputFile ======= + TYPE, PUBLIC :: ADsk_InputFile + LOGICAL :: Echo = .false. !< Echo the input file [-] + REAL(DbKi) :: DT = 0.0_R8Ki !< Time step for module time integration [s] + REAL(ReKi) :: AirDens = 0.0_ReKi !< Air density [kg/m^3] + REAL(ReKi) :: RotorRad = 0.0_ReKi !< Rotor radius [m] + LOGICAL :: SumPrint = .false. !< Print summary data to .sum [-] + INTEGER(IntKi) :: NumOuts = 0_IntKi !< Number of outputs [-] + CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: OutList !< List of user-requested output channels [-] + TYPE(ADsk_AeroTable) :: AeroTable !< Data table [-] + END TYPE ADsk_InputFile +! ======================= +! ========= ADsk_InitInputType ======= + TYPE, PUBLIC :: ADsk_InitInputType + CHARACTER(1024) :: InputFile !< Name of the input file [-] + CHARACTER(1024) :: RootName !< RootName for writing output files [-] + REAL(ReKi) :: RotorRad = 0.0_ReKi !< Rotor radius [m] + REAL(ReKi) , DIMENSION(1:3) :: HubPosition = 0.0_ReKi !< Hub position -- center of rotor [m] + REAL(R8Ki) , DIMENSION(1:3,1:3) :: HubOrientation = 0.0_R8Ki !< Hub orientation [-] + REAL(ReKi) :: defAirDens = 0.0_ReKi !< Default atmospheric density from the driver; may be overwritten [kg/m^3] + LOGICAL :: Linearize = .false. !< this module cannot be linearized at present [-] + LOGICAL :: UseInputFile = .TRUE. !< Supplied by Driver: .TRUE. if using a input file, .FALSE. if all inputs are being passed in by the caller [-] + TYPE(FileInfoType) :: PassedFileData !< If we don't use the input file, pass everything through this [-] + TYPE(FlowFieldType) , POINTER :: FlowField => NULL() !< Pointer of InflowWinds flow field data type [-] + END TYPE ADsk_InitInputType +! ======================= +! ========= ADsk_InitOutputType ======= + TYPE, PUBLIC :: ADsk_InitOutputType + CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr !< Names of the output-to-file channels [-] + CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt !< Units of the output-to-file channels [-] + TYPE(ProgDesc) :: Ver !< This module's name, version, and date [-] + END TYPE ADsk_InitOutputType +! ======================= +! ========= ADsk_InputType ======= + TYPE, PUBLIC :: ADsk_InputType + TYPE(MeshType) :: HubMotion !< Hub motion [-] + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Rotor speed [rad/s] + REAL(ReKi) :: BlPitch = 0.0_ReKi !< blade pitch [rad] + END TYPE ADsk_InputType +! ======================= +! ========= ADsk_OutputType ======= + TYPE, PUBLIC :: ADsk_OutputType + TYPE(MeshType) :: AeroLoads !< Mesh containing the forces and moments from the aero loading [-] + REAL(ReKi) :: YawErr = 0.0_ReKi !< Nacelle-yaw error, i.e., the angle about positive Z from the rotor centerline to the rotor-disk-averaged relative wind velocity (ambient + rotor motion), both projected onto the horizontal plane [rad] + REAL(ReKi) :: PsiSkew = 0.0_ReKi !< Azimuth angle (from the nominally vertical axis in the disk plane, Z_disk ) to the vector about which the inflow skew angle is defined, i.e., the angle about positive X_disk from Z_disk to the vector about which the positive inflow skew angle is defined [rad] + REAL(ReKi) :: ChiSkew = 0.0_ReKi !< Inflow skew angle [rad] + REAL(ReKi) :: VRel = 0.0_ReKi !< Rotor-disk-averaged relative wind speed (ambient + rotor motion), normal to disk [m/s] + REAL(ReKi) :: Ct = 0.0_ReKi !< Thrust force coefficient (normal to disk) [-] + REAL(ReKi) :: Cq = 0.0_ReKi !< Torque coefficient (normal to disk) [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: WriteOutput !< Data to be written to an output file: see WriteOutputHdr for names of each variable [see WriteOutputUnt] + END TYPE ADsk_OutputType +! ======================= +! ========= ADsk_ContinuousStateType ======= + TYPE, PUBLIC :: ADsk_ContinuousStateType + REAL(ReKi) :: DummyContState = 0.0_ReKi !< [-] + END TYPE ADsk_ContinuousStateType +! ======================= +! ========= ADsk_DiscreteStateType ======= + TYPE, PUBLIC :: ADsk_DiscreteStateType + REAL(ReKi) :: DummyDiscreteState = 0.0_ReKi !< [-] + END TYPE ADsk_DiscreteStateType +! ======================= +! ========= ADsk_ConstraintStateType ======= + TYPE, PUBLIC :: ADsk_ConstraintStateType + REAL(ReKi) :: DummyConstrState = 0.0_ReKi !< [-] + END TYPE ADsk_ConstraintStateType +! ======================= +! ========= ADsk_OtherStateType ======= + TYPE, PUBLIC :: ADsk_OtherStateType + INTEGER(IntKi) :: DummyOtherState = 0_IntKi !< [-] + END TYPE ADsk_OtherStateType +! ======================= +! ========= ADsk_ParameterType ======= + TYPE, PUBLIC :: ADsk_ParameterType + CHARACTER(1024) :: RootName !< RootName for writing output files [-] + REAL(DbKi) :: DT = 0.0_R8Ki !< Time step for module time integration [s] + REAL(ReKi) :: RotorRad = 0.0_ReKi !< Rotor radius [m] + REAL(ReKi) :: AirDens = 0.0_ReKi !< Air density [kg/m^3] + INTEGER(IntKi) :: NumOuts = 0_IntKi !< Number of outputs [-] + REAL(ReKi) :: halfRhoA = 0.0_ReKi !< half air density times rotor swept area [kg/m] + TYPE(ADsk_AeroTable) :: AeroTable !< Data table [-] + LOGICAL :: UseTSR = .false. !< Use TSR values from table instead of VRel + RtSpd [-] + TYPE(OutParmType) , DIMENSION(:), ALLOCATABLE :: OutParam !< Names and units (and other characteristics) of all requested output parameters [-] + TYPE(FlowFieldType) , POINTER :: FlowField => NULL() !< Pointer of InflowWinds flow field data type [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: DiskWindPosRel !< Disk locations for sampling to get disk avarage velocity (relative to hub) [m] + END TYPE ADsk_ParameterType +! ======================= +! ========= ADsk_MiscVarType ======= + TYPE, PUBLIC :: ADsk_MiscVarType + INTEGER(IntKi) , DIMENSION(1:5) :: idx_last = 0_IntKi !< Last indices used in lookup search [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AllOuts !< Array of all outputs [-] + REAL(SiKi) , DIMENSION(1:3) :: x_hat = 0.0_R4Ki !< Acuator disk X direction unit vector (global) [-] + REAL(SiKi) , DIMENSION(1:3) :: y_hat = 0.0_R4Ki !< Acuator disk Y direction unit vector (global) [-] + REAL(SiKi) , DIMENSION(1:3) :: z_hat = 0.0_R4Ki !< Acuator disk Z direction unit vector (global) [-] + REAL(SiKi) :: VRel = 0.0_R4Ki !< magnitude of VRel (output as y%VRel) [m/s] + REAL(SiKi) :: VRel_xd = 0.0_R4Ki !< relative wind velocity along disk normal [m/s] + REAL(SiKi) :: lambda = 0.0_R4Ki !< TSR - tip speed ratio [-] + REAL(SiKi) :: Chi = 0.0_R4Ki !< Inflow skew angle [rad] + REAL(SiKi) , DIMENSION(1:3) :: C_F = 0.0_R4Ki !< Force coefficients from table [-] + REAL(SiKi) , DIMENSION(1:3) :: C_M = 0.0_R4Ki !< Moment coefficients from table [-] + REAL(SiKi) , DIMENSION(1:3) :: Force = 0.0_R4Ki !< Force calculated in actuator disk coordinates [N] + REAL(SiKi) , DIMENSION(1:3) :: Moment = 0.0_R4Ki !< Moment calculated in actuator disk coordinates [N-m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: DiskWindPosAbs !< Disk locations for sampling to get disk avarage velocity (absolute for getting wind) [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: DiskWindVel !< Wind speed at disk locations for disk velocity [m/s] + REAL(ReKi) , DIMENSION(1:3) :: DiskAvgVel = 0.0_ReKi !< Average wind speed across rotor disk [m/s] + END TYPE ADsk_MiscVarType +! ======================= +CONTAINS + +subroutine ADsk_CopyAeroTable(SrcAeroTableData, DstAeroTableData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_AeroTable), intent(in) :: SrcAeroTableData + type(ADsk_AeroTable), intent(inout) :: DstAeroTableData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(5), UB(5) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'ADsk_CopyAeroTable' + ErrStat = ErrID_None + ErrMsg = '' + DstAeroTableData%N_TSR = SrcAeroTableData%N_TSR + DstAeroTableData%N_RtSpd = SrcAeroTableData%N_RtSpd + DstAeroTableData%N_VRel = SrcAeroTableData%N_VRel + DstAeroTableData%N_Pitch = SrcAeroTableData%N_Pitch + DstAeroTableData%N_Skew = SrcAeroTableData%N_Skew + if (allocated(SrcAeroTableData%TSR)) then + LB(1:1) = lbound(SrcAeroTableData%TSR, kind=B8Ki) + UB(1:1) = ubound(SrcAeroTableData%TSR, kind=B8Ki) + if (.not. allocated(DstAeroTableData%TSR)) then + allocate(DstAeroTableData%TSR(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%TSR.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%TSR = SrcAeroTableData%TSR + end if + if (allocated(SrcAeroTableData%RtSpd)) then + LB(1:1) = lbound(SrcAeroTableData%RtSpd, kind=B8Ki) + UB(1:1) = ubound(SrcAeroTableData%RtSpd, kind=B8Ki) + if (.not. allocated(DstAeroTableData%RtSpd)) then + allocate(DstAeroTableData%RtSpd(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%RtSpd.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%RtSpd = SrcAeroTableData%RtSpd + end if + if (allocated(SrcAeroTableData%VRel)) then + LB(1:1) = lbound(SrcAeroTableData%VRel, kind=B8Ki) + UB(1:1) = ubound(SrcAeroTableData%VRel, kind=B8Ki) + if (.not. allocated(DstAeroTableData%VRel)) then + allocate(DstAeroTableData%VRel(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%VRel.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%VRel = SrcAeroTableData%VRel + end if + if (allocated(SrcAeroTableData%Pitch)) then + LB(1:1) = lbound(SrcAeroTableData%Pitch, kind=B8Ki) + UB(1:1) = ubound(SrcAeroTableData%Pitch, kind=B8Ki) + if (.not. allocated(DstAeroTableData%Pitch)) then + allocate(DstAeroTableData%Pitch(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%Pitch.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%Pitch = SrcAeroTableData%Pitch + end if + if (allocated(SrcAeroTableData%Skew)) then + LB(1:1) = lbound(SrcAeroTableData%Skew, kind=B8Ki) + UB(1:1) = ubound(SrcAeroTableData%Skew, kind=B8Ki) + if (.not. allocated(DstAeroTableData%Skew)) then + allocate(DstAeroTableData%Skew(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%Skew.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%Skew = SrcAeroTableData%Skew + end if + if (allocated(SrcAeroTableData%C_Fx)) then + LB(1:5) = lbound(SrcAeroTableData%C_Fx, kind=B8Ki) + UB(1:5) = ubound(SrcAeroTableData%C_Fx, kind=B8Ki) + if (.not. allocated(DstAeroTableData%C_Fx)) then + allocate(DstAeroTableData%C_Fx(LB(1):UB(1),LB(2):UB(2),LB(3):UB(3),LB(4):UB(4),LB(5):UB(5)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%C_Fx.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%C_Fx = SrcAeroTableData%C_Fx + end if + if (allocated(SrcAeroTableData%C_Fy)) then + LB(1:5) = lbound(SrcAeroTableData%C_Fy, kind=B8Ki) + UB(1:5) = ubound(SrcAeroTableData%C_Fy, kind=B8Ki) + if (.not. allocated(DstAeroTableData%C_Fy)) then + allocate(DstAeroTableData%C_Fy(LB(1):UB(1),LB(2):UB(2),LB(3):UB(3),LB(4):UB(4),LB(5):UB(5)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%C_Fy.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%C_Fy = SrcAeroTableData%C_Fy + end if + if (allocated(SrcAeroTableData%C_Fz)) then + LB(1:5) = lbound(SrcAeroTableData%C_Fz, kind=B8Ki) + UB(1:5) = ubound(SrcAeroTableData%C_Fz, kind=B8Ki) + if (.not. allocated(DstAeroTableData%C_Fz)) then + allocate(DstAeroTableData%C_Fz(LB(1):UB(1),LB(2):UB(2),LB(3):UB(3),LB(4):UB(4),LB(5):UB(5)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%C_Fz.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%C_Fz = SrcAeroTableData%C_Fz + end if + if (allocated(SrcAeroTableData%C_Mx)) then + LB(1:5) = lbound(SrcAeroTableData%C_Mx, kind=B8Ki) + UB(1:5) = ubound(SrcAeroTableData%C_Mx, kind=B8Ki) + if (.not. allocated(DstAeroTableData%C_Mx)) then + allocate(DstAeroTableData%C_Mx(LB(1):UB(1),LB(2):UB(2),LB(3):UB(3),LB(4):UB(4),LB(5):UB(5)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%C_Mx.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%C_Mx = SrcAeroTableData%C_Mx + end if + if (allocated(SrcAeroTableData%C_My)) then + LB(1:5) = lbound(SrcAeroTableData%C_My, kind=B8Ki) + UB(1:5) = ubound(SrcAeroTableData%C_My, kind=B8Ki) + if (.not. allocated(DstAeroTableData%C_My)) then + allocate(DstAeroTableData%C_My(LB(1):UB(1),LB(2):UB(2),LB(3):UB(3),LB(4):UB(4),LB(5):UB(5)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%C_My.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%C_My = SrcAeroTableData%C_My + end if + if (allocated(SrcAeroTableData%C_Mz)) then + LB(1:5) = lbound(SrcAeroTableData%C_Mz, kind=B8Ki) + UB(1:5) = ubound(SrcAeroTableData%C_Mz, kind=B8Ki) + if (.not. allocated(DstAeroTableData%C_Mz)) then + allocate(DstAeroTableData%C_Mz(LB(1):UB(1),LB(2):UB(2),LB(3):UB(3),LB(4):UB(4),LB(5):UB(5)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroTableData%C_Mz.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroTableData%C_Mz = SrcAeroTableData%C_Mz + end if +end subroutine + +subroutine ADsk_DestroyAeroTable(AeroTableData, ErrStat, ErrMsg) + type(ADsk_AeroTable), intent(inout) :: AeroTableData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_DestroyAeroTable' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(AeroTableData%TSR)) then + deallocate(AeroTableData%TSR) + end if + if (allocated(AeroTableData%RtSpd)) then + deallocate(AeroTableData%RtSpd) + end if + if (allocated(AeroTableData%VRel)) then + deallocate(AeroTableData%VRel) + end if + if (allocated(AeroTableData%Pitch)) then + deallocate(AeroTableData%Pitch) + end if + if (allocated(AeroTableData%Skew)) then + deallocate(AeroTableData%Skew) + end if + if (allocated(AeroTableData%C_Fx)) then + deallocate(AeroTableData%C_Fx) + end if + if (allocated(AeroTableData%C_Fy)) then + deallocate(AeroTableData%C_Fy) + end if + if (allocated(AeroTableData%C_Fz)) then + deallocate(AeroTableData%C_Fz) + end if + if (allocated(AeroTableData%C_Mx)) then + deallocate(AeroTableData%C_Mx) + end if + if (allocated(AeroTableData%C_My)) then + deallocate(AeroTableData%C_My) + end if + if (allocated(AeroTableData%C_Mz)) then + deallocate(AeroTableData%C_Mz) + end if +end subroutine + +subroutine ADsk_PackAeroTable(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_AeroTable), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackAeroTable' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%N_TSR) + call RegPack(RF, InData%N_RtSpd) + call RegPack(RF, InData%N_VRel) + call RegPack(RF, InData%N_Pitch) + call RegPack(RF, InData%N_Skew) + call RegPackAlloc(RF, InData%TSR) + call RegPackAlloc(RF, InData%RtSpd) + call RegPackAlloc(RF, InData%VRel) + call RegPackAlloc(RF, InData%Pitch) + call RegPackAlloc(RF, InData%Skew) + call RegPackAlloc(RF, InData%C_Fx) + call RegPackAlloc(RF, InData%C_Fy) + call RegPackAlloc(RF, InData%C_Fz) + call RegPackAlloc(RF, InData%C_Mx) + call RegPackAlloc(RF, InData%C_My) + call RegPackAlloc(RF, InData%C_Mz) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackAeroTable(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_AeroTable), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackAeroTable' + integer(B8Ki) :: LB(5), UB(5) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%N_TSR); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%N_RtSpd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%N_VRel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%N_Pitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%N_Skew); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%TSR); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%RtSpd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%VRel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%Pitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%Skew); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%C_Fx); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%C_Fy); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%C_Fz); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%C_Mx); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%C_My); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%C_Mz); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyInputFile(SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_InputFile), intent(in) :: SrcInputFileData + type(ADsk_InputFile), intent(inout) :: DstInputFileData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_CopyInputFile' + ErrStat = ErrID_None + ErrMsg = '' + DstInputFileData%Echo = SrcInputFileData%Echo + DstInputFileData%DT = SrcInputFileData%DT + DstInputFileData%AirDens = SrcInputFileData%AirDens + DstInputFileData%RotorRad = SrcInputFileData%RotorRad + DstInputFileData%SumPrint = SrcInputFileData%SumPrint + DstInputFileData%NumOuts = SrcInputFileData%NumOuts + if (allocated(SrcInputFileData%OutList)) then + LB(1:1) = lbound(SrcInputFileData%OutList, kind=B8Ki) + UB(1:1) = ubound(SrcInputFileData%OutList, kind=B8Ki) + if (.not. allocated(DstInputFileData%OutList)) then + allocate(DstInputFileData%OutList(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInputFileData%OutList.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInputFileData%OutList = SrcInputFileData%OutList + end if + call ADsk_CopyAeroTable(SrcInputFileData%AeroTable, DstInputFileData%AeroTable, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine ADsk_DestroyInputFile(InputFileData, ErrStat, ErrMsg) + type(ADsk_InputFile), intent(inout) :: InputFileData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_DestroyInputFile' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(InputFileData%OutList)) then + deallocate(InputFileData%OutList) + end if + call ADsk_DestroyAeroTable(InputFileData%AeroTable, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine ADsk_PackInputFile(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_InputFile), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackInputFile' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Echo) + call RegPack(RF, InData%DT) + call RegPack(RF, InData%AirDens) + call RegPack(RF, InData%RotorRad) + call RegPack(RF, InData%SumPrint) + call RegPack(RF, InData%NumOuts) + call RegPackAlloc(RF, InData%OutList) + call ADsk_PackAeroTable(RF, InData%AeroTable) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackInputFile(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_InputFile), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackInputFile' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Echo); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%AirDens); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotorRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%SumPrint); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumOuts); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutList); if (RegCheckErr(RF, RoutineName)) return + call ADsk_UnpackAeroTable(RF, OutData%AeroTable) ! AeroTable +end subroutine + +subroutine ADsk_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_InitInputType), intent(in) :: SrcInitInputData + type(ADsk_InitInputType), intent(inout) :: DstInitInputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_CopyInitInput' + ErrStat = ErrID_None + ErrMsg = '' + DstInitInputData%InputFile = SrcInitInputData%InputFile + DstInitInputData%RootName = SrcInitInputData%RootName + DstInitInputData%RotorRad = SrcInitInputData%RotorRad + DstInitInputData%HubPosition = SrcInitInputData%HubPosition + DstInitInputData%HubOrientation = SrcInitInputData%HubOrientation + DstInitInputData%defAirDens = SrcInitInputData%defAirDens + DstInitInputData%Linearize = SrcInitInputData%Linearize + DstInitInputData%UseInputFile = SrcInitInputData%UseInputFile + call NWTC_Library_CopyFileInfoType(SrcInitInputData%PassedFileData, DstInitInputData%PassedFileData, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstInitInputData%FlowField => SrcInitInputData%FlowField +end subroutine + +subroutine ADsk_DestroyInitInput(InitInputData, ErrStat, ErrMsg) + type(ADsk_InitInputType), intent(inout) :: InitInputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_DestroyInitInput' + ErrStat = ErrID_None + ErrMsg = '' + call NWTC_Library_DestroyFileInfoType(InitInputData%PassedFileData, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + nullify(InitInputData%FlowField) +end subroutine + +subroutine ADsk_PackInitInput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_InitInputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackInitInput' + logical :: PtrInIndex + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%InputFile) + call RegPack(RF, InData%RootName) + call RegPack(RF, InData%RotorRad) + call RegPack(RF, InData%HubPosition) + call RegPack(RF, InData%HubOrientation) + call RegPack(RF, InData%defAirDens) + call RegPack(RF, InData%Linearize) + call RegPack(RF, InData%UseInputFile) + call NWTC_Library_PackFileInfoType(RF, InData%PassedFileData) + call RegPack(RF, associated(InData%FlowField)) + if (associated(InData%FlowField)) then + call RegPackPointer(RF, c_loc(InData%FlowField), PtrInIndex) + if (.not. PtrInIndex) then + call IfW_FlowField_PackFlowFieldType(RF, InData%FlowField) + end if + end if + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackInitInput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_InitInputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackInitInput' + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: stat + logical :: IsAllocAssoc + integer(B8Ki) :: PtrIdx + type(c_ptr) :: Ptr + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RootName); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotorRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubPosition); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubOrientation); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%defAirDens); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Linearize); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%UseInputFile); if (RegCheckErr(RF, RoutineName)) return + call NWTC_Library_UnpackFileInfoType(RF, OutData%PassedFileData) ! PassedFileData + if (associated(OutData%FlowField)) deallocate(OutData%FlowField) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackPointer(RF, Ptr, PtrIdx); if (RegCheckErr(RF, RoutineName)) return + if (c_associated(Ptr)) then + call c_f_pointer(Ptr, OutData%FlowField) + else + allocate(OutData%FlowField,stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%FlowField.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + RF%Pointers(PtrIdx) = c_loc(OutData%FlowField) + call IfW_FlowField_UnpackFlowFieldType(RF, OutData%FlowField) ! FlowField + end if + else + OutData%FlowField => null() + end if +end subroutine + +subroutine ADsk_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_InitOutputType), intent(in) :: SrcInitOutputData + type(ADsk_InitOutputType), intent(inout) :: DstInitOutputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_CopyInitOutput' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(SrcInitOutputData%WriteOutputHdr)) then + LB(1:1) = lbound(SrcInitOutputData%WriteOutputHdr, kind=B8Ki) + UB(1:1) = ubound(SrcInitOutputData%WriteOutputHdr, kind=B8Ki) + if (.not. allocated(DstInitOutputData%WriteOutputHdr)) then + allocate(DstInitOutputData%WriteOutputHdr(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitOutputData%WriteOutputHdr.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitOutputData%WriteOutputHdr = SrcInitOutputData%WriteOutputHdr + end if + if (allocated(SrcInitOutputData%WriteOutputUnt)) then + LB(1:1) = lbound(SrcInitOutputData%WriteOutputUnt, kind=B8Ki) + UB(1:1) = ubound(SrcInitOutputData%WriteOutputUnt, kind=B8Ki) + if (.not. allocated(DstInitOutputData%WriteOutputUnt)) then + allocate(DstInitOutputData%WriteOutputUnt(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitOutputData%WriteOutputUnt.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitOutputData%WriteOutputUnt = SrcInitOutputData%WriteOutputUnt + end if + call NWTC_Library_CopyProgDesc(SrcInitOutputData%Ver, DstInitOutputData%Ver, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine ADsk_DestroyInitOutput(InitOutputData, ErrStat, ErrMsg) + type(ADsk_InitOutputType), intent(inout) :: InitOutputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_DestroyInitOutput' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(InitOutputData%WriteOutputHdr)) then + deallocate(InitOutputData%WriteOutputHdr) + end if + if (allocated(InitOutputData%WriteOutputUnt)) then + deallocate(InitOutputData%WriteOutputUnt) + end if + call NWTC_Library_DestroyProgDesc(InitOutputData%Ver, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine ADsk_PackInitOutput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_InitOutputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackInitOutput' + if (RF%ErrStat >= AbortErrLev) return + call RegPackAlloc(RF, InData%WriteOutputHdr) + call RegPackAlloc(RF, InData%WriteOutputUnt) + call NWTC_Library_PackProgDesc(RF, InData%Ver) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackInitOutput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_InitOutputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackInitOutput' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt); if (RegCheckErr(RF, RoutineName)) return + call NWTC_Library_UnpackProgDesc(RF, OutData%Ver) ! Ver +end subroutine + +subroutine ADsk_CopyInput(SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_InputType), intent(inout) :: SrcInputData + type(ADsk_InputType), intent(inout) :: DstInputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_CopyInput' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcInputData%HubMotion, DstInputData%HubMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstInputData%RotSpeed = SrcInputData%RotSpeed + DstInputData%BlPitch = SrcInputData%BlPitch +end subroutine + +subroutine ADsk_DestroyInput(InputData, ErrStat, ErrMsg) + type(ADsk_InputType), intent(inout) :: InputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_DestroyInput' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( InputData%HubMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine ADsk_PackInput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_InputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackInput' + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%HubMotion) + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%BlPitch) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackInput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_InputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackInput' + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%HubMotion) ! HubMotion + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BlPitch); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyOutput(SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_OutputType), intent(inout) :: SrcOutputData + type(ADsk_OutputType), intent(inout) :: DstOutputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_CopyOutput' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcOutputData%AeroLoads, DstOutputData%AeroLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstOutputData%YawErr = SrcOutputData%YawErr + DstOutputData%PsiSkew = SrcOutputData%PsiSkew + DstOutputData%ChiSkew = SrcOutputData%ChiSkew + DstOutputData%VRel = SrcOutputData%VRel + DstOutputData%Ct = SrcOutputData%Ct + DstOutputData%Cq = SrcOutputData%Cq + if (allocated(SrcOutputData%WriteOutput)) then + LB(1:1) = lbound(SrcOutputData%WriteOutput, kind=B8Ki) + UB(1:1) = ubound(SrcOutputData%WriteOutput, kind=B8Ki) + if (.not. allocated(DstOutputData%WriteOutput)) then + allocate(DstOutputData%WriteOutput(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%WriteOutput.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstOutputData%WriteOutput = SrcOutputData%WriteOutput + end if +end subroutine + +subroutine ADsk_DestroyOutput(OutputData, ErrStat, ErrMsg) + type(ADsk_OutputType), intent(inout) :: OutputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_DestroyOutput' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( OutputData%AeroLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(OutputData%WriteOutput)) then + deallocate(OutputData%WriteOutput) + end if +end subroutine + +subroutine ADsk_PackOutput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_OutputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackOutput' + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%AeroLoads) + call RegPack(RF, InData%YawErr) + call RegPack(RF, InData%PsiSkew) + call RegPack(RF, InData%ChiSkew) + call RegPack(RF, InData%VRel) + call RegPack(RF, InData%Ct) + call RegPack(RF, InData%Cq) + call RegPackAlloc(RF, InData%WriteOutput) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackOutput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_OutputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackOutput' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%AeroLoads) ! AeroLoads + call RegUnpack(RF, OutData%YawErr); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PsiSkew); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ChiSkew); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%VRel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Ct); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Cq); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutput); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyContState(SrcContStateData, DstContStateData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_ContinuousStateType), intent(in) :: SrcContStateData + type(ADsk_ContinuousStateType), intent(inout) :: DstContStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_CopyContState' + ErrStat = ErrID_None + ErrMsg = '' + DstContStateData%DummyContState = SrcContStateData%DummyContState +end subroutine + +subroutine ADsk_DestroyContState(ContStateData, ErrStat, ErrMsg) + type(ADsk_ContinuousStateType), intent(inout) :: ContStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_DestroyContState' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine ADsk_PackContState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_ContinuousStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackContState' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DummyContState) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackContState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_ContinuousStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackContState' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DummyContState); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_DiscreteStateType), intent(in) :: SrcDiscStateData + type(ADsk_DiscreteStateType), intent(inout) :: DstDiscStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_CopyDiscState' + ErrStat = ErrID_None + ErrMsg = '' + DstDiscStateData%DummyDiscreteState = SrcDiscStateData%DummyDiscreteState +end subroutine + +subroutine ADsk_DestroyDiscState(DiscStateData, ErrStat, ErrMsg) + type(ADsk_DiscreteStateType), intent(inout) :: DiscStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_DestroyDiscState' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine ADsk_PackDiscState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_DiscreteStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackDiscState' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DummyDiscreteState) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackDiscState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_DiscreteStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackDiscState' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DummyDiscreteState); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyConstrState(SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_ConstraintStateType), intent(in) :: SrcConstrStateData + type(ADsk_ConstraintStateType), intent(inout) :: DstConstrStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_CopyConstrState' + ErrStat = ErrID_None + ErrMsg = '' + DstConstrStateData%DummyConstrState = SrcConstrStateData%DummyConstrState +end subroutine + +subroutine ADsk_DestroyConstrState(ConstrStateData, ErrStat, ErrMsg) + type(ADsk_ConstraintStateType), intent(inout) :: ConstrStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_DestroyConstrState' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine ADsk_PackConstrState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_ConstraintStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackConstrState' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DummyConstrState) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackConstrState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_ConstraintStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackConstrState' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DummyConstrState); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyOtherState(SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_OtherStateType), intent(in) :: SrcOtherStateData + type(ADsk_OtherStateType), intent(inout) :: DstOtherStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_CopyOtherState' + ErrStat = ErrID_None + ErrMsg = '' + DstOtherStateData%DummyOtherState = SrcOtherStateData%DummyOtherState +end subroutine + +subroutine ADsk_DestroyOtherState(OtherStateData, ErrStat, ErrMsg) + type(ADsk_OtherStateType), intent(inout) :: OtherStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_DestroyOtherState' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine ADsk_PackOtherState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_OtherStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackOtherState' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DummyOtherState) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackOtherState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_OtherStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackOtherState' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DummyOtherState); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_ParameterType), intent(in) :: SrcParamData + type(ADsk_ParameterType), intent(inout) :: DstParamData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1, i2 + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_CopyParam' + ErrStat = ErrID_None + ErrMsg = '' + DstParamData%RootName = SrcParamData%RootName + DstParamData%DT = SrcParamData%DT + DstParamData%RotorRad = SrcParamData%RotorRad + DstParamData%AirDens = SrcParamData%AirDens + DstParamData%NumOuts = SrcParamData%NumOuts + DstParamData%halfRhoA = SrcParamData%halfRhoA + call ADsk_CopyAeroTable(SrcParamData%AeroTable, DstParamData%AeroTable, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstParamData%UseTSR = SrcParamData%UseTSR + if (allocated(SrcParamData%OutParam)) then + LB(1:1) = lbound(SrcParamData%OutParam, kind=B8Ki) + UB(1:1) = ubound(SrcParamData%OutParam, kind=B8Ki) + if (.not. allocated(DstParamData%OutParam)) then + allocate(DstParamData%OutParam(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%OutParam.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyOutParmType(SrcParamData%OutParam(i1), DstParamData%OutParam(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + DstParamData%FlowField => SrcParamData%FlowField + if (allocated(SrcParamData%DiskWindPosRel)) then + LB(1:2) = lbound(SrcParamData%DiskWindPosRel, kind=B8Ki) + UB(1:2) = ubound(SrcParamData%DiskWindPosRel, kind=B8Ki) + if (.not. allocated(DstParamData%DiskWindPosRel)) then + allocate(DstParamData%DiskWindPosRel(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%DiskWindPosRel.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstParamData%DiskWindPosRel = SrcParamData%DiskWindPosRel + end if +end subroutine + +subroutine ADsk_DestroyParam(ParamData, ErrStat, ErrMsg) + type(ADsk_ParameterType), intent(inout) :: ParamData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1, i2 + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'ADsk_DestroyParam' + ErrStat = ErrID_None + ErrMsg = '' + call ADsk_DestroyAeroTable(ParamData%AeroTable, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(ParamData%OutParam)) then + LB(1:1) = lbound(ParamData%OutParam, kind=B8Ki) + UB(1:1) = ubound(ParamData%OutParam, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyOutParmType(ParamData%OutParam(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(ParamData%OutParam) + end if + nullify(ParamData%FlowField) + if (allocated(ParamData%DiskWindPosRel)) then + deallocate(ParamData%DiskWindPosRel) + end if +end subroutine + +subroutine ADsk_PackParam(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_ParameterType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackParam' + integer(B8Ki) :: i1, i2 + integer(B8Ki) :: LB(2), UB(2) + logical :: PtrInIndex + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%RootName) + call RegPack(RF, InData%DT) + call RegPack(RF, InData%RotorRad) + call RegPack(RF, InData%AirDens) + call RegPack(RF, InData%NumOuts) + call RegPack(RF, InData%halfRhoA) + call ADsk_PackAeroTable(RF, InData%AeroTable) + call RegPack(RF, InData%UseTSR) + call RegPack(RF, allocated(InData%OutParam)) + if (allocated(InData%OutParam)) then + call RegPackBounds(RF, 1, lbound(InData%OutParam, kind=B8Ki), ubound(InData%OutParam, kind=B8Ki)) + LB(1:1) = lbound(InData%OutParam, kind=B8Ki) + UB(1:1) = ubound(InData%OutParam, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_PackOutParmType(RF, InData%OutParam(i1)) + end do + end if + call RegPack(RF, associated(InData%FlowField)) + if (associated(InData%FlowField)) then + call RegPackPointer(RF, c_loc(InData%FlowField), PtrInIndex) + if (.not. PtrInIndex) then + call IfW_FlowField_PackFlowFieldType(RF, InData%FlowField) + end if + end if + call RegPackAlloc(RF, InData%DiskWindPosRel) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackParam(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_ParameterType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackParam' + integer(B8Ki) :: i1, i2 + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: stat + logical :: IsAllocAssoc + integer(B8Ki) :: PtrIdx + type(c_ptr) :: Ptr + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%RootName); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotorRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%AirDens); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumOuts); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%halfRhoA); if (RegCheckErr(RF, RoutineName)) return + call ADsk_UnpackAeroTable(RF, OutData%AeroTable) ! AeroTable + call RegUnpack(RF, OutData%UseTSR); if (RegCheckErr(RF, RoutineName)) return + if (allocated(OutData%OutParam)) deallocate(OutData%OutParam) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%OutParam(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%OutParam.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackOutParmType(RF, OutData%OutParam(i1)) ! OutParam + end do + end if + if (associated(OutData%FlowField)) deallocate(OutData%FlowField) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackPointer(RF, Ptr, PtrIdx); if (RegCheckErr(RF, RoutineName)) return + if (c_associated(Ptr)) then + call c_f_pointer(Ptr, OutData%FlowField) + else + allocate(OutData%FlowField,stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%FlowField.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + RF%Pointers(PtrIdx) = c_loc(OutData%FlowField) + call IfW_FlowField_UnpackFlowFieldType(RF, OutData%FlowField) ! FlowField + end if + else + OutData%FlowField => null() + end if + call RegUnpackAlloc(RF, OutData%DiskWindPosRel); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) + type(ADsk_MiscVarType), intent(in) :: SrcMiscData + type(ADsk_MiscVarType), intent(inout) :: DstMiscData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'ADsk_CopyMisc' + ErrStat = ErrID_None + ErrMsg = '' + DstMiscData%idx_last = SrcMiscData%idx_last + if (allocated(SrcMiscData%AllOuts)) then + LB(1:1) = lbound(SrcMiscData%AllOuts, kind=B8Ki) + UB(1:1) = ubound(SrcMiscData%AllOuts, kind=B8Ki) + if (.not. allocated(DstMiscData%AllOuts)) then + allocate(DstMiscData%AllOuts(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%AllOuts.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%AllOuts = SrcMiscData%AllOuts + end if + DstMiscData%x_hat = SrcMiscData%x_hat + DstMiscData%y_hat = SrcMiscData%y_hat + DstMiscData%z_hat = SrcMiscData%z_hat + DstMiscData%VRel = SrcMiscData%VRel + DstMiscData%VRel_xd = SrcMiscData%VRel_xd + DstMiscData%lambda = SrcMiscData%lambda + DstMiscData%Chi = SrcMiscData%Chi + DstMiscData%C_F = SrcMiscData%C_F + DstMiscData%C_M = SrcMiscData%C_M + DstMiscData%Force = SrcMiscData%Force + DstMiscData%Moment = SrcMiscData%Moment + if (allocated(SrcMiscData%DiskWindPosAbs)) then + LB(1:2) = lbound(SrcMiscData%DiskWindPosAbs, kind=B8Ki) + UB(1:2) = ubound(SrcMiscData%DiskWindPosAbs, kind=B8Ki) + if (.not. allocated(DstMiscData%DiskWindPosAbs)) then + allocate(DstMiscData%DiskWindPosAbs(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%DiskWindPosAbs.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%DiskWindPosAbs = SrcMiscData%DiskWindPosAbs + end if + if (allocated(SrcMiscData%DiskWindVel)) then + LB(1:2) = lbound(SrcMiscData%DiskWindVel, kind=B8Ki) + UB(1:2) = ubound(SrcMiscData%DiskWindVel, kind=B8Ki) + if (.not. allocated(DstMiscData%DiskWindVel)) then + allocate(DstMiscData%DiskWindVel(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%DiskWindVel.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%DiskWindVel = SrcMiscData%DiskWindVel + end if + DstMiscData%DiskAvgVel = SrcMiscData%DiskAvgVel +end subroutine + +subroutine ADsk_DestroyMisc(MiscData, ErrStat, ErrMsg) + type(ADsk_MiscVarType), intent(inout) :: MiscData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'ADsk_DestroyMisc' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(MiscData%AllOuts)) then + deallocate(MiscData%AllOuts) + end if + if (allocated(MiscData%DiskWindPosAbs)) then + deallocate(MiscData%DiskWindPosAbs) + end if + if (allocated(MiscData%DiskWindVel)) then + deallocate(MiscData%DiskWindVel) + end if +end subroutine + +subroutine ADsk_PackMisc(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ADsk_MiscVarType), intent(in) :: InData + character(*), parameter :: RoutineName = 'ADsk_PackMisc' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%idx_last) + call RegPackAlloc(RF, InData%AllOuts) + call RegPack(RF, InData%x_hat) + call RegPack(RF, InData%y_hat) + call RegPack(RF, InData%z_hat) + call RegPack(RF, InData%VRel) + call RegPack(RF, InData%VRel_xd) + call RegPack(RF, InData%lambda) + call RegPack(RF, InData%Chi) + call RegPack(RF, InData%C_F) + call RegPack(RF, InData%C_M) + call RegPack(RF, InData%Force) + call RegPack(RF, InData%Moment) + call RegPackAlloc(RF, InData%DiskWindPosAbs) + call RegPackAlloc(RF, InData%DiskWindVel) + call RegPack(RF, InData%DiskAvgVel) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_UnPackMisc(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ADsk_MiscVarType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'ADsk_UnPackMisc' + integer(B8Ki) :: LB(2), UB(2) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%idx_last); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%AllOuts); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%x_hat); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%y_hat); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%z_hat); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%VRel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%VRel_xd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%lambda); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Chi); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%C_F); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%C_M); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Force); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Moment); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%DiskWindPosAbs); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%DiskWindVel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DiskAvgVel); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine ADsk_Input_ExtrapInterp(u, t, u_out, t_out, ErrStat, ErrMsg) + ! + ! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time + ! values of u (which has values associated with times in t). Order of the interpolation is given by the size of u + ! + ! expressions below based on either + ! + ! f(t) = a + ! f(t) = a + b * t, or + ! f(t) = a + b * t + c * t**2 + ! + ! where a, b and c are determined as the solution to + ! f(t1) = u1, f(t2) = u2, f(t3) = u3 (as appropriate) + ! + !---------------------------------------------------------------------------------------------------------------------------------- + + type(ADsk_InputType), intent(inout) :: u(:) ! Input at t1 > t2 > t3 + real(DbKi), intent(in ) :: t(:) ! Times associated with the Inputs + type(ADsk_InputType), intent(inout) :: u_out ! Input at tin_out + real(DbKi), intent(in ) :: t_out ! time to be extrap/interp'd to + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + integer(IntKi) :: order ! order of polynomial fit (max 2) + integer(IntKi) :: ErrStat2 ! local errors + character(ErrMsgLen) :: ErrMsg2 ! local errors + character(*), PARAMETER :: RoutineName = 'ADsk_Input_ExtrapInterp' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + if (size(t) /= size(u)) then + call SetErrStat(ErrID_Fatal, 'size(t) must equal size(u)', ErrStat, ErrMsg, RoutineName) + return + endif + order = size(u) - 1 + select case (order) + case (0) + call ADsk_CopyInput(u(1), u_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (1) + call ADsk_Input_ExtrapInterp1(u(1), u(2), t, u_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (2) + call ADsk_Input_ExtrapInterp2(u(1), u(2), u(3), t, u_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case default + call SetErrStat(ErrID_Fatal, 'size(u) must be less than 4 (order must be less than 3).', ErrStat, ErrMsg, RoutineName) + return + end select +end subroutine + +SUBROUTINE ADsk_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = u1, f(t2) = u2 +! +!.................................................................................................................................. + + TYPE(ADsk_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 + TYPE(ADsk_InputType), INTENT(INOUT) :: u2 ! Input at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Inputs + TYPE(ADsk_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'ADsk_Input_ExtrapInterp1' + REAL(DbKi) :: a1, a2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF (EqualRealNos(t(1), t(2))) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg, RoutineName) + RETURN + END IF + + ! Calculate weighting factors from Lagrange polynomial + a1 = -(t_out - t(2))/t(2) + a2 = t_out/t(2) + + CALL MeshExtrapInterp1(u1%HubMotion, u2%HubMotion, tin, u_out%HubMotion, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + u_out%RotSpeed = a1*u1%RotSpeed + a2*u2%RotSpeed + CALL Angles_ExtrapInterp( u1%BlPitch, u2%BlPitch, tin, u_out%BlPitch, tin_out ) +END SUBROUTINE + +SUBROUTINE ADsk_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = u1, f(t2) = u2, f(t3) = u3 +! +!.................................................................................................................................. + + TYPE(ADsk_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 > t3 + TYPE(ADsk_InputType), INTENT(INOUT) :: u2 ! Input at t2 > t3 + TYPE(ADsk_InputType), INTENT(INOUT) :: u3 ! Input at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Inputs + TYPE(ADsk_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: a1,a2,a3 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'ADsk_Input_ExtrapInterp2' + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + + ! Calculate Lagrange polynomial coefficients + a1 = (t_out - t(2))*(t_out - t(3))/((t(1) - t(2))*(t(1) - t(3))) + a2 = (t_out - t(1))*(t_out - t(3))/((t(2) - t(1))*(t(2) - t(3))) + a3 = (t_out - t(1))*(t_out - t(2))/((t(3) - t(1))*(t(3) - t(2))) + CALL MeshExtrapInterp2(u1%HubMotion, u2%HubMotion, u3%HubMotion, tin, u_out%HubMotion, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + u_out%RotSpeed = a1*u1%RotSpeed + a2*u2%RotSpeed + a3*u3%RotSpeed + CALL Angles_ExtrapInterp( u1%BlPitch, u2%BlPitch, u3%BlPitch, tin, u_out%BlPitch, tin_out ) +END SUBROUTINE + +subroutine ADsk_Output_ExtrapInterp(y, t, y_out, t_out, ErrStat, ErrMsg) + ! + ! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time + ! values of y (which has values associated with times in t). Order of the interpolation is given by the size of y + ! + ! expressions below based on either + ! + ! f(t) = a + ! f(t) = a + b * t, or + ! f(t) = a + b * t + c * t**2 + ! + ! where a, b and c are determined as the solution to + ! f(t1) = y1, f(t2) = y2, f(t3) = y3 (as appropriate) + ! + !---------------------------------------------------------------------------------------------------------------------------------- + + type(ADsk_OutputType), intent(inout) :: y(:) ! Output at t1 > t2 > t3 + real(DbKi), intent(in ) :: t(:) ! Times associated with the Outputs + type(ADsk_OutputType), intent(inout) :: y_out ! Output at tin_out + real(DbKi), intent(in ) :: t_out ! time to be extrap/interp'd to + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + integer(IntKi) :: order ! order of polynomial fit (max 2) + integer(IntKi) :: ErrStat2 ! local errors + character(ErrMsgLen) :: ErrMsg2 ! local errors + character(*), PARAMETER :: RoutineName = 'ADsk_Output_ExtrapInterp' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + if (size(t) /= size(y)) then + call SetErrStat(ErrID_Fatal, 'size(t) must equal size(y)', ErrStat, ErrMsg, RoutineName) + return + endif + order = size(y) - 1 + select case (order) + case (0) + call ADsk_CopyOutput(y(1), y_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (1) + call ADsk_Output_ExtrapInterp1(y(1), y(2), t, y_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (2) + call ADsk_Output_ExtrapInterp2(y(1), y(2), y(3), t, y_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case default + call SetErrStat(ErrID_Fatal, 'size(y) must be less than 4 (order must be less than 3).', ErrStat, ErrMsg, RoutineName) + return + end select +end subroutine + +SUBROUTINE ADsk_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = y1, f(t2) = y2 +! +!.................................................................................................................................. + + TYPE(ADsk_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 + TYPE(ADsk_OutputType), INTENT(INOUT) :: y2 ! Output at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Outputs + TYPE(ADsk_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'ADsk_Output_ExtrapInterp1' + REAL(DbKi) :: a1, a2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i1 ! dim1 counter variable for arrays + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF (EqualRealNos(t(1), t(2))) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg, RoutineName) + RETURN + END IF + + ! Calculate weighting factors from Lagrange polynomial + a1 = -(t_out - t(2))/t(2) + a2 = t_out/t(2) + + CALL MeshExtrapInterp1(y1%AeroLoads, y2%AeroLoads, tin, y_out%AeroLoads, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + y_out%YawErr = a1*y1%YawErr + a2*y2%YawErr + y_out%PsiSkew = a1*y1%PsiSkew + a2*y2%PsiSkew + y_out%ChiSkew = a1*y1%ChiSkew + a2*y2%ChiSkew + y_out%VRel = a1*y1%VRel + a2*y2%VRel + y_out%Ct = a1*y1%Ct + a2*y2%Ct + y_out%Cq = a1*y1%Cq + a2*y2%Cq + IF (ALLOCATED(y_out%WriteOutput) .AND. ALLOCATED(y1%WriteOutput)) THEN + y_out%WriteOutput = a1*y1%WriteOutput + a2*y2%WriteOutput + END IF ! check if allocated +END SUBROUTINE + +SUBROUTINE ADsk_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = y1, f(t2) = y2, f(t3) = y3 +! +!.................................................................................................................................. + + TYPE(ADsk_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 > t3 + TYPE(ADsk_OutputType), INTENT(INOUT) :: y2 ! Output at t2 > t3 + TYPE(ADsk_OutputType), INTENT(INOUT) :: y3 ! Output at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Outputs + TYPE(ADsk_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: a1,a2,a3 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'ADsk_Output_ExtrapInterp2' + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i1 ! dim1 counter variable for arrays + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + + ! Calculate Lagrange polynomial coefficients + a1 = (t_out - t(2))*(t_out - t(3))/((t(1) - t(2))*(t(1) - t(3))) + a2 = (t_out - t(1))*(t_out - t(3))/((t(2) - t(1))*(t(2) - t(3))) + a3 = (t_out - t(1))*(t_out - t(2))/((t(3) - t(1))*(t(3) - t(2))) + CALL MeshExtrapInterp2(y1%AeroLoads, y2%AeroLoads, y3%AeroLoads, tin, y_out%AeroLoads, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + y_out%YawErr = a1*y1%YawErr + a2*y2%YawErr + a3*y3%YawErr + y_out%PsiSkew = a1*y1%PsiSkew + a2*y2%PsiSkew + a3*y3%PsiSkew + y_out%ChiSkew = a1*y1%ChiSkew + a2*y2%ChiSkew + a3*y3%ChiSkew + y_out%VRel = a1*y1%VRel + a2*y2%VRel + a3*y3%VRel + y_out%Ct = a1*y1%Ct + a2*y2%Ct + a3*y3%Ct + y_out%Cq = a1*y1%Cq + a2*y2%Cq + a3*y3%Cq + IF (ALLOCATED(y_out%WriteOutput) .AND. ALLOCATED(y1%WriteOutput)) THEN + y_out%WriteOutput = a1*y1%WriteOutput + a2*y2%WriteOutput + a3*y3%WriteOutput + END IF ! check if allocated +END SUBROUTINE +END MODULE AeroDisk_Types +!ENDOFREGISTRYGENERATEDFILE diff --git a/modules/aerodisk/src/driver/AeroDisk_Driver.f90 b/modules/aerodisk/src/driver/AeroDisk_Driver.f90 new file mode 100644 index 0000000000..92cb218add --- /dev/null +++ b/modules/aerodisk/src/driver/AeroDisk_Driver.f90 @@ -0,0 +1,415 @@ +!********************************************************************************************************************************** +!> ## AeroDisk_DriverCode: This code tests the AeroDisk module +!!.................................................................................................................................. +!! LICENSING +!! Copyright (C) 2012, 2015 National Renewable Energy Laboratory +!! +!! This file is part of AeroDisk. +!! +!! Licensed under the Apache License, Version 2.0 (the "License"); +!! you may not use this file except in compliance with the License. +!! You may obtain a copy of the License at +!! +!! http://www.apache.org/licenses/LICENSE-2.0 +!! +!! Unless required by applicable law or agreed to in writing, software +!! distributed under the License is distributed on an "AS IS" BASIS, +!! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +!! See the License for the specific language governing permissions and +!! limitations under the License. +!********************************************************************************************************************************** +PROGRAM AeroDisk_Driver + + USE NWTC_Library + USE VersionInfo + USE AeroDisk + USE AeroDisk_Types + USE AeroDisk_Driver_Subs + USE AeroDisk_Driver_Types + USE IfW_FLowField + + IMPLICIT NONE + + TYPE( ProgDesc ), PARAMETER :: ProgInfo = ProgDesc("ADsk_Driver","","") + INTEGER(IntKi) :: ADskDriver_Verbose = 5 ! Verbose level. 0 = none, 5 = some, 10 = lots + + + + integer(IntKi), parameter :: NumInp = 1 !< Number of inputs sent to AeroDisk_UpdateStates + + ! Program variables + real(DbKi) :: Time !< Variable for storing time, in seconds + real(DbKi) :: TimeInterval !< Interval between time steps, in seconds + real(DbKi) :: TStart !< Time to start + real(DbKi) :: TMax !< Maximum time if found by default + integer(IntKi) :: NumTSteps !< number of timesteps + logical :: TimeIntervalFound !< Interval between time steps, in seconds + real(DbKi) :: InputTime(NumInp) !< Variable for storing time associated with inputs, in seconds + real(DbKi), allocatable :: CaseTime(:) !< Timestamps for the case data + real(ReKi), allocatable :: CaseData(:,:) !< Data for the case. Corresponds to CaseTime + + type(ADsk_InitInputType) :: InitInData !< Input data for initialization + type(ADsk_InitOutputType) :: InitOutData !< Output data from initialization + + type(ADsk_ContinuousStateType) :: x !< Continuous states + type(ADsk_DiscreteStateType) :: xd !< Discrete states + type(ADsk_ConstraintStateType) :: z !< Constraint states + type(ADsk_ConstraintStateType) :: Z_residual !< Residual of the constraint state functions (Z) + type(ADsk_OtherStateType) :: OtherState !< Other states + type(ADsk_MiscVarType) :: misc !< Optimization variables + + type(ADsk_ParameterType) :: p !< Parameters + type(ADsk_InputType) :: u(NumInp) !< System inputs + type(ADsk_OutputType) :: y !< System outputs + + ! Local variables for this code + TYPE(ADskDriver_Flags) :: CLSettingsFlags ! Flags indicating which command line arguments were specified + TYPE(ADskDriver_Settings) :: CLSettings ! Command line arguments passed in + TYPE(ADskDriver_Flags) :: SettingsFlags ! Flags indicating which settings were specified (includes CL and ipt file) + TYPE(ADskDriver_Settings) :: Settings ! Driver settings + REAL(DbKi) :: Timer(1:2) ! Keep track of how long this takes to run + type(FileInfoType) :: DvrFileInfo ! Input file stored in FileInfoType structure + type(FlowFieldType), target :: FlowField ! FlowField data + + ! Data transfer + real(ReKi) :: Yaw ! Yaw angle from table + real(R8Ki) :: Force(6) + real(R8Ki) :: Displacement(6) + real(R8Ki) :: Theta(3) + real(R8Ki) :: Orientation_loc(3,3) ! orientation DCM for finding current hub orientation + + INTEGER(IntKi) :: n !< Loop counter (for time step) + integer(IntKi) :: i !< generic loop counter + integer(IntKi) :: DimIdx !< Index of current dimension + integer(IntKi) :: TmpIdx !< Index of last point accessed by dimension + INTEGER(IntKi) :: ErrStat !< Status of error message + CHARACTER(ErrMsgLen) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + CHARACTER(200) :: git_commit ! String containing the current git commit hash + TYPE(ProgDesc), PARAMETER :: version = ProgDesc( 'AeroDisk Driver', '', '' ) ! The version number of this program. + integer(IntKi) :: DvrOut + character(1024) :: OutputFileRootName + + + ! initialize library + call NWTC_Init + call DispNVD(ProgInfo) + DvrOut=-1 ! Set output unit to negative + + ! Display the copyright notice + CALL DispCopyrightLicense( version%Name ) + ! Obtain OpenFAST git commit hash + git_commit = QueryGitVersion() + ! Tell our users what they're running + CALL WrScr( ' Running '//GetNVD( version )//' a part of OpenFAST - '//TRIM(git_Commit)//NewLine//' linked with '//TRIM( GetNVD( NWTC_Ver ))//NewLine ) + + ! Start the timer + call CPU_TIME( Timer(1) ) + + ! Initialize the driver settings to their default values (same as the CL -- command line -- values) + call InitSettingsFlags( ProgInfo, CLSettings, CLSettingsFlags ) + Settings = CLSettings + SettingsFlags = CLSettingsFlags + + ! Parse the input line + call RetrieveArgs( CLSettings, CLSettingsFlags, ErrStat, ErrMsg ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL ProgAbort( ErrMsg ) + ELSEIF ( ErrStat /= 0 ) THEN + CALL WrScr( NewLine//ErrMsg ) + ErrStat = ErrID_None + ENDIF + + ! Check if we are doing verbose error reporting + IF ( CLSettingsFlags%VVerbose ) ADskDriver_Verbose = 10_IntKi + IF ( CLSettingsFlags%Verbose ) ADskDriver_Verbose = 7_IntKi + + ! Verbose error reporting + IF ( ADskDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr('--- Settings from the command line: ---') + CALL printSettings( CLSettingsFlags, CLSettings ) + CALL WrSCr(NewLine) + ENDIF + + ! Verbose error reporting + IF ( ADskDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr('--- Driver settings (before reading driver ipt file): ---') + CALL printSettings( SettingsFlags, Settings ) + CALL WrScr(NewLine) + ENDIF + + + ! Copy the input file information from the CLSettings to the Settings. + ! At this point only one input file type can be set. + IF ( CLSettingsFlags%DvrIptFile ) THEN + SettingsFlags%DvrIptFile = CLSettingsFlags%DvrIptFile + Settings%DvrIptFileName = CLSettings%DvrIptFileName + ELSE + SettingsFlags%ADskIptFile = CLSettingsFlags%ADskIptFile + Settings%ADskIptFileName = CLSettings%ADskIptFileName + ENDIF + + ! If the filename given was not the ADsk input file (-ifw option), then it is treated + ! as the driver input file (flag should be set correctly by RetrieveArgs). So, we must + ! open this. + IF ( SettingsFlags%DvrIptFile ) THEN + + ! Read the driver input file + CALL ProcessComFile( CLSettings%DvrIptFileName, DvrFileInfo, ErrStat, ErrMsg ) + call CheckErr('') + + ! For diagnostic purposes, the following can be used to display the contents + ! of the DvrFileInfo data structure. + ! call Print_FileInfo_Struct( CU, DvrFileInfo ) ! CU is the screen -- different number on different systems. + + ! Parse the input file + CALL ParseDvrIptFile( CLSettings%DvrIptFileName, DvrFileInfo, SettingsFlags, Settings, ProgInfo, CaseTime, CaseData, ErrStat, ErrMsg ) + call CheckErr('') + + ! VVerbose error reporting + IF ( ADskDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr(NewLine//'--- Driver settings after reading the driver ipt file: ---') + CALL printSettings( SettingsFlags, Settings ) + CALL WrScr(NewLine) + ENDIF + + ! VVerbose error reporting + IF ( ADskDriver_Verbose >= 10_IntKi ) CALL WrScr('Updating driver settings with command line arguments') + + ELSE + + ! VVerbose error reporting + IF ( ADskDriver_Verbose >= 10_IntKi ) CALL WrScr('No driver input file used. Updating driver settings with command line arguments') + + ENDIF + + ! Since there were no settings picked up from the driver input file, we need to copy over all + ! the CLSettings into the regular Settings. The SettingsFlags%DvrIptFile is a flag indicating + ! if the driver input file read. + CALL UpdateSettingsWithCL( SettingsFlags, Settings, CLSettingsFlags, CLSettings, SettingsFlags%DvrIptFile, ErrStat, ErrMsg ) + call CheckErr('') + + ! Verbose error reporting + IF ( ADskDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr(NewLine//'--- Driver settings after copying over CL settings: ---') + CALL printSettings( SettingsFlags, Settings ) + CALL WrScr(NewLine) + ENDIF + + + + !------------------------------------------ + ! Logic for timestep and total time for sim. + !------------------------------------------ + if ( SettingsFlags%TStart ) then + TStart = Settings%TStart + else + TStart = 0.0_DbKi + ! TODO: if using the input file, could start at the initial time given there (set the TStart with a "default" input option) + endif + + + + TimeIntervalFound=.true. ! If specified or default value set + ! DT - timestep. If default was specified, then calculate default level. + if ( SettingsFlags%DTdefault ) then + ! Set a value to start with (something larger than any expected DT). + TimeIntervalFound=.false. + TimeInterval=1000.0_DbKi + ! Step through all lines to get smallest DT + do n=min(2,size(CaseTime)),size(CaseTime) ! Start at 2nd point (min to avoid stepping over end for single line files) + TimeInterval=min(TimeInterval, real(CaseTime(n)-CaseTime(n-1), DbKi)) + TimeIntervalFound=.true. + enddo + if (TimeIntervalFound) then + call WrScr('Using smallest DT from data file: '//trim(Num2LStr(TimeInterval))//' seconds.') + else + call WrScr('No time timesteps found in input displacement file. Using only one timestep.') + endif + endif + + + ! TMax and NumTSteps from input file or from the value specified (specified overrides) + if ( SettingsFlags%NumTimeStepsDefault ) then + TMax = CaseTime(size(CaseTime)) + NumTSteps = ceiling( TMax / TimeInterval ) + elseif ( SettingsFlags%NumTimeSteps ) then ! Override with number of timesteps + TMax = TimeInterval * Settings%NumTimeSteps + TStart + NumTSteps = Settings%NumTimeSteps + else + NumTSteps = 1_IntKi + TMax = TimeInterval * NumTSteps + endif + + + !------------------------------------------ + ! Save wind data and set pointer + !------------------------------------------ + + call StoreWindData(FlowField,ErrStat,ErrMsg) + call CheckErr('') + + + + ! Routines called in initialization + !............................................................................................................................... + + InitInData%InputFile = Settings%ADskIptFileName + InitInData%RootName = Settings%OutRootName + InitInData%defAirDens = Settings%AirDens + InitInData%RotorRad = Settings%RotorRad + InitInData%HubPosition = (/ 0.0_ReKi, 0.0_ReKi, Settings%RotorHeight /) + ! Set to include the shafttilt, but no other settings. This is an euler angle order + Theta = (/ 0.0_R8Ki, real(Settings%ShftTilt, R8Ki), 0.0_R8Ki /) + InitInData%HubOrientation = EulerConstruct( Theta ) + InitInData%FlowField => FlowField ! pointer to wind data + + + ! Initialize the module + CALL ADsk_Init( InitInData, u(1), p, x, xd, z, OtherState, y, misc, TimeInterval, InitOutData, ErrStat, ErrMsg ) + IF ( ErrStat /= ErrID_None ) THEN ! Check if there was an error and do something about it if necessary + call CheckErr('After Init: ') + END IF + + ! Set the output file + call GetRoot(Settings%OutRootName,OutputFileRootName) + call Dvr_InitializeOutputFile(DvrOut, InitOutData, OutputFileRootName, ErrStat, ErrMsg) + call CheckErr('Setting output file'); + + ! Destroy initialization data + CALL ADsk_DestroyInitInput( InitInData, ErrStat, ErrMsg ) + CALL ADsk_DestroyInitOutput( InitOutData, ErrStat, ErrMsg ) + + + + ! Routines called in loose coupling -- the glue code may implement this in various ways + !............................................................................................................................... + + + TmpIdx = 0_IntKi + + DO n = 0,NumTSteps + Time = n*TimeInterval+TStart + InputTime(1) = Time + + ! interpolate into the input data to get the wind info. Set this as u then run { InterpStpReal( X, Xary, Yary, indx, size) } + ! NOTE: converting CaseTime into ReKi is going to slow things down, but this is a demo driver, not a production tool, so I'm not + ! going to spend time fixing it. + + ! RotSpeed + u(1)%RotSpeed = InterpStp( real(Time,ReKi), real(CaseTime(:),ReKi), CaseData(4,:), TmpIdx, size(CaseTime) ) + ! Pitch + u(1)%BlPitch = InterpStp( real(Time,ReKi), real(CaseTime(:),ReKi), CaseData(5,:), TmpIdx, size(CaseTime) ) + ! Yaw + Yaw = InterpStp( real(Time,ReKi), real(CaseTime(:),ReKi), CaseData(6,:), TmpIdx, size(CaseTime) ) + + ! Now set the turbine orientation info -- note we don't include azimuth (code doesn't use it) + Theta = (/ 0.0_R8Ki, 0.0_R8Ki, real(Yaw,R8Ki) /) + orientation_loc = EulerConstruct(Theta) + u(1)%HubMotion%Orientation(:,:,1) = matmul(orientation_loc, u(1)%HubMotion%RefOrientation(:,:,1)) + ! No motions (rotation vel isn't used in code) + u(1)%HubMotion%TranslationDisp = 0.0_R8Ki + u(1)%HubMotion%TranslationVel = 0.0_ReKi + u(1)%HubMotion%RotationVel = 0.0_ReKi + + ! Calculate outputs at n + CALL ADsk_CalcOutput( Time, u(1), p, x, xd, z, OtherState, y, misc, ErrStat, ErrMsg ); + call CheckErr('After CalcOutput: '); + + ! There are no states to update in AeroDisk, but for completeness we add this. + ! Get state variables at next step: INPUT at step n, OUTPUT at step n + 1 + CALL ADsk_UpdateStates( Time, n, u, InputTime, p, x, xd, z, OtherState, misc, ErrStat, ErrMsg ); + call CheckErr(''); + + !call Dvr_WriteOutputLine(Time,DvrOut,p%OutFmt,y) + call Dvr_WriteOutputLine(Time,DvrOut,"ES20.12E2",y) + END DO + + + + + !............................................................................................................................... + ! Routine to terminate program execution + !............................................................................................................................... + if (DvrOut>0) close(DvrOut) + CALL ADsk_End( u(1), p, x, xd, z, OtherState, y, misc, ErrStat, ErrMsg ) + + ! Cleanup data + call Cleanup() + + IF ( ErrStat /= ErrID_None ) THEN + CALL WrScr( 'ErrorsAfter End: '//ErrMsg ) + ELSE + call WrSCr( 'AeroDisk completed' ) + END IF + + + +CONTAINS + subroutine CheckErr(Text) + character(*), intent(in) :: Text + IF ( ErrStat /= ErrID_None ) THEN ! Check if there was an error and do something about it if necessary + CALL WrScr( Text//ErrMsg ) + if ( ErrStat >= AbortErrLev ) then + call Cleanup() + call ProgEnd() + endif + END IF + end subroutine CheckErr + subroutine Cleanup() + call IfW_FlowField_DestroyFlowFieldType(FlowField,ErrStat,ErrMsg) ! ignore messages from here + end subroutine + subroutine ProgEnd() + ! Placeholder for moment + Call ProgAbort('Fatal error encountered. Ending.') + end subroutine ProgEnd + subroutine StoreWindData(FlowField,ErrStat2,ErrMsg2) + type(FlowFieldType), target, intent( out) :: FlowField + integer(IntKi), intent( out) :: ErrStat2 + character(ErrMsgLen), intent( out) :: ErrMsg2 + integer(IntKi) :: i, NumTSteps + + NumTSteps = size(CaseTime) + + ! Setup flow field + FlowField%FieldType = Uniform_FieldType + FlowField%Uniform%DataSize = NumTSteps + ! The following either fail catastrophically, or pass just fine. So no need for complex error handling. + call AllocAry(FlowField%Uniform%Time, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%VelH, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%VelV, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%VelGust, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%AngleH, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%AngleV, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%ShrH, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%ShrV, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + call AllocAry(FlowField%Uniform%LinShrV, NumTSteps, '', ErrStat2, ErrMsg2); if (ErrStat2 >= AbortErrLev) return + + ! WindSpeed data + do i=1,NumTSteps + FlowField%Uniform%Time(i) = CaseTime(i) + FlowField%Uniform%VelH(i) = sqrt( CaseData(1,i)*CaseData(1,i) + CaseData(2,i)*CaseData(2,i) ) + FlowField%Uniform%AngleH(i) = atan2(CaseData(2,i),CaseData(1,i)) ! angle in horizontal plane + FlowField%Uniform%VelV(i) = CaseData(3,i) + enddo + + ! Set a few constants so calculations work + FlowField%Uniform%RefLength= 1.0_ReKi ! set this or we get a divide by zero + FlowField%Uniform%RefHeight= 1.0_ReKi ! set this or we get a divide by zero + ! Set other stuff to zero + FlowField%Uniform%ShrV = 0.0_ReKi ! no shear profile + FlowField%Uniform%VelGust = 0.0_ReKi + FlowField%Uniform%AngleV = 0.0_ReKi + FlowField%Uniform%ShrH = 0.0_ReKi + FlowField%Uniform%LinShrV = 0.0_ReKi + ! The following defaults are already set, but setting here in case somnething is later changed + FlowField%RefPosition = 0.0_ReKi + FlowField%PropagationDir = 0.0_ReKi + FlowField%VFlowAngle = 0.0_ReKi + FlowField%VelInterpCubic = .false. + FlowField%RotateWindBox = .false. + FlowField%AccFieldValid = .false. + FlowField%RotToWind = 0.0_ReKi + FlowField%RotFromWind = 0.0_ReKi + end subroutine StoreWindData +END PROGRAM AeroDisk_Driver diff --git a/modules/aerodisk/src/driver/AeroDisk_Driver_Subs.f90 b/modules/aerodisk/src/driver/AeroDisk_Driver_Subs.f90 new file mode 100644 index 0000000000..16b1ebca91 --- /dev/null +++ b/modules/aerodisk/src/driver/AeroDisk_Driver_Subs.f90 @@ -0,0 +1,797 @@ +!********************************************************************************************************************************** +! +! MODULE: AeroDisk_Driver_Subs - This module contains subroutines used by the AeroDisk Driver program +! +!********************************************************************************************************************************** +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2024 National Renewable Energy Laboratory +! +! This file is part of AeroDisk. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +!********************************************************************************************************************************** +MODULE AeroDisk_Driver_Subs + + USE NWTC_Library + USE AeroDisk_Driver_Types + IMPLICIT NONE + +! NOTE: This is loosely based on the InflowWind driver code. + +CONTAINS +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!> Print out help information +SUBROUTINE DispHelpText() + ! Statement about usage + CALL WrScr("") + CALL WrScr(" Syntax: AeroDisk_Driver [options]") + CALL WrScr("") + CALL WrScr(" where: -- Name of driver input file to use") + CALL WrScr(" options: "//SWChar//"adsk -- treat as name of AeroDisk input file") + CALL WrScr(" (no driver input file)") + CALL WrScr("") + CALL WrScr(" The following options will overwrite values in the driver input file:") + CALL WrScr(" "//SwChar//"DT[#] -- timestep ") + CALL WrScr(" "//SwChar//"TStart[#] -- start time ") + CALL WrScr(" "//SwChar//"TSteps[#] -- number of timesteps ") + CALL WrScr(" "//SwChar//"v -- verbose output ") + CALL WrScr(" "//SwChar//"vv -- very verbose output ") + CALL WrScr(" "//SwChar//"NonLinear -- only return non-linear portion of reaction force") + CALL WrScr(" "//SwChar//"help -- print this help menu and exit") + CALL WrScr("") + CALL WrScr(" Notes:") + CALL WrScr(" -- Options are not case sensitive.") + CALL WrScr("") +!FIXME: update this +END SUBROUTINE DispHelpText + + +subroutine InitSettingsFlags( ProgInfo, CLSettings, CLFlags ) + implicit none + ! Storing the arguments + type( ProgDesc ), intent(in ) :: ProgInfo + type( ADskDriver_Settings ), intent( out) :: CLSettings !< Command line arguments passed in + type( ADskDriver_Flags ), intent( out) :: CLFlags !< Flags indicating which command line arguments were specified + + ! Set some CLSettings to null/default values + CLSettings%DvrIptFileName = "" ! No input name name until set + CLSettings%ADskIptFileName = "" ! No ADsk input file name until set + CLSettings%NumTimeSteps = 0_IntKi + CLSettings%DT = 0.0_DbKi + CLSettings%TStart = 0.0_ReKi + CLSettings%ProgInfo = ProgInfo ! Driver info + + ! Set some CLFlags to null/default values + CLFlags%DvrIptFile = .FALSE. ! Driver input filename given as command line argument + CLFlags%ADskIptFile = .FALSE. ! AeroDisk input filename given as command line argument + CLFlags%TStart = .FALSE. ! specified time to start at + CLFlags%NumTimeSteps = .FALSE. ! specified a number of timesteps + CLFlags%NumTimeStepsDefault = .FALSE. ! specified 'DEFAULT' for number of timesteps + CLFlags%DT = .FALSE. ! specified a resolution in time + CLFlags%DTDefault = .FALSE. ! specified 'DEFAULT' for resolution in time + CLFlags%Verbose = .FALSE. ! Turn on verbose error reporting? + CLFlags%VVerbose = .FALSE. ! Turn on very verbose error reporting? + +end subroutine InitSettingsFlags + +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!> This subroutine retrieves the command line arguments and passes them to the +!! AeroDisk_driver_subs::parsearg routine for processing. +SUBROUTINE RetrieveArgs( CLSettings, CLFlags, ErrStat, ErrMsg ) + ! Storing the arguments + type( ADskDriver_Flags ), intent( out) :: CLFlags !< Flags indicating which command line arguments were specified + type( ADskDriver_Settings ), intent( out) :: CLSettings !< Command line arguments passed in + integer(IntKi), intent( out) :: ErrStat + CHARACTER(*), intent( out) :: ErrMsg + + ! Local variable + integer(IntKi) :: i !< Generic counter + character(1024) :: Arg !< argument given + character(1024) :: ArgUC !< Upper case argument to check + integer(IntKi) :: NumInputArgs !< Number of argements passed in from command line + logical :: adskFlag !< The -adsk flag was set + character(1024) :: FileName !< Filename from the command line. + logical :: FileNameGiven !< Flag indicating if a filename was given. + integer(IntKi) :: ErrStatTmp !< Temporary error status (for calls) + character(1024) :: ErrMsgTmp !< Temporary error message (for calls) + + ! initialize some things + CLFlags%DvrIptFile = .FALSE. + ErrStat = ErrID_None + ErrStatTmp = ErrID_None + ErrMsg = '' + ErrMsgTmp = '' + adskFlag = .FALSE. + FileNameGiven = .FALSE. + FileName = '' + + ! Check how many arguments are passed in + NumInputArgs = COMMAND_ARGUMENT_COUNT() + + ! exit if we don't have enough + IF (NumInputArgs == 0) THEN + CALL SetErrStat(ErrID_Fatal," Insufficient Arguments. Use option "//SwChar//"help for help menu.", & + ErrStat,ErrMsg,'RetrieveArgs') + RETURN + ENDIF + + + ! Loop through all the arguments, and store them + DO i=1,NumInputArgs + ! get the ith argument + CALL get_command_argument(i, Arg) + ArgUC = Arg + + ! convert to uppercase + CALL Conv2UC( ArgUC ) + + ! Check to see if it is a control parameter or the filename + IF ( INDEX( SwChar, ArgUC(1:1) ) > 0 ) THEN + + ! check to see if we asked for help + IF ( ArgUC(2:5) == "HELP" ) THEN + CALL DispHelpText() + CALL ProgExit(0) + ENDIF + + + ! Check the argument and put it where it belongs + ! chop the SwChar off before passing the argument + CALL ParseArg( CLSettings, CLFlags, ArgUC(2:), Arg(2:), adskFlag, ErrStatTmp, ErrMsgTmp ) + CALL SetErrStat(ErrStatTmp,ErrMsgTmp,ErrStat,ErrMsg,'RetrieveArgs') + IF (ErrStat>AbortErrLev) RETURN + + ELSE + + ! since there is no switch character, assume it is the filename, unless we already set one + IF ( FileNameGiven ) THEN + CALL SetErrStat(ErrID_Fatal," Multiple driver input filenames given: "//TRIM(FileName)//", "//TRIM(Arg), & + ErrStat,ErrMsg,'RetrieveArgs') + RETURN + ELSE + FileName = TRIM(Arg) + FileNameGiven = .TRUE. + ENDIF + + ENDIF + END DO + + + ! Was a filename given? + IF ( .NOT. FileNameGiven ) THEN + CALL SetErrStat( ErrID_Fatal, " No filename given.", ErrStat, ErrMsg, 'RetrieveArgs' ) + RETURN + ENDIF + + ! Was the -adsk flag set? If so, the filename is the AeroDisk input file. Otherwise + ! it is the driver input file. + IF ( adskFlag ) THEN + CLSettings%ADskIptFileName = TRIM(FileName) + CLFlags%ADskIptFile = .TRUE. + call GetRoot( CLSettings%ADskIptFileName, CLSettings%OutRootName ) + CLFlags%OutRootName = .TRUE. + ELSE + CLSettings%DvrIptFileName = TRIM(FileName) + CLFlags%DvrIptFile = .TRUE. + ENDIF + + + + !------------------------------------------------------------------------------- + !------------------------------------------------------------------------------- + CONTAINS + + + !------------------------------------------------------------------------------- + !> Convert a string to a real number + FUNCTION StringToReal( StringIn, ErrStat ) + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT(IN ) :: StringIn + + REAL(ReKi) :: StringToReal + INTEGER(IntKi) :: ErrStatTmp ! Temporary variable to hold the error status + + read( StringIn, *, iostat=ErrStatTmp) StringToReal + + ! If that isn't a number, only warn since we can continue by skipping this value + IF ( ErrStatTmp .ne. 0 ) ErrStat = ErrID_Warn + + END FUNCTION StringToReal + + + + !------------------------------------------------------------------------------- + SUBROUTINE ParseArg( CLSettings, CLFlags, ThisArgUC, ThisArg, adskFlagSet, ErrStat, ErrMsg ) + !-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-! + ! Parse and store the input argument ! + !-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-! + + USE NWTC_Library + USE AeroDisk_Driver_Types + USE AeroDisk_Types + + IMPLICIT NONE + + ! Storing the arguments + TYPE( ADskDriver_Flags ), INTENT(INOUT) :: CLFlags ! Flags indicating which arguments were specified + TYPE( ADskDriver_Settings ), INTENT(INOUT) :: CLSettings ! Arguments passed in + + CHARACTER(*), INTENT(IN ) :: ThisArgUC ! The current argument (upper case for testing) + CHARACTER(*), INTENT(IN ) :: ThisArg ! The current argument (as passed in for error messages) + LOGICAL, INTENT(INOUT) :: adskFlagSet ! Was the -adsk flag given? + + ! Error Handling + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + + + ! local variables + INTEGER(IntKi) :: Delim1 ! where the [ is + INTEGER(IntKi) :: Delim2 ! where the ] is + INTEGER(IntKi) :: DelimSep ! where the : is + REAL(ReKi) :: TempReal ! temp variable to hold a real + + INTEGER(IntKi) :: ErrStatTmp ! Temporary error status for calls + + + + ! Initialize some things + ErrStat = ErrID_None + ErrStatTmp = ErrID_None + ErrMsg = '' + + ! Get the delimiters -- returns 0 if there isn't one + Delim1 = INDEX(ThisArgUC,'[') + Delim2 = INDEX(ThisArgUC,']') + DelimSep = INDEX(ThisArgUC,':') + + + ! check that if there is an opening bracket, then there is a closing one + IF ( (Delim1 > 0_IntKi ) .and. (Delim2 < Delim1) ) THEN + CALL SetErrStat(ErrID_Warn," Syntax error in option: '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArg') + RETURN + ENDIF + + ! check that if there is a colon, then there are brackets + IF ( (DelimSep > 0_IntKi) .and. (Delim1 == 0_IntKi) ) THEN + CALL SetErrStat(ErrID_Warn," Syntax error in option: '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArg') + RETURN + ENDIF + + + ! If no delimeters were given, than this option is simply a flag + IF ( Delim1 == 0_IntKi ) THEN + ! check to see if the filename is the name of the ADsk input file + IF ( ThisArgUC(1:4) == "ADSK" ) THEN + adskFlagSet = .TRUE. ! More logic in the routine that calls this one to set things. + RETURN + ELSEIF ( ThisArgUC(1:2) == "VV" ) THEN + CLFlags%VVerbose = .TRUE. + RETURN + ELSEIF ( ThisArgUC(1:1) == "V" ) THEN + CLFlags%Verbose = .TRUE. + RETURN + ELSE + CALL SetErrStat( ErrID_Warn," Unrecognized option '"//SwChar//TRIM(ThisArg)//"'. Ignoring. Use option "//SwChar//"help for list of options.", & + ErrStat,ErrMsg,'ParseArg') + ENDIF + + ENDIF + + + ! "DT[#]" + IF( ThisArgUC(1:Delim1) == "DT[" ) THEN + TempReal = StringToReal( ThisArgUC(Delim1+1:Delim2-1), ErrStat ) + IF ( ErrStat == ErrID_None ) THEN + CLFlags%Dt = .TRUE. + CLSettings%DT = abs(TempReal) + ELSE + CLFlags%Dt = .FALSE. + IF ( ErrStat == ErrID_Warn ) THEN + CALL SetErrStat(ErrStatTmp," Invalid number in option '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArgs') + ELSE + CALL SetErrStat( ErrID_Fatal," Something failed in parsing option '"//SwChar//TRIM(ThisArg)//"'.", & + ErrStat, ErrMsg, 'ParseArg') + ENDIF + RETURN + ENDIF + + + ! "TSTEPS[#]" + ELSEIF( ThisArgUC(1:Delim1) == "TSTEPS[" ) THEN + TempReal = StringToReal( ThisArgUC(Delim1+1:Delim2-1), ErrStat ) + IF ( ErrStat == ErrID_None ) THEN + CLFlags%NumTimeSteps = .TRUE. + CLSettings%NumTimeSteps = nint(abs(TempReal)) + ELSE + CLFlags%NumTimeSteps = .FALSE. + CLSettings%NumTimeSteps = 1_IntKi + IF ( ErrStat == ErrID_Warn ) THEN + CALL SetErrStat(ErrStatTmp," Invalid number in option '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArgs') + ELSE + CALL SetErrStat( ErrID_Fatal," Something failed in parsing option '"//SwChar//TRIM(ThisArg)//"'.", & + ErrStat, ErrMsg, 'ParseArg') + ENDIF + RETURN + ENDIF + + + + ! "TSTART[#]" + ELSEIF( ThisArgUC(1:Delim1) == "TSTART[" ) THEN + TempReal = StringToReal( ThisArgUC(Delim1+1:Delim2-1), ErrStat ) + IF ( ErrStat == ErrID_None ) THEN + CLFlags%TStart = .TRUE. + CLSettings%TStart = abs(TempReal) + ELSE + CLFlags%TStart = .FALSE. + IF ( ErrStat == ErrID_Warn ) THEN + CALL SetErrStat(ErrStatTmp," Invalid number in option '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArgs') + ELSE + CALL SetErrStat( ErrID_Fatal," Something failed in parsing option '"//SwChar//TRIM(ThisArg)//"'.", & + ErrStat, ErrMsg, 'ParseArg') + ENDIF + RETURN + ENDIF +!FIXME: add in the other inputs here. + + ELSE + ErrMsg = " Unrecognized option: '"//SwChar//TRIM(ThisArg)//"'. Ignoring. Use option "//SwChar//"help for list of options." + ErrStat = ErrID_Warn + ENDIF + + END SUBROUTINE ParseArg + !------------------------------------------------------------------------------- + +END SUBROUTINE RetrieveArgs + + +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!> This subroutine reads the driver input file and sets up the flags and settings +!! for the driver code. Any settings from the command line options will override +!! this. +SUBROUTINE ParseDvrIptFile( DvrFileName, DvrFileInfo, DvrFlags, DvrSettings, ProgInfo, CaseTime, CaseData, ErrStat, ErrMsg ) + + CHARACTER(1024), INTENT(IN ) :: DvrFileName + type(FileInfoType), INTENT(IN ) :: DvrFileInfo ! Input file stored in FileInfoType structure + TYPE(ADskDriver_Flags), INTENT(INOUT) :: DvrFlags + TYPE(ADskDriver_Settings), INTENT(INOUT) :: DvrSettings + TYPE(ProgDesc), INTENT(IN ) :: ProgInfo + real(DbKi), allocatable, intent( out) :: CaseTime(:) + real(ReKi), allocatable, intent( out) :: CaseData(:,:) + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! returns a non-zero value when an error occurs + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + + ! Local variables + INTEGER(IntKi) :: CurLine ! Current line in parsing + INTEGER(IntKi) :: TabLines ! Number of lines in the table + integer(IntKi) :: i !< generic loop counter + real(DbKi) :: TmpDb7(7) !< temporary real array + CHARACTER(1024) :: RootName ! Root name of AeroDisk driver input file + + ! Input file echoing + LOGICAL :: EchoFileContents ! Do we echo the driver file out or not? + INTEGER(IntKi) :: UnEc ! The local unit number for this module's echo file + CHARACTER(1024) :: EchoFileName ! Name of AeroDisk driver echo file + + ! Time steps + CHARACTER(1024) :: InputChr ! Character string for timesteps and input file names (to handle DEFAULT or NONE value) + + ! Local error handling + INTEGER(IntKi) :: ios !< I/O status + INTEGER(IntKi) :: ErrStatTmp !< Temporary error status for calls + CHARACTER(1024) :: ErrMsgTmp !< Temporary error messages for calls + + + ! Initialize the echo file unit to -1 which is the default to prevent echoing, we will alter this based on user input + UnEc = -1 + + call GetRoot( DvrFileName, RootName ) + + !====== General ==================================================================================== + CurLine = 4 ! Skip the first three lines as they are known to be header lines and separators + call ParseVar( DvrFileInfo, CurLine, 'Echo', EchoFileContents, ErrStatTmp, ErrMsgTmp ) + if (Failed()) return; + + if ( EchoFileContents ) then + CALL OpenEcho ( UnEc, TRIM(RootName)//'.ech', ErrStatTmp, ErrMsgTmp ) + if (Failed()) return; + WRITE(UnEc, '(A)') 'Echo file for AeroDisk driver input file: '//trim(DvrFileName) + ! Write the first three lines into the echo file + WRITE(UnEc, '(A)') DvrFileInfo%Lines(1) + WRITE(UnEc, '(A)') DvrFileInfo%Lines(2) + WRITE(UnEc, '(A)') DvrFileInfo%Lines(3) + + CurLine = 4 + call ParseVar( DvrFileInfo, CurLine, 'Echo', EchoFileContents, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + endif + + !====== Primary file and rootname ================================================================== + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! AeroDisk input file + call ParseVar( DvrFileInfo, CurLine, "ADskIptFile", DvrSettings%ADskIptFileName, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%ADskIptFile = .TRUE. + + ! AeroDisk output root name + call ParseVar( DvrFileInfo, CurLine, "OutRootName", DvrSettings%OutRootName, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%OutRootName = .TRUE. + + + !====== Geometry and Environment ==================================================================== + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! AirDens + call ParseVar( DvrFileInfo, CurLine, "AirDens", DvrSettings%AirDens, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%AirDens = .TRUE. + + ! RotorRad + call ParseVar( DvrFileInfo, CurLine, "RotorRad", DvrSettings%RotorRad, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%RotorRad = .TRUE. + + ! RotorHeight + call ParseVar( DvrFileInfo, CurLine, "RotorHeight", DvrSettings%RotorHeight, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%RotorHeight = .TRUE. + + ! ShftTilt + call ParseVar( DvrFileInfo, CurLine, "ShftTilt", DvrSettings%ShftTilt, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%ShftTilt = .TRUE. + + + !====== Case analysis ========== ==================================================================== + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! TStart -- start time + call ParseVar( DvrFileInfo, CurLine, "TStart", DvrSettings%TStart, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%TStart = .TRUE. + + + ! DT -- Timestep size for the driver to take (or DEFAULT for what the file contains) + call ParseVar( DvrFileInfo, CurLine, "DT", InputChr, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + + ! Check if we asked for the DEFAULT (use what is in the file) + CALL Conv2UC( InputChr ) + IF ( TRIM(InputChr) == 'DEFAULT' ) THEN ! we asked for the default value + DvrFlags%DT = .TRUE. + DvrFlags%DTDefault = .TRUE. ! This flag tells us to use the inflow wind file values + ELSE + ! We probably have a number if it isn't 'DEFAULT', so do an internal read and check to + ! make sure that it was appropriately interpretted. + READ (InputChr,*,IOSTAT=IOS) DvrSettings%DT + IF ( IOS /= 0 ) THEN ! problem in the read, so parse the error. + CALL CheckIOS ( IOS, '', 'DT',NumType, ErrStatTmp, ErrMsgTmp ) + if (Failed()) return + ELSE ! Was ok, so set the flags + DvrFlags%DT = .TRUE. + DvrFlags%DTDefault = .FALSE. + ENDIF + ENDIF + + + ! Number of timesteps + call ParseVar( DvrFileInfo, CurLine, "NumTimeSteps", InputChr, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + + ! Check if we asked for the DEFAULT (use what is in the file) + CALL Conv2UC( InputChr ) + IF ( TRIM(InputChr) == 'DEFAULT' ) THEN ! we asked for the default value + DvrFlags%NumTimeSteps = .FALSE. + DvrFlags%NumTimeStepsDefault = .TRUE. ! This flag tells us to use the inflow wind file values + ELSE + ! We probably have a number if it isn't 'DEFAULT', so do an internal read and check to + ! make sure that it was appropriately interpretted. + READ (InputChr,*,IOSTAT=IOS) DvrSettings%NumTimeSteps + IF ( IOS /= 0 ) THEN ! problem in the read, so parse the error. + CALL CheckIOS ( IOS, '', 'NumTimeSteps',NumType, ErrStatTmp, ErrMsgTmp ) + if (Failed()) return + ELSE ! Was ok, so set the flags + DvrFlags%NumTimeSteps = .TRUE. + DvrFlags%NumTimeStepsDefault = .FALSE. + ENDIF + ENDIF + + + ! Column headers + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) + CurLine = CurLine + 1 + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) + CurLine = CurLine + 1 + + + ! Last line of table is assumed to be last line in file (or included file) + TabLines = DvrFileInfo%NumLines - CurLine + 1 + call AllocAry( CaseTime, TabLines, 'CaseTime', ErrStatTmp, ErrMsgTmp ); if (Failed()) return; + call AllocAry( CaseData, 6, TabLines, 'CaseData', ErrStatTmp, ErrMsgTmp ); if (Failed()) return; + do i=1,Tablines + call ParseAry ( DvrFileInfo, CurLine, 'Coordinates', TmpDb7, 7, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return; + ! Set Time_(s) + CaseTime(i) = TmpDb7(1) + ! Set time, wind_x, wind_y, wind_z + CaseData(1:3,i) = real(TmpDb7(2:4),ReKi) + ! Set RotSpeed (rpm -> rad/s) + CaseData(4,i) = real(TmpDb7(5),ReKi) * Pi / 30.0_ReKi + ! Set Pitch (deg -> rad) + CaseData(5,i) = real(TmpDb7(6),ReKi) * D2R + ! Set Yaw (deg -> rad) + CaseData(6,i) = real(TmpDb7(7),ReKi) * D2R + enddo + + + ! Close the echo and input file + CALL CleanupEchoFile( EchoFileContents, UnEc ) + + +CONTAINS + + !> Set error status, close stuff, and return + logical function Failed() + CALL SetErrStat(ErrStatTmp,ErrMsgTmp,ErrStat,ErrMsg,'ParseDvrIptFile') + if (ErrStat >= AbortErrLev) then + CALL CleanupEchoFile( EchoFileContents, UnEc ) + endif + Failed = ErrStat >= AbortErrLev + end function Failed + + !> Clean up the module echo file + subroutine CleanupEchoFile( EchoFlag, UnEcho) + logical, intent(in ) :: EchoFlag ! local version of echo flag + integer(IntKi), intent(in ) :: UnEcho ! echo unit number + + ! Close this module's echo file + if ( EchoFlag ) then + close(UnEcho) + endif + END SUBROUTINE CleanupEchoFile + +END SUBROUTINE ParseDvrIptFile + + +!> This subroutine copies an command line (CL) settings over to the program settings. Warnings are +!! issued if anything is changed from what the driver input file requested. +SUBROUTINE UpdateSettingsWithCL( DvrFlags, DvrSettings, CLFlags, CLSettings, DVRIPT, ErrStat, ErrMsg ) + + TYPE(ADskDriver_Flags), INTENT(INOUT) :: DvrFlags + TYPE(ADskDriver_Settings), INTENT(INOUT) :: DvrSettings + TYPE(ADskDriver_Flags), INTENT(IN ) :: CLFlags + TYPE(ADskDriver_Settings), INTENT(IN ) :: CLSettings + LOGICAL, INTENT(IN ) :: DVRIPT + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + + + ! Local variables + INTEGER(IntKi) :: ErrStatTmp !< Temporary error status for calls + CHARACTER(1024) :: ErrMsgTmp !< Temporary error status for calls + LOGICAL :: WindGridModify !< Did we modify any of the WindGrid related settings? + character(*), parameter :: RoutineName = 'UpdateSettingsWithCL' + + ! Initialization + WindGridModify = .FALSE. + + ! Initialize the error handling + ErrStat = ErrID_None + ErrMsg = '' + ErrStatTmp = ErrID_None + ErrMsgTmp = '' + + + !-------------------------------------------- + ! Did we change any time information? + !-------------------------------------------- + + ! Check TStart + IF ( CLFlags%TStart ) THEN + IF ( DvrFlags%TStart .AND. ( .NOT. EqualRealNos(DvrSettings%TStart, CLSettings%TStart) ) ) THEN + CALL SetErrStat( ErrID_Warn, ' Overriding driver input value for TStart with '//TRIM(Num2LStr(CLSettings%TStart))//'.', & + ErrStat,ErrMsg,RoutineName) + ELSE + DvrFlags%TStart = .TRUE. + ENDIF + DvrSettings%TStart = CLSettings%TStart + ENDIF + + ! Check DT + IF ( CLFlags%DT ) THEN + IF ( DvrFlags%DT .AND. ( .NOT. EqualRealNos(DvrSettings%DT, CLSettings%DT) ) ) THEN + CALL SetErrStat( ErrID_Warn, ' Overriding driver input value for DT with '//TRIM(Num2LStr(CLSettings%DT))//'.', & + ErrStat,ErrMsg,RoutineName) + ELSE + DvrFlags%DT = .TRUE. + ENDIF + DvrSettings%DT = CLSettings%DT + DvrFlags%DTDefault = .FALSE. + ENDIF + + ! Check NumTimeSteps + IF ( CLFlags%NumTimeSteps ) THEN + IF ( DvrFlags%NumTimeSteps .AND. ( DvrSettings%NumTimeSteps /= CLSettings%NumTimeSteps ) ) THEN + CALL SetErrStat( ErrID_Warn, ' Overriding driver input value for NumTimeSteps with '// & + TRIM(Num2LStr(CLSettings%NumTimeSteps))//'.',& + ErrStat,ErrMsg,RoutineName) + ELSE + DvrFlags%NumTimeSteps = .TRUE. + ENDIF + DvrSettings%NumTimeSteps = CLSettings%NumTimeSteps + DvrFlags%NumTimeStepsDefault = .FALSE. + ENDIF + + ! Make sure there is at least one timestep + DvrSettings%NumTimeSteps = MAX(DvrSettings%NumTimeSteps,1_IntKi) + + + !-------------------------------------------- + ! If there was no driver input file, we need to set a few things. + !-------------------------------------------- + + IF ( .NOT. DVRIPT ) THEN + + ! Do we need to set the NumTimeStepsDefault flag? + IF ( .NOT. DvrFlags%NumTimeSteps ) THEN + DvrFlags%NumTimeStepsDefault = .TRUE. + CALL SetErrStat( ErrID_Info,' The number of timesteps is not specified. Defaulting to what is in the input series file.', & + ErrStat,ErrMsg,RoutineName) + ENDIF + ENDIF + + +!FIXME: remove this after parsing rest of input file. + ! If no DT value has been set (DEFAULT requested), we need to set a default to pass into ADsk + IF ( .NOT. DvrFlags%DT ) THEN + DvrSettings%DT = 0.025_DbKi ! This value gets passed into the ADsk_Init routine, so something must be set. + ENDIF + + +END SUBROUTINE UpdateSettingsWithCL + + + +!> This routine exists only to support the development of the module. It will not be needed after the module is complete. +SUBROUTINE printSettings( DvrFlags, DvrSettings ) + ! The arguments + TYPE( ADskDriver_Flags ), INTENT(IN ) :: DvrFlags !< Flags indicating which settings were set + TYPE( ADskDriver_Settings ), INTENT(IN ) :: DvrSettings !< Stored settings + + CALL WrsCr(TRIM(GetNVD(DvrSettings%ProgInfo))) + CALL WrScr(' DvrIptFile: '//FLAG(DvrFlags%DvrIptFile)// ' '//TRIM(DvrSettings%DvrIptFileName)) + CALL WrScr(' ADskIptFile: '//FLAG(DvrFlags%ADskIptFile)// ' '//TRIM(DvrSettings%ADskIptFileName)) + CALL WrScr(' TStart: '//FLAG(DvrFlags%TStart)// ' '//TRIM(Num2LStr(DvrSettings%TStart))) + IF ( DvrFlags%DTDefault) THEN + CALL WrScr(' DT: '//FLAG(DvrFlags%DT)// ' DEFAULT') + ELSE + CALL WrScr(' DT: '//FLAG(DvrFlags%DT)// ' '//TRIM(Num2LStr(DvrSettings%DT))) + ENDIF + IF ( DvrFlags%NumTimeStepsDefault) THEN + CALL WrScr(' NumTimeSteps: '//FLAG(DvrFlags%NumTimeSteps)// ' DEFAULT') + ELSE + CALL WrScr(' NumTimeSteps: '//FLAG(DvrFlags%NumTimeSteps)// ' '//TRIM(Num2LStr(DvrSettings%NumTimeSteps))) + ENDIF + CALL WrScr(' Verbose: '//FLAG(DvrFlags%Verbose)) + CALL WrScr(' VVerbose: '//FLAG(DvrFlags%VVerbose)) + RETURN +END SUBROUTINE printSettings + + +!> This routine exists only to support the development of the module. It will not be kept after the module is complete. +!! This routine takes a flag setting (LOGICAL) and exports either 'T' or '-' for T/F (respectively) +FUNCTION FLAG(flagval) + LOGICAL, INTENT(IN ) :: flagval !< Value of the flag + CHARACTER(1) :: FLAG !< character interpretation (for prettiness when printing) + IF ( flagval ) THEN + FLAG = 'T' + ELSE + FLAG = '-' + ENDIF + RETURN +END FUNCTION FLAG + + +SUBROUTINE Dvr_InitializeOutputFile(OutUnit,IntOutput,RootName,ErrStat,ErrMsg) + integer(IntKi), intent( out):: OutUnit + type(ADsk_InitOutputType), intent(in ):: IntOutput ! Output for initialization routine + integer(IntKi), intent( out):: ErrStat ! Error status of the operation + character(*), intent( out):: ErrMsg ! Error message if ErrStat /= ErrID_None + character(*), intent(in ):: RootName + integer(IntKi) :: i + integer(IntKi) :: numOuts + integer(IntKi) :: ErrStat2 ! Temporary Error status + character(ErrMsgLen) :: ErrMsg2 ! Temporary Error message + character(*), parameter :: RoutineName = 'Dvr_InitializeOutputFile' + + ErrStat = ErrID_none + ErrMsg = "" + + CALL GetNewUnit(OutUnit,ErrStat2,ErrMsg2) + CALL OpenFOutFile ( OutUnit, trim(RootName)//'.out', ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (ErrStat >= AbortErrLev) return + + write (OutUnit,'(/,A)') 'Predictions were generated on '//CurDate()//' at '//CurTime()//' using '//trim(GetNVD(IntOutput%Ver)) + write (OutUnit,'()' ) !print a blank line + + numOuts = size(IntOutput%WriteOutputHdr) + !...................................................... + ! Write the names of the output parameters on one line: + !...................................................... + + write (OutUnit,'()') + write (OutUnit,'()') + write (OutUnit,'()') + + call WrFileNR ( OutUnit, 'Time' ) + + do i=1,NumOuts + call WrFileNR ( OutUnit, tab//IntOutput%WriteOutputHdr(i) ) + end do ! i + + write (OutUnit,'()') + + !...................................................... + ! Write the units of the output parameters on one line: + !...................................................... + + call WrFileNR ( OutUnit, '(s)' ) + + do i=1,NumOuts + call WrFileNR ( Outunit, tab//trim(IntOutput%WriteOutputUnt(i)) ) + end do ! i + + write (OutUnit,'()') + + +END SUBROUTINE Dvr_InitializeOutputFile + +!---------------------------------------------------------------------------------------------------------------------------------- +SUBROUTINE Dvr_WriteOutputLine(t,OutUnit, OutFmt, Output) + real(DbKi) , intent(in ) :: t ! simulation time (s) + integer(IntKi) , intent(in ) :: OutUnit ! Status of error message + character(*) , intent(in ) :: OutFmt + type(ADsk_OutputType), intent(in ) :: Output + integer(IntKi) :: errStat ! Status of error message (we're going to ignore errors in writing to the file) + character(ErrMsgLen) :: errMsg ! Error message if ErrStat /= ErrID_None + character(200) :: frmt ! A string to hold a format specifier + character(15) :: tmpStr ! temporary string to print the time output as text + + frmt = '"'//tab//'"'//trim(OutFmt) ! format for array elements from individual modules + + ! time + write( tmpStr, '(F15.6)' ) t + call WrFileNR( OutUnit, tmpStr ) + call WrNumAryFileNR ( OutUnit, Output%WriteOutput, frmt, errStat, errMsg ) + + ! write a new line (advance to the next line) + write (OutUnit,'()') +end subroutine Dvr_WriteOutputLine + + +END MODULE AeroDisk_Driver_Subs diff --git a/modules/aerodisk/src/driver/AeroDisk_Driver_Types.f90 b/modules/aerodisk/src/driver/AeroDisk_Driver_Types.f90 new file mode 100644 index 0000000000..6378703b0f --- /dev/null +++ b/modules/aerodisk/src/driver/AeroDisk_Driver_Types.f90 @@ -0,0 +1,78 @@ +!********************************************************************************************************************************** +! +! MODULE: ADsk_Driver_Types - This module contains types used by the AeroDisk Driver program to store arguments passed in +! +! The types listed here are used within the AeroDisk Driver program to store the settings. These settings are read in as +! command line arguments, then stored within these types. +! +!********************************************************************************************************************************** +! +!.................................................................................................................................. +! LICENSING +! Copyright (C) 2024 National Renewable Energy Laboratory +! +! This file is part of AeroDisk. +! +! AeroDisk is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as +! published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +! of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License along with AeroDisk. +! If not, see . +! +!********************************************************************************************************************************** +module AeroDisk_Driver_Types + + use NWTC_Library + use AeroDisk_Types + + implicit none + + !> This contains flags to note if the settings were made. This same data structure is + !! used both during the driver input file and the command line options. + !! + !! NOTE: The WindFileType is only set if it is given as a command line option. Otherwise + !! it is handled internally by InflowWInd. + !! + !! NOTE: The wind direction is specified by the AeroDisk input file. + type :: ADskDriver_Flags + logical :: DvrIptFile = .FALSE. !< Was an input file name given on the command line? + logical :: ADskIptFile = .FALSE. !< Was an AeroDisk input file requested? + logical :: OutRootName = .FALSE. !< Was an AeroDisk output rootname + logical :: AirDens = .FALSE. !< Air density + logical :: RotorRad = .FALSE. !< rotor radius + logical :: RotorHeight = .FALSE. !< rotor height + logical :: ShftTilt = .FALSE. !< shaft tilt + logical :: TStart = .FALSE. !< specified a start time + logical :: NumTimeSteps = .FALSE. !< specified a number of timesteps to process + logical :: NumTimeStepsDefault = .FALSE. !< specified a 'DEFAULT' for number of timesteps to process + logical :: DT = .FALSE. !< specified a resolution in time + logical :: DTDefault = .FALSE. !< specified a 'DEFAULT' for the time resolution + logical :: Verbose = .FALSE. !< Verbose error reporting + logical :: VVerbose = .FALSE. !< Very Verbose error reporting + end type ADskDriver_Flags + + + ! This contains all the settings (possible passed in arguments). + type :: ADskDriver_Settings + character(1024) :: DvrIptFileName !< Driver input file name + character(1024) :: ADskIptFileName !< Filename of AeroDisk input file to read (if no driver input file) + character(1024) :: OutRootName !< Output root name + + real(ReKi) :: AirDens !< Air density (kg/m^3) + real(ReKi) :: RotorRad !< rotor radius (m) + real(ReKi) :: RotorHeight !< rotor height (m) + real(ReKi) :: ShftTilt !< shaft tilt (deg) + + real(DbKi) :: TStart !< Start time + integer(IntKi) :: NumTimeSteps !< Number of timesteps + real(DbKi) :: DT !< resolution of time + + type(ProgDesc) :: ProgInfo !< Program info + type(ProgDesc) :: ADskProgInfo !< Program info for AeroDisk + end type ADskDriver_Settings + + +end module AeroDisk_Driver_Types diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index 24495c485f..c4e7f79f84 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1401,8 +1401,6 @@ subroutine SetParameters( InitInp, InputFileData, RotData, p, p_AD, ErrStat, Err ErrStat = ErrID_None ErrMsg = "" - ! NOTE: p_AD%FlowField is set in the glue code (or ADI module); seems like FlowField should be an initialization input so that would be clearer for new developers... - p_AD%UA_Flag = InputFileData%UA_Init%UAMod > UA_None p_AD%CompAeroMaps = InitInp%CompAeroMaps diff --git a/modules/elastodyn/src/ElastoDyn.f90 b/modules/elastodyn/src/ElastoDyn.f90 index 8bdce2741b..3ada99d44f 100644 --- a/modules/elastodyn/src/ElastoDyn.f90 +++ b/modules/elastodyn/src/ElastoDyn.f90 @@ -120,6 +120,7 @@ SUBROUTINE ED_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut ! Read the input file and validate the data !............................................................................................ p%BD4Blades = .NOT. InitInp%CompElast ! if we're not using ElastoDyn for the blades, use BeamDyn + p%RigidAero = InitInp%RigidAero ! If AeroDisk is used, set blades to all be rigid p%RootName = InitInp%RootName ! FAST already adds '.ED' to the root name p%CompAeroMaps = InitInp%CompAeroMaps @@ -129,13 +130,15 @@ SUBROUTINE ED_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut CALL CheckError( ErrStat2, ErrMsg2 ) IF ( ErrStat >= AbortErrLev ) RETURN - IF ( p%BD4Blades ) THEN - - ! Set DOFs to FALSE for whatever values you don't want on for BeamDyn + + IF ( p%BD4Blades .or. p%RigidAero ) THEN + ! Set DOFs to make rotor rigid InputFileData%FlapDOF1 = .FALSE. InputFileData%FlapDOF2 = .FALSE. InputFileData%EdgeDOF = .FALSE. - + ENDIF + + IF ( p%BD4Blades ) THEN ! Set other values not used for BeamDyn InputFileData%OoPDefl = 0.0_ReKi InputFileData%IPDefl = 0.0_ReKi @@ -144,7 +147,6 @@ SUBROUTINE ED_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut InputFileData%NBlGages = 0 InputFileData%BldGagNd = 0 InputFileData%BldNodes = 0 - END IF IF (p%CompAeroMaps) THEN @@ -4204,7 +4206,7 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) ! ..... Developer must add checking for invalid inputs here: ..... -if (p%BD4Blades) then +if (p%BD4Blades .or. p%RigidAero) then startIndx = 1 else startIndx = p%NumBl+1 @@ -4388,7 +4390,7 @@ SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) InvalidOutput(HSSBrTq) = p%method == Method_RK4 - IF ( p%BD4Blades ) THEN + IF ( p%BD4Blades .or. p%RigidAero ) THEN InvalidOutput( Q_B1E1) = .TRUE. InvalidOutput( Q_B1F1) = .TRUE. InvalidOutput( Q_B1F2) = .TRUE. diff --git a/modules/elastodyn/src/ElastoDyn_Registry.txt b/modules/elastodyn/src/ElastoDyn_Registry.txt index 2e48ff0d79..65f45797a0 100644 --- a/modules/elastodyn/src/ElastoDyn_Registry.txt +++ b/modules/elastodyn/src/ElastoDyn_Registry.txt @@ -21,6 +21,7 @@ param ElastoDyn/ED - IntKi ED_NMX - 4 - "Used in updating predictor-corrector va typedef ElastoDyn/ED InitInputType CHARACTER(1024) InputFile - - - "Name of the input file" - typedef ^ InitInputType Logical Linearize - .FALSE. - "Flag that tells this module if the glue code wants to linearize." - typedef ^ InitInputType LOGICAL CompElast - - - "flag to determine if ElastoDyn is computing blade loads (true) or BeamDyn is (false)" - +typedef ^ InitInputType LOGICAL RigidAero - - - "flag to determine if ElastoDyn if blades are rigid for aero -- when AeroDisk is used" - typedef ^ InitInputType CHARACTER(1024) RootName - - - "RootName for writing output files" typedef ^ InitInputType ReKi Gravity - - - "Gravitational acceleration" m/s^2 typedef ^ InitInputType IntKi MHK - - - "MHK turbine type switch" - @@ -771,6 +772,7 @@ typedef ^ ParameterType IntKi method - - - "Identifier for integration method (1 typedef ^ ParameterType ReKi PtfmCMxt - - - "Downwind distance from the ground level [onshore], MSL [offshore wind or floating MHK], or seabed [fixed MHK] to the platform CM" meters typedef ^ ParameterType ReKi PtfmCMyt - - - "Lateral distance from the ground level [onshore], MSL [offshore wind or floating MHK], or seabed [fixed MHK] to the platform CM" meters typedef ^ ParameterType LOGICAL BD4Blades - - - "flag to determine if BeamDyn is computing blade loads (true) or ElastoDyn is (false)" - +typedef ^ ParameterType LOGICAL RigidAero - - - "flag to determine if ElastoDyn if blades are rigid for aero -- when AeroDisk is used" - typedef ^ ParameterType IntKi YawFrctMod - - - "Identifier for YawFrctMod (0 [no friction], 1 [does not use Fz at bearing], or 2 [does use Fz at bearing]" - typedef ^ ParameterType R8Ki M_CD - - - "Dynamic friction moment at null yaw rate" N-m typedef ^ ParameterType R8Ki M_CSMAX - - - "Maximum Coulomb friction torque" N-m diff --git a/modules/elastodyn/src/ElastoDyn_Types.f90 b/modules/elastodyn/src/ElastoDyn_Types.f90 index 39c5e876f0..886da1125f 100644 --- a/modules/elastodyn/src/ElastoDyn_Types.f90 +++ b/modules/elastodyn/src/ElastoDyn_Types.f90 @@ -39,6 +39,7 @@ MODULE ElastoDyn_Types CHARACTER(1024) :: InputFile !< Name of the input file [-] LOGICAL :: Linearize = .FALSE. !< Flag that tells this module if the glue code wants to linearize. [-] LOGICAL :: CompElast = .false. !< flag to determine if ElastoDyn is computing blade loads (true) or BeamDyn is (false) [-] + LOGICAL :: RigidAero = .false. !< flag to determine if ElastoDyn if blades are rigid for aero -- when AeroDisk is used [-] CHARACTER(1024) :: RootName !< RootName for writing output files [-] REAL(ReKi) :: Gravity = 0.0_ReKi !< Gravitational acceleration [m/s^2] INTEGER(IntKi) :: MHK = 0_IntKi !< MHK turbine type switch [-] @@ -785,6 +786,7 @@ MODULE ElastoDyn_Types REAL(ReKi) :: PtfmCMxt = 0.0_ReKi !< Downwind distance from the ground level [onshore], MSL [offshore wind or floating MHK], or seabed [fixed MHK] to the platform CM [meters] REAL(ReKi) :: PtfmCMyt = 0.0_ReKi !< Lateral distance from the ground level [onshore], MSL [offshore wind or floating MHK], or seabed [fixed MHK] to the platform CM [meters] LOGICAL :: BD4Blades = .false. !< flag to determine if BeamDyn is computing blade loads (true) or ElastoDyn is (false) [-] + LOGICAL :: RigidAero = .false. !< flag to determine if ElastoDyn if blades are rigid for aero -- when AeroDisk is used [-] INTEGER(IntKi) :: YawFrctMod = 0_IntKi !< Identifier for YawFrctMod (0 [no friction], 1 [does not use Fz at bearing], or 2 [does use Fz at bearing] [-] REAL(R8Ki) :: M_CD = 0.0_R8Ki !< Dynamic friction moment at null yaw rate [N-m] REAL(R8Ki) :: M_CSMAX = 0.0_R8Ki !< Maximum Coulomb friction torque [N-m] @@ -876,6 +878,7 @@ subroutine ED_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSta DstInitInputData%InputFile = SrcInitInputData%InputFile DstInitInputData%Linearize = SrcInitInputData%Linearize DstInitInputData%CompElast = SrcInitInputData%CompElast + DstInitInputData%RigidAero = SrcInitInputData%RigidAero DstInitInputData%RootName = SrcInitInputData%RootName DstInitInputData%Gravity = SrcInitInputData%Gravity DstInitInputData%MHK = SrcInitInputData%MHK @@ -901,6 +904,7 @@ subroutine ED_PackInitInput(RF, Indata) call RegPack(RF, InData%InputFile) call RegPack(RF, InData%Linearize) call RegPack(RF, InData%CompElast) + call RegPack(RF, InData%RigidAero) call RegPack(RF, InData%RootName) call RegPack(RF, InData%Gravity) call RegPack(RF, InData%MHK) @@ -918,6 +922,7 @@ subroutine ED_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%InputFile); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%Linearize); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%CompElast); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RigidAero); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%RootName); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%Gravity); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MHK); if (RegCheckErr(RF, RoutineName)) return @@ -5953,6 +5958,7 @@ subroutine ED_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) DstParamData%PtfmCMxt = SrcParamData%PtfmCMxt DstParamData%PtfmCMyt = SrcParamData%PtfmCMyt DstParamData%BD4Blades = SrcParamData%BD4Blades + DstParamData%RigidAero = SrcParamData%RigidAero DstParamData%YawFrctMod = SrcParamData%YawFrctMod DstParamData%M_CD = SrcParamData%M_CD DstParamData%M_CSMAX = SrcParamData%M_CSMAX @@ -6459,6 +6465,7 @@ subroutine ED_PackParam(RF, Indata) call RegPack(RF, InData%PtfmCMxt) call RegPack(RF, InData%PtfmCMyt) call RegPack(RF, InData%BD4Blades) + call RegPack(RF, InData%RigidAero) call RegPack(RF, InData%YawFrctMod) call RegPack(RF, InData%M_CD) call RegPack(RF, InData%M_CSMAX) @@ -6723,6 +6730,7 @@ subroutine ED_UnPackParam(RF, OutData) call RegUnpack(RF, OutData%PtfmCMxt); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PtfmCMyt); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%BD4Blades); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RigidAero); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%YawFrctMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%M_CD); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%M_CSMAX); if (RegCheckErr(RF, RoutineName)) return diff --git a/modules/hydrodyn/src/WAMIT2.f90 b/modules/hydrodyn/src/WAMIT2.f90 index 22a2e689dd..b1c1f07ce8 100644 --- a/modules/hydrodyn/src/WAMIT2.f90 +++ b/modules/hydrodyn/src/WAMIT2.f90 @@ -2224,7 +2224,7 @@ SUBROUTINE SumQTF_InitCalc( InitInp, p, SumQTFData, SumQTFForce, ErrMsg, ErrStat !> The phase shift due to an (x,y) offset for second order difference frequencies is of the form !! \f$ exp[-\imath ( k(\omega_1) ( X cos(\beta(w_1)) + Y sin(\beta(w_1)) ) !! 1 k(\omega_2) ( X cos(\beta(w_2)) + Y sin(\beta(w_2)) ) ) ]\f$. - !! For the first term, \f$ \omega_1 = \omega_2 \$f. + !! For the first term, \f$ \omega_1 = \omega_2 \f$. ! NOTE: the phase shift applies to the aWaveElevC of the incoming wave. Including it here instead ! of above is mathematically equivalent, but only because each frequency has only one wave ! direction associated with it through the equal energy approach used in multidirectional waves. diff --git a/modules/inflowwind/src/InflowWind.txt b/modules/inflowwind/src/InflowWind.txt index e79e30dcb2..3dd63debae 100644 --- a/modules/inflowwind/src/InflowWind.txt +++ b/modules/inflowwind/src/InflowWind.txt @@ -124,6 +124,7 @@ typedef ^ ^ DbKi DT typedef ^ ^ ReKi WindViXYZprime :: - - "List of XYZ coordinates for velocity measurements, translated to the wind coordinate system (prime coordinates). This equals MATMUL( RotToWind, ParamData%WindViXYZ )" meters typedef ^ ^ ReKi WindViXYZ :: - - "List of XYZ coordinates for wind velocity measurements, 3xNWindVel" meters typedef ^ ^ FlowFieldType &FlowField - - - "Flow field data to represent all wind types" - +#FIXME: PositionAvg is used for DiskVel. However DiskVel does not appear to be used anymore, so this could be removed. ADP-2024.08.01 typedef ^ ^ ReKi PositionAvg :: - - "(non-rotated) positions of points used for averaging wind speed" meters typedef ^ ^ IntKi NWindVel - - - "Number of points in the wind velocity list" - typedef ^ ^ IntKi NumOuts - 0 - "Number of parameters in the output list (number of outputs requested)" - @@ -146,6 +147,7 @@ typedef ^ ^ ReKi HubOrientat typedef ^ OutputType ReKi VelocityUVW :: - - "Array holding the U,V,W velocity for a given timestep" meters/sec typedef ^ OutputType ReKi AccelUVW :: - - "Array holding the U,V,W acceleration for a given timestep" meters/sec typedef ^ OutputType ReKi WriteOutput : - - "Array with values to output to file" - +#FIXME: is DiskVel still used? ADP-2024.08.01 typedef ^ ^ ReKi DiskVel {3} - - "Vector holding the U,V,W average velocity of the disk" meters/sec typedef ^ ^ ReKi HubVel {3} - - "Vector holding the U,V,W velocity at the hub" meters/sec typedef ^ ^ lidar_OutputType lidar - - - "Lidar data" - diff --git a/modules/inflowwind/src/InflowWind_Subs.f90 b/modules/inflowwind/src/InflowWind_Subs.f90 index 0ae749b985..80ce1af13a 100644 --- a/modules/inflowwind/src/InflowWind_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Subs.f90 @@ -1658,6 +1658,7 @@ SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, Fil END SUBROUTINE CalculateOutput !==================================================================================================== +!FIXME: is this routine necessary anymore? !> this routine calculates a rotor-averaged mean velocity, DiskVel SUBROUTINE InflowWind_GetRotorSpatialAverage( Time, InputData, p, x, xd, z, OtherStates, m, MeanVelocity, ErrStat, ErrMsg ) diff --git a/modules/nwtc-library/src/NWTC_IO.f90 b/modules/nwtc-library/src/NWTC_IO.f90 index 09bba4a224..e2b42cc264 100644 --- a/modules/nwtc-library/src/NWTC_IO.f90 +++ b/modules/nwtc-library/src/NWTC_IO.f90 @@ -3911,7 +3911,7 @@ SUBROUTINE ParseSiVarWDefault ( FileInfo, LineNum, ExpVarName, Var, VarDefault, CALL Conv2UC( defaultStr ) IF ( INDEX(defaultStr, "DEFAULT" ) /= 1 ) THEN ! If it's not "default", read this variable LineNum = LineNum - 1 ! back up a line - CALL ParseVar ( FileInfo, LineNum, ExpVarName, Var, ErrStatLcl, ErrMsg2, UnEc ) + CALL ParseVar ( FileInfo, LineNum, ExpVarName, Var, ErrStatLcl, ErrMsg2 ) CALL SetErrStat( ErrStatLcl, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE Var = VarDefault ! "DEFAULT" value diff --git a/modules/openfast-library/CMakeLists.txt b/modules/openfast-library/CMakeLists.txt index f312e1ad54..a694bcdddb 100644 --- a/modules/openfast-library/CMakeLists.txt +++ b/modules/openfast-library/CMakeLists.txt @@ -43,6 +43,7 @@ add_library(openfast_prelib STATIC target_link_libraries(openfast_prelib nwtclibs versioninfolib + aerodisklib aerodynlib extloadslib beamdynlib @@ -59,6 +60,7 @@ target_link_libraries(openfast_prelib orcaflexlib sctypeslib seastlib + sedlib servodynlib subdynlib ) diff --git a/modules/openfast-library/src/FAST_Library.f90 b/modules/openfast-library/src/FAST_Library.f90 index 17cffd3253..b260d1ee57 100644 --- a/modules/openfast-library/src/FAST_Library.f90 +++ b/modules/openfast-library/src/FAST_Library.f90 @@ -785,6 +785,11 @@ subroutine FAST_ExtInfw_Init(iTurb_c, TMax, InputFileName_c, TurbIDforName, OutF else NumTwrElem_c = 0 endif + ELSEIF (Turbine(iTurb)%p_FAST%CompAero == MODULE_ADsk) THEN + call WrScr("AeroDisk cannot be used with ExtInfw through the FAST_Library interface") + ErrStat = AbortErrLev + ErrMsg = "AeroDisk cannot be used with ExtInfw through the FAST_Library interface" + if (Failed()) return ELSE NumBl_c = 0 NumBlElem_c = 0 diff --git a/modules/openfast-library/src/FAST_Registry.txt b/modules/openfast-library/src/FAST_Registry.txt index 8a491b3298..3aa97961cb 100644 --- a/modules/openfast-library/src/FAST_Registry.txt +++ b/modules/openfast-library/src/FAST_Registry.txt @@ -12,10 +12,12 @@ # ...... Include files (definitions from NWTC Library and module components) ............................................................................ include Registry_NWTC_Library.txt usefrom ElastoDyn_Registry.txt +usefrom SED_Registry.txt usefrom Registry_BeamDyn.txt usefrom ServoDyn_Registry.txt usefrom AeroDyn_Registry.txt -usefrom ExtLoads_Registry.txt +usefrom AeroDisk_Registry.txt +usefrom ExtLoads_Registry.txt usefrom SubDyn_Registry.txt usefrom SeaState.txt usefrom HydroDyn.txt @@ -55,7 +57,9 @@ param ^ - INTEGER Module_MD - 16 - "MoorDyn" - param ^ - INTEGER Module_Orca - 17 - "OrcaFlex integration (HD/Mooring)" - param ^ - INTEGER Module_IceF - 18 - "IceFloe" - param ^ - INTEGER Module_IceD - 19 - "IceDyn" - -param ^ - INTEGER NumModules - 19 - "The number of modules available in FAST" - +param ^ - INTEGER Module_ADsk - 20 - "AeroDisk" - +param ^ - INTEGER Module_SED - 21 - "Simplified-ElastoDyn" - +param ^ - INTEGER NumModules - 21 - "The number of modules available in FAST" - # Other Constants param ^ - INTEGER MaxNBlades - 3 - "Maximum number of blades allowed on a turbine" - param ^ - INTEGER IceD_MaxLegs - 4 - "because I don't know how many legs there are before calling IceD_Init and I don't want to copy the data because of sibling mesh issues, I'm going to allocate IceD based on this number" - @@ -125,9 +129,9 @@ typedef ^ FAST_ParameterType Reki UJacSclFact - - - "Scaling factor used to get typedef ^ FAST_ParameterType IntKi SizeJac_Opt1 {9} - - "(1)=size of matrix; (2)=size of ED portion; (3)=size of SD portion [2 meshes]; (4)=size of HD portion; (5)=size of BD portion blade 1; (6)=size of BD portion blade 2; (7)=size of BD portion blade 3; (8)=size of Orca portion; (9)=size of ExtPtfm portion;" - typedef ^ FAST_ParameterType IntKi SolveOption - - - "Switch to determine which solve option we are going to use (see Solve_FullOpt1, etc)" - # Feature switches and flags: -typedef ^ FAST_ParameterType IntKi CompElast - - - "Compute blade loads (switch) {Module_ED; Module_BD}" - +typedef ^ FAST_ParameterType IntKi CompElast - - - "Compute blade loads (switch) {Module_ED; Module_BD; Module_SED}" - typedef ^ FAST_ParameterType IntKi CompInflow - - - "Compute inflow wind conditions (switch) {Module_None; Module_IfW; Module_ExtInfw}" - -typedef ^ FAST_ParameterType IntKi CompAero - - - "Compute aerodynamic loads (switch) {Module_None; Module_AD}" - +typedef ^ FAST_ParameterType IntKi CompAero - - - "Compute aerodynamic loads (switch) {Module_None; Module_ADsk; Module_AD}" - typedef ^ FAST_ParameterType IntKi CompServo - - - "Compute control and electrical-drive dynamics (switch) {Module_None; Module_SrvD}" - typedef ^ FAST_ParameterType IntKi CompSeaSt - - - "Compute sea states; wave kinematics (switch) {Module_None; Module_SeaSt}" - typedef ^ FAST_ParameterType IntKi CompHydro - - - "Compute hydrodynamic loads (switch) {Module_None; Module_HD}" - @@ -151,7 +155,7 @@ typedef ^ FAST_ParameterType ReKi Pvap - - - "Vapour pressure typedef ^ FAST_ParameterType ReKi WtrDpth - - - "Water depth" m typedef ^ FAST_ParameterType ReKi MSL2SWL - - - "Offset between still-water level and mean sea level" m # Input file names: -typedef ^ FAST_ParameterType CHARACTER(1024) EDFile - - - "The name of the ElastoDyn input file" - +typedef ^ FAST_ParameterType CHARACTER(1024) EDFile - - - "The name of the ElastoDyn/Simplified-ElastoDyn input file" - typedef ^ FAST_ParameterType CHARACTER(1024) BDBldFile {MaxNBlades} - - "Name of files containing BeamDyn inputs for each blade" - typedef ^ FAST_ParameterType CHARACTER(1024) InflowFile - - - "Name of file containing inflow wind input parameters" - typedef ^ FAST_ParameterType CHARACTER(1024) AeroFile - - - "Name of file containing aerodynamic input parameters" - @@ -245,6 +249,7 @@ typedef ^ ^ ED_DiscreteStateType xd_ED typedef ^ ^ ED_ConstraintStateType z_ED {:} - - "Constraint states" typedef ^ ^ ED_OtherStateType OtherSt_ED {:} - - "Other states" typedef ^ ^ ED_InputType u_ED {:} - - "System inputs" +# ..... No Simplified-ElastoDyn data ........................................................................................... # ..... ServoDyn OP data ....................................................................................................... typedef FAST FAST_LinStateSave SrvD_ContinuousStateType x_SrvD {:} - - "Continuous states" typedef ^ ^ SrvD_DiscreteStateType xd_SrvD {:} - - "Discrete states" @@ -257,7 +262,8 @@ typedef ^ ^ AD_DiscreteStateType xd_AD typedef ^ ^ AD_ConstraintStateType z_AD {:} - - "Constraint states" typedef ^ ^ AD_OtherStateType OtherSt_AD {:} - - "Other states" typedef ^ ^ AD_InputType u_AD {:} - - "System inputs" -# ..... InflowWind OP data ....................................................................................................... +# ..... No AeroDisk data ...................................................................................................... +# ..... InflowWind OP data .................................................................................................... typedef FAST FAST_LinStateSave InflowWind_ContinuousStateType x_IfW {:} - - "Continuous states" typedef ^ ^ InflowWind_DiscreteStateType xd_IfW {:} - - "Discrete states" typedef ^ ^ InflowWind_ConstraintStateType z_IfW {:} - - "Constraint states" @@ -452,6 +458,21 @@ typedef ^ ^ DbKi InputTimes {:} - - "Array of times associated with Input Array" typedef ^ ^ DbKi InputTimes_Saved {:} - - "Backup Array of times associated with Input Array" +# ..... Simplified-ElastoDyn data ............................................................................................ +typedef FAST SED_Data SED_ContinuousStateType x {2} - - "Continuous states" +typedef ^ ^ SED_DiscreteStateType xd {2} - - "Discrete states" +typedef ^ ^ SED_ConstraintStateType z {2} - - "Constraint states" +typedef ^ ^ SED_OtherStateType OtherSt {2} - - "Other states" +typedef ^ ^ SED_ParameterType p - - - "Parameters" +typedef ^ ^ SED_InputType u - - - "System inputs" +typedef ^ ^ SED_OutputType y - - - "System outputs" +typedef ^ ^ SED_MiscVarType m - - - "Misc (optimization) variables not associated with time" +typedef ^ ^ SED_OutputType Output {:} - - "Array of outputs associated with CalcSteady Azimuths" +typedef ^ ^ SED_OutputType y_interp - - - "interpolated system outputs for CalcSteady" +typedef ^ ^ SED_InputType Input {:} - - "Array of inputs associated with InputTimes" +typedef ^ ^ DbKi InputTimes {:} - - "Array of times associated with Input Array" + + # ..... ServoDyn data ....................................................................................................... typedef FAST ServoDyn_Data SrvD_ContinuousStateType x {NumStateTimes} - - "Continuous states" typedef ^ ^ SrvD_DiscreteStateType xd {NumStateTimes} - - "Discrete states" @@ -496,6 +517,20 @@ typedef ^ ^ ExtLd_OutputType y - - - "System outputs" typedef ^ ^ ExtLd_MiscVarType m - - - "Misc/optimization variables" typedef ^ ^ DbKi InputTimes {:} - - "Array of times associated with Input Array" +# ..... AeroDisk data ....................................................................................................... +typedef FAST AeroDisk_Data ADsk_ContinuousStateType x {2} - - "Continuous states" +typedef ^ ^ ADsk_DiscreteStateType xd {2} - - "Discrete states" +typedef ^ ^ ADsk_ConstraintStateType z {2} - - "Constraint states" +typedef ^ ^ ADsk_OtherStateType OtherSt {2} - - "Other states" +typedef ^ ^ ADsk_ParameterType p - - - "Parameters" +typedef ^ ^ ADsk_InputType u - - - "System inputs" +typedef ^ ^ ADsk_OutputType y - - - "System outputs" +typedef ^ ^ ADsk_MiscVarType m - - - "Misc/optimization variables" +typedef ^ ^ ADsk_OutputType Output {:} - - "Array of outputs associated with CalcSteady Azimuths" +typedef ^ ^ ADsk_OutputType y_interp - - - "interpolated system outputs for CalcSteady" +typedef ^ ^ ADsk_InputType Input {:} - - "Array of inputs associated with InputTimes" +typedef ^ ^ DbKi InputTimes {:} - - "Array of times associated with Input Array" + # ..... InflowWind data ....................................................................................................... typedef FAST InflowWind_Data InflowWind_ContinuousStateType x {NumStateTimes} - - "Continuous states" typedef ^ ^ InflowWind_DiscreteStateType xd {NumStateTimes} - - "Discrete states" @@ -690,11 +725,14 @@ typedef ^ FAST_ModuleMapType MeshMapType BStC_P_2_BD_P_B {:}{:} - - "Map ServoD typedef ^ FAST_ModuleMapType MeshMapType SStC_P_P_2_SubStructure {:} - - "Map ServoDyn/SStC platform point mesh load to SubDyn/ElastoDyn point load mesh" typedef ^ FAST_ModuleMapType MeshMapType SubStructure_2_SStC_P_P {:} - - "Map SubDyn y3mesh or ED platform mesh motion to ServoDyn/SStC point mesh" # ED --> SrvD -- PlatformPtMesh motion to SrvD%PtfmMotionMesh for passing to DLL -typedef ^ FAST_ModuleMapType MeshMapType ED_P_2_SrvD_P_P - - - "Map ElastoDyn platform point mesh motion to ServoDyn point mesh -- for passing to controller" -# ED/BD <-> AD (blades) +typedef ^ FAST_ModuleMapType MeshMapType ED_P_2_SrvD_P_P - - - "Map ElastoDyn/Simplified-ElastoDyn platform point mesh motion to ServoDyn point mesh -- for passing to controller" +# ED/BD/SED <-> AD (blades) typedef ^ FAST_ModuleMapType MeshMapType BDED_L_2_AD_L_B {:} - - "Map ElastoDyn BladeLn2Mesh point meshes OR BeamDyn BldMotion line2 meshes to AeroDyn14 InputMarkers OR AeroDyn BladeMotion line2 meshes" typedef ^ FAST_ModuleMapType MeshMapType AD_L_2_BDED_B {:} - - "Map AeroDyn14 InputMarkers or AeroDyn BladeLoad line2 meshes to ElastoDyn BladePtLoad point meshes or BeamDyn BldMotion line2 meshes" typedef ^ FAST_ModuleMapType MeshMapType BD_L_2_BD_L {:} - - "Map BeamDyn BldMotion output meshes to locations on the BD input DistrLoad mesh stored in MeshMapType%y_BD_BldMotion_4Loads (BD input and output meshes are not siblings and in fact have nodes at different locations" +typedef ^ FAST_ModuleMapType MeshMapType SED_P_2_AD_L_B {:} - - "Map Simplified-ElastoDyn BladeRoot point meshes to rigid AeroDyn BladeMotion line2 meshes" +typedef ^ FAST_ModuleMapType MeshMapType SED_P_2_AD_P_R {:} - - "Map Simplified-ElastoDyn BladeRootMotion point meshes to AeroDyn BladeRootMotion point meshes" +typedef ^ FAST_ModuleMapType MeshMapType AD_L_2_SED_P {:} - - "Map AeroDyn blade load output mesh to Simplified-ElastoDyn Hub point mesh" # ED <-> AD (nacelle, tower, hub, blade root, tailfin) typedef ^ FAST_ModuleMapType MeshMapType ED_P_2_AD_P_N - - - "Map ElastoDyn Nacelle point motion mesh to AeroDyn Nacelle point motion mesh" typedef ^ FAST_ModuleMapType MeshMapType AD_P_2_ED_P_N - - - "Map AeroDyn Nacelle point load mesh to ElastoDyn nacelle point load mesh" @@ -704,6 +742,16 @@ typedef ^ FAST_ModuleMapType MeshMapType ED_L_2_AD_L_T - - - "Map ElastoDyn Towe typedef ^ FAST_ModuleMapType MeshMapType AD_L_2_ED_P_T - - - "Map AeroDyn14 Twr_InputMarkers or AeroDyn TowerLoad line2 mesh to ElastoDyn TowerPtLoads point mesh" typedef ^ FAST_ModuleMapType MeshMapType ED_P_2_AD_P_R {:} - - "Map ElastoDyn BladeRootMotion point meshes to AeroDyn BladeRootMotion point meshes" typedef ^ FAST_ModuleMapType MeshMapType ED_P_2_AD_P_H - - - "Map ElastoDyn HubPtMotion point mesh to AeroDyn HubMotion point mesh" +# ED <-> ADsk (hub) +typedef ^ FAST_ModuleMapType MeshMapType ADsk_P_2_ED_P_H - - - "Map AeroDisk point load mesh to ElastoDyn hub point load mesh" +typedef ^ FAST_ModuleMapType MeshMapType ED_P_2_ADsk_P_H - - - "Map ElastoDyn HubPtMotion point mesh to AeroDisk HubMotion point mesh" +# SED <-> AD (nacelle, tower, hub, blade root) +typedef ^ FAST_ModuleMapType MeshMapType SED_P_2_AD_P_N - - - "Map Simplified-ElastoDyn Nacelle point motion mesh to AeroDyn Nacelle point motion mesh" +typedef ^ FAST_ModuleMapType MeshMapType SED_L_2_AD_L_T - - - "Map Simplified-ElastoDyn TowerLn2Mesh line2 mesh to AeroDyn TowerMotion line2 mesh" +typedef ^ FAST_ModuleMapType MeshMapType SED_P_2_AD_P_H - - - "Map Simplified-ElastoDyn HubPtMotion point mesh to AeroDyn HubMotion point mesh" +# SED <-> ADsk (hub) +typedef ^ FAST_ModuleMapType MeshMapType ADsk_P_2_SED_P_H - - - "Map AeroDisk point load mesh to Simplfied-ElastoDyn hub point load mesh" +typedef ^ FAST_ModuleMapType MeshMapType SED_P_2_ADsk_P_H - - - "Map Simplified-ElastoDyn HubPtMotion point mesh to AeroDisk HubMotion point mesh" typedef ^ FAST_ModuleMapType MeshMapType AD_P_2_ED_P_H - - - "Map AeroDyn HubLoad point mesh to ElastoDyn HubPtLoad point mesh" # ED/BD <-> ExtLd (blades) typedef ^ FAST_ModuleMapType MeshMapType BDED_L_2_ExtLd_P_B {:} - - "Map ElastoDyn/BeamDyn BladeLn2Mesh point meshes OR BeamDyn BldMotion line2 meshes to ExtLoads point meshes" @@ -746,6 +794,7 @@ typedef ^ FAST_ModuleMapType MeshType y_BD_BldMotion_4Loads {:} - - "BD blade mo typedef ^ FAST_ModuleMapType MeshType u_BD_Distrload {:} - - "copy of BD DistrLoad input meshes" typedef ^ FAST_ModuleMapType MeshType u_Orca_PtfmMesh - - - "copy of Orca PtfmMesh input mesh" typedef ^ FAST_ModuleMapType MeshType u_ExtPtfm_PtfmMesh - - - "copy of ExtPtfm_MCKF PtfmMesh input mesh" +typedef ^ FAST_ModuleMapType MeshType u_SED_HubPtLoad - - - "copy of SED input mesh" #typedef ^ FAST_ModuleMapType MeshType u_FarmMD_CoupledLoads - - - "FAST-internal copy of MoorDyn's CoupledLoads output mesh for use with shared moorings in FAST.Farm" # for steady-state solve (convert 1 blade to all blades) typedef ^ FAST_ModuleMapType R8Ki HubOrient {:}{:}{:} - - "Orientation matrix to translate results from blade 1 to remaining blades in aeromaps" "(-)" @@ -780,12 +829,16 @@ typedef ^ FAST_MiscVarType FAST_MiscLinType Lin - - - "misc data for linearizati # ..... FAST_InitData data ....................................................................................................... typedef ^ FAST_InitData ED_InitInputType InData_ED - - - "ED Initialization input data" typedef ^ FAST_InitData ED_InitOutputType OutData_ED - - - "ED Initialization output data" +typedef ^ FAST_InitData SED_InitInputType InData_SED - - - "SED Initialization input data" +typedef ^ FAST_InitData SED_InitOutputType OutData_SED - - - "SED Initialization output data" typedef ^ FAST_InitData BD_InitInputType InData_BD - - - "BD Initialization input data" typedef ^ FAST_InitData BD_InitOutputType OutData_BD : - - "BD Initialization output data" typedef ^ FAST_InitData SrvD_InitInputType InData_SrvD - - - "SrvD Initialization input data" typedef ^ FAST_InitData SrvD_InitOutputType OutData_SrvD - - - "SrvD Initialization output data" typedef ^ FAST_InitData AD_InitInputType InData_AD - - - "AD Initialization input data" typedef ^ FAST_InitData AD_InitOutputType OutData_AD - - - "AD Initialization output data" +typedef ^ FAST_InitData ADsk_InitInputType InData_ADsk - - - "ADsk Initialization input data" +typedef ^ FAST_InitData ADsk_InitOutputType OutData_ADsk - - - "ADsk Initialization output data" typedef ^ FAST_InitData ExtLd_InitInputType InData_ExtLd - - - "ExtLd Initialization input data" typedef ^ FAST_InitData ExtLd_InitOutputType OutData_ExtLd - - - "ExtLd Initialization output data" typedef ^ FAST_InitData InflowWind_InitInputType InData_IfW - - - "IfW Initialization input data" @@ -851,9 +904,11 @@ typedef ^ FAST_TurbineType FAST_OutputFileType y_FAST - - - "Output variables fo typedef ^ FAST_TurbineType FAST_MiscVarType m_FAST - - - "Miscellaneous variables" - typedef ^ FAST_TurbineType FAST_ModuleMapType MeshMapData - - - "Data for mapping between modules" - typedef ^ FAST_TurbineType ElastoDyn_Data ED - - - "Data for the ElastoDyn module" - +typedef ^ FAST_TurbineType SED_Data SED - - - "Data for the Simplified-ElastoDyn module" - typedef ^ FAST_TurbineType BeamDyn_Data BD - - - "Data for the BeamDyn module" - typedef ^ FAST_TurbineType ServoDyn_Data SrvD - - - "Data for the ServoDyn module" - typedef ^ FAST_TurbineType AeroDyn_Data AD - - - "Data for the AeroDyn module" - +typedef ^ FAST_TurbineType AeroDisk_Data ADsk - - - "Data for the AeroDisk module" - typedef ^ FAST_TurbineType ExtLoads_Data ExtLd - - - "Data for the External loads module" - typedef ^ FAST_TurbineType InflowWind_Data IfW - - - "Data for InflowWind module" - typedef ^ FAST_TurbineType ExternalInflow_Data ExtInfw - - - "Data for ExternalInflow integration module" - diff --git a/modules/openfast-library/src/FAST_SS_Subs.f90 b/modules/openfast-library/src/FAST_SS_Subs.f90 index afbff76bf3..c06c67beff 100644 --- a/modules/openfast-library/src/FAST_SS_Subs.f90 +++ b/modules/openfast-library/src/FAST_SS_Subs.f90 @@ -99,7 +99,7 @@ SUBROUTINE FAST_InitializeSteadyState_T( Turbine, ErrStat, ErrMsg ) Turbine%TurbID = 1 CALL FAST_InitializeAll( t_initial, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, CompAeroMaps, ErrStat, ErrMsg ) @@ -242,8 +242,8 @@ SUBROUTINE FAST_SteadyState(p_FAST, y_FAST, m_FAST, ED, BD, AD, MeshMapData, Err !---------------------------------------------------------------------------------------- n_global = real(n_case, DbKi) ! n_global is double-precision so that we can reuse existing code. - CALL WrOutputLine( n_global, p_FAST, y_FAST, UnusedAry, UnusedAry, ED%y%WriteOutput, & - AD%y, UnusedAry, UnusedAry, UnusedAry, UnusedAry, UnusedAry, UnusedAry, & + CALL WrOutputLine( n_global, p_FAST, y_FAST, UnusedAry, UnusedAry, ED%y%WriteOutput, UnusedAry, & + AD%y, UnusedAry, UnusedAry, UnusedAry, UnusedAry, UnusedAry, UnusedAry, UnusedAry, & UnusedAry, UnusedAry, UnusedAry, UnusedAry, y_IceD, BD%y, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index 8eb3d451cf..b051cac1d8 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -28,9 +28,11 @@ MODULE FAST_Solver USE FAST_ModTypes USE AeroDyn + USE AeroDisk USE ExtLoads USE InflowWind USE ElastoDyn + USE SED USE BeamDyn USE FEAMooring USE MoorDyn @@ -216,14 +218,16 @@ END SUBROUTINE BD_InputSolve !---------------------------------------------------------------------------------------------------------------------------------- !> This routine sets the inputs required for ED--using the Option 2 solve method. Currently the only inputs not solved in this routine !! are the fields on PlatformPtMesh, which are solved in Option 1. The fields on HubPtLoad are solved in both Option 2 and Option 1. -SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, y_AD, y_SrvD, u_AD, y_ExtLd, m_ExtLd, u_ExtLd, p_ExtLd, u_SrvD, MeshMapData, ErrStat, ErrMsg ) +SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, y_AD, y_ADsk, y_SrvD, u_AD, u_ADsk, y_ExtLd, m_ExtLd, u_ExtLd, p_ExtLd, u_SrvD, MeshMapData, ErrStat, ErrMsg ) !.................................................................................................................................. TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Glue-code simulation parameters TYPE(ED_InputType), INTENT(INOUT) :: u_ED !< ED Inputs at t TYPE(ED_OutputType), INTENT(IN ) :: y_ED !< ElastoDyn outputs (need translation displacement on meshes for loads mapping) TYPE(AD_OutputType), INTENT(IN ) :: y_AD !< AeroDyn outputs + TYPE(ADsk_OutputType), INTENT(IN ) :: y_ADsk !< AeroDisk outputs TYPE(AD_InputType), INTENT(IN ) :: u_AD !< AD inputs (for AD-ED load transfer) + TYPE(ADsk_InputType), INTENT(IN ) :: u_ADsk !< ADsk inputs (for ADsk-ED load transfer) TYPE(ExtLd_OutputType), INTENT(INOUT) :: y_ExtLd !< ExtLoads outputs TYPE(ExtLd_MiscVarType), INTENT(INOUT) :: m_ExtLd !< ExtLoads misc var TYPE(ExtLd_InputType), INTENT(IN ) :: u_ExtLd !< ExtLoads inputs (for ExtLoads-ED load transfer) @@ -255,7 +259,13 @@ SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, y_AD, y_SrvD, u_AD, y_ExtLd, m_Ext ErrStat = ErrID_None ErrMsg = "" - + + ! ED inputs on hub + if (p_FAST%CompAero == Module_ADsk) then + CALL Transfer_Point_to_Point( y_ADsk%AeroLoads, u_ED%HubPtLoad, MeshMapData%ADsk_P_2_ED_P_H, ErrStat2, ErrMsg2, u_ADsk%HubMotion, y_ED%HubPtMotion ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + endif + ! ED inputs on blade from AeroDyn IF (p_FAST%CompElast == Module_ED) THEN @@ -447,19 +457,91 @@ SUBROUTINE ED_InputSolve( p_FAST, u_ED, y_ED, y_AD, y_SrvD, u_AD, y_ExtLd, m_Ext end do end do END IF - end if - END SUBROUTINE ED_InputSolve + + !---------------------------------------------------------------------------------------------------------------------------------- -!> This routine determines the points in space where InflowWind needs to compute wind speeds. -SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD, OtherSt_AD, y_ED, ErrStat, ErrMsg ) +!> This routine sets the inputs required for SED--using the Option 2 solve method; currently the only input not solved in this routine +!! are the fields on PlatformPtMesh and HubPtLoad, which are solved in option 1. +SUBROUTINE SED_InputSolve( p_FAST, u_SED, y_SED, y_AD, y_ADsk, y_SrvD, u_AD, u_ADsk, u_SrvD, MeshMapData, ErrStat, ErrMsg ) + TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Glue-code simulation parameters + TYPE(SED_InputType), INTENT(INOUT) :: u_SED !< SED Inputs at t + TYPE(SED_OutputType), INTENT(INOUT) :: y_SED !< Simplified-ElastoDyn outputs (need translation displacement on meshes for loads mapping) + TYPE(AD_OutputType), INTENT(IN ) :: y_AD !< AeroDyn outputs + TYPE(ADsk_OutputType), INTENT(IN ) :: y_ADsk !< AeroDisk outputs + TYPE(AD_InputType), INTENT(IN ) :: u_AD !< AD inputs (for AD-ED load transfer) + TYPE(ADsk_InputType), INTENT(IN ) :: u_ADsk !< ADsk inputs (for ADsk-ED load transfer) + TYPE(SrvD_OutputType), INTENT(IN ) :: y_SrvD !< ServoDyn outputs + TYPE(SrvD_InputType), INTENT(IN ) :: u_SrvD !< ServoDyn inputs + + TYPE(FAST_ModuleMapType), INTENT(INOUT) :: MeshMapData !< Data for mapping between modules + INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status + CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message + INTEGER(IntKi) :: k ! Loops through blades + INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation + CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None + CHARACTER(*), PARAMETER :: RoutineName = 'SED_InputSolve' + + ErrStat = ErrID_None + ErrMsg = "" + + !----------- + ! Aero Loads + !----------- + u_SED%HubPtLoad%Force = 0.0_ReKi + u_SED%HubPtLoad%Moment = 0.0_ReKi + + if (p_FAST%CompAero == Module_AD) then + + ! AD --> SED hub + do k=1,size(y_AD%rotors(1)%BladeLoad) + !u_BD_RootMotion and y_ED2%HubPtMotion contain the displaced positions for load calculations + CALL Transfer_Line2_to_Point( y_AD%rotors(1)%BladeLoad(k), MeshMapData%u_SED_HubPtLoad, MeshMapData%AD_L_2_SED_P(k), ErrStat2, ErrMsg2, u_AD%rotors(1)%BladeMotion(k), y_SED%HubPtMotion) + if (Failed()) return + u_SED%HubPtLoad%Force = u_SED%HubPtLoad%Force + MeshMapData%u_SED_HubPtLoad%Force + u_SED%HubPtLoad%Moment = u_SED%HubPtLoad%Moment + MeshMapData%u_SED_HubPtLoad%Moment + end do + + elseif (p_FAST%CompAero == Module_ADsk) then + + ! ADsk --> SED hub + CALL Transfer_Point_to_Point( y_ADsk%AeroLoads, u_SED%HubPtLoad, MeshMapData%ADsk_P_2_SED_P_H, ErrStat2, ErrMsg2, u_ADsk%HubMotion, y_SED%HubPtMotion ) + if (Failed()) return + + endif + + + !----------- + ! Controls + !----------- + if ( p_FAST%CompServo == Module_SrvD ) then + u_SED%GenTrq = y_SrvD%GenTrq + u_SED%HSSBrTrqC = y_SrvD%HSSBrTrqC + u_SED%BlPitchCom = y_SrvD%BlPitchCom + u_SED%YawPosCom = y_SrvD%YawPosCom + u_SED%YawRateCom = y_SrvD%YawRateCom + endif + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed +END SUBROUTINE SED_InputSolve + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine determines the points in space where InflowWind needs to compute wind speeds. +SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD, OtherSt_AD, y_ED,y_SED, ErrStat, ErrMsg ) TYPE(InflowWind_InputType), INTENT(INOUT) :: u_IfW !< The inputs to InflowWind TYPE(InflowWind_ParameterType), INTENT(IN ) :: p_IfW !< The parameters to InflowWind TYPE(AD_InputType), INTENT(IN) :: u_AD !< The input meshes (already calculated) from AeroDyn TYPE(AD_OtherStateType), INTENT(IN) :: OtherSt_AD !< The wake points from AeroDyn are in here (Free Vortex Wake) TYPE(ED_OutputType), INTENT(IN) :: y_ED !< The outputs of the structural dynamics module (for IfW Lidar) + TYPE(SED_OutputType), INTENT(IN ) :: y_SED !< The outputs of the structural dynamics module (for IfW Lidar) TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< FAST parameter data TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< misc FAST data, including inputs from external codes like Simulink @@ -482,10 +564,23 @@ SUBROUTINE IfW_InputSolve( p_FAST, m_FAST, u_IfW, p_IfW, u_AD, OtherSt_AD, y_ED, Node = 0 IF (p_FAST%CompServo == MODULE_SrvD) THEN Node = Node + 1 - u_IfW%PositionXYZ(:,Node) = y_ED%HubPtMotion%Position(:,1) ! undisplaced position. Maybe we want to use the displaced position (y_ED%HubPtMotion%TranslationDisp) at some point in time. - END IF + if (p_FAST%CompElast == Module_SED) then + u_IfW%PositionXYZ(:,Node) = y_SED%HubPtMotion%Position(:,1) + else + u_IfW%PositionXYZ(:,Node) = y_ED%HubPtMotion%Position(:,1) ! undisplaced position. Maybe we want to use the displaced position (y_ED%HubPtMotion%TranslationDisp) at some point in time. + endif + END IF + + !FIXME: is DiskVel still used? The following is used in DiskVel calculations + if (p_FAST%CompElast == Module_SED) then + u_IfW%HubPosition = y_SED%HubPtMotion%Position(:,1) + y_SED%HubPtMotion%TranslationDisp(:,1) + u_IfW%HubOrientation = y_SED%HubPtMotion%Orientation(:,:,1) + else + u_IfW%HubPosition = y_ED%HubPtMotion%Position(:,1) + y_ED%HubPtMotion%TranslationDisp(:,1) + u_IfW%HubOrientation = y_ED%HubPtMotion%Orientation(:,:,1) + endif + -!FIXME: is this necessary after the pointers??? IF ( p_FAST%MHK /= MHK_None ) THEN u_IfW%PositionXYZ(3,:) = u_IfW%PositionXYZ(3,:) + p_FAST%WtrDpth ENDIF @@ -576,13 +671,14 @@ END SUBROUTINE ExtLd_UpdateFlowField !---------------------------------------------------------------------------------------------------------------------------------- !> This routine sets all the AeroDyn inputs, except for the wind inflow values. -SUBROUTINE AD_InputSolve_NoIfW( p_FAST, u_AD, y_SrvD, y_ED, BD, MeshMapData, ErrStat, ErrMsg ) +SUBROUTINE AD_InputSolve_NoIfW( p_FAST, u_AD, y_SrvD, y_ED, y_SED, BD, MeshMapData, ErrStat, ErrMsg ) ! Passed variables TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< FAST parameter data TYPE(AD_InputType), INTENT(INOUT) :: u_AD !< The inputs to AeroDyn14 TYPE(SrvD_OutputType), INTENT(IN ) :: y_SrvD !< ServoDyn outputs - TYPE(ED_OutputType), INTENT(IN) :: y_ED !< The outputs from the structural dynamics module + TYPE(ED_OutputType), INTENT(IN) :: y_ED !< The outputs from the structural dynamics module ED + TYPE(SED_OutputType), INTENT(IN) :: y_SED !< The outputs from the structural dynamics module SED TYPE(BeamDyn_Data), INTENT(IN) :: BD !< The data from BeamDyn (want the outputs only, but it's in an array) TYPE(FAST_ModuleMapType), INTENT(INOUT) :: MeshMapData !< Data for mapping between modules @@ -608,28 +704,50 @@ SUBROUTINE AD_InputSolve_NoIfW( p_FAST, u_AD, y_SrvD, y_ED, BD, MeshMapData, Err !------------------------------------------------------------------------------------------------- ! tower - IF (u_AD%rotors(1)%TowerMotion%Committed) THEN - - CALL Transfer_Line2_to_Line2( y_ED%TowerLn2Mesh, u_AD%rotors(1)%TowerMotion, MeshMapData%ED_L_2_AD_L_T, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%TowerMotion' ) - + IF (u_AD%rotors(1)%TowerMotion%Committed) then + if (y_SED%TowerLn2Mesh%Committed) then + CALL Transfer_Line2_to_Line2( y_SED%TowerLn2Mesh, u_AD%rotors(1)%TowerMotion, MeshMapData%SED_L_2_AD_L_T, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%TowerMotion' ) + elseif (y_ED%TowerLn2Mesh%Committed) then + CALL Transfer_Line2_to_Line2( y_ED%TowerLn2Mesh, u_AD%rotors(1)%TowerMotion, MeshMapData%ED_L_2_AD_L_T, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%TowerMotion' ) + endif END IF ! hub - CALL Transfer_Point_to_Point( y_ED%HubPtMotion, u_AD%rotors(1)%HubMotion, MeshMapData%ED_P_2_AD_P_H, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%HubMotion' ) + if (p_FAST%CompElast == Module_SED) then + CALL Transfer_Point_to_Point( y_SED%HubPtMotion, u_AD%rotors(1)%HubMotion, MeshMapData%SED_P_2_AD_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%HubMotion' ) + else + CALL Transfer_Point_to_Point( y_ED%HubPtMotion, u_AD%rotors(1)%HubMotion, MeshMapData%ED_P_2_AD_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%HubMotion' ) + endif ! blade root - DO k=1,size(y_ED%BladeRootMotion) - CALL Transfer_Point_to_Point( y_ED%BladeRootMotion(k), u_AD%rotors(1)%BladeRootMotion(k), MeshMapData%ED_P_2_AD_P_R(k), ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%BladeRootMotion('//trim(num2lstr(k))//')' ) - END DO + if (p_FAST%CompElast == Module_SED) then + DO k=1,size(y_SED%BladeRootMotion) + CALL Transfer_Point_to_Point( y_SED%BladeRootMotion(k), u_AD%rotors(1)%BladeRootMotion(k), MeshMapData%SED_P_2_AD_P_R(k), ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%BladeRootMotion('//trim(num2lstr(k))//')' ) + END DO + else + DO k=1,size(y_ED%BladeRootMotion) + CALL Transfer_Point_to_Point( y_ED%BladeRootMotion(k), u_AD%rotors(1)%BladeRootMotion(k), MeshMapData%ED_P_2_AD_P_R(k), ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%BladeRootMotion('//trim(num2lstr(k))//')' ) + END DO + endif ! blades - IF (p_FAST%CompElast == Module_ED ) THEN + IF (p_FAST%CompElast == Module_SED) THEN + ! get rigid motion from SED + DO k=1,size(u_AD%rotors(1)%BladeMotion) + CALL Transfer_Point_to_Line2( y_SED%BladeRootMotion(k), u_AD%rotors(1)%BladeMotion(k), MeshMapData%SED_P_2_AD_L_B(k), ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName//':u_AD%BladeMotion('//trim(num2lstr(k))//')' ) + END DO + + ELSEIF (p_FAST%CompElast == Module_ED ) THEN DO k=1,size(y_ED%BladeLn2Mesh) CALL Transfer_Line2_to_Line2( y_ED%BladeLn2Mesh(k), u_AD%rotors(1)%BladeMotion(k), MeshMapData%BDED_L_2_AD_L_B(k), ErrStat2, ErrMsg2 ) @@ -648,10 +766,13 @@ SUBROUTINE AD_InputSolve_NoIfW( p_FAST, u_AD, y_SrvD, y_ED, BD, MeshMapData, Err ! nacelle IF (u_AD%rotors(1)%NacelleMotion%Committed) THEN - - CALL Transfer_Point_to_Point( y_ED%NacelleMotion, u_AD%rotors(1)%NacelleMotion, MeshMapData%ED_P_2_AD_P_N, ErrStat2, ErrMsg2 ) - call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - + if (p_FAST%CompElast == Module_SED) then + CALL Transfer_Point_to_Point( y_SED%NacelleMotion, u_AD%rotors(1)%NacelleMotion, MeshMapData%SED_P_2_AD_P_N, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + else + CALL Transfer_Point_to_Point( y_ED%NacelleMotion, u_AD%rotors(1)%NacelleMotion, MeshMapData%ED_P_2_AD_P_N, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + endif END IF ! Tailfin - Transfer ElastoDyn CM motion to AeroDyn ref point motion @@ -745,15 +866,46 @@ SUBROUTINE ExtLd_InputSolve_NoIfW( p_FAST, u_ExtLd, p_ExtLd, y_ED, BD, MeshMapDa CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) END SUBROUTINE ExtLd_InputSolve_NoIfW + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine sets all the AeroDisk, except for the wind inflow values. +SUBROUTINE ADsk_InputSolve_NoIfW( p_FAST, u_ADsk, y_ED, y_SED, MeshMapData, ErrStat, ErrMsg ) + TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< parameter FAST data + TYPE(ADsk_InputType), INTENT(INOUT) :: u_ADsk !< The inputs to AeroDyn14 + TYPE(ED_OutputType), INTENT(IN ) :: y_ED !< The outputs from the structural dynamics module + TYPE(SED_OutputType), INTENT(IN ) :: y_SED !< The outputs from the structural dynamics module + TYPE(FAST_ModuleMapType), INTENT(INOUT) :: MeshMapData !< Data for mapping between modules + INTEGER(IntKi), intent( out) :: ErrStat !< Error status of the operation + CHARACTER(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + ErrStat = ErrID_None + ErrMsg = "" + + !------------------------------------------------------------------------------------------------- + ! Hub positions, orientations, and velocities: + !------------------------------------------------------------------------------------------------- + if (p_FAST%CompElast == Module_SED) then + CALL Transfer_Point_to_Point( y_SED%HubPtMotion, u_ADsk%HubMotion, MeshMapData%SED_P_2_ADsk_P_H, ErrStat, ErrMsg ) + u_ADsk%RotSpeed = y_SED%RotSpeed + u_ADsk%BlPitch = y_SED%BlPitch(1) ! ADsk only uses collective blade pitch + else + CALL Transfer_Point_to_Point( y_ED%HubPtMotion, u_ADsk%HubMotion, MeshMapData%ED_P_2_ADsk_P_H, ErrStat, ErrMsg ) + u_ADsk%RotSpeed = y_ED%RotSpeed + u_ADsk%BlPitch = y_ED%BlPitch(1) ! ADsk only uses collective blade pitch + endif + +END SUBROUTINE ADsk_InputSolve_NoIfW + + !---------------------------------------------------------------------------------------------------------------------------------- !> This routine sets the inputs required for ServoDyn -SUBROUTINE SrvD_InputSolve( p_FAST, m_FAST, u_SrvD, y_ED, y_IfW, y_ExtInfw, p_ExtLd, y_BD, y_SD, MeshMapData, ErrStat, ErrMsg ) +SUBROUTINE SrvD_InputSolve( p_FAST, m_FAST, u_SrvD, y_ED, y_SED, y_IfW, y_ExtInfw, p_ExtLd, y_BD, y_SD, MeshMapData, ErrStat, ErrMsg ) !.................................................................................................................................. TYPE(FAST_ParameterType), INTENT(IN) :: p_FAST !< Glue-code simulation parameters TYPE(FAST_MiscVarType), INTENT(IN) :: m_FAST !< Glue-code misc variables (including inputs from external sources like Simulink) TYPE(SrvD_InputType), INTENT(INOUT) :: u_SrvD !< ServoDyn Inputs at t TYPE(ED_OutputType),TARGET, INTENT(IN) :: y_ED !< ElastoDyn outputs + TYPE(SED_OutputType),TARGET, INTENT(IN) :: y_SED !< Simplified-ElastoDyn outputs TYPE(InflowWind_OutputType), INTENT(IN) :: y_IfW !< InflowWind outputs TYPE(ExtInfw_OutputType), INTENT(IN) :: y_ExtInfw !< ExternalInflow outputs TYPE(ExtLd_ParameterType), INTENT(in) :: p_ExtLd !< Parameters of ExtLoads @@ -776,13 +928,15 @@ SUBROUTINE SrvD_InputSolve( p_FAST, m_FAST, u_SrvD, y_ED, y_IfW, y_ExtInfw, p_Ex INTEGER(IntKi) :: ErrStat2 ! temporary Error status of the operation CHARACTER(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None CHARACTER(*), PARAMETER :: RoutineName = 'SrvD_InputSolve' - - IF (p_FAST%CompSub == Module_SD) THEN + + if (p_FAST%CompElast == Module_SED) then + SubStructureMotion => y_SED%PlatformPtMesh + elseif (p_FAST%CompSub == Module_SD) then SubStructureMotion => y_SD%y3Mesh - ELSE + else SubStructureMotion => y_ED%PlatformPtMesh - END IF - + endif + ErrStat = ErrID_None ErrMsg = "" @@ -831,67 +985,106 @@ SUBROUTINE SrvD_InputSolve( p_FAST, m_FAST, u_SrvD, y_ED, y_IfW, y_ExtInfw, p_Ex if (allocated(u_SrvD%MsrPositionsz)) u_SrvD%MsrPositionsz = 0.0 ENDIF - - - - - ! ServoDyn inputs from combination of InflowWind and ElastoDyn - - u_SrvD%YawAngle = y_ED%YawAngle !nacelle yaw plus platform yaw - u_SrvD%YawErr = u_SrvD%WindDir - u_SrvD%YawAngle ! the nacelle yaw error estimate (positive about zi-axis) + if (p_FAST%CompElast == Module_SED) then + ! ServoDyn inputs from combination of InflowWind and ElastoDyn + u_SrvD%YawAngle = y_SED%Yaw !nacelle yaw (platform rigid) + u_SrvD%YawErr = u_SrvD%WindDir - u_SrvD%YawAngle ! the nacelle yaw error estimate (positive about zi-axis) - ! ServoDyn inputs from ElastoDyn - u_SrvD%Yaw = y_ED%Yaw !nacelle yaw - u_SrvD%YawRate = y_ED%YawRate - u_SrvD%BlPitch = y_ED%BlPitch - u_SrvD%LSS_Spd = y_ED%LSS_Spd - u_SrvD%HSS_Spd = y_ED%HSS_Spd - u_SrvD%RotSpeed = y_ED%RotSpeed - - IF ( p_FAST%CompElast == Module_BD ) THEN + ! ServoDyn inputs from Simplified-ElastoDyn + u_SrvD%Yaw = y_SED%Yaw !nacelle yaw + u_SrvD%YawRate = y_SED%YawRate + u_SrvD%LSS_Spd = y_SED%RotSpeed + u_SrvD%HSS_Spd = y_SED%HSS_Spd + u_SrvD%RotSpeed = y_SED%RotSpeed + u_SrvD%BlPitch = y_SED%BlPitch - ! translate "b" system output from BD into "c" system for SrvD - do k=1,p_FAST%nBeams - u_SrvD%RootMxc(k) = y_BD(k)%RootMxr*COS(y_ED%BlPitch(k)) + y_BD(k)%RootMyr*SIN(y_ED%BlPitch(k)) - u_SrvD%RootMyc(k) = -y_BD(k)%RootMxr*SIN(y_ED%BlPitch(k)) + y_BD(k)%RootMyr*COS(y_ED%BlPitch(k)) - end do - - ELSE - u_SrvD%RootMxc = y_ED%RootMxc ! fixed-size arrays: always size 3 - u_SrvD%RootMyc = y_ED%RootMyc ! fixed-size arrays: always size 3 - END IF + ! root moments + u_SrvD%RootMxc = 0.0_ReKi ! y_ED%RootMxc ! fixed-size arrays: always size 3 + u_SrvD%RootMyc = 0.0_ReKi ! y_ED%RootMyc ! fixed-size arrays: always size 3 - - u_SrvD%YawBrTAxp = y_ED%YawBrTAxp - u_SrvD%YawBrTAyp = y_ED%YawBrTAyp - u_SrvD%LSSTipPxa = y_ED%LSSTipPxa + u_SrvD%YawBrTAxp = 0.0_ReKi ! y_ED%YawBrTAxp + u_SrvD%YawBrTAyp = 0.0_ReKi ! y_ED%YawBrTAyp + u_SrvD%LSSTipPxa = y_SED%LSSTipPxa - u_SrvD%LSSTipMxa = y_ED%LSSTipMxa - u_SrvD%LSSTipMya = y_ED%LSSTipMya - u_SrvD%LSSTipMza = y_ED%LSSTipMza - u_SrvD%LSSTipMys = y_ED%LSSTipMys - u_SrvD%LSSTipMzs = y_ED%LSSTipMzs - - u_SrvD%YawBrMyn = y_ED%YawBrMyn - u_SrvD%YawBrMzn = y_ED%YawBrMzn - u_SrvD%NcIMURAxs = y_ED%NcIMURAxs - u_SrvD%NcIMURAys = y_ED%NcIMURAys - u_SrvD%NcIMURAzs = y_ED%NcIMURAzs + u_SrvD%LSSTipMxa = y_SED%RotTrq + u_SrvD%LSSTipMya = 0.0_ReKi ! y_ED%LSSTipMya + u_SrvD%LSSTipMza = 0.0_ReKi ! y_ED%LSSTipMza + u_SrvD%LSSTipMys = 0.0_ReKi ! y_ED%LSSTipMys + u_SrvD%LSSTipMzs = 0.0_ReKi ! y_ED%LSSTipMzs - u_SrvD%RotPwr = y_ED%RotPwr + u_SrvD%YawBrMyn = 0.0_ReKi ! y_ED%YawBrMyn + u_SrvD%YawBrMzn = 0.0_ReKi ! y_ED%YawBrMzn + u_SrvD%NcIMURAxs = 0.0_ReKi ! y_ED%NcIMURAxs + u_SrvD%NcIMURAys = 0.0_ReKi ! y_ED%NcIMURAys + u_SrvD%NcIMURAzs = 0.0_ReKi ! y_ED%NcIMURAzs - u_SrvD%LSShftFxa = y_ED%LSShftFxa - u_SrvD%LSShftFys = y_ED%LSShftFys - u_SrvD%LSShftFzs = y_ED%LSShftFzs + u_SrvD%RotPwr = y_SED%RotPwr - ! ! ServoDyn inputs from AeroDyn - !IF ( p_FAST%CompAero == Module_AD ) THEN - !ELSE - !END IF - ! + u_SrvD%LSShftFxa = 0.0_ReKi ! y_ED%LSShftFxa + u_SrvD%LSShftFys = 0.0_ReKi ! y_ED%LSShftFys + u_SrvD%LSShftFzs = 0.0_ReKi ! y_ED%LSShftFzs + else + + ! ServoDyn inputs from combination of InflowWind and ElastoDyn + + u_SrvD%YawAngle = y_ED%YawAngle !nacelle yaw plus platform yaw + u_SrvD%YawErr = u_SrvD%WindDir - u_SrvD%YawAngle ! the nacelle yaw error estimate (positive about zi-axis) + + + ! ServoDyn inputs from ElastoDyn + u_SrvD%Yaw = y_ED%Yaw !nacelle yaw + u_SrvD%YawRate = y_ED%YawRate + u_SrvD%BlPitch = y_ED%BlPitch + u_SrvD%LSS_Spd = y_ED%LSS_Spd + u_SrvD%HSS_Spd = y_ED%HSS_Spd + u_SrvD%RotSpeed = y_ED%RotSpeed + + IF ( p_FAST%CompElast == Module_BD ) THEN + + ! translate "b" system output from BD into "c" system for SrvD + do k=1,p_FAST%nBeams + u_SrvD%RootMxc(k) = y_BD(k)%RootMxr*COS(y_ED%BlPitch(k)) + y_BD(k)%RootMyr*SIN(y_ED%BlPitch(k)) + u_SrvD%RootMyc(k) = -y_BD(k)%RootMxr*SIN(y_ED%BlPitch(k)) + y_BD(k)%RootMyr*COS(y_ED%BlPitch(k)) + end do + + ELSE + u_SrvD%RootMxc = y_ED%RootMxc ! fixed-size arrays: always size 3 + u_SrvD%RootMyc = y_ED%RootMyc ! fixed-size arrays: always size 3 + END IF + + + u_SrvD%YawBrTAxp = y_ED%YawBrTAxp + u_SrvD%YawBrTAyp = y_ED%YawBrTAyp + u_SrvD%LSSTipPxa = y_ED%LSSTipPxa + + u_SrvD%LSSTipMxa = y_ED%LSSTipMxa + u_SrvD%LSSTipMya = y_ED%LSSTipMya + u_SrvD%LSSTipMza = y_ED%LSSTipMza + u_SrvD%LSSTipMys = y_ED%LSSTipMys + u_SrvD%LSSTipMzs = y_ED%LSSTipMzs + + u_SrvD%YawBrMyn = y_ED%YawBrMyn + u_SrvD%YawBrMzn = y_ED%YawBrMzn + u_SrvD%NcIMURAxs = y_ED%NcIMURAxs + u_SrvD%NcIMURAys = y_ED%NcIMURAys + u_SrvD%NcIMURAzs = y_ED%NcIMURAzs + + u_SrvD%RotPwr = y_ED%RotPwr + + u_SrvD%LSShftFxa = y_ED%LSShftFxa + u_SrvD%LSShftFys = y_ED%LSShftFys + u_SrvD%LSShftFzs = y_ED%LSShftFzs + + ! ! ServoDyn inputs from AeroDyn + !IF ( p_FAST%CompAero == Module_AD ) THEN + !ELSE + !END IF + ! + endif ! SED/ED + ! Platform motion mesh to pass to DLL -- NOTE: this is only the transition piece motion, and only passed when DLL is used IF (y_ED%PlatformPtMesh%Committed .and. u_SrvD%PtfmMotionMesh%Committed ) THEN CALL Transfer_Point_to_Point( y_ED%PlatformPtMesh, u_SrvD%PtfmMotionMesh, MeshMapData%ED_P_2_SrvD_P_P, ErrStat2, ErrMsg2 ) @@ -956,7 +1149,7 @@ SUBROUTINE SrvD_InputSolve( p_FAST, m_FAST, u_SrvD, y_ED, y_IfW, y_ExtInfw, p_Ex ! we're going to use the extrapolated values instead of the old values (Simulink inputs are from t, not t+dt) CALL SrvD_SetExternalInputs( p_FAST, m_FAST, u_SrvD ) #endif - + END SUBROUTINE SrvD_InputSolve !---------------------------------------------------------------------------------------------------------------------------------- @@ -1111,6 +1304,7 @@ SUBROUTINE Transfer_SrvD_to_SD_MD( p_FAST, y_SrvD, u_SD, u_MD ) TYPE(SD_InputType), INTENT(INOUT) :: u_SD !< SubDyn input TYPE(MD_InputType), INTENT(INOUT) :: u_MD !< MoorDyn input + if (p_FAST%CompElast == Module_SED) return ! StCs not used with SED if (p_FAST%CompServo /= Module_SrvD) return ! transfer SrvD outputs to other modules used in option 1: @@ -1163,7 +1357,9 @@ SUBROUTINE Transfer_Structure_to_Opt1Inputs( this_time, this_state, p_FAST, y_ED ErrStat = ErrID_None ErrMsg = "" - PlatformMotion => y_ED%PlatformPtMesh + if (p_FAST%CompElast == Module_SED) return ! HD, SD, and BD not used with SED + + PlatformMotion => y_ED%PlatformPtMesh IF (p_FAST%CompSub == Module_SD) THEN SubstructureMotion => SD%y%y3Mesh @@ -3749,12 +3945,13 @@ SUBROUTINE Perturb_u_FullOpt1( p_FAST, Jac_u_indx, n, u_perturb, u_ED_perturb, u END SUBROUTINE Perturb_u_FullOpt1 !---------------------------------------------------------------------------------------------------------------------------------- !> This routine resets the remap flags on all of the meshes -SUBROUTINE ResetRemapFlags(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD ) +SUBROUTINE ResetRemapFlags(p_FAST, ED, SED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD ) !............................................................................................................................... TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data @@ -3779,27 +3976,38 @@ SUBROUTINE ResetRemapFlags(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAP ! Reset each mesh's RemapFlag (after calling all InputSolve routines): !..................................................................... - ! ElastoDyn meshes - ED%Input( 1)%PlatformPtMesh%RemapFlag = .FALSE. - ED%y%PlatformPtMesh%RemapFlag = .FALSE. - ED%Input( 1)%TowerPtLoads%RemapFlag = .FALSE. - ED%y%TowerLn2Mesh%RemapFlag = .FALSE. - DO K=1,SIZE(ED%y%BladeRootMotion) + if (p_FAST%CompElast == Module_SED) then + ! Simplified-ElastoDyn meshes + SED%y%PlatformPtMesh%RemapFlag = .FALSE. + DO K=1,SIZE(SED%y%BladeRootMotion) + SED%y%BladeRootMotion(K)%RemapFlag = .FALSE. + END DO + SED%y%NacelleMotion%RemapFlag = .FALSE. + SED%y%HubPtMotion%RemapFlag = .FALSE. + SED%Input(1)%HubPtLoad%RemapFlag = .FALSE. + else + ! ElastoDyn meshes + ED%Input( 1)%PlatformPtMesh%RemapFlag = .FALSE. + ED%y%PlatformPtMesh%RemapFlag = .FALSE. + ED%Input( 1)%TowerPtLoads%RemapFlag = .FALSE. + ED%y%TowerLn2Mesh%RemapFlag = .FALSE. + DO K=1,SIZE(ED%y%BladeRootMotion) ED%y%BladeRootMotion(K)%RemapFlag = .FALSE. - END DO - if (allocated(ED%Input(1)%BladePtLoads)) then - DO K=1,SIZE(ED%Input(1)%BladePtLoads) - ED%Input( 1)%BladePtLoads(K)%RemapFlag = .FALSE. - ED%y%BladeLn2Mesh(K)%RemapFlag = .FALSE. END DO - end if - - ED%Input( 1)%NacelleLoads%RemapFlag = .FALSE. - ED%y%NacelleMotion%RemapFlag = .FALSE. - ED%Input( 1)%TFinCMLoads%RemapFlag = .FALSE. - ED%y%TFinCMMotion%RemapFlag = .FALSE. - ED%Input( 1)%HubPtLoad%RemapFlag = .FALSE. - ED%y%HubPtMotion%RemapFlag = .FALSE. + if (allocated(ED%Input(1)%BladePtLoads)) then + DO K=1,SIZE(ED%Input(1)%BladePtLoads) + ED%Input( 1)%BladePtLoads(K)%RemapFlag = .FALSE. + ED%y%BladeLn2Mesh(K)%RemapFlag = .FALSE. + END DO + end if + + ED%Input( 1)%NacelleLoads%RemapFlag = .FALSE. + ED%y%NacelleMotion%RemapFlag = .FALSE. + ED%Input( 1)%TFinCMLoads%RemapFlag = .FALSE. + ED%y%TFinCMMotion%RemapFlag = .FALSE. + ED%Input( 1)%HubPtLoad%RemapFlag = .FALSE. + ED%y%HubPtMotion%RemapFlag = .FALSE. + endif ! BeamDyn meshes IF ( p_FAST%CompElast == Module_BD ) THEN @@ -3976,14 +4184,16 @@ SUBROUTINE ResetRemapFlags(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAP END SUBROUTINE ResetRemapFlags !---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes all of the mapping data structures needed between the various modules. -SUBROUTINE InitModuleMappings(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg) +SUBROUTINE InitModuleMappings(p_FAST, ED, SED, BD, AD, ADsk, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg) !............................................................................................................................... TYPE(FAST_ParameterType), INTENT(INOUT) :: p_FAST !< Parameters for the glue code TYPE(ElastoDyn_Data),TARGET,INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data),TARGET, INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(HydroDyn_Data), INTENT(INOUT) :: HD !< HydroDyn data TYPE(SubDyn_Data), TARGET, INTENT(INOUT) :: SD !< SubDyn data @@ -4019,24 +4229,31 @@ SUBROUTINE InitModuleMappings(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, ErrStat = ErrID_None ErrMsg = "" - - IF (p_FAST%CompElast == Module_BD) THEN - NumBl = p_FAST%nBeams ! BeamDyn might set this to 1 blade for aeromaps (instead of SIZE(ED%y%BladeRootMotion,1)) - ELSE - NumBl = SIZE(ED%y%BladeRootMotion,1) - END IF + + if (p_FAST%CompElast == Module_SED) then + NumBl = SIZE(SED%y%BladeRootMotion,1) + PlatformMotion => SED%y%PlatformPtMesh + elseif (p_FAST%CompElast == Module_ED) then + NumBl = SIZE(ED%y%BladeRootMotion,1) + PlatformMotion => ED%y%PlatformPtMesh + PlatformLoads => ED%Input(1)%PlatformPtMesh + elseif (p_FAST%CompElast == Module_BD) then + NumBl = p_FAST%nBeams ! BeamDyn might set this to 1 blade for aeromaps (instead of SIZE(ED%y%BladeRootMotion,1)) PlatformMotion => ED%y%PlatformPtMesh PlatformLoads => ED%Input(1)%PlatformPtMesh + endif - IF (p_FAST%CompSub == MODULE_SD) THEN - SubstructureMotion2HD => SD%y%y2Mesh - SubstructureMotion => SD%y%y3Mesh - SubstructureLoads => SD%Input(1)%LMesh - ELSE ! all of these get mapped to ElastoDyn ! (offshore floating with rigid substructure) - SubstructureMotion2HD => ED%y%PlatformPtMesh - SubstructureMotion => ED%y%PlatformPtMesh - SubstructureLoads => ED%Input(1)%PlatformPtMesh - END IF + if (p_FAST%CompElast /= Module_SED) then ! HD cannot be used with SED + IF (p_FAST%CompSub == MODULE_SD) THEN + SubstructureMotion2HD => SD%y%y2Mesh + SubstructureMotion => SD%y%y3Mesh + SubstructureLoads => SD%Input(1)%LMesh + ELSE ! all of these get mapped to ElastoDyn ! (offshore floating with rigid substructure) + SubstructureMotion2HD => ED%y%PlatformPtMesh + SubstructureMotion => ED%y%PlatformPtMesh + SubstructureLoads => ED%Input(1)%PlatformPtMesh + END IF + endif !............................................................................................................................ @@ -4235,85 +4452,148 @@ SUBROUTINE InitModuleMappings(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, !------------------------- -! ElastoDyn <-> AeroDyn +! ElastoDyn/Simplified-ElastoDyn <-> AeroDyn15 !------------------------- IF ( (p_FAST%CompAero == Module_AD) .OR. (p_FAST%CompAero == Module_ExtLd) ) THEN ! ED-AD and/or BD-AD ! allocate per-blade space for mapping to structural module - - ! Blade root meshes - ALLOCATE( MeshMapData%ED_P_2_AD_P_R(NumBl), STAT=ErrStat2 ) - IF ( ErrStat2 /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, 'Error allocating MeshMapData%ED_P_2_AD_P_R.', ErrStat, ErrMsg, RoutineName ) - RETURN - END IF - - ! Blade meshes: (allocate two mapping data structures to number of blades, then allocate data inside the structures) - ALLOCATE( MeshMapData%BDED_L_2_AD_L_B(NumBl), MeshMapData%AD_L_2_BDED_B(NumBl), STAT=ErrStat2 ) - IF ( ErrStat2 /= 0 ) THEN - CALL SetErrStat( ErrID_Fatal, 'Error allocating MeshMapData%BDED_L_2_AD_L_B and MeshMapData%AD_L_2_BDED_B.', & - ErrStat, ErrMsg, RoutineName ) - RETURN - END IF - + if (p_FAST%CompElast == Module_SED) then + + ! Blade root meshes + ALLOCATE( MeshMapData%SED_P_2_AD_P_R(NumBl), STAT=ErrStat2 ) + IF ( ErrStat2 /= 0 ) THEN + CALL SetErrStat( ErrID_Fatal, 'Error allocating MeshMapData%SED_P_2_AD_P_R.', ErrStat, ErrMsg, RoutineName ) + RETURN + END IF + + ! Blade meshes: Map SED blade root to AD blade line. Load is completely ignored. + ALLOCATE( MeshMapData%SED_P_2_AD_L_B(NumBl), MeshMapData%AD_L_2_SED_P(NumBl), STAT=ErrStat2 ) + IF ( ErrStat2 /= 0 ) THEN + CALL SetErrStat( ErrID_Fatal, 'Error allocating MeshMapData%SED_P_2_AD_L_B and MeshMapData%AD_L_2_SED_P.', ErrStat, ErrMsg, RoutineName ) + RETURN + END IF + + else + + ! Blade root meshes + ALLOCATE( MeshMapData%ED_P_2_AD_P_R(NumBl), STAT=ErrStat2 ) + IF ( ErrStat2 /= 0 ) THEN + CALL SetErrStat( ErrID_Fatal, 'Error allocating MeshMapData%ED_P_2_AD_P_R.', ErrStat, ErrMsg, RoutineName ) + RETURN + END IF + + ! Blade meshes: (allocate two mapping data structures to number of blades, then allocate data inside the structures) + ALLOCATE( MeshMapData%BDED_L_2_AD_L_B(NumBl), MeshMapData%AD_L_2_BDED_B(NumBl), STAT=ErrStat2 ) + IF ( ErrStat2 /= 0 ) THEN + CALL SetErrStat( ErrID_Fatal, 'Error allocating MeshMapData%BDED_L_2_AD_L_B and MeshMapData%AD_L_2_BDED_B.', & + ErrStat, ErrMsg, RoutineName ) + RETURN + END IF + + endif +!------------------------- +! Simplified-ElastoDyn <-> AeroDyn +!------------------------- + IF ( p_FAST%CompElast == Module_SED ) then + ! blade root meshes + DO K=1,NumBl + CALL MeshMapCreate( SED%y%BladeRootMotion(K), AD%Input(1)%rotors(1)%BladeRootMotion(K), MeshMapData%SED_P_2_AD_P_R(K), ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':SED_2_AD_RootMotion('//TRIM(Num2LStr(K))//')' ) + END DO + + ! Hub point mesh + CALL MeshMapCreate( SED%y%HubPtMotion, AD%Input(1)%rotors(1)%HubMotion, MeshMapData%SED_P_2_AD_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':SED_2_AD_HubMotion' ) + + ! Tower mesh: (SED does not use tower loads, so only motion mapped) + IF ( AD%Input(1)%rotors(1)%TowerMotion%Committed ) THEN + CALL MeshMapCreate( SED%y%TowerLn2Mesh, AD%Input(1)%rotors(1)%TowerMotion, MeshMapData%SED_L_2_AD_L_T, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':SED_2_AD_TowerMotion' ) + END IF + ! Nacelle mesh: (SED does not use nacelle loads, so only motion mapped) + IF ( AD%Input(1)%rotors(1)%NacelleMotion%Committed ) THEN + CALL MeshMapCreate( SED%y%NacelleMotion, AD%Input(1)%rotors(1)%NacelleMotion, MeshMapData%SED_P_2_AD_P_N, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':SED_2_AD_NacelleMotion' ) + endif + !------------------------- ! ElastoDyn <-> AeroDyn !------------------------- + ELSE ! ED or BD + ! blade root meshes + DO K=1,NumBl + CALL MeshMapCreate( ED%y%BladeRootMotion(K), AD%Input(1)%rotors(1)%BladeRootMotion(K), MeshMapData%ED_P_2_AD_P_R(K), ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_RootMotion('//TRIM(Num2LStr(K))//')' ) + END DO - ! blade root meshes - DO K=1,NumBl - CALL MeshMapCreate( ED%y%BladeRootMotion(K), AD%Input(1)%rotors(1)%BladeRootMotion(K), MeshMapData%ED_P_2_AD_P_R(K), ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_RootMotion('//TRIM(Num2LStr(K))//')' ) - END DO + ! Hub point mesh: + IF ( AD%Input(1)%rotors(1)%HubMotion%Committed ) THEN + CALL MeshMapCreate( ED%y%HubPtMotion, AD%Input(1)%rotors(1)%HubMotion, MeshMapData%ED_P_2_AD_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_HubMotion' ) + CALL MeshMapCreate( AD%y%rotors(1)%HubLoad, ED%Input(1)%HubPtLoad, MeshMapData%AD_P_2_ED_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_HubLoad' ) + + CALL MeshCopy( ED%Input(1)%HubPtLoad, MeshMapData%u_ED_HubPtLoad, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':u_ED_HubPtLoad' ) + END IF + - ! Hub point mesh: - IF ( AD%Input(1)%rotors(1)%HubMotion%Committed ) THEN - CALL MeshMapCreate( ED%y%HubPtMotion, AD%Input(1)%rotors(1)%HubMotion, MeshMapData%ED_P_2_AD_P_H, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_HubMotion' ) - CALL MeshMapCreate( AD%y%rotors(1)%HubLoad, ED%Input(1)%HubPtLoad, MeshMapData%AD_P_2_ED_P_H, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_HubLoad' ) + ! Tower mesh: + IF ( AD%Input(1)%rotors(1)%TowerMotion%Committed ) THEN + CALL MeshMapCreate( ED%y%TowerLn2Mesh, AD%Input(1)%rotors(1)%TowerMotion, MeshMapData%ED_L_2_AD_L_T, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_TowerMotion' ) + + IF ( AD%y%rotors(1)%TowerLoad%Committed ) THEN + CALL MeshMapCreate( AD%y%rotors(1)%TowerLoad, ED%Input(1)%TowerPtLoads, MeshMapData%AD_L_2_ED_P_T, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_TowerLoad' ) + END IF + END IF + + ! Nacelle mesh: + IF ( AD%Input(1)%rotors(1)%NacelleMotion%Committed ) THEN + CALL MeshMapCreate( ED%y%NacelleMotion, AD%Input(1)%rotors(1)%NacelleMotion, MeshMapData%ED_P_2_AD_P_N, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_NacelleMotion' ) + CALL MeshMapCreate( AD%y%rotors(1)%NacelleLoad, ED%Input(1)%NacelleLoads, MeshMapData%AD_P_2_ED_P_N, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_NacelleLoads' ) + if (.not. MeshMapData%u_ED_NacelleLoads%Committed ) then ! May have been set for NStC intance + CALL MeshCopy( ED%Input(1)%NacelleLoads, MeshMapData%u_ED_NacelleLoads, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':u_ED_NacelleLoads' ) + endif + END IF - CALL MeshCopy( ED%Input(1)%HubPtLoad, MeshMapData%u_ED_HubPtLoad, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':u_ED_HubPtLoad' ) + ! Tailfin mesh: + if ( AD%Input(1)%rotors(1)%TFinMotion%Committed ) then + CALL MeshMapCreate( ED%y%TFinCMMotion, AD%Input(1)%rotors(1)%TFinMotion, MeshMapData%ED_P_2_AD_P_TF, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_TailFinMotion' ) + CALL MeshMapCreate( AD%y%rotors(1)%TFinLoad, ED%Input(1)%TFinCMLoads, MeshMapData%AD_P_2_ED_P_TF, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_TailFinLoads' ) + endif END IF - - ! Tower mesh: - IF ( AD%Input(1)%rotors(1)%TowerMotion%Committed ) THEN - CALL MeshMapCreate( ED%y%TowerLn2Mesh, AD%Input(1)%rotors(1)%TowerMotion, MeshMapData%ED_L_2_AD_L_T, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_TowerMotion' ) - - IF ( AD%y%rotors(1)%TowerLoad%Committed ) THEN - CALL MeshMapCreate( AD%y%rotors(1)%TowerLoad, ED%Input(1)%TowerPtLoads, MeshMapData%AD_L_2_ED_P_T, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_TowerLoad' ) - END IF - END IF - - ! Nacelle mesh: - IF ( AD%Input(1)%rotors(1)%NacelleMotion%Committed ) THEN - CALL MeshMapCreate( ED%y%NacelleMotion, AD%Input(1)%rotors(1)%NacelleMotion, MeshMapData%ED_P_2_AD_P_N, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_NacelleMotion' ) - CALL MeshMapCreate( AD%y%rotors(1)%NacelleLoad, ED%Input(1)%NacelleLoads, MeshMapData%AD_P_2_ED_P_N, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_NacelleLoads' ) - if (.not. MeshMapData%u_ED_NacelleLoads%Committed ) then ! May have been set for NStC intance - CALL MeshCopy( ED%Input(1)%NacelleLoads, MeshMapData%u_ED_NacelleLoads, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':u_ED_NacelleLoads' ) - endif - endif - ! Tailfin mesh: - if ( AD%Input(1)%rotors(1)%TFinMotion%Committed ) then - CALL MeshMapCreate( ED%y%TFinCMMotion, AD%Input(1)%rotors(1)%TFinMotion, MeshMapData%ED_P_2_AD_P_TF, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_AD_TailFinMotion' ) - CALL MeshMapCreate( AD%y%rotors(1)%TFinLoad, ED%Input(1)%TFinCMLoads, MeshMapData%AD_P_2_ED_P_TF, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_2_ED_TailFinLoads' ) - endif - - IF ( p_FAST%CompElast == Module_ED ) then + IF ( p_FAST%CompElast == Module_SED ) then +!------------------------- +! Simplified-ElastoDyn <-> AeroDyn +!------------------------- + + ! Blade meshes: + DO K=1,NumBl + CALL MeshMapCreate( SED%y%BladeRootMotion(K), AD%Input(1)%rotors(1)%BladeMotion(K), MeshMapData%SED_P_2_AD_L_B(K), ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':SED_2_AD_BladeMotion('//TRIM(Num2LStr(K))//')' ) + CALL MeshMapCreate( AD%y%rotors(1)%BladeLoad(K), SED%Input(1)%HubPtLoad, MeshMapData%AD_L_2_SED_P(K), ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':AD_L_2_SED_P('//TRIM(Num2LStr(K))//')' ) + END DO + CALL MeshCopy ( SED%Input(1)%HubPtLoad, MeshMapData%u_SED_HubPtLoad, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':u_SED_HubPtLoad' ) + + ELSEIF ( p_FAST%CompElast == Module_ED ) then +!------------------------- +! ElastoDyn <-> AeroDyn +!------------------------- ! Blade meshes: DO K=1,NumBl @@ -4376,7 +4656,28 @@ SUBROUTINE InitModuleMappings(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, END IF ! CompElast - END IF ! AeroDyn/AeroDyn14 to structural code + + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN ! ED-ADsk + if (p_FAST%CompElast == Module_SED) then +!------------------------- +! Simplified-ElastoDyn <-> AeroDisk +!------------------------- + ! Hub point mesh + CALL MeshMapCreate( SED%y%HubPtMotion, ADsk%Input(1)%HubMotion, MeshMapData%SED_P_2_ADsk_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':SED_2_ADsk_HubMotion' ) + CALL MeshMapCreate( ADsk%y%AeroLoads, SED%Input(1)%HubPtLoad, MeshMapData%ADsk_P_2_SED_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ADsk_2_SED_HubPtLoad' ) + else +!------------------------- +! ElastoDyn <-> AeroDisk +!------------------------- + ! Hub point mesh + CALL MeshMapCreate( ED%y%HubPtMotion, ADsk%Input(1)%HubMotion, MeshMapData%ED_P_2_ADsk_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ED_2_ADsk_HubMotion' ) + CALL MeshMapCreate( ADsk%y%AeroLoads, ED%Input(1)%HubPtLoad, MeshMapData%ADsk_P_2_ED_P_H, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName//':ADsk_2_ED_HubPtLoad' ) + endif ! SED/ED + END IF ! AeroDyn14/AeroDyn/AeroDisk to structural code IF ( p_FAST%CompAero == Module_ExtLd ) THEN ! ED-ExtLd and/or BD-ExtLd @@ -4666,7 +4967,7 @@ SUBROUTINE InitModuleMappings(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, !............................................................................................................................ ! reset the remap flags (do this before making the copies else the copies will always have remap = true) !............................................................................................................................ - CALL ResetRemapFlags(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD ) + CALL ResetRemapFlags(p_FAST, ED, SED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD ) !............................................................................................................................ ! initialize the temporary input meshes (for input-output solves in Solve Option 1): @@ -4774,8 +5075,8 @@ END SUBROUTINE InitModuleMappings !! once at the start of the n_t_global loop and once in the j_pc loop, using different states. !! *** Note that modules that do not have direct feedthrough should be called first. *** SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, calcJacobian, NextJacCalcTime, & - p_FAST, m_FAST, WriteThisStep, ED, BD, & - SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) + p_FAST, m_FAST, WriteThisStep, ED, SED, BD, & + SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) REAL(DbKi) , intent(in ) :: this_time !< The current simulation time (actual or time of prediction) INTEGER(IntKi) , intent(in ) :: this_state !< Index into the state array (current or predicted states) INTEGER(IntKi) , intent(in ) :: n_t_global !< current time step (used only for SrvD hack) @@ -4787,9 +5088,11 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca LOGICAL , INTENT(IN ) :: WriteThisStep !< Will we print the WriteOutput values this step? TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -4852,11 +5155,11 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca !> Solve option 2 (modules without direct feedthrough): - CALL SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, n_t_global < 0, WriteThisStep) + CALL SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, n_t_global < 0, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) #ifdef OUTPUT_MASS_MATRIX - if (n_t_global == 0) then + if (n_t_global == 0 .and. p_FAST%CompElast /= Module_SED) then UnMM = -1 CALL GetNewUnit( UnMM, ErrStat2, ErrMsg2 ) CALL OpenFOutFile( UnMM, TRIM(p_FAST%OutFileRoot)//'.EDMassMatrix', ErrStat2, ErrMsg2) @@ -4876,7 +5179,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca CALL Transfer_Structure_to_Opt1Inputs( this_time, this_state, p_FAST, ED%y, HD%Input(1), SD, ExtPtfm%Input(1), & MAPp%Input(1), FEAM%Input(1), MD%Input(1), & Orca%Input(1), BD%Input(1,:), SrvD%Input(1), MeshMapData, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) !> Solve option 1 (rigorous solve on loads/accelerations) @@ -4888,12 +5191,12 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca IF ( p_FAST%CompAero == Module_AD ) THEN - CALL AD_InputSolve_NoIfW( p_FAST, AD%Input(1), SrvD%y, ED%y, BD, MeshMapData, ErrStat2, ErrMsg2 ) + CALL AD_InputSolve_NoIfW( p_FAST, AD%Input(1), SrvD%y, ED%y, SED%y, BD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE IF (p_FAST%CompAero == Module_ExtLd ) THEN - CALL AD_InputSolve_NoIfW( p_FAST, AD%Input(1), SrvD%y, ED%y, BD, MeshMapData, ErrStat2, ErrMsg2 ) + CALL AD_InputSolve_NoIfW( p_FAST, AD%Input(1), SrvD%y, ED%y, SED%y, BD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL ExtLd_UpdateFlowField( p_FAST, AD%Input(1), AD%m, ExtLd, ErrStat2, ErrMsg2 ) @@ -4908,7 +5211,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca END IF IF ( p_FAST%CompInflow == Module_IfW ) THEN - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD%Input(1), AD%OtherSt(this_state), ED%y, ErrStat2, ErrMsg2 ) + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD%Input(1), AD%OtherSt(this_state), ED%y, SED%y, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE IF ( p_FAST%CompInflow == Module_ExtInfw ) THEN ! ExternalInflow is the driver and it sets these inputs outside of this solve; the ExternalInflow inputs and outputs thus don't change @@ -4920,7 +5223,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca IF ( p_FAST%CompServo == Module_SrvD ) THEN - CALL SrvD_InputSolve( p_FAST, m_FAST, SrvD%Input(1), ED%y, IfW%y, ExtInfw%y, ExtLd%p, BD%y, SD%y, MeshmapData, ErrStat2, ErrMsg2 ) + CALL SrvD_InputSolve( p_FAST, m_FAST, SrvD%Input(1), ED%y, SED%y, IfW%y, ExtInfw%y, ExtLd%p, BD%y, SD%y, MeshmapData, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END IF @@ -4934,7 +5237,7 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca ! Reset each mesh's RemapFlag (after calling all InputSolve routines): !..................................................................... - CALL ResetRemapFlags(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + CALL ResetRemapFlags(p_FAST, ED, SED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) END SUBROUTINE CalcOutputs_And_SolveForInputs @@ -5112,8 +5415,8 @@ SUBROUTINE SolveOption1(this_time, this_state, calcJacobian, p_FAST, ED, BD, HD, END IF - ! Map motions for ServodDyn Structural control (TMD) if used. - IF ( p_FAST%CompServo == Module_SrvD ) THEN + ! Map motions for ServodDyn Structural control (TMD) if used (not allowed with SED). + IF ( p_FAST%CompServo == Module_SrvD .and. p_FAST%CompElast /= Module_SED ) THEN call Transfer_Substructure_to_SStC( SrvD%Input(1), SubstructureMotion, MeshMapData, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) END IF @@ -5136,7 +5439,7 @@ SUBROUTINE SolveOption1(this_time, this_state, calcJacobian, p_FAST, ED, BD, HD, END SUBROUTINE SolveOption1 !---------------------------------------------------------------------------------------------------------------------------------- !> This routine implements the first part of the "option 2" solve for inputs that apply to BeamDyn and AeroDyn -SUBROUTINE SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, WriteThisStep) +SUBROUTINE SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, WriteThisStep) REAL(DbKi) , intent(in ) :: this_time !< The current simulation time (actual or time of prediction) INTEGER(IntKi) , intent(in ) :: this_state !< Index into the state array (current or predicted states) @@ -5144,9 +5447,11 @@ SUBROUTINE SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, BD, A TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Misc variables for the glue code (including external inputs) TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data TYPE(FAST_ModuleMapType), INTENT(INOUT) :: MeshMapData !< Data for mapping between modules @@ -5168,7 +5473,11 @@ SUBROUTINE SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, BD, A ErrStat = ErrID_None ErrMsg = "" - + + IF ( p_FAST%CompElast == Module_SED ) THEN + CALL SED_CalcOutput( this_time, SED%Input(1), SED%p, SED%x(this_state), SED%xd(this_state), SED%z(this_state), SED%OtherSt(this_state), SED%y, SED%m, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ELSE CALL ED_CalcOutput( this_time, ED%Input(1), ED%p, ED%x(this_state), ED%xd(this_state), ED%z(this_state), ED%OtherSt(this_state), ED%y, ED%m, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -5177,12 +5486,13 @@ SUBROUTINE SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, BD, A CALL Transfer_ED_to_BD(ED%y, BD%Input(1,:), MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat, ErrMsg,RoutineName ) END IF - + + END IF END SUBROUTINE SolveOption2a_Inp2BD !---------------------------------------------------------------------------------------------------------------------------------- !> This routine implements the first part of the "option 2" solve for inputs that apply to AeroDyn & InflowWind -SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, WriteThisStep) +SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, WriteThisStep) REAL(DbKi) , intent(in ) :: this_time !< The current simulation time (actual or time of prediction) INTEGER(IntKi) , intent(in ) :: this_state !< Index into the state array (current or predicted states) @@ -5190,9 +5500,11 @@ SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Misc variables for the glue code (including external inputs) TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -5230,15 +5542,18 @@ SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, IF ( ( p_FAST%CompAero == Module_AD ) .OR. (p_FAST%CompAero == Module_ExtLd) ) THEN ! note that this uses BD outputs, which are from the previous step (and need to be initialized) - CALL AD_InputSolve_NoIfW( p_FAST, AD%Input(1), SrvD%y, ED%y, BD, MeshMapData, ErrStat2, ErrMsg2 ) + CALL AD_InputSolve_NoIfW( p_FAST, AD%Input(1), SrvD%y, ED%y, SED%y, BD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - END IF + ELSE IF ( p_FAST%CompAero == Module_ADsk ) THEN + CALL ADsk_InputSolve_NoIfW( p_FAST, ADsk%Input(1), ED%y, SED%y, MeshMapData, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + END IF IF (p_FAST%CompInflow == Module_IfW) THEN ! must be done after ED_CalcOutput and before AD_CalcOutput and SrvD - CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD%Input(1), AD%OtherSt(this_state), ED%y, ErrStat2, ErrMsg2 ) ! do we want this to be curr states + CALL IfW_InputSolve( p_FAST, m_FAST, IfW%Input(1), IfW%p, AD%Input(1), AD%OtherSt(this_state), ED%y, SED%y, ErrStat2, ErrMsg2 ) ! do we want this to be curr states CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) !ELSE IF ( p_FAST%CompInflow == Module_ExtInfw ) THEN ! ! ExternalInflow is the driver and it computes outputs outside of this solve; the ExternalInflow inputs and outputs thus don't change @@ -5252,7 +5567,7 @@ SUBROUTINE SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, END SUBROUTINE SolveOption2b_Inp2IfW !---------------------------------------------------------------------------------------------------------------------------------- !> This routine implements the first part of the "option 2" solve for inputs that apply to AeroDyn and ServoDyn. -SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, WriteThisStep) +SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, WriteThisStep) REAL(DbKi) , intent(in ) :: this_time !< The current simulation time (actual or time of prediction) INTEGER(IntKi) , intent(in ) :: this_state !< Index into the state array (current or predicted states) @@ -5260,10 +5575,12 @@ SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Misc variables for the glue code (including external inputs) TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(SubDyn_Data), INTENT(INOUT) :: SD !< SubDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -5290,9 +5607,15 @@ SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, IF (p_FAST%CompInflow == Module_IfW) THEN ! get Lidar position directly from hub mesh (may map meshes later) - IfW%Input(1)%lidar%HubDisplacementX = ED%y%HubPtMotion%TranslationDisp(1,1) - IfW%Input(1)%lidar%HubDisplacementY = ED%y%HubPtMotion%TranslationDisp(2,1) - IfW%Input(1)%lidar%HubDisplacementZ = ED%y%HubPtMotion%TranslationDisp(3,1) + if ( p_FAST%CompElast == Module_SED ) then + IfW%Input(1)%lidar%HubDisplacementX = SED%y%HubPtMotion%TranslationDisp(1,1) + IfW%Input(1)%lidar%HubDisplacementY = SED%y%HubPtMotion%TranslationDisp(2,1) + IfW%Input(1)%lidar%HubDisplacementZ = SED%y%HubPtMotion%TranslationDisp(3,1) + else + IfW%Input(1)%lidar%HubDisplacementX = ED%y%HubPtMotion%TranslationDisp(1,1) + IfW%Input(1)%lidar%HubDisplacementY = ED%y%HubPtMotion%TranslationDisp(2,1) + IfW%Input(1)%lidar%HubDisplacementZ = ED%y%HubPtMotion%TranslationDisp(3,1) + endif CALL InflowWind_CalcOutput( this_time, IfW%Input(1), IfW%p, IfW%x(this_state), IfW%xd(this_state), IfW%z(this_state), & IfW%OtherSt(this_state), IfW%y, IfW%m, ErrStat2, ErrMsg2 ) @@ -5311,18 +5634,14 @@ SUBROUTINE SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, END IF IF (p_FAST%CompAero == Module_ExtLd ) THEN - ! The outputs from ExternalInflow need to be transfered to the FlowField for use by AeroDyn, this seems like the right place call ExtLd_UpdateFlowField( p_FAST, AD%Input(1), AD%m, ExtLd, ErrStat2, ErrMsg2 ) call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - END IF IF ( p_FAST%CompServo == Module_SrvD ) THEN - - CALL SrvD_InputSolve( p_FAST, m_FAST, SrvD%Input(1), ED%y, IfW%y, ExtInfw%y, ExtLd%p, BD%y, SD%y, MeshMapData, ErrStat2, ErrMsg2 ) - + CALL SrvD_InputSolve( p_FAST, m_FAST, SrvD%Input(1), ED%y, SED%y, IfW%y, ExtInfw%y, ExtLd%p, BD%y, SD%y, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END IF @@ -5330,7 +5649,7 @@ END SUBROUTINE SolveOption2c_Inp2AD_SrvD !---------------------------------------------------------------------------------------------------------------------------------- !> This routine implements the "option 2" solve for all inputs without direct links to HD, SD, MAP, or the ED platform reference !! point. -SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, firstCall, WriteThisStep) +SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat, ErrMsg, firstCall, WriteThisStep) !............................................................................................................................... LOGICAL , intent(in ) :: firstCall !< flag to determine how to call ServoDyn (a hack) REAL(DbKi) , intent(in ) :: this_time !< The current simulation time (actual or time of prediction) @@ -5340,10 +5659,12 @@ SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Misc variables for the glue code (including external inputs) TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(SubDyn_Data), INTENT(INOUT) :: SD !< SubDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLD !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -5372,13 +5693,13 @@ SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd ! SolveOption2* routines are being called in FAST_AdvanceStates, but the first time we call CalcOutputs_And_SolveForInputs, we haven't called the AdvanceStates routine IF (firstCall) THEN ! call ElastoDyn's CalcOutput & compute BD inputs from ED: - CALL SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) + CALL SolveOption2a_Inp2BD(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! compute AD position inputs; compute all of IfW inputs from ED/BD outputs: - CALL SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) + CALL SolveOption2b_Inp2IfW(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! call IfW's CalcOutput; transfer wind-inflow inputs to AD; compute all of SrvD inputs: - CALL SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) + CALL SolveOption2c_Inp2AD_SrvD(this_time, this_state, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! ELSE ! these subroutines are called in the AdvanceStates routine before BD, IfW, AD, and SrvD states are updated. This gives a more accurate solution that would otherwise require a correction step. END IF @@ -5388,6 +5709,10 @@ SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd CALL AD_CalcOutput( this_time, AD%Input(1), AD%p, AD%x(this_state), AD%xd(this_state), AD%z(this_state), & AD%OtherSt(this_state), AD%y, AD%m, ErrStat2, ErrMsg2, WriteThisStep ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ELSE IF ( p_FAST%CompAero == Module_ADsk ) THEN + CALL ADsk_CalcOutput( this_time, ADsk%Input(1), ADsk%p, ADsk%x(this_state), ADsk%xd(this_state), ADsk%z(this_state), & + ADsk%OtherSt(this_state), ADsk%y, ADsk%m, ErrStat2, ErrMsg2, WriteThisStep ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE IF (p_FAST%CompAero == Module_ExtLd ) THEN @@ -5431,8 +5756,13 @@ SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd !bjj: note ED%Input(1) may be a sibling mesh of output, but ED%u is not (routine may update something that needs to be shared between siblings) - CALL ED_InputSolve( p_FAST, ED%Input(1), ED%y, AD%y, SrvD%y, AD%Input(1), ExtLd%y, ExtLd%m, ExtLd%u, ExtLd%p, SrvD%Input(1), MeshMapData, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (p_FAST%CompElast == Module_SED) then + CALL SED_InputSolve( p_FAST, SED%Input(1), SED%y, AD%y, ADsk%y, SrvD%y, AD%Input(1), ADsk%Input(1), SrvD%Input(1), MeshMapData, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + else + CALL ED_InputSolve( p_FAST, ED%Input(1), ED%y, AD%y, ADsk%y, SrvD%y, AD%Input(1), ADsk%Input(1), ExtLd%y, ExtLd%m, ExtLd%u, ExtLd%p, SrvD%Input(1), MeshMapData, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + endif CALL BD_InputSolve( p_FAST, BD, AD%y, AD%Input(1), ExtLd%m, ExtLd%y, ExtLd%u, ExtLd%p, ED%y, SrvD%y, SrvD%Input(1), MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -5440,7 +5770,7 @@ SUBROUTINE SolveOption2(this_time, this_state, p_FAST, m_FAST, ED, BD, AD, ExtLd END SUBROUTINE SolveOption2 !---------------------------------------------------------------------------------------------------------------------------------- !> This routines advances the states of each module -SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, & +SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg, WriteThisStep ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial simulation time (almost always 0) @@ -5449,9 +5779,11 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, Sr TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -5494,30 +5826,52 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, Sr ! (note that we need to copy the states because UpdateStates updates the values ! and we need to have the old values [at m_FAST%t_global] for the next j_pc step) !---------------------------------------------------------------------------------------- - ! ElastoDyn: get predicted states - CALL ED_CopyContState (ED%x( STATE_CURR), ED%x( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyDiscState (ED%xd(STATE_CURR), ED%xd(STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyConstrState (ED%z( STATE_CURR), ED%z( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyOtherState (ED%OtherSt( STATE_CURR), ED%OtherSt( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - - DO j_ss = 1, p_FAST%n_substeps( MODULE_ED ) - n_t_module = n_t_global*p_FAST%n_substeps( MODULE_ED ) + j_ss - 1 - t_module = n_t_module*p_FAST%dt_module( MODULE_ED ) + t_initial - - CALL ED_UpdateStates( t_module, n_t_module, ED%Input, ED%InputTimes, ED%p, ED%x(STATE_PRED), ED%xd(STATE_PRED), & - ED%z(STATE_PRED), ED%OtherSt(STATE_PRED), ED%m, ErrStat2, ErrMsg2 ) + if (p_FAST%CompElast == Module_SED) then + ! Simplified-ElastoDyn: get predicted states + CALL SED_CopyContState (SED%x( STATE_CURR), SED%x( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyDiscState (SED%xd(STATE_CURR), SED%xd(STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - IF (ErrStat >= AbortErrLev) RETURN - END DO !j_ss + CALL SED_CopyConstrState (SED%z( STATE_CURR), SED%z( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyOtherState (SED%OtherSt( STATE_CURR), SED%OtherSt( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + DO j_ss = 1, p_FAST%n_substeps( MODULE_ED ) + n_t_module = n_t_global*p_FAST%n_substeps( MODULE_ED ) + j_ss - 1 + t_module = n_t_module*p_FAST%dt_module( MODULE_ED ) + t_initial + + CALL SED_UpdateStates( t_module, n_t_module, SED%Input, SED%InputTimes, SED%p, SED%x(STATE_PRED), SED%xd(STATE_PRED), & + SED%z(STATE_PRED), SED%OtherSt(STATE_PRED), SED%m, ErrStat2, ErrMsg2 ) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + END DO !j_ss + else + ! ElastoDyn: get predicted states + CALL ED_CopyContState (ED%x( STATE_CURR), ED%x( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyDiscState (ED%xd(STATE_CURR), ED%xd(STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyConstrState (ED%z( STATE_CURR), ED%z( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyOtherState (ED%OtherSt( STATE_CURR), ED%OtherSt( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + DO j_ss = 1, p_FAST%n_substeps( MODULE_ED ) + n_t_module = n_t_global*p_FAST%n_substeps( MODULE_ED ) + j_ss - 1 + t_module = n_t_module*p_FAST%dt_module( MODULE_ED ) + t_initial + + CALL ED_UpdateStates( t_module, n_t_module, ED%Input, ED%InputTimes, ED%p, ED%x(STATE_PRED), ED%xd(STATE_PRED), & + ED%z(STATE_PRED), ED%OtherSt(STATE_PRED), ED%m, ErrStat2, ErrMsg2 ) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat >= AbortErrLev) RETURN + END DO !j_ss + endif ! BeamDyn doesn't like extrapolated rotations, so we will calculate them from ED and transfer instead of doing a correction step. ! (Also calls ED_CalcOutput here so that we can use it for AeroDyn optimization, too): - CALL SolveOption2a_Inp2BD(t_global_next, STATE_PRED, p_FAST, m_FAST, ED, BD, AD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) + CALL SolveOption2a_Inp2BD(t_global_next, STATE_PRED, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( p_FAST%CompElast == Module_BD ) THEN @@ -5549,7 +5903,7 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, Sr ! because AeroDyn DBEMT states depend heavily on getting inputs correct, we are overwriting its inputs with updated structural outputs here - CALL SolveOption2b_Inp2IfW(t_global_next, STATE_PRED, p_FAST, m_FAST, ED, BD, AD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) + CALL SolveOption2b_Inp2IfW(t_global_next, STATE_PRED, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -5576,7 +5930,7 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, Sr ! because AeroDyn DBEMT states depend heavily on getting inputs correct, we are overwriting its inputs with updated inflow outputs here - CALL SolveOption2c_Inp2AD_SrvD(t_global_next, STATE_PRED, p_FAST, m_FAST, ED, BD, AD, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) + CALL SolveOption2c_Inp2AD_SrvD(t_global_next, STATE_PRED, p_FAST, m_FAST, ED, SED, BD, AD, ADsk, ExtLd, SD, SrvD, IfW, ExtInfw, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! AeroDyn: get predicted states @@ -5598,7 +5952,24 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, Sr AD%xd(STATE_PRED), AD%z(STATE_PRED), AD%OtherSt(STATE_PRED), AD%m, ErrStat2, ErrMsg2 ) CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END DO !j_ss - END IF + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN + CALL ADsk_CopyContState (ADsk%x( STATE_CURR), ADsk%x( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyDiscState (ADsk%xd(STATE_CURR), ADsk%xd(STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyConstrState (ADsk%z( STATE_CURR), ADsk%z( STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyOtherState( ADsk%OtherSt(STATE_CURR), ADsk%OtherSt(STATE_PRED), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + DO j_ss = 1, p_FAST%n_substeps( MODULE_ADsk ) + n_t_module = n_t_global*p_FAST%n_substeps( MODULE_ADsk ) + j_ss - 1 + t_module = n_t_module*p_FAST%dt_module( MODULE_ADsk ) + t_initial + CALL ADsk_UpdateStates( t_module, n_t_module, ADsk%Input, ADsk%InputTimes, ADsk%p, ADsk%x(STATE_PRED), & + ADsk%xd(STATE_PRED), ADsk%z(STATE_PRED), ADsk%OtherSt(STATE_PRED), ADsk%m, ErrStat2, ErrMsg2 ) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + END DO !j_ss + END IF IF (p_FAST%CompAero == Module_ExtLd ) THEN ! DO WE HAVE TO DO SOMETHING HERE? @@ -5819,7 +6190,7 @@ SUBROUTINE FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, Sr END SUBROUTINE FAST_AdvanceStates !---------------------------------------------------------------------------------------------------------------------------------- !> This routine extrapolates inputs to modules to give predicted values at t+dt. -SUBROUTINE FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, & +SUBROUTINE FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, & IceF, IceD, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_global_next !< next global time step (t + dt), at which we're extrapolating inputs (and ED outputs) @@ -5827,9 +6198,11 @@ SUBROUTINE FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, BD, SrvD, A TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(HydroDyn_Data), INTENT(INOUT) :: HD !< HydroDyn data @@ -5865,19 +6238,35 @@ SUBROUTINE FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, BD, SrvD, A ErrStat = ErrID_None ErrMsg = "" - ! ElastoDyn - CALL ED_Input_ExtrapInterp(ED%Input, ED%InputTimes, ED%u, t_global_next, ErrStat2, ErrMsg2) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + if (p_FAST%CompElast == Module_SED) then + ! Simplified-ElastoDyn + CALL SED_Input_ExtrapInterp(SED%Input, SED%InputTimes, SED%u, t_global_next, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) - DO j = p_FAST%InterpOrder, 1, -1 - CALL ED_CopyInput (ED%Input(j), ED%Input(j+1), MESH_UPDATECOPY, Errstat2, ErrMsg2) + DO j = p_FAST%InterpOrder, 1, -1 + CALL SED_CopyInput (SED%Input(j), SED%Input(j+1), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + SED%InputTimes(j+1) = SED%InputTimes(j) + END DO + + CALL SED_CopyInput (SED%u, SED%Input(1), MESH_UPDATECOPY, Errstat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) - ED%InputTimes(j+1) = ED%InputTimes(j) - END DO - - CALL ED_CopyInput (ED%u, ED%Input(1), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) - ED%InputTimes(1) = t_global_next + SED%InputTimes(1) = t_global_next + else + ! ElastoDyn + CALL ED_Input_ExtrapInterp(ED%Input, ED%InputTimes, ED%u, t_global_next, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + + DO j = p_FAST%InterpOrder, 1, -1 + CALL ED_CopyInput (ED%Input(j), ED%Input(j+1), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + ED%InputTimes(j+1) = ED%InputTimes(j) + END DO + + CALL ED_CopyInput (ED%u, ED%Input(1), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + ED%InputTimes(1) = t_global_next + endif ! BeamDyn @@ -5921,7 +6310,22 @@ SUBROUTINE FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, BD, SrvD, A CALL AD_CopyInput (AD%u, AD%Input(1), MESH_UPDATECOPY, Errstat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) AD%InputTimes(1) = t_global_next - + + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN + CALL ADsk_Input_ExtrapInterp(ADsk%Input, ADsk%InputTimes, ADsk%u, t_global_next, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + + ! Shift "window" of ADsk%Input + DO j = p_FAST%InterpOrder, 1, -1 + CALL ADsk_CopyInput (ADsk%Input(j), ADsk%Input(j+1), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + ADsk%InputTimes(j+1) = ADsk%InputTimes(j) + END DO + + CALL ADsk_CopyInput (ADsk%u, ADsk%Input(1), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName ) + ADsk%InputTimes(1) = t_global_next + END IF ! CompAero IF (p_FAST%CompAero == Module_ExtLd ) THEN diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 667f40fcdb..46d37d96e6 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -50,18 +50,18 @@ SUBROUTINE FAST_InitializeAll_T( t_initial, TurbID, Turbine, ErrStat, ErrMsg, In IF (PRESENT(InFile)) THEN IF (PRESENT(ExternInitData)) THEN CALL FAST_InitializeAll( t_initial, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX,& + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX,& Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, CompAeroMaps, ErrStat, ErrMsg, InFile, ExternInitData ) ELSE CALL FAST_InitializeAll( t_initial, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, CompAeroMaps, ErrStat, ErrMsg, InFile ) END IF ELSE CALL FAST_InitializeAll( t_initial, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, CompAeroMaps, ErrStat, ErrMsg ) END IF @@ -70,7 +70,7 @@ SUBROUTINE FAST_InitializeAll_T( t_initial, TurbID, Turbine, ErrStat, ErrMsg, In END SUBROUTINE FAST_InitializeAll_T !---------------------------------------------------------------------------------------------------------------------------------- !> Routine to call Init routine for each module. This routine sets all of the init input data for each module. -SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, SeaSt, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, CompAeroMaps, ErrStat, ErrMsg, InFile, ExternInitData ) use ElastoDyn_Parameters, only: Method_RK4 @@ -81,9 +81,11 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -229,80 +231,106 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, p_FAST%dt_module = p_FAST%dt ! initialize time steps for each module - ! ........................ - ! initialize ElastoDyn (must be done first) - ! ........................ - - ALLOCATE( ED%Input( p_FAST%InterpOrder+1 ), ED%InputTimes( p_FAST%InterpOrder+1 ),STAT = ErrStat2 ) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal,"Error allocating ED%Input and ED%InputTimes.",ErrStat,ErrMsg,RoutineName) - CALL Cleanup() - RETURN - END IF - - ALLOCATE( ED%Input_Saved( p_FAST%InterpOrder+1 ), ED%InputTimes_Saved( p_FAST%InterpOrder+1 ), ED%Output_bak( p_FAST%InterpOrder+1 ),STAT = ErrStat2 ) - IF (ErrStat2 /= 0) THEN - CALL SetErrStat(ErrID_Fatal,"Error allocating ED%Input_Saved, ED%Output_bak, and ED%InputTimes_Saved.",ErrStat,ErrMsg,RoutineName) + if (p_FAST%CompElast == Module_SED) then + ! ........................ + ! initialize Simplified-ElastoDyn (must be done first) + ! ........................ + ALLOCATE( SED%Input( p_FAST%InterpOrder+1 ), SED%InputTimes( p_FAST%InterpOrder+1 ),STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal,"Error allocating SED%Input and SED%InputTimes.",ErrStat,ErrMsg,RoutineName) + CALL Cleanup() + RETURN + END IF + + Init%InData_SED%Linearize = p_FAST%Linearize + Init%InData_SED%InputFile = p_FAST%EDFile + Init%InData_SED%RootName = TRIM(p_FAST%OutFileRoot)//'.'//TRIM(y_FAST%Module_Abrev(Module_SED)) + + CALL SED_Init( Init%InData_SED, SED%Input(1), SED%p, SED%x(STATE_CURR), SED%xd(STATE_CURR), SED%z(STATE_CURR), SED%OtherSt(STATE_CURR), & + SED%y, SED%m, p_FAST%dt_module( MODULE_SED ), Init%OutData_SED, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + p_FAST%ModuleInitialized(Module_SED) = .TRUE. + CALL SetModuleSubstepTime(Module_SED, p_FAST, y_FAST, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + NumBl = Init%OutData_SED%NumBl + + else + ! ........................ + ! initialize ElastoDyn (must be done first) + ! ........................ + ALLOCATE( ED%Input( p_FAST%InterpOrder+1 ), ED%InputTimes( p_FAST%InterpOrder+1 ),STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal,"Error allocating ED%Input and ED%InputTimes.",ErrStat,ErrMsg,RoutineName) + CALL Cleanup() + RETURN + END IF + + ALLOCATE( ED%Input_Saved( p_FAST%InterpOrder+1 ), ED%InputTimes_Saved( p_FAST%InterpOrder+1 ), ED%Output_bak( p_FAST%InterpOrder+1 ),STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal,"Error allocating ED%Input_Saved, ED%Output_bak, and ED%InputTimes_Saved.",ErrStat,ErrMsg,RoutineName) + CALL Cleanup() + RETURN + END IF + + Init%InData_ED%Linearize = p_FAST%Linearize + Init%InData_ED%CompAeroMaps = p_FAST%CompAeroMaps + Init%InData_ED%RotSpeed = p_FAST%RotSpeedInit + Init%InData_ED%InputFile = p_FAST%EDFile + + Init%InData_ED%RootName = TRIM(p_FAST%OutFileRoot)//'.'//TRIM(y_FAST%Module_Abrev(Module_ED)) + Init%InData_ED%CompElast = p_FAST%CompElast == Module_ED + + Init%InData_ED%Gravity = p_FAST%Gravity + + Init%InData_ED%MHK = p_FAST%MHK + Init%InData_ED%WtrDpth = p_FAST%WtrDpth + + CALL ED_Init( Init%InData_ED, ED%Input(1), ED%p, ED%x(STATE_CURR), ED%xd(STATE_CURR), ED%z(STATE_CURR), ED%OtherSt(STATE_CURR), & + ED%y, ED%m, p_FAST%dt_module( MODULE_ED ), Init%OutData_ED, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + p_FAST%ModuleInitialized(Module_ED) = .TRUE. + CALL SetModuleSubstepTime(Module_ED, p_FAST, y_FAST, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + allocate( y_FAST%Lin%Modules(MODULE_ED)%Instance(1), stat=ErrStat2) + if (ErrStat2 /= 0 ) then + call SetErrStat(ErrID_Fatal, "Error allocating Lin%Modules(ED).", ErrStat, ErrMsg, RoutineName ) + else + + if (allocated(Init%OutData_ED%LinNames_y)) call move_alloc(Init%OutData_ED%LinNames_y,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%Names_y) + if (allocated(Init%OutData_ED%LinNames_x)) call move_alloc(Init%OutData_ED%LinNames_x,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%Names_x) + if (allocated(Init%OutData_ED%LinNames_u)) call move_alloc(Init%OutData_ED%LinNames_u,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%Names_u) + if (allocated(Init%OutData_ED%RotFrame_y)) call move_alloc(Init%OutData_ED%RotFrame_y,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%RotFrame_y) + if (allocated(Init%OutData_ED%RotFrame_x)) call move_alloc(Init%OutData_ED%RotFrame_x,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%RotFrame_x) + if (allocated(Init%OutData_ED%DerivOrder_x)) call move_alloc(Init%OutData_ED%DerivOrder_x,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%DerivOrder_x) + if (allocated(Init%OutData_ED%RotFrame_u)) call move_alloc(Init%OutData_ED%RotFrame_u,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%RotFrame_u) + if (allocated(Init%OutData_ED%IsLoad_u )) call move_alloc(Init%OutData_ED%IsLoad_u ,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%IsLoad_u ) + + if (allocated(Init%OutData_ED%WriteOutputHdr)) y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%NumOutputs = size(Init%OutData_ED%WriteOutputHdr) + end if + + IF (ErrStat >= AbortErrLev) THEN CALL Cleanup() RETURN END IF - - Init%InData_ED%Linearize = p_FAST%Linearize - Init%InData_ED%CompAeroMaps = p_FAST%CompAeroMaps - Init%InData_ED%RotSpeed = p_FAST%RotSpeedInit - Init%InData_ED%InputFile = p_FAST%EDFile - - Init%InData_ED%RootName = TRIM(p_FAST%OutFileRoot)//'.'//TRIM(y_FAST%Module_Abrev(Module_ED)) - Init%InData_ED%CompElast = p_FAST%CompElast == Module_ED - - Init%InData_ED%Gravity = p_FAST%Gravity - - Init%InData_ED%MHK = p_FAST%MHK - Init%InData_ED%WtrDpth = p_FAST%WtrDpth - - CALL ED_Init( Init%InData_ED, ED%Input(1), ED%p, ED%x(STATE_CURR), ED%xd(STATE_CURR), ED%z(STATE_CURR), ED%OtherSt(STATE_CURR), & - ED%y, ED%m, p_FAST%dt_module( MODULE_ED ), Init%OutData_ED, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - - p_FAST%ModuleInitialized(Module_ED) = .TRUE. - CALL SetModuleSubstepTime(Module_ED, p_FAST, y_FAST, ErrStat2, ErrMsg2) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - - allocate( y_FAST%Lin%Modules(MODULE_ED)%Instance(1), stat=ErrStat2) - if (ErrStat2 /= 0 ) then - call SetErrStat(ErrID_Fatal, "Error allocating Lin%Modules(ED).", ErrStat, ErrMsg, RoutineName ) - else - - if (allocated(Init%OutData_ED%LinNames_y)) call move_alloc(Init%OutData_ED%LinNames_y,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%Names_y) - if (allocated(Init%OutData_ED%LinNames_x)) call move_alloc(Init%OutData_ED%LinNames_x,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%Names_x) - if (allocated(Init%OutData_ED%LinNames_u)) call move_alloc(Init%OutData_ED%LinNames_u,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%Names_u) - if (allocated(Init%OutData_ED%RotFrame_y)) call move_alloc(Init%OutData_ED%RotFrame_y,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%RotFrame_y) - if (allocated(Init%OutData_ED%RotFrame_x)) call move_alloc(Init%OutData_ED%RotFrame_x,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%RotFrame_x) - if (allocated(Init%OutData_ED%DerivOrder_x)) call move_alloc(Init%OutData_ED%DerivOrder_x,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%DerivOrder_x) - if (allocated(Init%OutData_ED%RotFrame_u)) call move_alloc(Init%OutData_ED%RotFrame_u,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%RotFrame_u) - if (allocated(Init%OutData_ED%IsLoad_u )) call move_alloc(Init%OutData_ED%IsLoad_u ,y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%IsLoad_u ) - - if (allocated(Init%OutData_ED%WriteOutputHdr)) y_FAST%Lin%Modules(MODULE_ED)%Instance(1)%NumOutputs = size(Init%OutData_ED%WriteOutputHdr) - end if - - IF (ErrStat >= AbortErrLev) THEN - CALL Cleanup() - RETURN - END IF - - NumBl = Init%OutData_ED%NumBl - p_FAST%GearBox_index = Init%OutData_ED%GearBox_index - - - if (p_FAST%CalcSteady) then - if ( EqualRealNos(Init%OutData_ED%RotSpeed, 0.0_ReKi) ) then - p_FAST%TrimCase = TrimCase_none - p_FAST%NLinTimes = 1 - p_FAST%LinInterpOrder = 0 ! constant values - elseif ( Init%OutData_ED%isFixed_GenDOF ) then - p_FAST%TrimCase = TrimCase_none + + NumBl = Init%OutData_ED%NumBl + p_FAST%GearBox_index = Init%OutData_ED%GearBox_index + + + if (p_FAST%CalcSteady) then + if ( EqualRealNos(Init%OutData_ED%RotSpeed, 0.0_ReKi) ) then + p_FAST%TrimCase = TrimCase_none + p_FAST%NLinTimes = 1 + p_FAST%LinInterpOrder = 0 ! constant values + elseif ( Init%OutData_ED%isFixed_GenDOF ) then + p_FAST%TrimCase = TrimCase_none + end if end if - end if + endif ! SED/ED ! ........................ @@ -468,13 +496,15 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, ! lidar Init%InData_IfW%lidar%Tmax = p_FAST%TMax - Init%InData_IfW%lidar%HubPosition = ED%y%HubPtMotion%Position(:,1) - - Init%InData_IfW%lidar%HubPosition = ED%y%HubPtMotion%Position(:,1) - if ( p_FAST%CompElast == Module_BD ) then - Init%InData_IfW%RadAvg = TwoNorm(BD%y(1)%BldMotion%Position(:,1) - BD%y(1)%BldMotion%Position(:,BD%y(1)%BldMotion%Nnodes)) - else + if (p_FAST%CompElast == Module_SED) then + Init%InData_IfW%lidar%HubPosition = SED%y%HubPtMotion%Position(:,1) + Init%InData_IfW%RadAvg = Init%OutData_SED%BladeLength + elseif ( p_FAST%CompElast == Module_ED ) then + Init%InData_IfW%lidar%HubPosition = ED%y%HubPtMotion%Position(:,1) Init%InData_IfW%RadAvg = Init%OutData_ED%BladeLength + elseif ( p_FAST%CompElast == Module_BD ) then + Init%InData_IfW%lidar%HubPosition = ED%y%HubPtMotion%Position(:,1) + Init%InData_IfW%RadAvg = TwoNorm(BD%y(1)%BldMotion%Position(:,1) - BD%y(1)%BldMotion%Position(:,BD%y(1)%BldMotion%Nnodes)) end if IF ( PRESENT(ExternInitData) ) THEN @@ -673,7 +703,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, ! ........................ - ! initialize AeroDyn15 + ! initialize AeroDyn / ADsk ! ........................ ALLOCATE( AD%Input( p_FAST%InterpOrder+1 ), AD%InputTimes( p_FAST%InterpOrder+1 ), STAT = ErrStat2 ) IF (ErrStat2 /= 0) THEN @@ -689,6 +719,13 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, RETURN END IF + ALLOCATE( ADsk%Input( p_FAST%InterpOrder+1 ), ADsk%InputTimes( p_FAST%InterpOrder+1 ), STAT = ErrStat2 ) + IF (ErrStat2 /= 0) THEN + CALL SetErrStat(ErrID_Fatal,"Error allocating ADsk%Input and ADsk%InputTimes.",ErrStat,ErrMsg,RoutineName) + CALL Cleanup() + RETURN + END IF + IF ( (p_FAST%CompAero == Module_AD) .OR. (p_FAST%CompAero == Module_ExtLd) ) THEN allocate(Init%InData_AD%rotors(1), stat=ErrStat2) @@ -792,6 +829,33 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AirDens = Init%OutData_AD%rotors(1)%AirDens + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN + Init%InData_ADsk%InputFile = p_FAST%AeroFile + Init%InData_ADsk%RootName = p_FAST%OutFileRoot + ! NOTE: cone angle is not included in the RotorRad calculation!!! + if (p_FAST%CompElast == Module_SED) then + Init%InData_ADsk%RotorRad = Init%OutData_SED%HubRad + Init%OutData_SED%BladeLength + Init%InData_ADsk%HubPosition = SED%y%HubPtMotion%Position(:,1) + Init%InData_ADsk%HubOrientation = SED%y%HubPtMotion%RefOrientation(:,:,1) + else + Init%InData_ADsk%RotorRad = Init%OutData_ED%HubRad + Init%OutData_ED%BladeLength + Init%InData_ADsk%HubPosition = ED%y%HubPtMotion%Position(:,1) + Init%InData_ADsk%HubOrientation = ED%y%HubPtMotion%RefOrientation(:,:,1) + endif + Init%InData_ADsk%defAirDens = p_FAST%AirDens + Init%InData_ADsk%Linearize = p_FAST%Linearize ! NOTE: This module cannot be linearized + Init%InData_ADsk%UseInputFile = .true. + !Init%InData_ADsk%PassedFileData = ! Passing filename instead of file contents + IF (p_FAST%CompInflow == Module_IfW) Init%InData_ADsk%FlowField => Init%OutData_IfW%FlowField + + CALL ADsk_Init( Init%InData_ADsk, ADsk%Input(1), ADsk%p, ADsk%x(STATE_CURR), ADsk%xd(STATE_CURR), ADsk%z(STATE_CURR), & + ADsk%OtherSt(STATE_CURR), ADsk%y, ADsk%m, p_FAST%dt_module( MODULE_ADsk ), Init%OutData_ADsk, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + p_FAST%ModuleInitialized(Module_ADsk) = .TRUE. + CALL SetModuleSubstepTime(Module_ADsk, p_FAST, y_FAST, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + END IF ! CompAero IF ( p_FAST%CompAero == Module_ExtLd ) THEN @@ -1380,47 +1444,84 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, IF ( p_FAST%CompServo == Module_SrvD ) THEN Init%InData_SrvD%InputFile = p_FAST%ServoFile Init%InData_SrvD%RootName = TRIM(p_FAST%OutFileRoot)//'.'//TRIM(y_FAST%Module_Abrev(Module_SrvD)) - Init%InData_SrvD%NumBl = Init%OutData_ED%NumBl + Init%InData_SrvD%NumBl = NumBl Init%InData_SrvD%Gravity = (/ 0.0_ReKi, 0.0_ReKi, -p_FAST%Gravity /) ! "Gravitational acceleration vector" m/s^2 - Init%InData_SrvD%NacRefPos(1:3) = ED%y%NacelleMotion%Position(1:3,1) - Init%InData_SrvD%NacTransDisp(1:3) = ED%y%NacelleMotion%TranslationDisp(1:3,1) ! R8Ki - Init%InData_SrvD%NacRefOrient(1:3,1:3) = ED%y%NacelleMotion%RefOrientation(1:3,1:3,1) ! R8Ki - Init%InData_SrvD%NacOrient(1:3,1:3) = ED%y%NacelleMotion%Orientation(1:3,1:3,1) ! R8Ki - Init%InData_SrvD%TwrBaseRefPos = Init%OutData_ED%TwrBaseRefPos - Init%InData_SrvD%TwrBaseTransDisp = Init%OutData_ED%TwrBaseTransDisp - Init%InData_SrvD%TwrBaseRefOrient = Init%OutData_ED%TwrBaseRefOrient ! R8Ki - Init%InData_SrvD%TwrBaseOrient = Init%OutData_ED%TwrBaseOrient ! R8Ki - Init%InData_SrvD%PtfmRefPos(1:3) = ED%y%PlatformPtMesh%Position(1:3,1) - Init%InData_SrvD%PtfmTransDisp(1:3) = ED%y%PlatformPtMesh%TranslationDisp(1:3,1) - Init%InData_SrvD%PtfmRefOrient(1:3,1:3)= ED%y%PlatformPtMesh%RefOrientation(1:3,1:3,1) ! R8Ki - Init%InData_SrvD%PtfmOrient(1:3,1:3) = ED%y%PlatformPtMesh%Orientation(1:3,1:3,1) ! R8Ki + + CALL AllocAry(Init%InData_SrvD%BlPitchInit, NumBl, 'BlPitchInit', ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= abortErrLev) then ! make sure allocatable arrays are valid before setting them + CALL Cleanup() + RETURN + end if + + if (p_FAST%CompElast == Module_SED) then + Init%InData_SrvD%NacRefPos(1:3) = SED%y%NacelleMotion%Position(1:3,1) + Init%InData_SrvD%NacTransDisp(1:3) = SED%y%NacelleMotion%TranslationDisp(1:3,1) ! R8Ki + Init%InData_SrvD%NacRefOrient(1:3,1:3) = SED%y%NacelleMotion%RefOrientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%NacOrient(1:3,1:3) = SED%y%NacelleMotion%Orientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%TwrBaseRefPos = 0.0_ReKi + Init%InData_SrvD%TwrBaseTransDisp = 0.0_R8Ki + Init%InData_SrvD%TwrBaseRefOrient = 0.0_R8Ki + Init%InData_SrvD%TwrBaseOrient = 0.0_R8Ki + Init%InData_SrvD%PtfmRefPos(1:3) = SED%y%PlatformPtMesh%Position(1:3,1) + Init%InData_SrvD%PtfmTransDisp(1:3) = SED%y%PlatformPtMesh%TranslationDisp(1:3,1) ! R8Ki + Init%InData_SrvD%PtfmRefOrient(1:3,1:3)= SED%y%PlatformPtMesh%RefOrientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%PtfmOrient(1:3,1:3) = SED%y%PlatformPtMesh%Orientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%RotSpeedRef = Init%OutData_SED%RotSpeed + Init%InData_SrvD%BlPitchInit = Init%OutData_SED%BlPitch + else + Init%InData_SrvD%NacRefPos(1:3) = ED%y%NacelleMotion%Position(1:3,1) + Init%InData_SrvD%NacTransDisp(1:3) = ED%y%NacelleMotion%TranslationDisp(1:3,1) ! R8Ki + Init%InData_SrvD%NacRefOrient(1:3,1:3) = ED%y%NacelleMotion%RefOrientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%NacOrient(1:3,1:3) = ED%y%NacelleMotion%Orientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%TwrBaseRefPos = Init%OutData_ED%TwrBaseRefPos + Init%InData_SrvD%TwrBaseTransDisp = Init%OutData_ED%TwrBaseTransDisp ! R8Ki + Init%InData_SrvD%TwrBaseRefOrient = Init%OutData_ED%TwrBaseRefOrient ! R8Ki + Init%InData_SrvD%TwrBaseOrient = Init%OutData_ED%TwrBaseOrient ! R8Ki + Init%InData_SrvD%PtfmRefPos(1:3) = ED%y%PlatformPtMesh%Position(1:3,1) + Init%InData_SrvD%PtfmTransDisp(1:3) = ED%y%PlatformPtMesh%TranslationDisp(1:3,1) ! R8Ki + Init%InData_SrvD%PtfmRefOrient(1:3,1:3)= ED%y%PlatformPtMesh%RefOrientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%PtfmOrient(1:3,1:3) = ED%y%PlatformPtMesh%Orientation(1:3,1:3,1) ! R8Ki + Init%InData_SrvD%RotSpeedRef = Init%OutData_ED%RotSpeed + Init%InData_SrvD%BlPitchInit = Init%OutData_ED%BlPitch + endif Init%InData_SrvD%TMax = p_FAST%TMax Init%InData_SrvD%AirDens = AirDens Init%InData_SrvD%AvgWindSpeed = Init%OutData_IfW%WindFileInfo%MWS Init%InData_SrvD%Linearize = p_FAST%Linearize Init%InData_SrvD%TrimCase = p_FAST%TrimCase Init%InData_SrvD%TrimGain = p_FAST%TrimGain - Init%InData_SrvD%RotSpeedRef = Init%OutData_ED%RotSpeed Init%InData_SrvD%InterpOrder = p_FAST%InterpOrder - CALL AllocAry( Init%InData_SrvD%BladeRootRefPos, 3, Init%OutData_ED%NumBl, 'Init%InData_SrvD%BladeRootRefPos', errStat2, ErrMsg2) + CALL AllocAry( Init%InData_SrvD%BladeRootRefPos, 3, NumBl, 'Init%InData_SrvD%BladeRootRefPos', errStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - CALL AllocAry( Init%InData_SrvD%BladeRootTransDisp, 3, Init%OutData_ED%NumBl, 'Init%InData_SrvD%BladeRootTransDisp', errStat2, ErrMsg2) + CALL AllocAry( Init%InData_SrvD%BladeRootTransDisp, 3, NumBl, 'Init%InData_SrvD%BladeRootTransDisp', errStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - CALL AllocAry( Init%InData_SrvD%BladeRootRefOrient, 3, 3, Init%OutData_ED%NumBl, 'Init%InData_SrvD%BladeRootRefOrient', errStat2, ErrMsg2) + CALL AllocAry( Init%InData_SrvD%BladeRootRefOrient, 3, 3, NumBl, 'Init%InData_SrvD%BladeRootRefOrient', errStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - CALL AllocAry( Init%InData_SrvD%BladeRootOrient, 3, 3, Init%OutData_ED%NumBl, 'Init%InData_SrvD%BladeRootOrient', errStat2, ErrMsg2) + CALL AllocAry( Init%InData_SrvD%BladeRootOrient, 3, 3, NumBl, 'Init%InData_SrvD%BladeRootOrient', errStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) IF (ErrStat >= AbortErrLev) THEN CALL Cleanup() RETURN END IF - do k=1,Init%OutData_ED%NumBl - Init%InData_SrvD%BladeRootRefPos(:,k) = ED%y%BladeRootMotion(k)%Position(:,1) - Init%InData_SrvD%BladeRootTransDisp(:,k) = ED%y%BladeRootMotion(k)%TranslationDisp(:,1) - Init%InData_SrvD%BladeRootRefOrient(:,:,k)= ED%y%BladeRootMotion(k)%RefOrientation(:,:,1) - Init%InData_SrvD%BladeRootOrient(:,:,k) = ED%y%BladeRootMotion(k)%Orientation(:,:,1) - enddo + ! Set blade root info -- used for Blade StC. Set from SED even though SED is not compatible -- we won't know + ! if the BStC was used until after calling SrvD_Init. + if (p_FAST%CompElast == Module_SED) then + do k=1,NumBl + Init%InData_SrvD%BladeRootRefPos(:,k) = SED%y%BladeRootMotion(k)%Position(:,1) + Init%InData_SrvD%BladeRootTransDisp(:,k) = SED%y%BladeRootMotion(k)%TranslationDisp(:,1) + Init%InData_SrvD%BladeRootRefOrient(:,:,k)= SED%y%BladeRootMotion(k)%RefOrientation(:,:,1) + Init%InData_SrvD%BladeRootOrient(:,:,k) = SED%y%BladeRootMotion(k)%Orientation(:,:,1) + enddo + else + do k=1,NumBl + Init%InData_SrvD%BladeRootRefPos(:,k) = ED%y%BladeRootMotion(k)%Position(:,1) + Init%InData_SrvD%BladeRootTransDisp(:,k) = ED%y%BladeRootMotion(k)%TranslationDisp(:,1) + Init%InData_SrvD%BladeRootRefOrient(:,:,k)= ED%y%BladeRootMotion(k)%RefOrientation(:,:,1) + Init%InData_SrvD%BladeRootOrient(:,:,k) = ED%y%BladeRootMotion(k)%Orientation(:,:,1) + enddo + endif IF ( PRESENT(ExternInitData) ) THEN @@ -1464,15 +1565,6 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, call SetSrvDCableControls() - CALL AllocAry(Init%InData_SrvD%BlPitchInit, Init%OutData_ED%NumBl, 'BlPitchInit', ErrStat2, ErrMsg2) - CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - - if (ErrStat >= abortErrLev) then ! make sure allocatable arrays are valid before setting them - CALL Cleanup() - RETURN - end if - - Init%InData_SrvD%BlPitchInit = Init%OutData_ED%BlPitch CALL SrvD_Init( Init%InData_SrvD, SrvD%Input(1), SrvD%p, SrvD%x(STATE_CURR), SrvD%xd(STATE_CURR), SrvD%z(STATE_CURR), & SrvD%OtherSt(STATE_CURR), SrvD%y, SrvD%m, p_FAST%dt_module( MODULE_SrvD ), Init%OutData_SrvD, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -1517,8 +1609,15 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, CALL SetErrStat(ErrID_Fatal,'ElastoDyn must use the AB4 or ABM4 integration method to implement high-speed shaft braking.',ErrStat,ErrMsg,RoutineName) ENDIF END IF ! Init%OutData_SrvD%UseHSSBrake - - + + ! SED module is not compatible with structural controls + if (p_FAST%CompElast == Module_SED) then + if (allocated(SrvD%Input(1)%BStCMotionMesh)) call SetErrStat(ErrID_Fatal,'Blade Structural Controls (BStC) from ServoDyn are not compatable with the Simplified-ElastoDyn module (SED).',ErrStat,ErrMsg,RoutineName) + if (allocated(SrvD%Input(1)%NStCMotionMesh)) call SetErrStat(ErrID_Fatal,'Nacelle Structural Controls (NStC) from ServoDyn are not compatable with the Simplified-ElastoDyn module (SED).',ErrStat,ErrMsg,RoutineName) + if (allocated(SrvD%Input(1)%TStCMotionMesh)) call SetErrStat(ErrID_Fatal,'Tower Structural Controls (TStC) from ServoDyn are not compatable with the Simplified-ElastoDyn module (SED).',ErrStat,ErrMsg,RoutineName) + if (allocated(SrvD%Input(1)%SStCMotionMesh)) call SetErrStat(ErrID_Fatal,'Substructure Structural Controls (SStC) from ServoDyn are not compatable with the Simplified-ElastoDyn module (SED).',ErrStat,ErrMsg,RoutineName) + endif + END IF @@ -1534,7 +1633,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, ! Initialize mesh-mapping data ! ------------------------------------------------------------------------- - CALL InitModuleMappings(p_FAST, ED, BD, AD, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2) + CALL InitModuleMappings(p_FAST, ED, SED, BD, AD, ADsk, ExtLd, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) IF (ErrStat >= AbortErrLev) THEN @@ -1615,7 +1714,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, ! Initialize data for VTK output ! ------------------------------------------------------------------------- if ( p_FAST%WrVTK > VTK_None ) then - call SetVTKParameters(p_FAST, Init%OutData_ED, Init%OutData_AD, Init%OutData_SeaSt, Init%OutData_HD, ED, BD, AD, HD, ErrStat2, ErrMsg2) + call SetVTKParameters(p_FAST, Init%OutData_ED, Init%OutData_SED, Init%OutData_AD, Init%OutData_SeaSt, Init%OutData_HD, ED, SED, BD, AD, HD, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) end if @@ -1921,8 +2020,10 @@ SUBROUTINE FAST_Init( p, m_FAST, y_FAST, t_initial, InputFile, ErrStat, ErrMsg, y_FAST%Module_Ver( Module_IfW )%Name = 'InflowWind' y_FAST%Module_Ver( Module_ExtInfw)%Name = 'ExternalInflow integration' y_FAST%Module_Ver( Module_ED )%Name = 'ElastoDyn' + y_FAST%Module_Ver( Module_SED )%Name = 'Simplified-ElastoDyn' y_FAST%Module_Ver( Module_BD )%Name = 'BeamDyn' y_FAST%Module_Ver( Module_AD )%Name = 'AeroDyn' + y_FAST%Module_Ver( Module_ADsk )%Name = 'AeroDisk' y_FAST%Module_Ver( Module_ExtLd )%Name = 'ExtLoads' y_FAST%Module_Ver( Module_SrvD )%Name = 'ServoDyn' y_FAST%Module_Ver( Module_SeaSt )%Name = 'SeaState' @@ -1940,8 +2041,10 @@ SUBROUTINE FAST_Init( p, m_FAST, y_FAST, t_initial, InputFile, ErrStat, ErrMsg, y_FAST%Module_Abrev( Module_IfW ) = 'IfW' y_FAST%Module_Abrev( Module_ExtInfw) = 'ExtInfw' y_FAST%Module_Abrev( Module_ED ) = 'ED' + y_FAST%Module_Abrev( Module_SED ) = 'SED' y_FAST%Module_Abrev( Module_BD ) = 'BD' y_FAST%Module_Abrev( Module_AD ) = 'AD' + y_FAST%Module_Abrev( Module_ADsk ) = 'ADsk' y_FAST%Module_Abrev( Module_ExtLd ) = 'ExtLd' y_FAST%Module_Abrev( Module_SrvD ) = 'SrvD' y_FAST%Module_Abrev( Module_SeaSt ) = 'SEA' @@ -2099,7 +2202,7 @@ SUBROUTINE ValidateInputData(p, m_FAST, ErrStat, ErrMsg) IF ( p%KMax < 1_IntKi ) CALL SetErrStat( ErrID_Fatal, 'KMax must be greater than 0.', ErrStat, ErrMsg, RoutineName ) IF (p%CompElast == Module_Unknown) CALL SetErrStat( ErrID_Fatal, 'CompElast must be 1 (ElastoDyn) or 2 (BeamDyn).', ErrStat, ErrMsg, RoutineName ) - IF (p%CompAero == Module_Unknown) CALL SetErrStat( ErrID_Fatal, 'CompAero must be 0 (None), 1 (AeroDyn14), 2 (AeroDyn), or 3 (ExtLoads).', ErrStat, ErrMsg, RoutineName ) + IF (p%CompAero == Module_Unknown) CALL SetErrStat( ErrID_Fatal, 'CompAero must be 0 (None), 1 (AeroDisk), 2 (AeroDyn), or 3 (ExtLoads).', ErrStat, ErrMsg, RoutineName ) IF (p%CompServo == Module_Unknown) CALL SetErrStat( ErrID_Fatal, 'CompServo must be 0 (None) or 1 (ServoDyn).', ErrStat, ErrMsg, RoutineName ) IF (p%CompSeaSt == Module_Unknown) CALL SetErrStat( ErrID_Fatal, 'CompSeaSt must be 0 (None) or 1 (SeaState).', ErrStat, ErrMsg, RoutineName ) IF (p%CompHydro == Module_Unknown) CALL SetErrStat( ErrID_Fatal, 'CompHydro must be 0 (None) or 1 (HydroDyn).', ErrStat, ErrMsg, RoutineName ) @@ -2125,6 +2228,16 @@ SUBROUTINE ValidateInputData(p, m_FAST, ErrStat, ErrMsg) IF (p%CompSub == Module_ExtPtfm) CALL SetErrStat( ErrID_Fatal, 'HydroDyn cannot be used if ExtPtfm_MCKF is used. Set CompHydro = 0 or CompSub < 2 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) END IF + ! SED cannot be used with certain modules + if (p%CompElast == Module_SED) then + if (p%CompSub == Module_SD) call SetErrStat( ErrID_Fatal, 'Simplified-ElastoDyn (SED) cannot be used with SubDyn. Set CompSub == 0 or CompElast /= 3 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + if (p%CompHydro == Module_HD) call SetErrStat( ErrID_Fatal, 'Simplified-ElastoDyn (SED) cannot be used with HydroDyn. Set CompHydro == 0 or CompElast /= 3 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + if (p%CompIce /= Module_None) call SetErrStat( ErrID_Fatal, 'Simplified-ElastoDyn (SED) cannot be used with any ice modules. Set CompIce == 0 or CompElast /= 3 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + if (p%CompMooring /= Module_None) call SetErrStat( ErrID_Fatal, 'Simplified-ElastoDyn (SED) cannot be used with any mooring modules. Set CompMooring == 0 or CompElast /= 3 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + if (p%MHK == 1 .or. p%MHK == 2) call SetErrStat( ErrID_Fatal, 'Simplified-ElastoDyn (SED) cannot be used with an MHK turbine. Set MHK == 0 or CompElast /= 3 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + if (p%CompInflow == Module_ExtInfw) call SetErrStat( ErrID_Fatal, 'Simplified-ElastoDyn (SED) cannot be used with ExtInfw. Set CompInflow /= 2 or CompElast /= 3 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + endif + IF (p%CompMooring == Module_Orca .and. p%CompSub /= Module_None) CALL SetErrStat( ErrID_Fatal, 'SubDyn and ExtPtfm cannot be used if OrcaFlex is used. Set CompSub = 0 or CompMooring < 4 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) @@ -2136,8 +2249,15 @@ SUBROUTINE ValidateInputData(p, m_FAST, ErrStat, ErrMsg) IF (p%CompHydro /= Module_HD) CALL SetErrStat( ErrID_Fatal, 'HydroDyn must be used when IceDyn is used. Set CompHydro > 0 or CompIce = 0 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) END IF + IF (p%CompElast == Module_BD .and. p%CompAero == Module_ADsk) CALL SetErrStat( ErrID_Fatal, 'AeroDisk cannot be used when BeamDyn is used. Change CompAero or CompElast in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + + ! No method at the moment for getting disk average velocity from ExtInfw + if (p%CompAero == Module_ADsk .and. p%CompInflow == MODULE_ExtInfw) call SetErrStat( ErrID_Fatal, 'AeroDisk cannot be used with ExtInflow or the library interface', ErrStat, ErrMsg, RoutineName ) + if ((p%CompAero == Module_ExtLd) .and. (p%CompInflow /= Module_NONE) ) call SetErrStat(ErrID_Fatal, 'Inflow module cannot be used when ExtLoads is used. Change CompAero or CompInflow in the OpenFAST input file.', ErrStat, ErrMsg, RoutineName) + IF (p%CompAero == Module_ADsk .and. p%MHK /= MHK_None) CALL SetErrStat( ErrID_Fatal, 'AeroDisk cannot be used with an MHK turbine. Change CompAero or MHK in the FAST input file.', ErrStat, ErrMsg, RoutineName ) + IF (p%MHK /= MHK_None .and. p%MHK /= MHK_FixedBottom .and. p%MHK /= MHK_Floating) CALL SetErrStat( ErrID_Fatal, 'MHK switch is invalid. Set MHK to 0, 1, or 2 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) IF (p%MHK /= MHK_None .and. p%Linearize) CALL SetErrStat( ErrID_Fatal, 'Linearization has not yet been implemented for an MHK turbine. Change MHK or Linearize in the FAST input file.', ErrStat, ErrMsg, RoutineName ) @@ -2209,6 +2329,7 @@ SUBROUTINE ValidateInputData(p, m_FAST, ErrStat, ErrMsg) end if ! now, make sure we haven't asked for any modules that we can't yet linearize: + if (p%CompAero == MODULE_ADsk) call SetErrStat(ErrID_Fatal,'Linearization is not implemented for the AeroDisk module.',ErrStat, ErrMsg, RoutineName) if (p%CompInflow == MODULE_ExtInfw) call SetErrStat(ErrID_Fatal,'Linearization is not implemented for the ExternalInflow coupling.',ErrStat, ErrMsg, RoutineName) if (p%CompSub /= MODULE_None .and. p%CompSub /= MODULE_SD ) call SetErrStat(ErrID_Fatal,'Linearization is not implemented for the ExtPtfm_MCKF substructure module.',ErrStat, ErrMsg, RoutineName) if (p%CompMooring /= MODULE_None .and. p%CompMooring == MODULE_FEAM) call SetErrStat(ErrID_Fatal,'Linearization is not implemented for the FEAMooring mooring module.',ErrStat, ErrMsg, RoutineName) @@ -2285,8 +2406,13 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) ! and save the module version info for later use, too: !...................................................... - y_FAST%Module_Ver( Module_ED ) = Init%OutData_ED%Ver - y_FAST%FileDescLines(2) = TRIM(y_FAST%FileDescLines(2) ) //'; '//TRIM(GetNVD(y_FAST%Module_Ver( Module_ED ) )) + IF ( p_FAST%CompElast == Module_SED ) THEN + y_FAST%Module_Ver( Module_SED ) = Init%OutData_SED%Ver + y_FAST%FileDescLines(2) = TRIM(y_FAST%FileDescLines(2) ) //'; '//TRIM(GetNVD(y_FAST%Module_Ver( Module_SED ) )) + ELSE + y_FAST%Module_Ver( Module_ED ) = Init%OutData_ED%Ver + y_FAST%FileDescLines(2) = TRIM(y_FAST%FileDescLines(2) ) //'; '//TRIM(GetNVD(y_FAST%Module_Ver( Module_ED ) )) + END IF IF ( p_FAST%CompElast == Module_BD ) THEN y_FAST%Module_Ver( Module_BD ) = Init%OutData_BD(1)%Ver ! call copy routine for this type if it every uses dynamic memory @@ -2305,6 +2431,9 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) IF ( p_FAST%CompAero == Module_AD .OR. p_FAST%CompAero == Module_ExtLd) THEN y_FAST%Module_Ver( Module_AD ) = Init%OutData_AD%Ver y_FAST%FileDescLines(2) = TRIM(y_FAST%FileDescLines(2) ) //'; '//TRIM(GetNVD(y_FAST%Module_Ver( Module_AD ) )) + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN + y_FAST%Module_Ver( Module_ADsk ) = Init%OutData_ADsk%Ver + y_FAST%FileDescLines(2) = TRIM(y_FAST%FileDescLines(2) ) //'; '//TRIM(GetNVD(y_FAST%Module_Ver( Module_ADsk ) )) END IF IF ( p_FAST%CompServo == Module_SrvD ) THEN @@ -2360,6 +2489,7 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) IF ( ALLOCATED( Init%OutData_IfW%WriteOutputHdr ) ) y_FAST%numOuts(Module_IfW) = SIZE(Init%OutData_IfW%WriteOutputHdr) IF ( ALLOCATED( Init%OutData_ExtInfw%WriteOutputHdr ) ) y_FAST%numOuts(Module_ExtInfw) = SIZE(Init%OutData_ExtInfw%WriteOutputHdr) IF ( ALLOCATED( Init%OutData_ED%WriteOutputHdr ) ) y_FAST%numOuts(Module_ED) = SIZE(Init%OutData_ED%WriteOutputHdr) + IF ( ALLOCATED( Init%OutData_SED%WriteOutputHdr ) ) y_FAST%numOuts(Module_SED) = SIZE(Init%OutData_SED%WriteOutputHdr) do i=1,p_FAST%nBeams IF ( ALLOCATED( Init%OutData_BD(i)%WriteOutputHdr) ) y_FAST%numOuts(Module_BD) = y_FAST%numOuts(Module_BD) + SIZE(Init%OutData_BD(i)%WriteOutputHdr) end do @@ -2367,6 +2497,7 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) IF ( ALLOCATED( Init%OutData_AD%rotors)) then IF ( ALLOCATED( Init%OutData_AD%rotors(1)%WriteOutputHdr)) y_FAST%numOuts(Module_AD) = SIZE(Init%OutData_AD%rotors(1)%WriteOutputHdr) ENDIF + IF ( ALLOCATED( Init%OutData_ADsk%WriteOutputHdr ) ) y_FAST%numOuts(Module_ADsk) = SIZE(Init%OutData_ADsk%WriteOutputHdr) IF ( ALLOCATED( Init%OutData_SrvD%WriteOutputHdr ) ) y_FAST%numOuts(Module_SrvD) = SIZE(Init%OutData_SrvD%WriteOutputHdr) IF ( ALLOCATED( Init%OutData_SeaSt%WriteOutputHdr ) ) y_FAST%numOuts(Module_SeaSt) = SIZE(Init%OutData_SeaSt%WriteOutputHdr) IF ( ALLOCATED( Init%OutData_HD%WriteOutputHdr ) ) y_FAST%numOuts(Module_HD) = SIZE(Init%OutData_HD%WriteOutputHdr) @@ -2445,6 +2576,12 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) indxNext = indxNext + 1 END DO + DO i=1,y_FAST%numOuts(Module_SED) !Simnplified-ElastoDyn + y_FAST%ChannelNames(indxNext) = Init%OutData_SED%WriteOutputHdr(i) + y_FAST%ChannelUnits(indxNext) = Init%OutData_SED%WriteOutputUnt(i) + indxNext = indxNext + 1 + END DO + IF ( y_FAST%numOuts(Module_BD) > 0_IntKi ) THEN !BeamDyn do i=1,p_FAST%nBeams if ( allocated(Init%OutData_BD(i)%WriteOutputHdr) ) then @@ -2466,6 +2603,12 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) indxNext = indxNext + 1 END DO + DO i=1,y_FAST%numOuts(Module_ADsk) !AeroDisk + y_FAST%ChannelNames(indxNext) = Init%OutData_ADsk%WriteOutputHdr(i) + y_FAST%ChannelUnits(indxNext) = Init%OutData_ADsk%WriteOutputUnt(i) + indxNext = indxNext + 1 + END DO + DO i=1,y_FAST%numOuts(Module_SrvD) !ServoDyn y_FAST%ChannelNames(indxNext) = Init%OutData_SrvD%WriteOutputHdr(i) y_FAST%ChannelUnits(indxNext) = Init%OutData_SrvD%WriteOutputUnt(i) @@ -2868,7 +3011,7 @@ SUBROUTINE FAST_ReadPrimaryFile( InputFile, p, m_FAST, OverrideAbortErrLev, ErrS end if ! CompElast - Compute structural dynamics (switch) {1=ElastoDyn; 2=ElastoDyn + BeamDyn for blades}: - CALL ReadVar( UnIn, InputFile, p%CompElast, "CompElast", "Compute structural dynamics (switch) {1=ElastoDyn; 2=ElastoDyn + BeamDyn for blades}", ErrStat2, ErrMsg2, UnEc) + CALL ReadVar( UnIn, InputFile, p%CompElast, "CompElast", "Compute structural dynamics (switch) {1=ElastoDyn; 2=ElastoDyn + BeamDyn for blades; 3=Simplified-ElastoDyn}", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if ( ErrStat >= AbortErrLev ) then call cleanup() @@ -2880,6 +3023,8 @@ SUBROUTINE FAST_ReadPrimaryFile( InputFile, p, m_FAST, OverrideAbortErrLev, ErrS p%CompElast = Module_ED ELSEIF ( p%CompElast == 2 ) THEN p%CompElast = Module_BD + ELSEIF ( p%CompElast == 3 ) THEN + p%CompElast = Module_SED ELSE p%CompElast = Module_Unknown END IF @@ -2903,8 +3048,8 @@ SUBROUTINE FAST_ReadPrimaryFile( InputFile, p, m_FAST, OverrideAbortErrLev, ErrS p%CompInflow = Module_Unknown END IF - ! CompAero - Compute aerodynamic loads (switch) {0=None; 1=AeroDyn}: - CALL ReadVar( UnIn, InputFile, p%CompAero, "CompAero", "Compute aerodynamic loads (switch) {0=None; 1=AeroDyn14; 2=AeroDyn; 3=ExtLoads}", ErrStat2, ErrMsg2, UnEc) + ! CompAero - Compute aerodynamic loads (switch) {0=None; 1=AeroDisk; 2=AeroDyn; 3=ExtLoads}: + CALL ReadVar( UnIn, InputFile, p%CompAero, "CompAero", "Compute aerodynamic loads (switch) {0=None; 1=AeroDisk; 2=AeroDyn; 3=ExtLoads}", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if ( ErrStat >= AbortErrLev ) then call cleanup() @@ -2914,6 +3059,8 @@ SUBROUTINE FAST_ReadPrimaryFile( InputFile, p, m_FAST, OverrideAbortErrLev, ErrS ! immediately convert to values used inside the code: IF ( p%CompAero == 0 ) THEN p%CompAero = Module_NONE + ELSEIF ( p%CompAero == 1 ) THEN + p%CompAero = Module_ADsk ELSEIF ( p%CompAero == 2 ) THEN p%CompAero = Module_AD ELSEIF ( p%CompAero == 3 ) THEN @@ -3913,14 +4060,16 @@ end subroutine cleanup END SUBROUTINE FAST_ReadSteadyStateFile !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets up the information needed for plotting VTK surfaces. -SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitOutData_SeaSt, InitOutData_HD, ED, BD, AD, HD, ErrStat, ErrMsg) +SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_SED, InitOutData_AD, InitOutData_SeaSt, InitOutData_HD, ED, SED, BD, AD, HD, ErrStat, ErrMsg) TYPE(FAST_ParameterType), INTENT(INOUT) :: p_FAST !< The parameters of the glue code TYPE(ED_InitOutputType), INTENT(IN ) :: InitOutData_ED !< The initialization output from structural dynamics module + TYPE(SED_InitOutputType), INTENT(IN ) :: InitOutData_SED !< The initialization output from structural dynamics module TYPE(AD_InitOutputType), INTENT(INOUT) :: InitOutData_AD !< The initialization output from AeroDyn TYPE(SeaSt_InitOutputType), INTENT(INOUT) :: InitOutData_SeaSt !< The initialization output from SeaState TYPE(HydroDyn_InitOutputType),INTENT(INOUT) :: InitOutData_HD !< The initialization output from HydroDyn TYPE(ElastoDyn_Data), TARGET, INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data TYPE(HydroDyn_Data), INTENT(IN ) :: HD !< HydroDyn data @@ -3966,7 +4115,11 @@ SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitOutData_ end if ! determine number of blades - NumBl = InitOutData_ED%NumBl + if (p_FAST%CompElast == Module_SED) then + NumBl = InitOutData_SED%NumBl + else + NumBl = InitOutData_ED%NumBl + endif ! initialize the vtk data p_FAST%VTK_Surface%NumSectors = 25 @@ -4006,8 +4159,13 @@ SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitOutData_ !........................................................................................................ ! we're going to create a box using these dimensions - y = ED%y%HubPtMotion%Position(3, 1) - ED%y%NacelleMotion%Position(3, 1) - x = TwoNorm( ED%y%HubPtMotion%Position(1:2,1) - ED%y%NacelleMotion%Position(1:2,1) ) - p_FAST%VTK_Surface%HubRad + if (p_FAST%CompElast == Module_SED) then + y = SED%y%HubPtMotion%Position(3, 1) - SED%y%NacelleMotion%Position(3, 1) + x = TwoNorm( SED%y%HubPtMotion%Position(1:2,1) - SED%y%NacelleMotion%Position(1:2,1) ) - p_FAST%VTK_Surface%HubRad + else + y = ED%y%HubPtMotion%Position(3, 1) - ED%y%NacelleMotion%Position(3, 1) + x = TwoNorm( ED%y%HubPtMotion%Position(1:2,1) - ED%y%NacelleMotion%Position(1:2,1) ) - p_FAST%VTK_Surface%HubRad + endif p_FAST%VTK_Surface%NacelleBox(:,1) = (/ -x, y, 0.0_SiKi /) @@ -4107,6 +4265,7 @@ SUBROUTINE SetVTKParameters(p_FAST, InitOutData_ED, InitOutData_AD, InitOutData_ CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) IF (ErrStat >= AbortErrLev) RETURN END DO +! ELSE IF (p_FAST%CompElast == Module_SED) THEN ! no blade surface info from SED ELSE call WrScr('Using generic blade surfaces for ElastoDyn (rectangular airfoil, constant chord). ') ! TODO make this an option DO K=1,NumBl @@ -4638,7 +4797,7 @@ SUBROUTINE ExtLd_SetInitInput(InitInData_ExtLd, InitOutData_ED, y_ED, InitOutDat InitInData_ExtLd%nNodesVel = InitOutData_AD%nNodesVel - RETURN + RETURN END SUBROUTINE ExtLd_SetInitInput !---------------------------------------------------------------------------------------------------------------------------------- @@ -4721,6 +4880,10 @@ SUBROUTINE FAST_WrSum( p_FAST, y_FAST, MeshMapData, ErrStat, ErrMsg ) Fmt = '(4x,A)' WRITE (y_FAST%UnSum,Fmt) TRIM( GetNVD( NWTC_Ver ) ) + DescStr = GetNVD( y_FAST%Module_Ver( Module_ED ) ) + IF ((p_FAST%CompElast /= Module_ED) .or. (p_FAST%CompElast /= Module_BD)) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + DO I = 2,NumModules IF (p_FAST%ModuleInitialized(I)) THEN WRITE (y_FAST%UnSum,Fmt) TRIM( GetNVD( y_FAST%Module_Ver( I ) ) ) @@ -4730,6 +4893,66 @@ SUBROUTINE FAST_WrSum( p_FAST, y_FAST, MeshMapData, ErrStat, ErrMsg ) END IF END DO + DescStr = GetNVD( y_FAST%Module_Ver( Module_SED ) ) + IF ( p_FAST%CompElast /= Module_SED ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_IfW ) ) + IF ( p_FAST%CompInflow /= Module_IfW ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + ! I'm not going to write the openfoam module info to the summary file + !DescStr = GetNVD( y_FAST%Module_Ver( Module_OpFM ) ) + !IF ( p_FAST%CompInflow /= Module_OpFM ) DescStr = TRIM(DescStr)//NotUsedTxt + !WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_AD ) ) + IF ( p_FAST%CompAero /= Module_AD ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_ADsk ) ) + IF ( p_FAST%CompAero /= Module_ADsk ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_SrvD ) ) + IF ( p_FAST%CompServo /= Module_SrvD ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_HD ) ) + IF ( p_FAST%CompHydro /= Module_HD ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_SD ) ) + IF ( p_FAST%CompSub /= Module_SD ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_ExtPtfm ) ) + IF ( p_FAST%CompSub /= Module_ExtPtfm ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_MAP ) ) + IF ( p_FAST%CompMooring /= Module_MAP ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_FEAM ) ) + IF ( p_FAST%CompMooring /= Module_FEAM ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_MD ) ) + IF ( p_FAST%CompMooring /= Module_MD ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_Orca ) ) + IF ( p_FAST%CompMooring /= Module_Orca ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_IceF ) ) + IF ( p_FAST%CompIce /= Module_IceF ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) + + DescStr = GetNVD( y_FAST%Module_Ver( Module_IceD ) ) + IF ( p_FAST%CompIce /= Module_IceD ) DescStr = TRIM(DescStr)//NotUsedTxt + WRITE (y_FAST%UnSum,Fmt) TRIM( DescStr ) !.......................... Information from FAST input File ...................................... @@ -4863,14 +5086,14 @@ SUBROUTINE FAST_Solution0_T(Turbine, ErrStat, ErrMsg) CALL FAST_Solution0(Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX,& + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX,& Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat, ErrMsg ) END SUBROUTINE FAST_Solution0_T !---------------------------------------------------------------------------------------------------------------------------------- !> Routine that calls CalcOutput for the first time of the simulation (at t=0). After the initial solve, data arrays are initialized. -SUBROUTINE FAST_Solution0(p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE FAST_Solution0(p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, SeaSt, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -4878,9 +5101,11 @@ SUBROUTINE FAST_Solution0(p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -4937,7 +5162,7 @@ SUBROUTINE FAST_Solution0(p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, end if CALL CalcOutputs_And_SolveForInputs( n_t_global, t_initial, STATE_CURR, m_FAST%calcJacobian, m_FAST%NextJacCalcTime, & - p_FAST, m_FAST, y_FAST%WriteThisStep, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, & + p_FAST, m_FAST, y_FAST%WriteThisStep, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -4950,14 +5175,14 @@ SUBROUTINE FAST_Solution0(p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ! Check to see if we should output data this time step: !---------------------------------------------------------------------------------------- - CALL WriteOutputToFile(n_t_global_next, t_initial, p_FAST, y_FAST, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2) + CALL WriteOutputToFile(n_t_global_next, t_initial, p_FAST, y_FAST, ED, SED, BD, AD, ADsk, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ! turn off VTK output when if (p_FAST%WrVTK == VTK_InitOnly) then ! Write visualization data for initialization (and also note that we're ignoring any errors that occur doing so) - call WriteVTK(t_initial, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + call WriteVTK(t_initial, p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) end if @@ -4969,7 +5194,7 @@ SUBROUTINE FAST_Solution0(p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ! Initialize Input-Output arrays for interpolation/extrapolation: - CALL FAST_InitIOarrays( m_FAST%t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, HD, SD, ExtPtfm, & + CALL FAST_InitIOarrays( m_FAST%t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, IfW, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -4979,7 +5204,7 @@ END SUBROUTINE FAST_Solution0 !> This routine initializes the input and output arrays stored for extrapolation. They are initialized after the first input-output solve so that the first !! extrapolations are used with values from the solution, not just initial guesses. It also creates new copies of the state variables, which need to !! be stored for the predictor-corrector loop. -SUBROUTINE FAST_InitIOarrays( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, HD, SD, ExtPtfm, & +SUBROUTINE FAST_InitIOarrays( t_initial, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, IfW, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< start time of the simulation @@ -4988,9 +5213,11 @@ SUBROUTINE FAST_InitIOarrays( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, A TYPE(FAST_MiscVarType), INTENT(IN ) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(HydroDyn_Data), INTENT(INOUT) :: HD !< HydroDyn data TYPE(SubDyn_Data), INTENT(INOUT) :: SD !< SubDyn data @@ -5015,33 +5242,56 @@ SUBROUTINE FAST_InitIOarrays( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, A ErrStat = ErrID_None ErrMsg = "" - ! We fill ED%InputTimes with negative times, but the ED%Input values are identical for each of those times; this allows + ! We fill (S)ED%InputTimes with negative times, but the (S)ED%Input values are identical for each of those times; this allows ! us to use, e.g., quadratic interpolation that effectively acts as a zeroth-order extrapolation and first-order extrapolation ! for the first and second time steps. (The interpolation order in the ExtrapInput routines are determined as ! order = SIZE(ED%Input) - - - DO j = 1, p_FAST%InterpOrder + 1 - ED%InputTimes(j) = t_initial - (j - 1) * p_FAST%dt - END DO - - DO j = 2, p_FAST%InterpOrder + 1 - CALL ED_CopyInput (ED%Input(1), ED%Input(j), MESH_NEWCOPY, Errstat2, ErrMsg2) + + IF (p_FAST%CompElast == Module_SED) THEN + DO j = 1, p_FAST%InterpOrder + 1 + SED%InputTimes(j) = t_initial - (j - 1) * p_FAST%dt + END DO + + DO j = 2, p_FAST%InterpOrder + 1 + CALL SED_CopyInput (SED%Input(1), SED%Input(j), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + END DO + CALL SED_CopyInput (SED%Input(1), SED%u, MESH_NEWCOPY, Errstat2, ErrMsg2) ! do this to initialize meshes/allocatable arrays for output of ExtrapInterp routine CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - END DO - CALL ED_CopyInput (ED%Input(1), ED%u, MESH_NEWCOPY, Errstat2, ErrMsg2) ! do this to initialize meshes/allocatable arrays for output of ExtrapInterp routine - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - - ! Initialize predicted states for j_pc loop: - CALL ED_CopyContState (ED%x( STATE_CURR), ED%x( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyDiscState (ED%xd(STATE_CURR), ED%xd(STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyConstrState (ED%z( STATE_CURR), ED%z( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyOtherState (ED%OtherSt( STATE_CURR), ED%OtherSt( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) - CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - + + ! Initialize predicted states for j_pc loop: + CALL SED_CopyContState (SED%x( STATE_CURR), SED%x( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyDiscState (SED%xd(STATE_CURR), SED%xd(STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyConstrState (SED%z( STATE_CURR), SED%z( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyOtherState (SED%OtherSt( STATE_CURR), SED%OtherSt( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + ELSE + + DO j = 1, p_FAST%InterpOrder + 1 + ED%InputTimes(j) = t_initial - (j - 1) * p_FAST%dt + END DO + + DO j = 2, p_FAST%InterpOrder + 1 + CALL ED_CopyInput (ED%Input(1), ED%Input(j), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + END DO + CALL ED_CopyInput (ED%Input(1), ED%u, MESH_NEWCOPY, Errstat2, ErrMsg2) ! do this to initialize meshes/allocatable arrays for output of ExtrapInterp routine + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + ! Initialize predicted states for j_pc loop: + CALL ED_CopyContState (ED%x( STATE_CURR), ED%x( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyDiscState (ED%xd(STATE_CURR), ED%xd(STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyConstrState (ED%z( STATE_CURR), ED%z( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyOtherState (ED%OtherSt( STATE_CURR), ED%OtherSt( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ENDIF IF (p_FAST%CompElast == Module_BD ) THEN @@ -5128,6 +5378,31 @@ SUBROUTINE FAST_InitIOarrays( t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, A CALL AD_CopyOtherState(AD%OtherSt(STATE_CURR), AD%OtherSt(STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN + ! Copy values for interpolation/extrapolation: + + DO j = 1, p_FAST%InterpOrder + 1 + ADsk%InputTimes(j) = t_initial - (j - 1) * p_FAST%dt + END DO + + DO j = 2, p_FAST%InterpOrder + 1 + CALL ADsk_CopyInput (ADsk%Input(1), ADsk%Input(j), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + END DO + CALL ADsk_CopyInput (ADsk%Input(1), ADsk%u, MESH_NEWCOPY, Errstat2, ErrMsg2) ! do this to initialize meshes/allocatable arrays for output of ExtrapInterp routine + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + + ! Initialize predicted states for j_pc loop: + CALL ADsk_CopyContState (ADsk%x( STATE_CURR), ADsk%x( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyDiscState (ADsk%xd(STATE_CURR), ADsk%xd(STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyConstrState (ADsk%z( STATE_CURR), ADsk%z( STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyOtherState( ADsk%OtherSt(STATE_CURR), ADsk%OtherSt(STATE_PRED), MESH_NEWCOPY, Errstat2, ErrMsg2) + CALL SetErrStat( Errstat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + END IF ! CompAero == Module_AD @@ -7084,14 +7359,14 @@ SUBROUTINE FAST_Solution_T(t_initial, n_t_global, Turbine, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None CALL FAST_Solution(t_initial, n_t_global, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat, ErrMsg ) END SUBROUTINE FAST_Solution_T !---------------------------------------------------------------------------------------------------------------------------------- !> This routine takes data from n_t_global and gets values at n_t_global + 1 -SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, SeaSt, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial time @@ -7102,9 +7377,11 @@ SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< External loads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -7140,7 +7417,7 @@ SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ !! ## Step 1.a: set some variables and Extrapolate Inputs - call FAST_Prework(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & + call FAST_Prework(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -7150,7 +7427,7 @@ SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, !! ## Step 2: Correct (continue in loop) !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - call FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & + call FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -7158,7 +7435,7 @@ SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, !! ## Step 3: Save all final variables (advance to next time) and reset global time !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - call FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & + call FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -7166,7 +7443,7 @@ SUBROUTINE FAST_Solution(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, !---------------------------------------------------------------------------------------- !! Write outputs !---------------------------------------------------------------------------------------- - call FAST_WriteOutput(t_initial, n_t_global_next, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & + call FAST_WriteOutput(t_initial, n_t_global_next, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -7184,14 +7461,14 @@ SUBROUTINE FAST_Prework_T(t_initial, n_t_global, Turbine, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None CALL FAST_Prework(t_initial, n_t_global, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat, ErrMsg ) END SUBROUTINE FAST_Prework_T !---------------------------------------------------------------------------------------------------------------------------------- !> This routine does thde prep work to advance the time step from n_t_global to n_t_global + 1 -SUBROUTINE FAST_Prework(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & +SUBROUTINE FAST_Prework(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial time @@ -7202,9 +7479,11 @@ SUBROUTINE FAST_Prework(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, S TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< External loads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -7268,7 +7547,7 @@ SUBROUTINE FAST_Prework(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, S !! !! gives predicted values at t+dt !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - CALL FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, HD, SD, ExtPtfm, & + CALL FAST_ExtrapInterpMods( t_global_next, p_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -7287,14 +7566,14 @@ SUBROUTINE FAST_UpdateStates_T(t_initial, n_t_global, Turbine, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None CALL FAST_UpdateStates(t_initial, n_t_global, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat, ErrMsg ) END SUBROUTINE FAST_UpdateStates_T !---------------------------------------------------------------------------------------------------------------------------------- !> This routine takes data from n_t_global and predicts the states and output at n_t_global+1 -SUBROUTINE FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & +SUBROUTINE FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial time @@ -7305,9 +7584,11 @@ SUBROUTINE FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< External loads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -7374,7 +7655,7 @@ SUBROUTINE FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, !! STATE_PRED values contain values at t_global_next. !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - CALL FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, & + CALL FAST_AdvanceStates( t_initial, n_t_global, p_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2, WriteThisStep ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -7387,8 +7668,8 @@ SUBROUTINE FAST_UpdateStates(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, ! ControlInputGuess = ED%Input(1)%HSSBrTrqC !END IF - CALL CalcOutputs_And_SolveForInputs( n_t_global, t_global_next, STATE_PRED, m_FAST%calcJacobian, m_FAST%NextJacCalcTime, & - p_FAST, m_FAST, WriteThisStep, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) + CALL CalcOutputs_And_SolveForInputs( n_t_global, t_global_next, STATE_PRED, m_FAST%calcJacobian, m_FAST%NextJacCalcTime, & + p_FAST, m_FAST, WriteThisStep, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -7439,14 +7720,14 @@ SUBROUTINE FAST_AdvanceToNextTimeStep_T(t_initial, n_t_global, Turbine, ErrStat, CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None CALL FAST_AdvanceToNextTimeStep(t_initial, n_t_global, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat, ErrMsg ) END SUBROUTINE FAST_AdvanceToNextTimeStep_T !---------------------------------------------------------------------------------------------------------------------------------- !> This routine advances the time step from n_t_global to n_t_global + 1 and does all the relvant copying of data -SUBROUTINE FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & +SUBROUTINE FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial time @@ -7457,9 +7738,11 @@ SUBROUTINE FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_F TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< External loads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -7502,15 +7785,27 @@ SUBROUTINE FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_F !! copy the final predicted states from step t_global_next to actual states for that step !---------------------------------------------------------------------------------------- - ! ElastoDyn: copy final predictions to actual states - CALL ED_CopyContState (ED%x( STATE_PRED), ED%x( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyDiscState (ED%xd(STATE_PRED), ED%xd(STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyConstrState (ED%z( STATE_PRED), ED%z( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL ED_CopyOtherState (ED%OtherSt( STATE_PRED), ED%OtherSt( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF ( p_FAST%CompElast == Module_SED ) THEN + ! Simplified-ElastoDyn: copy final predictions to actual states + CALL SED_CopyContState (SED%x( STATE_PRED), SED%x( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyDiscState (SED%xd(STATE_PRED), SED%xd(STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyConstrState (SED%z( STATE_PRED), SED%z( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL SED_CopyOtherState (SED%OtherSt( STATE_PRED), SED%OtherSt( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ELSE + ! ElastoDyn: copy final predictions to actual states + CALL ED_CopyContState (ED%x( STATE_PRED), ED%x( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyDiscState (ED%xd(STATE_PRED), ED%xd(STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyConstrState (ED%z( STATE_PRED), ED%z( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ED_CopyOtherState (ED%OtherSt( STATE_PRED), ED%OtherSt( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ENDIF ! BeamDyn: copy final predictions to actual states @@ -7538,6 +7833,15 @@ SUBROUTINE FAST_AdvanceToNextTimeStep(t_initial, n_t_global, p_FAST, y_FAST, m_F CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL AD_CopyOtherState (AD%OtherSt(STATE_PRED), AD%OtherSt(STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ELSEIF ( p_FAST%CompAero == Module_ADsk ) THEN + CALL ADsk_CopyContState (ADsk%x( STATE_PRED), ADsk%x( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyDiscState (ADsk%xd(STATE_PRED), ADsk%xd(STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyConstrState (ADsk%z( STATE_PRED), ADsk%z( STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + CALL ADsk_CopyOtherState (ADsk%OtherSt(STATE_PRED), ADsk%OtherSt(STATE_CURR), MESH_UPDATECOPY, Errstat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END IF @@ -7687,14 +7991,14 @@ SUBROUTINE FAST_WriteOutput_T(t_initial, n_t_global, Turbine, ErrStat, ErrMsg ) CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None CALL FAST_WriteOutput(t_initial, n_t_global, Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, Turbine%SC_DX, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat, ErrMsg ) END SUBROUTINE FAST_WriteOutput_T !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes the outputs at this timestep -SUBROUTINE FAST_WriteOutput(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SC_DX, & +SUBROUTINE FAST_WriteOutput(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SC_DX, & SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial time @@ -7705,9 +8009,11 @@ SUBROUTINE FAST_WriteOutput(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, B TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(IN ) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(IN ) :: ExtLd !< External loads data TYPE(InflowWind_Data), INTENT(IN ) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(IN ) :: ExtInfw !< ExternalInflow data @@ -7743,7 +8049,7 @@ SUBROUTINE FAST_WriteOutput(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, B !---------------------------------------------------------------------------------------- !! Check to see if we should output data this time step: !---------------------------------------------------------------------------------------- - CALL WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & + CALL WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, SED, BD, AD, ADsk, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -7780,7 +8086,7 @@ END FUNCTION NeedWriteOutput !> This routine determines if it's time to write to the output files--based on a previous call to fast_subs::needwriteoutput--, and !! calls the routine to write to the files with the output data. It should be called after all the output solves for a given time !! have been completed, and assumes y_FAST\%WriteThisStep has been set. -SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, SED, BD, AD, ADsk, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & SrvD, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg) !............................................................................................................................... INTEGER(IntKi), INTENT(IN ) :: n_t_global !< Current global time step @@ -7789,9 +8095,11 @@ SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD, I TYPE(FAST_OutputFileType),INTENT(INOUT) :: y_FAST !< Output variables for the glue code TYPE(ElastoDyn_Data), INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(IN ) :: ADsk !< AeroDisk data TYPE(InflowWind_Data), INTENT(IN ) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(IN ) :: ExtInfw !< ExternalInflow data TYPE(SeaState_Data), INTENT(IN ) :: SeaSt !< SeaState data @@ -7821,8 +8129,8 @@ SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD, I IF ( y_FAST%WriteThisStep ) THEN ! Generate glue-code output file - CALL WrOutputLine( t_global, p_FAST, y_FAST, IfW%y%WriteOutput, ExtInfw%y%WriteOutput, ED%y%WriteOutput, & - AD%y, SrvD%y%WriteOutput, SeaSt%y%WriteOutput, HD%y%WriteOutput, SD%y%WriteOutput, ExtPtfm%y%WriteOutput, MAPp%y%WriteOutput, & + CALL WrOutputLine( t_global, p_FAST, y_FAST, IfW%y%WriteOutput, ExtInfw%y%WriteOutput, ED%y%WriteOutput, SED%y%WriteOutput, & + AD%y, ADsk%y%WriteOutput, SrvD%y%WriteOutput, SeaSt%y%WriteOutput, HD%y%WriteOutput, SD%y%WriteOutput, ExtPtfm%y%WriteOutput, MAPp%y%WriteOutput, & FEAM%y%WriteOutput, MD%y%WriteOutput, Orca%y%WriteOutput, IceF%y%WriteOutput, IceD%y, BD%y, ErrStat, ErrMsg ) ENDIF @@ -7830,7 +8138,7 @@ SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD, I ! Write visualization data (and also note that we're ignoring any errors that occur doing so) IF ( p_FAST%WrVTK == VTK_Animate ) THEN IF ( MOD( n_t_global, p_FAST%n_VTKTime ) == 0 ) THEN - call WriteVTK(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + call WriteVTK(t_global, p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) END IF END IF @@ -7838,7 +8146,7 @@ SUBROUTINE WriteOutputToFile(n_t_global, t_global, p_FAST, y_FAST, ED, BD, AD, I END SUBROUTINE WriteOutputToFile !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes the module output to the primary output file(s). -SUBROUTINE WrOutputLine( t, p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_AD, SrvDOutput, SeaStOutput, HDOutput, SDOutput, ExtPtfmOutput,& +SUBROUTINE WrOutputLine( t, p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, SEDOutput, y_AD, ADskOutput, SrvDOutput, SeaStOutput, HDOutput, SDOutput, ExtPtfmOutput,& MAPOutput, FEAMOutput, MDOutput, OrcaOutput, IceFOutput, y_IceD, y_BD, ErrStat, ErrMsg) IMPLICIT NONE @@ -7852,7 +8160,9 @@ SUBROUTINE WrOutputLine( t, p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, REAL(ReKi), ALLOCATABLE, INTENT(IN) :: IfWOutput (:) !< InflowWind WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: ExtInfwOutput (:) !< ExternalInflow WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: EDOutput (:) !< ElastoDyn WriteOutput values + REAL(ReKi), ALLOCATABLE, INTENT(IN) :: SEDOutput (:) !< Simplified-ElastoDyn WriteOutput values TYPE(AD_OutputType), INTENT(IN) :: y_AD !< AeroDyn outputs (WriteOutput values are subset of allocated Rotors) + REAL(ReKi), ALLOCATABLE, INTENT(IN) :: ADskOutput (:) !< AeroDisk WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: SrvDOutput (:) !< ServoDyn WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: SeaStOutput (:) !< SeaState WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: HDOutput (:) !< HydroDyn WriteOutput values @@ -7879,7 +8189,7 @@ SUBROUTINE WrOutputLine( t, p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, ErrStat = ErrID_None ErrMsg = '' - CALL FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_AD, SrvDOutput, SeaStOutput, HDOutput, SDOutput, ExtPtfmOutput, & + CALL FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, SEDOutput, y_AD, ADskOutput, SrvDOutput, SeaStOutput, HDOutput, SDOutput, ExtPtfmOutput, & MAPOutput, FEAMOutput, MDOutput, OrcaOutput, IceFOutput, y_IceD, y_BD, OutputAry) IF (p_FAST%WrTxtOutFile) THEN @@ -7939,7 +8249,7 @@ SUBROUTINE FillOutputAry_T(Turbine, Outputs) CALL FillOutputAry(Turbine%p_FAST, Turbine%y_FAST, Turbine%IfW%y%WriteOutput, Turbine%ExtInfw%y%WriteOutput, & - Turbine%ED%y%WriteOutput, Turbine%AD%y, Turbine%SrvD%y%WriteOutput, & + Turbine%ED%y%WriteOutput, Turbine%SED%y%WriteOutput, Turbine%AD%y, Turbine%ADsk%y%WriteOutput, Turbine%SrvD%y%WriteOutput, & Turbine%SeaSt%y%WriteOutput, Turbine%HD%y%WriteOutput, Turbine%SD%y%WriteOutput, Turbine%ExtPtfm%y%WriteOutput, Turbine%MAP%y%WriteOutput, & Turbine%FEAM%y%WriteOutput, Turbine%MD%y%WriteOutput, Turbine%Orca%y%WriteOutput, & Turbine%IceF%y%WriteOutput, Turbine%IceD%y, Turbine%BD%y, Outputs) @@ -7948,7 +8258,7 @@ END SUBROUTINE FillOutputAry_T !---------------------------------------------------------------------------------------------------------------------------------- !> This routine concatenates all of the WriteOutput values from the module Output into one array to be written to the FAST !! output file. -SUBROUTINE FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_AD, SrvDOutput, SeaStOutput, HDOutput, SDOutput, ExtPtfmOutput, & +SUBROUTINE FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, SEDOutput, y_AD, ADskOutput, SrvDOutput, SeaStOutput, HDOutput, SDOutput, ExtPtfmOutput, & MAPOutput, FEAMOutput, MDOutput, OrcaOutput, IceFOutput, y_IceD, y_BD, OutputAry) TYPE(FAST_ParameterType), INTENT(IN) :: p_FAST !< Glue-code simulation parameters @@ -7957,7 +8267,9 @@ SUBROUTINE FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_A REAL(ReKi), ALLOCATABLE, INTENT(IN) :: IfWOutput (:) !< InflowWind WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: ExtInfwOutput (:) !< ExternalInflow WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: EDOutput (:) !< ElastoDyn WriteOutput values + REAL(ReKi), ALLOCATABLE, INTENT(IN) :: SEDOutput (:) !< Simplified-ElastoDyn WriteOutput values TYPE(AD_OutputType), INTENT(IN) :: y_AD !< AeroDyn outputs (WriteOutput values are subset of allocated Rotors) + REAL(ReKi), ALLOCATABLE, INTENT(IN) :: ADskOutput (:) !< AeroDisk WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: SrvDOutput (:) !< ServoDyn WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: SeaStOutput (:) !< SeaState WriteOutput values REAL(ReKi), ALLOCATABLE, INTENT(IN) :: HDOutput (:) !< HydroDyn WriteOutput values @@ -8005,6 +8317,12 @@ SUBROUTINE FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_A indxNext = IndxLast + 1 END IF + IF ( y_FAST%numOuts(Module_SED) > 0 ) THEN + indxLast = indxNext + SIZE(SEDOutput) - 1 + OutputAry(indxNext:indxLast) = SEDOutput + indxNext = IndxLast + 1 + END IF + IF ( y_FAST%numOuts(Module_BD) > 0 ) THEN do i=1,SIZE(y_BD) indxLast = indxNext + SIZE(y_BD(i)%WriteOutput) - 1 @@ -8023,6 +8341,12 @@ SUBROUTINE FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_A end do END IF + IF ( y_FAST%numOuts(Module_ADsk) > 0 ) THEN + indxLast = indxNext + SIZE(ADskOutput) - 1 + OutputAry(indxNext:indxLast) = ADskOutput + indxNext = IndxLast + 1 + END IF + IF ( y_FAST%numOuts(Module_SrvD) > 0 ) THEN indxLast = indxNext + SIZE(SrvDOutput) - 1 OutputAry(indxNext:indxLast) = SrvDOutput @@ -8083,13 +8407,14 @@ SUBROUTINE FillOutputAry(p_FAST, y_FAST, IfWOutput, ExtInfwOutput, EDOutput, y_A END SUBROUTINE FillOutputAry !---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE WriteVTK(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) +SUBROUTINE WriteVTK(t_global, p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) REAL(DbKi), INTENT(IN ) :: t_global !< Current global time TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(INOUT) :: y_FAST !< Output variables for the glue code (only because we're updating VTK_LastWaveIndx) TYPE(FAST_ModuleMapType), INTENT(IN ) :: MeshMapData !< Data for mapping between modules TYPE(ElastoDyn_Data), INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data @@ -8113,14 +8438,16 @@ SUBROUTINE WriteVTK(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtI IF ( p_FAST%VTK_Type == VTK_Surf ) THEN - CALL WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + CALL WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) ELSE IF ( p_FAST%VTK_Type == VTK_Basic ) THEN - CALL WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + CALL WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) ELSE IF ( p_FAST%VTK_Type == VTK_All ) THEN - CALL WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + CALL WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) ELSE IF (p_FAST%VTK_Type==VTK_Old) THEN + if (p_FAST%CompElast /= Module_SED) then !FIXME: SED is not included in these routines!!!! CALL WriteInputMeshesToFile( ED%Input(1), AD%Input(1), SD%Input(1), HD%Input(1), MAPp%Input(1), BD%Input(1,:), TRIM(p_FAST%OutFileRoot)//'.InputMeshes.bin', ErrStat2, ErrMsg2) CALL WriteMotionMeshesToFile(t_global, ED%y, SD%Input(1), SD%y, HD%Input(1), MAPp%Input(1), BD%y, BD%Input(1,:), y_FAST%UnGra, ErrStat2, ErrMsg2, TRIM(p_FAST%OutFileRoot)//'.gra') + endif !unOut = -1 !CALL MeshWrBin ( unOut, AD%y%BladeLoad(2), ErrStat2, ErrMsg2, 'AD_2_ED_loads.bin'); IF (ErrStat2 /= ErrID_None) CALL WrScr(TRIM(ErrMsg2)) !CALL MeshWrBin ( unOut, ED%Input(1)%BladePtLoads(2),ErrStat2, ErrMsg2, 'AD_2_ED_loads.bin'); IF (ErrStat2 /= ErrID_None) CALL WrScr(TRIM(ErrMsg2)) @@ -8133,7 +8460,7 @@ SUBROUTINE WriteVTK(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtI END SUBROUTINE WriteVTK !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes all the committed meshes to VTK-formatted files. It doesn't bother with returning an error code. -SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) +SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code @@ -8141,6 +8468,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw TYPE(FAST_ModuleMapType), INTENT(IN ) :: MeshMapData !< Data for mapping between modules TYPE(ElastoDyn_Data), INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data @@ -8170,6 +8498,8 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw NumBl = 0 if (allocated(ED%y%BladeRootMotion)) then NumBl = SIZE(ED%y%BladeRootMotion) + elseif (allocated(SED%y%BladeRootMotion)) then + NumBl = SIZE(SED%y%BladeRootMotion) end if @@ -8241,7 +8571,16 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw call MeshWrVTK(p_FAST%TurbinePos, ED%y%BladeLn2Mesh(K), trim(p_FAST%VTK_OutFileRoot)//'.ED_BladeLn2Mesh_motion'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth ) call MeshWrVTK(p_FAST%TurbinePos, ED%Input(1)%BladePtLoads(K), trim(p_FAST%VTK_OutFileRoot)//'.ED_BladePtLoads'//trim(num2lstr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, ED%y%BladeLn2Mesh(K) ) END DO - END IF + ELSE if (p_FAST%CompElast == Module_SED .and. allocated(SED%Input)) then + ! Simplified-ElastoDyn + call MeshWrVTK(p_FAST%TurbinePos, SED%y%PlatformPtMesh, trim(p_FAST%VTK_OutFileRoot)//'.SED_PlatformPtMesh', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + call MeshWrVTK(p_FAST%TurbinePos, SED%y%TowerLn2Mesh, trim(p_FAST%VTK_OutFileRoot)//'.SED_TowerLn2Mesh', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + call MeshWrVTK(p_FAST%TurbinePos, SED%y%NacelleMotion, trim(p_FAST%VTK_OutFileRoot)//'.SED_NacelleMotion', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + call MeshWrVTK(p_FAST%TurbinePos, SED%y%HubPtMotion, trim(p_FAST%VTK_OutFileRoot)//'.SED_HubPtMotion', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + do k=1,NumBl + call MeshWrVTK(p_FAST%TurbinePos, SED%y%BladeRootMotion(k), trim(p_FAST%VTK_OutFileRoot)//'.SED_BladeRootMotion'//trim(Num2LStr(k)), y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + enddo + END IF ! ServoDyn if (allocated(SrvD%Input)) then @@ -8298,6 +8637,7 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw end if end if + call MeshWrVTK(p_FAST%TurbinePos, AD%y%rotors(1)%TowerLoad, trim(p_FAST%VTK_OutFileRoot)//'.AD_Tower', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, AD%Input(1)%rotors(1)%TowerMotion ) ! FVW submodule of AD15 if (allocated(AD%m%FVW_u)) then @@ -8312,6 +8652,9 @@ SUBROUTINE WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw end if END IF +! AeroDisk +!FIXME: add visualization for AeroDisk + ! HydroDyn IF ( p_FAST%CompHydro == Module_HD .and. allocated(HD%Input)) THEN call MeshWrVTK(p_FAST%TurbinePos, HD%Input(1)%PRPMesh, trim(p_FAST%VTK_OutFileRoot)//'.HD_PRP', y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth ) @@ -8402,13 +8745,14 @@ END SUBROUTINE WrVTK_AllMeshes !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes a minimal subset of meshes (enough to visualize the turbine) to VTK-formatted files. It doesn't bother with !! returning an error code. -SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) +SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) TYPE(FAST_ParameterType), INTENT(IN ) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(IN ) :: y_FAST !< Output variables for the glue code TYPE(FAST_ModuleMapType), INTENT(IN ) :: MeshMapData !< Data for mapping between modules TYPE(ElastoDyn_Data), INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data @@ -8432,6 +8776,8 @@ SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtIn NumBl = 0 if (allocated(ED%y%BladeRootMotion)) then NumBl = SIZE(ED%y%BladeRootMotion) + elseif (allocated(SED%y%BladeRootMotion)) then + NumBl = SIZE(SED%y%BladeRootMotion) end if @@ -8455,21 +8801,35 @@ SUBROUTINE WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtIn END DO END IF - if (allocated(ED%Input)) then - ! Nacelle - call MeshWrVTK(p_FAST%TurbinePos, ED%y%NacelleMotion, trim(p_FAST%VTK_OutFileRoot)//'.ED_Nacelle', y_FAST%VTK_count, & - p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, Sib=ED%Input(1)%NacelleLoads ) - ! TailFin - call MeshWrVTK(p_FAST%TurbinePos, ED%y%TFinCMMotion, trim(p_FAST%VTK_OutFileRoot)//'.ED_TailFin', y_FAST%VTK_count, & - p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, Sib=ED%Input(1)%TFinCMLoads ) - ! Hub - call MeshWrVTK(p_FAST%TurbinePos, ED%y%HubPtMotion, trim(p_FAST%VTK_OutFileRoot)//'.ED_Hub', y_FAST%VTK_count, & - p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, Sib=ED%Input(1)%HubPtLoad ) - ! Tower motions - call MeshWrVTK(p_FAST%TurbinePos, ED%y%TowerLn2Mesh, trim(p_FAST%VTK_OutFileRoot)//'.ED_TowerLn2Mesh_motion', & - y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth ) - end if - +! Nacelle + if (p_FAST%CompElast == Module_SED) then + if (allocated(SED%Input)) then + ! Nacelle + call MeshWrVTK(p_FAST%TurbinePos, SED%y%NacelleMotion, trim(p_FAST%VTK_OutFileRoot)//'.SED_NacelleMotion', y_FAST%VTK_count, & + p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + ! Hub + call MeshWrVTK(p_FAST%TurbinePos, SED%y%HubPtMotion, trim(p_FAST%VTK_OutFileRoot)//'.SED_HubPtMotion', y_FAST%VTK_count, & + p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + ! Tower motions + call MeshWrVTK(p_FAST%TurbinePos, SED%y%TowerLn2Mesh, trim(p_FAST%VTK_OutFileRoot)//'.SED_TowerLn2Mesh', y_FAST%VTK_count, & + p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth) + end if + else + if (allocated(ED%Input)) then + ! Nacelle + call MeshWrVTK(p_FAST%TurbinePos, ED%y%NacelleMotion, trim(p_FAST%VTK_OutFileRoot)//'.ED_Nacelle', y_FAST%VTK_count, & + p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, Sib=ED%Input(1)%NacelleLoads ) + ! TailFin + call MeshWrVTK(p_FAST%TurbinePos, ED%y%TFinCMMotion, trim(p_FAST%VTK_OutFileRoot)//'.ED_TailFin', y_FAST%VTK_count, & + p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, Sib=ED%Input(1)%TFinCMLoads ) + ! Hub + call MeshWrVTK(p_FAST%TurbinePos, ED%y%HubPtMotion, trim(p_FAST%VTK_OutFileRoot)//'.ED_Hub', y_FAST%VTK_count, & + p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth, Sib=ED%Input(1)%HubPtLoad ) + ! Tower motions + call MeshWrVTK(p_FAST%TurbinePos, ED%y%TowerLn2Mesh, trim(p_FAST%VTK_OutFileRoot)//'.ED_TowerLn2Mesh_motion', & + y_FAST%VTK_count, p_FAST%VTK_fields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth ) + end if + endif ! Substructure @@ -8521,7 +8881,7 @@ END SUBROUTINE WrVTK_BasicMeshes !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes a minimal subset of meshes with surfaces to VTK-formatted files. It doesn't bother with !! returning an error code. -SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) +SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) use FVW_IO, only: WrVTK_FVW REAL(DbKi), INTENT(IN ) :: t_global !< Current global time @@ -8530,6 +8890,7 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW TYPE(FAST_ModuleMapType), INTENT(IN ) :: MeshMapData !< Data for mapping between modules TYPE(ElastoDyn_Data), INTENT(IN ) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(IN ) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(IN ) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(IN ) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(IN ) :: AD !< AeroDyn data @@ -8555,6 +8916,8 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW NumBl = 0 if (allocated(ED%y%BladeRootMotion)) then NumBl = SIZE(ED%y%BladeRootMotion) + elseif (allocated(SED%y%BladeRootMotion)) then + NumBl = SIZE(SED%y%BladeRootMotion) end if ! Ground (written at initialization) @@ -8599,6 +8962,7 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW call MeshWrVTK_Ln2Surface (p_FAST%TurbinePos, ED%y%BladeLn2Mesh(K), trim(p_FAST%VTK_OutFileRoot)//'.Blade'//trim(num2lstr(k))//'Surface', & y_FAST%VTK_count, OutputFields, ErrStat2, ErrMsg2, p_FAST%VTK_tWidth , verts=p_FAST%VTK_Surface%BladeShape(K)%AirfoilCoords ) END DO +! ELSE IF ( p_FAST%CompElast == Module_SED ) THEN ! No surface info from SED END IF ! Free wake @@ -8658,7 +9022,7 @@ SUBROUTINE WrVTK_Surfaces(t_global, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW if (p_FAST%VTK_fields) then - call WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + call WrVTK_BasicMeshes(p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, HD, SD, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) end if @@ -8860,6 +9224,7 @@ SUBROUTINE WriteInputMeshesToFile(u_ED, u_AD, u_SD, u_HD, u_MAP, u_BD, FileName, CALL MeshWrBin( unOut, u_HD%WAMITMesh, ErrStat, ErrMsg ) CALL MeshWrBin( unOut, u_MAP%PtFairDisplacement, ErrStat, ErrMsg ) ! Add how many BD blade meshes there are: +!FIXME: if u_BD is not allocated, size could return garbage here!!!! NumBl = SIZE(u_BD,1) ! Note that NumBl is B4Ki WRITE( unOut, IOSTAT=ErrStat ) NumBl @@ -8924,6 +9289,7 @@ SUBROUTINE WriteMotionMeshesToFile(time, y_ED, u_SD, y_SD, u_HD, u_MAP, y_BD, u_ ! Add how many blade meshes there are: NumBl = SIZE(y_ED%BladeLn2Mesh,1) ! Note that NumBl is B4Ki WRITE( unOut, IOSTAT=ErrStat ) NumBl +!FIXME: if y_BD is not allocated, size could return garbage here!!!! NumBl = SIZE(y_BD,1) ! Note that NumBl is B4Ki WRITE( unOut, IOSTAT=ErrStat ) NumBl end if @@ -9065,7 +9431,7 @@ SUBROUTINE FAST_Linearize_T(t_initial, n_t_global, Turbine, ErrStat, ErrMsg) end if CALL CalcOutputs_And_SolveForInputs( -1, t_global, STATE_CURR, Turbine%m_FAST%calcJacobian, Turbine%m_FAST%NextJacCalcTime, & - Turbine%p_FAST, Turbine%m_FAST, .false., Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, & + Turbine%p_FAST, Turbine%m_FAST, .false., Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%ExtLd, Turbine%IfW, Turbine%ExtInfw, & Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -9129,14 +9495,14 @@ SUBROUTINE ExitThisProgram_T( Turbine, ErrLevel_in, StopTheProgram, ErrLocMsg, S IF (PRESENT(ErrLocMsg)) THEN CALL ExitThisProgram( Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%IfW, Turbine%ExtInfw, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%IfW, Turbine%ExtInfw, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrLevel_in, StopTheProgram, ErrLocMsg, SkipRunTimes ) ELSE CALL ExitThisProgram( Turbine%p_FAST, Turbine%y_FAST, Turbine%m_FAST, & - Turbine%ED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%IfW, Turbine%ExtInfw, & + Turbine%ED, Turbine%SED, Turbine%BD, Turbine%SrvD, Turbine%AD, Turbine%ADsk, Turbine%IfW, Turbine%ExtInfw, & Turbine%SeaSt, Turbine%HD, Turbine%SD, Turbine%ExtPtfm, Turbine%MAP, Turbine%FEAM, Turbine%MD, Turbine%Orca, & Turbine%IceF, Turbine%IceD, Turbine%MeshMapData, ErrLevel_in, StopTheProgram, SkipRunTimeMsg=SkipRunTimes ) @@ -9152,7 +9518,7 @@ END SUBROUTINE ExitThisProgram_T !! main program. If there was an error, it also aborts. Otherwise, it prints the run times and performs a normal exit. !! This routine should not be called from glue code (e.g., FAST_Prog.f90) or ExitThisProgram_T only. It should not be called in any !! of these driver routines. -SUBROUTINE ExitThisProgram( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE ExitThisProgram( p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrLevel_in, StopTheProgram, ErrLocMsg, SkipRunTimeMsg ) !............................................................................................................................... @@ -9162,9 +9528,11 @@ SUBROUTINE ExitThisProgram( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtIn TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data TYPE(SeaState_Data), INTENT(INOUT) :: SeaSt !< SeaState data @@ -9203,7 +9571,7 @@ SUBROUTINE ExitThisProgram( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtIn IF ( ErrorLevel >= AbortErrLev .AND. p_FAST%WrVTK > VTK_None .and. .not. m_FAST%Lin%FoundSteady) THEN p_FAST%VTK_OutFileRoot = trim(p_FAST%VTK_OutFileRoot)//'.DebugError' p_FAST%VTK_fields = .true. - CALL WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + CALL WrVTK_AllMeshes(p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) end if @@ -9214,7 +9582,7 @@ SUBROUTINE ExitThisProgram( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtIn ! End all modules - CALL FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat2, ErrMsg2 ) + CALL FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, IfW, SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat2, ErrMsg2 ) IF (ErrStat2 /= ErrID_None) THEN CALL WrScr( NewLine//RoutineName//':'//TRIM(ErrMsg2)//NewLine ) ErrorLevel = MAX(ErrorLevel,ErrStat2) @@ -9222,7 +9590,7 @@ SUBROUTINE ExitThisProgram( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtIn ! Destroy all data associated with FAST variables: - CALL FAST_DestroyAll( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) + CALL FAST_DestroyAll( p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) IF (ErrStat2 /= ErrID_None) THEN CALL WrScr( NewLine//RoutineName//':'//TRIM(ErrMsg2)//NewLine ) ErrorLevel = MAX(ErrorLevel,ErrStat2) @@ -9352,16 +9720,18 @@ SUBROUTINE FAST_EndOutput( p_FAST, y_FAST, m_FAST, ErrStat, ErrMsg ) END SUBROUTINE FAST_EndOutput !---------------------------------------------------------------------------------------------------------------------------------- !> This routine calls the end routines for each module that was previously initialized. -SUBROUTINE FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat, ErrMsg ) +SUBROUTINE FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, IfW, SeaSt, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, ErrStat, ErrMsg ) TYPE(FAST_ParameterType), INTENT(INOUT) :: p_FAST !< Parameters for the glue code TYPE(FAST_OutputFileType),INTENT(INOUT) :: y_FAST !< Output variables for the glue code TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(HydroDyn_Data), INTENT(INOUT) :: HD !< HydroDyn data TYPE(SubDyn_Data), INTENT(INOUT) :: SD !< SubDyn data @@ -9398,6 +9768,12 @@ SUBROUTINE FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, SeaSt, H CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) END IF + IF ( p_FAST%ModuleInitialized(Module_SED) ) THEN + CALL SED_End( SED%Input(1), SED%p, SED%x(STATE_CURR), SED%xd(STATE_CURR), SED%z(STATE_CURR), SED%OtherSt(STATE_CURR), & + SED%y, SED%m, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + END IF + IF ( p_FAST%ModuleInitialized(Module_BD) ) THEN DO k=1,p_FAST%nBeams @@ -9413,6 +9789,10 @@ SUBROUTINE FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, SeaSt, H CALL AD_End( AD%Input(1), AD%p, AD%x(STATE_CURR), AD%xd(STATE_CURR), AD%z(STATE_CURR), & AD%OtherSt(STATE_CURR), AD%y, AD%m, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ELSEIF ( p_FAST%ModuleInitialized(Module_ADsk) ) THEN + CALL ADsk_End( ADsk%Input(1), ADsk%p, ADsk%x(STATE_CURR), ADsk%xd(STATE_CURR), ADsk%z(STATE_CURR), & + ADsk%OtherSt(STATE_CURR), ADsk%y, ADsk%m, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) END IF IF ( p_FAST%ModuleInitialized(Module_IfW) ) THEN @@ -9493,7 +9873,7 @@ SUBROUTINE FAST_EndMods( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, SeaSt, H END SUBROUTINE FAST_EndMods !---------------------------------------------------------------------------------------------------------------------------------- !> This routine calls the destroy routines for each module. (It is basically a duplicate of FAST_DestroyTurbineType().) -SUBROUTINE FAST_DestroyAll( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE FAST_DestroyAll( p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat, ErrMsg ) TYPE(FAST_ParameterType), INTENT(INOUT) :: p_FAST !< Parameters for the glue code @@ -9501,6 +9881,7 @@ SUBROUTINE FAST_DestroyAll( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtIn TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data @@ -9551,6 +9932,10 @@ SUBROUTINE FAST_DestroyAll( p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, IfW, ExtIn CALL FAST_DestroyElastoDyn_Data( ED, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! Simplified-ElastoDyn + CALL FAST_DestroySED_Data( SED, ErrStat2, ErrMsg2 ) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + ! BeamDyn CALL FAST_DestroyBeamDyn_Data( BD, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -9990,7 +10375,8 @@ SUBROUTINE FAST_RestoreForVTKModeShape_Tary(t_initial, Turbine, InputFileName, E end if CALL FAST_RestoreForVTKModeShape_T(t_initial, Turbine(i_turb)%p_FAST, Turbine(i_turb)%y_FAST, Turbine(i_turb)%m_FAST, & - Turbine(i_turb)%ED, Turbine(i_turb)%BD, Turbine(i_turb)%SrvD, Turbine(i_turb)%AD, Turbine(i_turb)%ExtLd, Turbine(i_turb)%IfW, Turbine(i_turb)%ExtInfw, & + Turbine(i_turb)%ED, Turbine(i_turb)%SED, Turbine(i_turb)%BD, Turbine(i_turb)%SrvD, & + Turbine(i_turb)%AD, Turbine(i_turb)%ADsk, Turbine(i_turb)%ExtLd, Turbine(i_turb)%IfW, Turbine(i_turb)%ExtInfw, & Turbine(i_turb)%SeaSt, Turbine(i_turb)%HD, Turbine(i_turb)%SD, Turbine(i_turb)%ExtPtfm, Turbine(i_turb)%MAP, Turbine(i_turb)%FEAM, Turbine(i_turb)%MD, Turbine(i_turb)%Orca, & Turbine(i_turb)%IceF, Turbine(i_turb)%IceD, Turbine(i_turb)%MeshMapData, trim(InputFileName), ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -10001,7 +10387,7 @@ END SUBROUTINE FAST_RestoreForVTKModeShape_Tary !---------------------------------------------------------------------------------------------------------------------------------- !> This routine calculates the motions generated by mode shapes and outputs VTK data for it -SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & +SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, & MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, InputFileName, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t_initial !< initial time @@ -10011,9 +10397,11 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED, TYPE(FAST_MiscVarType), INTENT(INOUT) :: m_FAST !< Miscellaneous variables TYPE(ElastoDyn_Data), INTENT(INOUT) :: ED !< ElastoDyn data + TYPE(SED_Data), INTENT(INOUT) :: SED !< Simplified-ElastoDyn data (note: not linearized, but need for interfaces to routines called here) TYPE(BeamDyn_Data), INTENT(INOUT) :: BD !< BeamDyn data TYPE(ServoDyn_Data), INTENT(INOUT) :: SrvD !< ServoDyn data TYPE(AeroDyn_Data), INTENT(INOUT) :: AD !< AeroDyn data + TYPE(AeroDisk_Data), INTENT(INOUT) :: ADsk !< AeroDisk data TYPE(ExtLoads_Data), INTENT(INOUT) :: ExtLd !< ExtLoads data TYPE(InflowWind_Data), INTENT(INOUT) :: IfW !< InflowWind data TYPE(ExternalInflow_Data),INTENT(INOUT) :: ExtInfw !< ExternalInflow data @@ -10119,11 +10507,11 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED, IF (ErrStat >= AbortErrLev) RETURN CALL CalcOutputs_And_SolveForInputs( -1, m_FAST%Lin%LinTimes(iLinTime), STATE_CURR, m_FAST%calcJacobian, m_FAST%NextJacCalcTime, & - p_FAST, m_FAST, .true., ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) + p_FAST, m_FAST, .true., ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN - call WriteVTK(m_FAST%Lin%LinTimes(iLinTime), p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + call WriteVTK(m_FAST%Lin%LinTimes(iLinTime), p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) end do ! iLinTime case (2) @@ -10151,11 +10539,11 @@ SUBROUTINE FAST_RestoreForVTKModeShape_T(t_initial, p_FAST, y_FAST, m_FAST, ED, IF (ErrStat >= AbortErrLev) RETURN CALL CalcOutputs_And_SolveForInputs( -1, m_FAST%Lin%LinTimes(iLinTime), STATE_CURR, m_FAST%calcJacobian, m_FAST%NextJacCalcTime, & - p_FAST, m_FAST, .true., ED, BD, SrvD, AD, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) + p_FAST, m_FAST, .true., ED, SED, BD, SrvD, AD, ADsk, ExtLd, IfW, ExtInfw, HD, SD, ExtPtfm, MAPp, FEAM, MD, Orca, IceF, IceD, MeshMapData, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN - call WriteVTK(m_FAST%Lin%LinTimes(iLinTime)+tprime, p_FAST, y_FAST, MeshMapData, ED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) + call WriteVTK(m_FAST%Lin%LinTimes(iLinTime)+tprime, p_FAST, y_FAST, MeshMapData, ED, SED, BD, AD, IfW, ExtInfw, SeaSt, HD, SD, ExtPtfm, SrvD, MAPp, FEAM, MD, Orca, IceF, IceD) end do ! it end do ! iLinTime diff --git a/modules/openfast-library/src/FAST_Types.f90 b/modules/openfast-library/src/FAST_Types.f90 index 2a83214371..7486951dea 100644 --- a/modules/openfast-library/src/FAST_Types.f90 +++ b/modules/openfast-library/src/FAST_Types.f90 @@ -32,9 +32,11 @@ MODULE FAST_Types !--------------------------------------------------------------------------------------------------------------------------------- USE ElastoDyn_Types +USE SED_Types USE BeamDyn_Types USE ServoDyn_Types USE AeroDyn_Types +USE AeroDisk_Types USE ExtLoads_Types USE SubDyn_Types USE SeaState_Types @@ -70,7 +72,9 @@ MODULE FAST_Types INTEGER(IntKi), PUBLIC, PARAMETER :: Module_Orca = 17 ! OrcaFlex integration (HD/Mooring) [-] INTEGER(IntKi), PUBLIC, PARAMETER :: Module_IceF = 18 ! IceFloe [-] INTEGER(IntKi), PUBLIC, PARAMETER :: Module_IceD = 19 ! IceDyn [-] - INTEGER(IntKi), PUBLIC, PARAMETER :: NumModules = 19 ! The number of modules available in FAST [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: Module_ADsk = 20 ! AeroDisk [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: Module_SED = 21 ! Simplified-ElastoDyn [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: NumModules = 21 ! The number of modules available in FAST [-] INTEGER(IntKi), PUBLIC, PARAMETER :: MaxNBlades = 3 ! Maximum number of blades allowed on a turbine [-] INTEGER(IntKi), PUBLIC, PARAMETER :: IceD_MaxLegs = 4 ! because I don't know how many legs there are before calling IceD_Init and I don't want to copy the data because of sibling mesh issues, I'm going to allocate IceD based on this number [-] INTEGER(IntKi), PUBLIC, PARAMETER :: SS_Indx_Pitch = 1 ! pitch [-] @@ -143,9 +147,9 @@ MODULE FAST_Types REAL(ReKi) :: UJacSclFact = 0.0_ReKi !< Scaling factor used to get similar magnitudes between accelerations, forces, and moments in Jacobians [-] INTEGER(IntKi) , DIMENSION(1:9) :: SizeJac_Opt1 = 0_IntKi !< (1)=size of matrix; (2)=size of ED portion; (3)=size of SD portion [2 meshes]; (4)=size of HD portion; (5)=size of BD portion blade 1; (6)=size of BD portion blade 2; (7)=size of BD portion blade 3; (8)=size of Orca portion; (9)=size of ExtPtfm portion; [-] INTEGER(IntKi) :: SolveOption = 0_IntKi !< Switch to determine which solve option we are going to use (see Solve_FullOpt1, etc) [-] - INTEGER(IntKi) :: CompElast = 0_IntKi !< Compute blade loads (switch) {Module_ED; Module_BD} [-] + INTEGER(IntKi) :: CompElast = 0_IntKi !< Compute blade loads (switch) {Module_ED; Module_BD; Module_SED} [-] INTEGER(IntKi) :: CompInflow = 0_IntKi !< Compute inflow wind conditions (switch) {Module_None; Module_IfW; Module_ExtInfw} [-] - INTEGER(IntKi) :: CompAero = 0_IntKi !< Compute aerodynamic loads (switch) {Module_None; Module_AD} [-] + INTEGER(IntKi) :: CompAero = 0_IntKi !< Compute aerodynamic loads (switch) {Module_None; Module_ADsk; Module_AD} [-] INTEGER(IntKi) :: CompServo = 0_IntKi !< Compute control and electrical-drive dynamics (switch) {Module_None; Module_SrvD} [-] INTEGER(IntKi) :: CompSeaSt = 0_IntKi !< Compute sea states; wave kinematics (switch) {Module_None; Module_SeaSt} [-] INTEGER(IntKi) :: CompHydro = 0_IntKi !< Compute hydrodynamic loads (switch) {Module_None; Module_HD} [-] @@ -167,7 +171,7 @@ MODULE FAST_Types REAL(ReKi) :: Pvap = 0.0_ReKi !< Vapour pressure of working fluid [Pa] REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] REAL(ReKi) :: MSL2SWL = 0.0_ReKi !< Offset between still-water level and mean sea level [m] - CHARACTER(1024) :: EDFile !< The name of the ElastoDyn input file [-] + CHARACTER(1024) :: EDFile !< The name of the ElastoDyn/Simplified-ElastoDyn input file [-] CHARACTER(1024) , DIMENSION(1:MaxNBlades) :: BDBldFile !< Name of files containing BeamDyn inputs for each blade [-] CHARACTER(1024) :: InflowFile !< Name of file containing inflow wind input parameters [-] CHARACTER(1024) :: AeroFile !< Name of file containing aerodynamic input parameters [-] @@ -449,6 +453,22 @@ MODULE FAST_Types REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: InputTimes_Saved !< Backup Array of times associated with Input Array [-] END TYPE ElastoDyn_Data ! ======================= +! ========= SED_Data ======= + TYPE, PUBLIC :: SED_Data + TYPE(SED_ContinuousStateType) , DIMENSION(1:2) :: x !< Continuous states [-] + TYPE(SED_DiscreteStateType) , DIMENSION(1:2) :: xd !< Discrete states [-] + TYPE(SED_ConstraintStateType) , DIMENSION(1:2) :: z !< Constraint states [-] + TYPE(SED_OtherStateType) , DIMENSION(1:2) :: OtherSt !< Other states [-] + TYPE(SED_ParameterType) :: p !< Parameters [-] + TYPE(SED_InputType) :: u !< System inputs [-] + TYPE(SED_OutputType) :: y !< System outputs [-] + TYPE(SED_MiscVarType) :: m !< Misc (optimization) variables not associated with time [-] + TYPE(SED_OutputType) , DIMENSION(:), ALLOCATABLE :: Output !< Array of outputs associated with CalcSteady Azimuths [-] + TYPE(SED_OutputType) :: y_interp !< interpolated system outputs for CalcSteady [-] + TYPE(SED_InputType) , DIMENSION(:), ALLOCATABLE :: Input !< Array of inputs associated with InputTimes [-] + REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: InputTimes !< Array of times associated with Input Array [-] + END TYPE SED_Data +! ======================= ! ========= ServoDyn_Data ======= TYPE, PUBLIC :: ServoDyn_Data TYPE(SrvD_ContinuousStateType) , DIMENSION(1:NumStateTimes) :: x !< Continuous states [-] @@ -499,6 +519,22 @@ MODULE FAST_Types REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: InputTimes !< Array of times associated with Input Array [-] END TYPE ExtLoads_Data ! ======================= +! ========= AeroDisk_Data ======= + TYPE, PUBLIC :: AeroDisk_Data + TYPE(ADsk_ContinuousStateType) , DIMENSION(1:2) :: x !< Continuous states [-] + TYPE(ADsk_DiscreteStateType) , DIMENSION(1:2) :: xd !< Discrete states [-] + TYPE(ADsk_ConstraintStateType) , DIMENSION(1:2) :: z !< Constraint states [-] + TYPE(ADsk_OtherStateType) , DIMENSION(1:2) :: OtherSt !< Other states [-] + TYPE(ADsk_ParameterType) :: p !< Parameters [-] + TYPE(ADsk_InputType) :: u !< System inputs [-] + TYPE(ADsk_OutputType) :: y !< System outputs [-] + TYPE(ADsk_MiscVarType) :: m !< Misc/optimization variables [-] + TYPE(ADsk_OutputType) , DIMENSION(:), ALLOCATABLE :: Output !< Array of outputs associated with CalcSteady Azimuths [-] + TYPE(ADsk_OutputType) :: y_interp !< interpolated system outputs for CalcSteady [-] + TYPE(ADsk_InputType) , DIMENSION(:), ALLOCATABLE :: Input !< Array of inputs associated with InputTimes [-] + REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: InputTimes !< Array of times associated with Input Array [-] + END TYPE AeroDisk_Data +! ======================= ! ========= InflowWind_Data ======= TYPE, PUBLIC :: InflowWind_Data TYPE(InflowWind_ContinuousStateType) , DIMENSION(1:NumStateTimes) :: x !< Continuous states [-] @@ -710,10 +746,13 @@ MODULE FAST_Types TYPE(MeshMapType) , DIMENSION(:,:), ALLOCATABLE :: BStC_P_2_BD_P_B !< Map ServoDyn/BStC point mesh to BeamDyn point load mesh on the blade [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: SStC_P_P_2_SubStructure !< Map ServoDyn/SStC platform point mesh load to SubDyn/ElastoDyn point load mesh [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: SubStructure_2_SStC_P_P !< Map SubDyn y3mesh or ED platform mesh motion to ServoDyn/SStC point mesh [-] - TYPE(MeshMapType) :: ED_P_2_SrvD_P_P !< Map ElastoDyn platform point mesh motion to ServoDyn point mesh -- for passing to controller [-] + TYPE(MeshMapType) :: ED_P_2_SrvD_P_P !< Map ElastoDyn/Simplified-ElastoDyn platform point mesh motion to ServoDyn point mesh -- for passing to controller [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: BDED_L_2_AD_L_B !< Map ElastoDyn BladeLn2Mesh point meshes OR BeamDyn BldMotion line2 meshes to AeroDyn14 InputMarkers OR AeroDyn BladeMotion line2 meshes [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: AD_L_2_BDED_B !< Map AeroDyn14 InputMarkers or AeroDyn BladeLoad line2 meshes to ElastoDyn BladePtLoad point meshes or BeamDyn BldMotion line2 meshes [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: BD_L_2_BD_L !< Map BeamDyn BldMotion output meshes to locations on the BD input DistrLoad mesh stored in MeshMapType%y_BD_BldMotion_4Loads (BD input and output meshes are not siblings and in fact have nodes at different locations [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: SED_P_2_AD_L_B !< Map Simplified-ElastoDyn BladeRoot point meshes to rigid AeroDyn BladeMotion line2 meshes [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: SED_P_2_AD_P_R !< Map Simplified-ElastoDyn BladeRootMotion point meshes to AeroDyn BladeRootMotion point meshes [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: AD_L_2_SED_P !< Map AeroDyn blade load output mesh to Simplified-ElastoDyn Hub point mesh [-] TYPE(MeshMapType) :: ED_P_2_AD_P_N !< Map ElastoDyn Nacelle point motion mesh to AeroDyn Nacelle point motion mesh [-] TYPE(MeshMapType) :: AD_P_2_ED_P_N !< Map AeroDyn Nacelle point load mesh to ElastoDyn nacelle point load mesh [-] TYPE(MeshMapType) :: ED_P_2_AD_P_TF !< Map ElastoDyn TailFin CM point motion mesh to AeroDyn TailFin ref point motion mesh [-] @@ -722,6 +761,13 @@ MODULE FAST_Types TYPE(MeshMapType) :: AD_L_2_ED_P_T !< Map AeroDyn14 Twr_InputMarkers or AeroDyn TowerLoad line2 mesh to ElastoDyn TowerPtLoads point mesh [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: ED_P_2_AD_P_R !< Map ElastoDyn BladeRootMotion point meshes to AeroDyn BladeRootMotion point meshes [-] TYPE(MeshMapType) :: ED_P_2_AD_P_H !< Map ElastoDyn HubPtMotion point mesh to AeroDyn HubMotion point mesh [-] + TYPE(MeshMapType) :: ADsk_P_2_ED_P_H !< Map AeroDisk point load mesh to ElastoDyn hub point load mesh [-] + TYPE(MeshMapType) :: ED_P_2_ADsk_P_H !< Map ElastoDyn HubPtMotion point mesh to AeroDisk HubMotion point mesh [-] + TYPE(MeshMapType) :: SED_P_2_AD_P_N !< Map Simplified-ElastoDyn Nacelle point motion mesh to AeroDyn Nacelle point motion mesh [-] + TYPE(MeshMapType) :: SED_L_2_AD_L_T !< Map Simplified-ElastoDyn TowerLn2Mesh line2 mesh to AeroDyn TowerMotion line2 mesh [-] + TYPE(MeshMapType) :: SED_P_2_AD_P_H !< Map Simplified-ElastoDyn HubPtMotion point mesh to AeroDyn HubMotion point mesh [-] + TYPE(MeshMapType) :: ADsk_P_2_SED_P_H !< Map AeroDisk point load mesh to Simplfied-ElastoDyn hub point load mesh [-] + TYPE(MeshMapType) :: SED_P_2_ADsk_P_H !< Map Simplified-ElastoDyn HubPtMotion point mesh to AeroDisk HubMotion point mesh [-] TYPE(MeshMapType) :: AD_P_2_ED_P_H !< Map AeroDyn HubLoad point mesh to ElastoDyn HubPtLoad point mesh [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: BDED_L_2_ExtLd_P_B !< Map ElastoDyn/BeamDyn BladeLn2Mesh point meshes OR BeamDyn BldMotion line2 meshes to ExtLoads point meshes [-] TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: ExtLd_P_2_BDED_B !< Map ExtLoads at points to ElastoDyn BladePtLoad point meshes or BeamDyn BldMotion line2 meshes [-] @@ -756,6 +802,7 @@ MODULE FAST_Types TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: u_BD_Distrload !< copy of BD DistrLoad input meshes [-] TYPE(MeshType) :: u_Orca_PtfmMesh !< copy of Orca PtfmMesh input mesh [-] TYPE(MeshType) :: u_ExtPtfm_PtfmMesh !< copy of ExtPtfm_MCKF PtfmMesh input mesh [-] + TYPE(MeshType) :: u_SED_HubPtLoad !< copy of SED input mesh [-] REAL(R8Ki) , DIMENSION(:,:,:), ALLOCATABLE :: HubOrient !< Orientation matrix to translate results from blade 1 to remaining blades in aeromaps [(-)] END TYPE FAST_ModuleMapType ! ======================= @@ -792,12 +839,16 @@ MODULE FAST_Types TYPE, PUBLIC :: FAST_InitData TYPE(ED_InitInputType) :: InData_ED !< ED Initialization input data [-] TYPE(ED_InitOutputType) :: OutData_ED !< ED Initialization output data [-] + TYPE(SED_InitInputType) :: InData_SED !< SED Initialization input data [-] + TYPE(SED_InitOutputType) :: OutData_SED !< SED Initialization output data [-] TYPE(BD_InitInputType) :: InData_BD !< BD Initialization input data [-] TYPE(BD_InitOutputType) , DIMENSION(:), ALLOCATABLE :: OutData_BD !< BD Initialization output data [-] TYPE(SrvD_InitInputType) :: InData_SrvD !< SrvD Initialization input data [-] TYPE(SrvD_InitOutputType) :: OutData_SrvD !< SrvD Initialization output data [-] TYPE(AD_InitInputType) :: InData_AD !< AD Initialization input data [-] TYPE(AD_InitOutputType) :: OutData_AD !< AD Initialization output data [-] + TYPE(ADsk_InitInputType) :: InData_ADsk !< ADsk Initialization input data [-] + TYPE(ADsk_InitOutputType) :: OutData_ADsk !< ADsk Initialization output data [-] TYPE(ExtLd_InitInputType) :: InData_ExtLd !< ExtLd Initialization input data [-] TYPE(ExtLd_InitOutputType) :: OutData_ExtLd !< ExtLd Initialization output data [-] TYPE(InflowWind_InitInputType) :: InData_IfW !< IfW Initialization input data [-] @@ -866,9 +917,11 @@ MODULE FAST_Types TYPE(FAST_MiscVarType) :: m_FAST !< Miscellaneous variables [-] TYPE(FAST_ModuleMapType) :: MeshMapData !< Data for mapping between modules [-] TYPE(ElastoDyn_Data) :: ED !< Data for the ElastoDyn module [-] + TYPE(SED_Data) :: SED !< Data for the Simplified-ElastoDyn module [-] TYPE(BeamDyn_Data) :: BD !< Data for the BeamDyn module [-] TYPE(ServoDyn_Data) :: SrvD !< Data for the ServoDyn module [-] TYPE(AeroDyn_Data) :: AD !< Data for the AeroDyn module [-] + TYPE(AeroDisk_Data) :: ADsk !< Data for the AeroDisk module [-] TYPE(ExtLoads_Data) :: ExtLd !< Data for the External loads module [-] TYPE(InflowWind_Data) :: IfW !< Data for InflowWind module [-] TYPE(ExternalInflow_Data) :: ExtInfw !< Data for ExternalInflow integration module [-] @@ -7892,6 +7945,293 @@ subroutine FAST_UnPackElastoDyn_Data(RF, OutData) call RegUnpackAlloc(RF, OutData%InputTimes_Saved); if (RegCheckErr(RF, RoutineName)) return end subroutine +subroutine FAST_CopySED_Data(SrcSED_DataData, DstSED_DataData, CtrlCode, ErrStat, ErrMsg) + type(SED_Data), intent(inout) :: SrcSED_DataData + type(SED_Data), intent(inout) :: DstSED_DataData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FAST_CopySED_Data' + ErrStat = ErrID_None + ErrMsg = '' + LB(1:1) = lbound(SrcSED_DataData%x, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_CopyContState(SrcSED_DataData%x(i1), DstSED_DataData%x(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + LB(1:1) = lbound(SrcSED_DataData%xd, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_CopyDiscState(SrcSED_DataData%xd(i1), DstSED_DataData%xd(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + LB(1:1) = lbound(SrcSED_DataData%z, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_CopyConstrState(SrcSED_DataData%z(i1), DstSED_DataData%z(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + LB(1:1) = lbound(SrcSED_DataData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_CopyOtherState(SrcSED_DataData%OtherSt(i1), DstSED_DataData%OtherSt(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + call SED_CopyParam(SrcSED_DataData%p, DstSED_DataData%p, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call SED_CopyInput(SrcSED_DataData%u, DstSED_DataData%u, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call SED_CopyOutput(SrcSED_DataData%y, DstSED_DataData%y, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call SED_CopyMisc(SrcSED_DataData%m, DstSED_DataData%m, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcSED_DataData%Output)) then + LB(1:1) = lbound(SrcSED_DataData%Output, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%Output, kind=B8Ki) + if (.not. allocated(DstSED_DataData%Output)) then + allocate(DstSED_DataData%Output(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstSED_DataData%Output.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call SED_CopyOutput(SrcSED_DataData%Output(i1), DstSED_DataData%Output(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call SED_CopyOutput(SrcSED_DataData%y_interp, DstSED_DataData%y_interp, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcSED_DataData%Input)) then + LB(1:1) = lbound(SrcSED_DataData%Input, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%Input, kind=B8Ki) + if (.not. allocated(DstSED_DataData%Input)) then + allocate(DstSED_DataData%Input(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstSED_DataData%Input.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call SED_CopyInput(SrcSED_DataData%Input(i1), DstSED_DataData%Input(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcSED_DataData%InputTimes)) then + LB(1:1) = lbound(SrcSED_DataData%InputTimes, kind=B8Ki) + UB(1:1) = ubound(SrcSED_DataData%InputTimes, kind=B8Ki) + if (.not. allocated(DstSED_DataData%InputTimes)) then + allocate(DstSED_DataData%InputTimes(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstSED_DataData%InputTimes.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstSED_DataData%InputTimes = SrcSED_DataData%InputTimes + end if +end subroutine + +subroutine FAST_DestroySED_Data(SED_DataData, ErrStat, ErrMsg) + type(SED_Data), intent(inout) :: SED_DataData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FAST_DestroySED_Data' + ErrStat = ErrID_None + ErrMsg = '' + LB(1:1) = lbound(SED_DataData%x, kind=B8Ki) + UB(1:1) = ubound(SED_DataData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyContState(SED_DataData%x(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + LB(1:1) = lbound(SED_DataData%xd, kind=B8Ki) + UB(1:1) = ubound(SED_DataData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyDiscState(SED_DataData%xd(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + LB(1:1) = lbound(SED_DataData%z, kind=B8Ki) + UB(1:1) = ubound(SED_DataData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyConstrState(SED_DataData%z(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + LB(1:1) = lbound(SED_DataData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(SED_DataData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyOtherState(SED_DataData%OtherSt(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + call SED_DestroyParam(SED_DataData%p, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call SED_DestroyInput(SED_DataData%u, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call SED_DestroyOutput(SED_DataData%y, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call SED_DestroyMisc(SED_DataData%m, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(SED_DataData%Output)) then + LB(1:1) = lbound(SED_DataData%Output, kind=B8Ki) + UB(1:1) = ubound(SED_DataData%Output, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyOutput(SED_DataData%Output(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(SED_DataData%Output) + end if + call SED_DestroyOutput(SED_DataData%y_interp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(SED_DataData%Input)) then + LB(1:1) = lbound(SED_DataData%Input, kind=B8Ki) + UB(1:1) = ubound(SED_DataData%Input, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyInput(SED_DataData%Input(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(SED_DataData%Input) + end if + if (allocated(SED_DataData%InputTimes)) then + deallocate(SED_DataData%InputTimes) + end if +end subroutine + +subroutine FAST_PackSED_Data(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_Data), intent(in) :: InData + character(*), parameter :: RoutineName = 'FAST_PackSED_Data' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + LB(1:1) = lbound(InData%x, kind=B8Ki) + UB(1:1) = ubound(InData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackContState(RF, InData%x(i1)) + end do + LB(1:1) = lbound(InData%xd, kind=B8Ki) + UB(1:1) = ubound(InData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackDiscState(RF, InData%xd(i1)) + end do + LB(1:1) = lbound(InData%z, kind=B8Ki) + UB(1:1) = ubound(InData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackConstrState(RF, InData%z(i1)) + end do + LB(1:1) = lbound(InData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(InData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackOtherState(RF, InData%OtherSt(i1)) + end do + call SED_PackParam(RF, InData%p) + call SED_PackInput(RF, InData%u) + call SED_PackOutput(RF, InData%y) + call SED_PackMisc(RF, InData%m) + call RegPack(RF, allocated(InData%Output)) + if (allocated(InData%Output)) then + call RegPackBounds(RF, 1, lbound(InData%Output, kind=B8Ki), ubound(InData%Output, kind=B8Ki)) + LB(1:1) = lbound(InData%Output, kind=B8Ki) + UB(1:1) = ubound(InData%Output, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackOutput(RF, InData%Output(i1)) + end do + end if + call SED_PackOutput(RF, InData%y_interp) + call RegPack(RF, allocated(InData%Input)) + if (allocated(InData%Input)) then + call RegPackBounds(RF, 1, lbound(InData%Input, kind=B8Ki), ubound(InData%Input, kind=B8Ki)) + LB(1:1) = lbound(InData%Input, kind=B8Ki) + UB(1:1) = ubound(InData%Input, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackInput(RF, InData%Input(i1)) + end do + end if + call RegPackAlloc(RF, InData%InputTimes) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine FAST_UnPackSED_Data(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_Data), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'FAST_UnPackSED_Data' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + LB(1:1) = lbound(OutData%x, kind=B8Ki) + UB(1:1) = ubound(OutData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_UnpackContState(RF, OutData%x(i1)) ! x + end do + LB(1:1) = lbound(OutData%xd, kind=B8Ki) + UB(1:1) = ubound(OutData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_UnpackDiscState(RF, OutData%xd(i1)) ! xd + end do + LB(1:1) = lbound(OutData%z, kind=B8Ki) + UB(1:1) = ubound(OutData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_UnpackConstrState(RF, OutData%z(i1)) ! z + end do + LB(1:1) = lbound(OutData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(OutData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_UnpackOtherState(RF, OutData%OtherSt(i1)) ! OtherSt + end do + call SED_UnpackParam(RF, OutData%p) ! p + call SED_UnpackInput(RF, OutData%u) ! u + call SED_UnpackOutput(RF, OutData%y) ! y + call SED_UnpackMisc(RF, OutData%m) ! m + if (allocated(OutData%Output)) deallocate(OutData%Output) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Output(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Output.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call SED_UnpackOutput(RF, OutData%Output(i1)) ! Output + end do + end if + call SED_UnpackOutput(RF, OutData%y_interp) ! y_interp + if (allocated(OutData%Input)) deallocate(OutData%Input) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Input(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Input.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call SED_UnpackInput(RF, OutData%Input(i1)) ! Input + end do + end if + call RegUnpackAlloc(RF, OutData%InputTimes); if (RegCheckErr(RF, RoutineName)) return +end subroutine + subroutine FAST_CopyServoDyn_Data(SrcServoDyn_DataData, DstServoDyn_DataData, CtrlCode, ErrStat, ErrMsg) type(ServoDyn_Data), intent(inout) :: SrcServoDyn_DataData type(ServoDyn_Data), intent(inout) :: DstServoDyn_DataData @@ -8787,6 +9127,293 @@ subroutine FAST_UnPackExtLoads_Data(RF, OutData) call RegUnpackAlloc(RF, OutData%InputTimes); if (RegCheckErr(RF, RoutineName)) return end subroutine +subroutine FAST_CopyAeroDisk_Data(SrcAeroDisk_DataData, DstAeroDisk_DataData, CtrlCode, ErrStat, ErrMsg) + type(AeroDisk_Data), intent(inout) :: SrcAeroDisk_DataData + type(AeroDisk_Data), intent(inout) :: DstAeroDisk_DataData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FAST_CopyAeroDisk_Data' + ErrStat = ErrID_None + ErrMsg = '' + LB(1:1) = lbound(SrcAeroDisk_DataData%x, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_CopyContState(SrcAeroDisk_DataData%x(i1), DstAeroDisk_DataData%x(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + LB(1:1) = lbound(SrcAeroDisk_DataData%xd, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_CopyDiscState(SrcAeroDisk_DataData%xd(i1), DstAeroDisk_DataData%xd(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + LB(1:1) = lbound(SrcAeroDisk_DataData%z, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_CopyConstrState(SrcAeroDisk_DataData%z(i1), DstAeroDisk_DataData%z(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + LB(1:1) = lbound(SrcAeroDisk_DataData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_CopyOtherState(SrcAeroDisk_DataData%OtherSt(i1), DstAeroDisk_DataData%OtherSt(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + call ADsk_CopyParam(SrcAeroDisk_DataData%p, DstAeroDisk_DataData%p, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call ADsk_CopyInput(SrcAeroDisk_DataData%u, DstAeroDisk_DataData%u, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call ADsk_CopyOutput(SrcAeroDisk_DataData%y, DstAeroDisk_DataData%y, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call ADsk_CopyMisc(SrcAeroDisk_DataData%m, DstAeroDisk_DataData%m, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcAeroDisk_DataData%Output)) then + LB(1:1) = lbound(SrcAeroDisk_DataData%Output, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%Output, kind=B8Ki) + if (.not. allocated(DstAeroDisk_DataData%Output)) then + allocate(DstAeroDisk_DataData%Output(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroDisk_DataData%Output.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call ADsk_CopyOutput(SrcAeroDisk_DataData%Output(i1), DstAeroDisk_DataData%Output(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call ADsk_CopyOutput(SrcAeroDisk_DataData%y_interp, DstAeroDisk_DataData%y_interp, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcAeroDisk_DataData%Input)) then + LB(1:1) = lbound(SrcAeroDisk_DataData%Input, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%Input, kind=B8Ki) + if (.not. allocated(DstAeroDisk_DataData%Input)) then + allocate(DstAeroDisk_DataData%Input(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroDisk_DataData%Input.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call ADsk_CopyInput(SrcAeroDisk_DataData%Input(i1), DstAeroDisk_DataData%Input(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcAeroDisk_DataData%InputTimes)) then + LB(1:1) = lbound(SrcAeroDisk_DataData%InputTimes, kind=B8Ki) + UB(1:1) = ubound(SrcAeroDisk_DataData%InputTimes, kind=B8Ki) + if (.not. allocated(DstAeroDisk_DataData%InputTimes)) then + allocate(DstAeroDisk_DataData%InputTimes(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstAeroDisk_DataData%InputTimes.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstAeroDisk_DataData%InputTimes = SrcAeroDisk_DataData%InputTimes + end if +end subroutine + +subroutine FAST_DestroyAeroDisk_Data(AeroDisk_DataData, ErrStat, ErrMsg) + type(AeroDisk_Data), intent(inout) :: AeroDisk_DataData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FAST_DestroyAeroDisk_Data' + ErrStat = ErrID_None + ErrMsg = '' + LB(1:1) = lbound(AeroDisk_DataData%x, kind=B8Ki) + UB(1:1) = ubound(AeroDisk_DataData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_DestroyContState(AeroDisk_DataData%x(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + LB(1:1) = lbound(AeroDisk_DataData%xd, kind=B8Ki) + UB(1:1) = ubound(AeroDisk_DataData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_DestroyDiscState(AeroDisk_DataData%xd(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + LB(1:1) = lbound(AeroDisk_DataData%z, kind=B8Ki) + UB(1:1) = ubound(AeroDisk_DataData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_DestroyConstrState(AeroDisk_DataData%z(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + LB(1:1) = lbound(AeroDisk_DataData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(AeroDisk_DataData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_DestroyOtherState(AeroDisk_DataData%OtherSt(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + call ADsk_DestroyParam(AeroDisk_DataData%p, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call ADsk_DestroyInput(AeroDisk_DataData%u, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call ADsk_DestroyOutput(AeroDisk_DataData%y, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call ADsk_DestroyMisc(AeroDisk_DataData%m, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(AeroDisk_DataData%Output)) then + LB(1:1) = lbound(AeroDisk_DataData%Output, kind=B8Ki) + UB(1:1) = ubound(AeroDisk_DataData%Output, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_DestroyOutput(AeroDisk_DataData%Output(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(AeroDisk_DataData%Output) + end if + call ADsk_DestroyOutput(AeroDisk_DataData%y_interp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(AeroDisk_DataData%Input)) then + LB(1:1) = lbound(AeroDisk_DataData%Input, kind=B8Ki) + UB(1:1) = ubound(AeroDisk_DataData%Input, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_DestroyInput(AeroDisk_DataData%Input(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(AeroDisk_DataData%Input) + end if + if (allocated(AeroDisk_DataData%InputTimes)) then + deallocate(AeroDisk_DataData%InputTimes) + end if +end subroutine + +subroutine FAST_PackAeroDisk_Data(RF, Indata) + type(RegFile), intent(inout) :: RF + type(AeroDisk_Data), intent(in) :: InData + character(*), parameter :: RoutineName = 'FAST_PackAeroDisk_Data' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + LB(1:1) = lbound(InData%x, kind=B8Ki) + UB(1:1) = ubound(InData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_PackContState(RF, InData%x(i1)) + end do + LB(1:1) = lbound(InData%xd, kind=B8Ki) + UB(1:1) = ubound(InData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_PackDiscState(RF, InData%xd(i1)) + end do + LB(1:1) = lbound(InData%z, kind=B8Ki) + UB(1:1) = ubound(InData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_PackConstrState(RF, InData%z(i1)) + end do + LB(1:1) = lbound(InData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(InData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_PackOtherState(RF, InData%OtherSt(i1)) + end do + call ADsk_PackParam(RF, InData%p) + call ADsk_PackInput(RF, InData%u) + call ADsk_PackOutput(RF, InData%y) + call ADsk_PackMisc(RF, InData%m) + call RegPack(RF, allocated(InData%Output)) + if (allocated(InData%Output)) then + call RegPackBounds(RF, 1, lbound(InData%Output, kind=B8Ki), ubound(InData%Output, kind=B8Ki)) + LB(1:1) = lbound(InData%Output, kind=B8Ki) + UB(1:1) = ubound(InData%Output, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_PackOutput(RF, InData%Output(i1)) + end do + end if + call ADsk_PackOutput(RF, InData%y_interp) + call RegPack(RF, allocated(InData%Input)) + if (allocated(InData%Input)) then + call RegPackBounds(RF, 1, lbound(InData%Input, kind=B8Ki), ubound(InData%Input, kind=B8Ki)) + LB(1:1) = lbound(InData%Input, kind=B8Ki) + UB(1:1) = ubound(InData%Input, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_PackInput(RF, InData%Input(i1)) + end do + end if + call RegPackAlloc(RF, InData%InputTimes) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine FAST_UnPackAeroDisk_Data(RF, OutData) + type(RegFile), intent(inout) :: RF + type(AeroDisk_Data), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'FAST_UnPackAeroDisk_Data' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + LB(1:1) = lbound(OutData%x, kind=B8Ki) + UB(1:1) = ubound(OutData%x, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_UnpackContState(RF, OutData%x(i1)) ! x + end do + LB(1:1) = lbound(OutData%xd, kind=B8Ki) + UB(1:1) = ubound(OutData%xd, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_UnpackDiscState(RF, OutData%xd(i1)) ! xd + end do + LB(1:1) = lbound(OutData%z, kind=B8Ki) + UB(1:1) = ubound(OutData%z, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_UnpackConstrState(RF, OutData%z(i1)) ! z + end do + LB(1:1) = lbound(OutData%OtherSt, kind=B8Ki) + UB(1:1) = ubound(OutData%OtherSt, kind=B8Ki) + do i1 = LB(1), UB(1) + call ADsk_UnpackOtherState(RF, OutData%OtherSt(i1)) ! OtherSt + end do + call ADsk_UnpackParam(RF, OutData%p) ! p + call ADsk_UnpackInput(RF, OutData%u) ! u + call ADsk_UnpackOutput(RF, OutData%y) ! y + call ADsk_UnpackMisc(RF, OutData%m) ! m + if (allocated(OutData%Output)) deallocate(OutData%Output) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Output(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Output.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call ADsk_UnpackOutput(RF, OutData%Output(i1)) ! Output + end do + end if + call ADsk_UnpackOutput(RF, OutData%y_interp) ! y_interp + if (allocated(OutData%Input)) deallocate(OutData%Input) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Input(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Input.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call ADsk_UnpackInput(RF, OutData%Input(i1)) ! Input + end do + end if + call RegUnpackAlloc(RF, OutData%InputTimes); if (RegCheckErr(RF, RoutineName)) return +end subroutine + subroutine FAST_CopyInflowWind_Data(SrcInflowWind_DataData, DstInflowWind_DataData, CtrlCode, ErrStat, ErrMsg) type(InflowWind_Data), intent(in) :: SrcInflowWind_DataData type(InflowWind_Data), intent(inout) :: DstInflowWind_DataData @@ -12499,6 +13126,54 @@ subroutine FAST_CopyModuleMapType(SrcModuleMapTypeData, DstModuleMapTypeData, Ct if (ErrStat >= AbortErrLev) return end do end if + if (allocated(SrcModuleMapTypeData%SED_P_2_AD_L_B)) then + LB(1:1) = lbound(SrcModuleMapTypeData%SED_P_2_AD_L_B, kind=B8Ki) + UB(1:1) = ubound(SrcModuleMapTypeData%SED_P_2_AD_L_B, kind=B8Ki) + if (.not. allocated(DstModuleMapTypeData%SED_P_2_AD_L_B)) then + allocate(DstModuleMapTypeData%SED_P_2_AD_L_B(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstModuleMapTypeData%SED_P_2_AD_L_B.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%SED_P_2_AD_L_B(i1), DstModuleMapTypeData%SED_P_2_AD_L_B(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcModuleMapTypeData%SED_P_2_AD_P_R)) then + LB(1:1) = lbound(SrcModuleMapTypeData%SED_P_2_AD_P_R, kind=B8Ki) + UB(1:1) = ubound(SrcModuleMapTypeData%SED_P_2_AD_P_R, kind=B8Ki) + if (.not. allocated(DstModuleMapTypeData%SED_P_2_AD_P_R)) then + allocate(DstModuleMapTypeData%SED_P_2_AD_P_R(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstModuleMapTypeData%SED_P_2_AD_P_R.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%SED_P_2_AD_P_R(i1), DstModuleMapTypeData%SED_P_2_AD_P_R(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcModuleMapTypeData%AD_L_2_SED_P)) then + LB(1:1) = lbound(SrcModuleMapTypeData%AD_L_2_SED_P, kind=B8Ki) + UB(1:1) = ubound(SrcModuleMapTypeData%AD_L_2_SED_P, kind=B8Ki) + if (.not. allocated(DstModuleMapTypeData%AD_L_2_SED_P)) then + allocate(DstModuleMapTypeData%AD_L_2_SED_P(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstModuleMapTypeData%AD_L_2_SED_P.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%AD_L_2_SED_P(i1), DstModuleMapTypeData%AD_L_2_SED_P(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%ED_P_2_AD_P_N, DstModuleMapTypeData%ED_P_2_AD_P_N, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -12536,6 +13211,27 @@ subroutine FAST_CopyModuleMapType(SrcModuleMapTypeData, DstModuleMapTypeData, Ct call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%ED_P_2_AD_P_H, DstModuleMapTypeData%ED_P_2_AD_P_H, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%ADsk_P_2_ED_P_H, DstModuleMapTypeData%ADsk_P_2_ED_P_H, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%ED_P_2_ADsk_P_H, DstModuleMapTypeData%ED_P_2_ADsk_P_H, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%SED_P_2_AD_P_N, DstModuleMapTypeData%SED_P_2_AD_P_N, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%SED_L_2_AD_L_T, DstModuleMapTypeData%SED_L_2_AD_L_T, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%SED_P_2_AD_P_H, DstModuleMapTypeData%SED_P_2_AD_P_H, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%ADsk_P_2_SED_P_H, DstModuleMapTypeData%ADsk_P_2_SED_P_H, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%SED_P_2_ADsk_P_H, DstModuleMapTypeData%SED_P_2_ADsk_P_H, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return call NWTC_Library_CopyMeshMapType(SrcModuleMapTypeData%AD_P_2_ED_P_H, DstModuleMapTypeData%AD_P_2_ED_P_H, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -12795,6 +13491,9 @@ subroutine FAST_CopyModuleMapType(SrcModuleMapTypeData, DstModuleMapTypeData, Ct call MeshCopy(SrcModuleMapTypeData%u_ExtPtfm_PtfmMesh, DstModuleMapTypeData%u_ExtPtfm_PtfmMesh, CtrlCode, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcModuleMapTypeData%u_SED_HubPtLoad, DstModuleMapTypeData%u_SED_HubPtLoad, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return if (allocated(SrcModuleMapTypeData%HubOrient)) then LB(1:3) = lbound(SrcModuleMapTypeData%HubOrient, kind=B8Ki) UB(1:3) = ubound(SrcModuleMapTypeData%HubOrient, kind=B8Ki) @@ -12992,6 +13691,33 @@ subroutine FAST_DestroyModuleMapType(ModuleMapTypeData, ErrStat, ErrMsg) end do deallocate(ModuleMapTypeData%BD_L_2_BD_L) end if + if (allocated(ModuleMapTypeData%SED_P_2_AD_L_B)) then + LB(1:1) = lbound(ModuleMapTypeData%SED_P_2_AD_L_B, kind=B8Ki) + UB(1:1) = ubound(ModuleMapTypeData%SED_P_2_AD_L_B, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%SED_P_2_AD_L_B(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(ModuleMapTypeData%SED_P_2_AD_L_B) + end if + if (allocated(ModuleMapTypeData%SED_P_2_AD_P_R)) then + LB(1:1) = lbound(ModuleMapTypeData%SED_P_2_AD_P_R, kind=B8Ki) + UB(1:1) = ubound(ModuleMapTypeData%SED_P_2_AD_P_R, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%SED_P_2_AD_P_R(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(ModuleMapTypeData%SED_P_2_AD_P_R) + end if + if (allocated(ModuleMapTypeData%AD_L_2_SED_P)) then + LB(1:1) = lbound(ModuleMapTypeData%AD_L_2_SED_P, kind=B8Ki) + UB(1:1) = ubound(ModuleMapTypeData%AD_L_2_SED_P, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%AD_L_2_SED_P(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(ModuleMapTypeData%AD_L_2_SED_P) + end if call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%ED_P_2_AD_P_N, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%AD_P_2_ED_P_N, ErrStat2, ErrMsg2) @@ -13015,6 +13741,20 @@ subroutine FAST_DestroyModuleMapType(ModuleMapTypeData, ErrStat, ErrMsg) end if call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%ED_P_2_AD_P_H, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%ADsk_P_2_ED_P_H, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%ED_P_2_ADsk_P_H, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%SED_P_2_AD_P_N, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%SED_L_2_AD_L_T, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%SED_P_2_AD_P_H, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%ADsk_P_2_SED_P_H, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%SED_P_2_ADsk_P_H, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call NWTC_Library_DestroyMeshMapType(ModuleMapTypeData%AD_P_2_ED_P_H, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (allocated(ModuleMapTypeData%BDED_L_2_ExtLd_P_B)) then @@ -13156,6 +13896,8 @@ subroutine FAST_DestroyModuleMapType(ModuleMapTypeData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call MeshDestroy( ModuleMapTypeData%u_ExtPtfm_PtfmMesh, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( ModuleMapTypeData%u_SED_HubPtLoad, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (allocated(ModuleMapTypeData%HubOrient)) then deallocate(ModuleMapTypeData%HubOrient) end if @@ -13330,6 +14072,33 @@ subroutine FAST_PackModuleMapType(RF, Indata) call NWTC_Library_PackMeshMapType(RF, InData%BD_L_2_BD_L(i1)) end do end if + call RegPack(RF, allocated(InData%SED_P_2_AD_L_B)) + if (allocated(InData%SED_P_2_AD_L_B)) then + call RegPackBounds(RF, 1, lbound(InData%SED_P_2_AD_L_B, kind=B8Ki), ubound(InData%SED_P_2_AD_L_B, kind=B8Ki)) + LB(1:1) = lbound(InData%SED_P_2_AD_L_B, kind=B8Ki) + UB(1:1) = ubound(InData%SED_P_2_AD_L_B, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%SED_P_2_AD_L_B(i1)) + end do + end if + call RegPack(RF, allocated(InData%SED_P_2_AD_P_R)) + if (allocated(InData%SED_P_2_AD_P_R)) then + call RegPackBounds(RF, 1, lbound(InData%SED_P_2_AD_P_R, kind=B8Ki), ubound(InData%SED_P_2_AD_P_R, kind=B8Ki)) + LB(1:1) = lbound(InData%SED_P_2_AD_P_R, kind=B8Ki) + UB(1:1) = ubound(InData%SED_P_2_AD_P_R, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%SED_P_2_AD_P_R(i1)) + end do + end if + call RegPack(RF, allocated(InData%AD_L_2_SED_P)) + if (allocated(InData%AD_L_2_SED_P)) then + call RegPackBounds(RF, 1, lbound(InData%AD_L_2_SED_P, kind=B8Ki), ubound(InData%AD_L_2_SED_P, kind=B8Ki)) + LB(1:1) = lbound(InData%AD_L_2_SED_P, kind=B8Ki) + UB(1:1) = ubound(InData%AD_L_2_SED_P, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%AD_L_2_SED_P(i1)) + end do + end if call NWTC_Library_PackMeshMapType(RF, InData%ED_P_2_AD_P_N) call NWTC_Library_PackMeshMapType(RF, InData%AD_P_2_ED_P_N) call NWTC_Library_PackMeshMapType(RF, InData%ED_P_2_AD_P_TF) @@ -13346,6 +14115,13 @@ subroutine FAST_PackModuleMapType(RF, Indata) end do end if call NWTC_Library_PackMeshMapType(RF, InData%ED_P_2_AD_P_H) + call NWTC_Library_PackMeshMapType(RF, InData%ADsk_P_2_ED_P_H) + call NWTC_Library_PackMeshMapType(RF, InData%ED_P_2_ADsk_P_H) + call NWTC_Library_PackMeshMapType(RF, InData%SED_P_2_AD_P_N) + call NWTC_Library_PackMeshMapType(RF, InData%SED_L_2_AD_L_T) + call NWTC_Library_PackMeshMapType(RF, InData%SED_P_2_AD_P_H) + call NWTC_Library_PackMeshMapType(RF, InData%ADsk_P_2_SED_P_H) + call NWTC_Library_PackMeshMapType(RF, InData%SED_P_2_ADsk_P_H) call NWTC_Library_PackMeshMapType(RF, InData%AD_P_2_ED_P_H) call RegPack(RF, allocated(InData%BDED_L_2_ExtLd_P_B)) if (allocated(InData%BDED_L_2_ExtLd_P_B)) then @@ -13460,6 +14236,7 @@ subroutine FAST_PackModuleMapType(RF, Indata) end if call MeshPack(RF, InData%u_Orca_PtfmMesh) call MeshPack(RF, InData%u_ExtPtfm_PtfmMesh) + call MeshPack(RF, InData%u_SED_HubPtLoad) call RegPackAlloc(RF, InData%HubOrient) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -13699,6 +14476,45 @@ subroutine FAST_UnPackModuleMapType(RF, OutData) call NWTC_Library_UnpackMeshMapType(RF, OutData%BD_L_2_BD_L(i1)) ! BD_L_2_BD_L end do end if + if (allocated(OutData%SED_P_2_AD_L_B)) deallocate(OutData%SED_P_2_AD_L_B) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%SED_P_2_AD_L_B(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%SED_P_2_AD_L_B.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%SED_P_2_AD_L_B(i1)) ! SED_P_2_AD_L_B + end do + end if + if (allocated(OutData%SED_P_2_AD_P_R)) deallocate(OutData%SED_P_2_AD_P_R) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%SED_P_2_AD_P_R(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%SED_P_2_AD_P_R.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%SED_P_2_AD_P_R(i1)) ! SED_P_2_AD_P_R + end do + end if + if (allocated(OutData%AD_L_2_SED_P)) deallocate(OutData%AD_L_2_SED_P) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%AD_L_2_SED_P(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%AD_L_2_SED_P.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%AD_L_2_SED_P(i1)) ! AD_L_2_SED_P + end do + end if call NWTC_Library_UnpackMeshMapType(RF, OutData%ED_P_2_AD_P_N) ! ED_P_2_AD_P_N call NWTC_Library_UnpackMeshMapType(RF, OutData%AD_P_2_ED_P_N) ! AD_P_2_ED_P_N call NWTC_Library_UnpackMeshMapType(RF, OutData%ED_P_2_AD_P_TF) ! ED_P_2_AD_P_TF @@ -13719,6 +14535,13 @@ subroutine FAST_UnPackModuleMapType(RF, OutData) end do end if call NWTC_Library_UnpackMeshMapType(RF, OutData%ED_P_2_AD_P_H) ! ED_P_2_AD_P_H + call NWTC_Library_UnpackMeshMapType(RF, OutData%ADsk_P_2_ED_P_H) ! ADsk_P_2_ED_P_H + call NWTC_Library_UnpackMeshMapType(RF, OutData%ED_P_2_ADsk_P_H) ! ED_P_2_ADsk_P_H + call NWTC_Library_UnpackMeshMapType(RF, OutData%SED_P_2_AD_P_N) ! SED_P_2_AD_P_N + call NWTC_Library_UnpackMeshMapType(RF, OutData%SED_L_2_AD_L_T) ! SED_L_2_AD_L_T + call NWTC_Library_UnpackMeshMapType(RF, OutData%SED_P_2_AD_P_H) ! SED_P_2_AD_P_H + call NWTC_Library_UnpackMeshMapType(RF, OutData%ADsk_P_2_SED_P_H) ! ADsk_P_2_SED_P_H + call NWTC_Library_UnpackMeshMapType(RF, OutData%SED_P_2_ADsk_P_H) ! SED_P_2_ADsk_P_H call NWTC_Library_UnpackMeshMapType(RF, OutData%AD_P_2_ED_P_H) ! AD_P_2_ED_P_H if (allocated(OutData%BDED_L_2_ExtLd_P_B)) deallocate(OutData%BDED_L_2_ExtLd_P_B) call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return @@ -13873,6 +14696,7 @@ subroutine FAST_UnPackModuleMapType(RF, OutData) end if call MeshUnpack(RF, OutData%u_Orca_PtfmMesh) ! u_Orca_PtfmMesh call MeshUnpack(RF, OutData%u_ExtPtfm_PtfmMesh) ! u_ExtPtfm_PtfmMesh + call MeshUnpack(RF, OutData%u_SED_HubPtLoad) ! u_SED_HubPtLoad call RegUnpackAlloc(RF, OutData%HubOrient); if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -14040,6 +14864,12 @@ subroutine FAST_CopyInitData(SrcInitDataData, DstInitDataData, CtrlCode, ErrStat call ED_CopyInitOutput(SrcInitDataData%OutData_ED, DstInitDataData%OutData_ED, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return + call SED_CopyInitInput(SrcInitDataData%InData_SED, DstInitDataData%InData_SED, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call SED_CopyInitOutput(SrcInitDataData%OutData_SED, DstInitDataData%OutData_SED, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return call BD_CopyInitInput(SrcInitDataData%InData_BD, DstInitDataData%InData_BD, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -14071,6 +14901,12 @@ subroutine FAST_CopyInitData(SrcInitDataData, DstInitDataData, CtrlCode, ErrStat call AD_CopyInitOutput(SrcInitDataData%OutData_AD, DstInitDataData%OutData_AD, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return + call ADsk_CopyInitInput(SrcInitDataData%InData_ADsk, DstInitDataData%InData_ADsk, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call ADsk_CopyInitOutput(SrcInitDataData%OutData_ADsk, DstInitDataData%OutData_ADsk, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return call ExtLd_CopyInitInput(SrcInitDataData%InData_ExtLd, DstInitDataData%InData_ExtLd, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -14166,6 +15002,10 @@ subroutine FAST_DestroyInitData(InitDataData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call ED_DestroyInitOutput(InitDataData%OutData_ED, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call SED_DestroyInitInput(InitDataData%InData_SED, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call SED_DestroyInitOutput(InitDataData%OutData_SED, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call BD_DestroyInitInput(InitDataData%InData_BD, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (allocated(InitDataData%OutData_BD)) then @@ -14185,6 +15025,10 @@ subroutine FAST_DestroyInitData(InitDataData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call AD_DestroyInitOutput(InitDataData%OutData_AD, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call ADsk_DestroyInitInput(InitDataData%InData_ADsk, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call ADsk_DestroyInitOutput(InitDataData%OutData_ADsk, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call ExtLd_DestroyInitInput(InitDataData%InData_ExtLd, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call ExtLd_DestroyInitOutput(InitDataData%OutData_ExtLd, ErrStat2, ErrMsg2) @@ -14248,6 +15092,8 @@ subroutine FAST_PackInitData(RF, Indata) if (RF%ErrStat >= AbortErrLev) return call ED_PackInitInput(RF, InData%InData_ED) call ED_PackInitOutput(RF, InData%OutData_ED) + call SED_PackInitInput(RF, InData%InData_SED) + call SED_PackInitOutput(RF, InData%OutData_SED) call BD_PackInitInput(RF, InData%InData_BD) call RegPack(RF, allocated(InData%OutData_BD)) if (allocated(InData%OutData_BD)) then @@ -14262,6 +15108,8 @@ subroutine FAST_PackInitData(RF, Indata) call SrvD_PackInitOutput(RF, InData%OutData_SrvD) call AD_PackInitInput(RF, InData%InData_AD) call AD_PackInitOutput(RF, InData%OutData_AD) + call ADsk_PackInitInput(RF, InData%InData_ADsk) + call ADsk_PackInitOutput(RF, InData%OutData_ADsk) call ExtLd_PackInitInput(RF, InData%InData_ExtLd) call ExtLd_PackInitOutput(RF, InData%OutData_ExtLd) call InflowWind_PackInitInput(RF, InData%InData_IfW) @@ -14302,6 +15150,8 @@ subroutine FAST_UnPackInitData(RF, OutData) if (RF%ErrStat /= ErrID_None) return call ED_UnpackInitInput(RF, OutData%InData_ED) ! InData_ED call ED_UnpackInitOutput(RF, OutData%OutData_ED) ! OutData_ED + call SED_UnpackInitInput(RF, OutData%InData_SED) ! InData_SED + call SED_UnpackInitOutput(RF, OutData%OutData_SED) ! OutData_SED call BD_UnpackInitInput(RF, OutData%InData_BD) ! InData_BD if (allocated(OutData%OutData_BD)) deallocate(OutData%OutData_BD) call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return @@ -14320,6 +15170,8 @@ subroutine FAST_UnPackInitData(RF, OutData) call SrvD_UnpackInitOutput(RF, OutData%OutData_SrvD) ! OutData_SrvD call AD_UnpackInitInput(RF, OutData%InData_AD) ! InData_AD call AD_UnpackInitOutput(RF, OutData%OutData_AD) ! OutData_AD + call ADsk_UnpackInitInput(RF, OutData%InData_ADsk) ! InData_ADsk + call ADsk_UnpackInitOutput(RF, OutData%OutData_ADsk) ! OutData_ADsk call ExtLd_UnpackInitInput(RF, OutData%InData_ExtLd) ! InData_ExtLd call ExtLd_UnpackInitOutput(RF, OutData%OutData_ExtLd) ! OutData_ExtLd call InflowWind_UnpackInitInput(RF, OutData%InData_IfW) ! InData_IfW @@ -14531,6 +15383,9 @@ subroutine FAST_CopyTurbineType(SrcTurbineTypeData, DstTurbineTypeData, CtrlCode call FAST_CopyElastoDyn_Data(SrcTurbineTypeData%ED, DstTurbineTypeData%ED, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return + call FAST_CopySED_Data(SrcTurbineTypeData%SED, DstTurbineTypeData%SED, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return call FAST_CopyBeamDyn_Data(SrcTurbineTypeData%BD, DstTurbineTypeData%BD, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -14540,6 +15395,9 @@ subroutine FAST_CopyTurbineType(SrcTurbineTypeData, DstTurbineTypeData, CtrlCode call FAST_CopyAeroDyn_Data(SrcTurbineTypeData%AD, DstTurbineTypeData%AD, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return + call FAST_CopyAeroDisk_Data(SrcTurbineTypeData%ADsk, DstTurbineTypeData%ADsk, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return call FAST_CopyExtLoads_Data(SrcTurbineTypeData%ExtLd, DstTurbineTypeData%ExtLd, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -14603,12 +15461,16 @@ subroutine FAST_DestroyTurbineType(TurbineTypeData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call FAST_DestroyElastoDyn_Data(TurbineTypeData%ED, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call FAST_DestroySED_Data(TurbineTypeData%SED, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call FAST_DestroyBeamDyn_Data(TurbineTypeData%BD, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call FAST_DestroyServoDyn_Data(TurbineTypeData%SrvD, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call FAST_DestroyAeroDyn_Data(TurbineTypeData%AD, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call FAST_DestroyAeroDisk_Data(TurbineTypeData%ADsk, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call FAST_DestroyExtLoads_Data(TurbineTypeData%ExtLd, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call FAST_DestroyInflowWind_Data(TurbineTypeData%IfW, ErrStat2, ErrMsg2) @@ -14650,9 +15512,11 @@ subroutine FAST_PackTurbineType(RF, Indata) call FAST_PackMisc(RF, InData%m_FAST) call FAST_PackModuleMapType(RF, InData%MeshMapData) call FAST_PackElastoDyn_Data(RF, InData%ED) + call FAST_PackSED_Data(RF, InData%SED) call FAST_PackBeamDyn_Data(RF, InData%BD) call FAST_PackServoDyn_Data(RF, InData%SrvD) call FAST_PackAeroDyn_Data(RF, InData%AD) + call FAST_PackAeroDisk_Data(RF, InData%ADsk) call FAST_PackExtLoads_Data(RF, InData%ExtLd) call FAST_PackInflowWind_Data(RF, InData%IfW) call FAST_PackExternalInflow_Data(RF, InData%ExtInfw) @@ -14681,9 +15545,11 @@ subroutine FAST_UnPackTurbineType(RF, OutData) call FAST_UnpackMisc(RF, OutData%m_FAST) ! m_FAST call FAST_UnpackModuleMapType(RF, OutData%MeshMapData) ! MeshMapData call FAST_UnpackElastoDyn_Data(RF, OutData%ED) ! ED + call FAST_UnpackSED_Data(RF, OutData%SED) ! SED call FAST_UnpackBeamDyn_Data(RF, OutData%BD) ! BD call FAST_UnpackServoDyn_Data(RF, OutData%SrvD) ! SrvD call FAST_UnpackAeroDyn_Data(RF, OutData%AD) ! AD + call FAST_UnpackAeroDisk_Data(RF, OutData%ADsk) ! ADsk call FAST_UnpackExtLoads_Data(RF, OutData%ExtLd) ! ExtLd call FAST_UnpackInflowWind_Data(RF, OutData%IfW) ! IfW call FAST_UnpackExternalInflow_Data(RF, OutData%ExtInfw) ! ExtInfw diff --git a/modules/servodyn/src/ServoDyn.f90 b/modules/servodyn/src/ServoDyn.f90 index 7fa4b05678..b6b79bca21 100644 --- a/modules/servodyn/src/ServoDyn.f90 +++ b/modules/servodyn/src/ServoDyn.f90 @@ -5201,6 +5201,10 @@ SUBROUTINE Yaw_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg - p%YawDamp*( u%YawRate - YawRateCom ) ! {-f(qd,q,t)}DampYaw; + ! Return the commands directly from the controller (used by SED module) + y%YawPosCom = YawPosCom + y%YawRateCom = YawRateCom + !................................................................... ! Apply trim case for linearization: ! prescribed yaw will be wrong in this case..... diff --git a/modules/servodyn/src/ServoDyn_Registry.txt b/modules/servodyn/src/ServoDyn_Registry.txt index 30013c593f..dfa3ec705d 100644 --- a/modules/servodyn/src/ServoDyn_Registry.txt +++ b/modules/servodyn/src/ServoDyn_Registry.txt @@ -560,6 +560,8 @@ typedef ^ OutputType ReKi WriteOutput {:} - - "Data to be written to an output f typedef ^ OutputType ReKi BlPitchCom {:} - 2pi "Commanded blade pitch angles" radians typedef ^ OutputType ReKi BlAirfoilCom {:} - - "Commanded Airfoil UserProp for blade. Passed to AD15 for airfoil interpolation (must be same units as given in AD15 airfoil tables)" - typedef ^ OutputType ReKi YawMom - - - "Torque transmitted through the yaw bearing" N-m +typedef ^ OutputType ReKi YawPosCom - - - "Yaw command from controller (for SED module)" rad +typedef ^ OutputType ReKi YawRateCom - - - "Yaw rate command from controller (for SED module)" rad/s typedef ^ OutputType ReKi GenTrq - - - "Electrical generator torque" N-m typedef ^ OutputType ReKi HSSBrTrqC - - - "Commanded HSS brake torque" N-m typedef ^ OutputType ReKi ElecPwr - - - "Electrical power" W diff --git a/modules/servodyn/src/ServoDyn_Types.f90 b/modules/servodyn/src/ServoDyn_Types.f90 index 806629157d..d41643068b 100644 --- a/modules/servodyn/src/ServoDyn_Types.f90 +++ b/modules/servodyn/src/ServoDyn_Types.f90 @@ -563,6 +563,8 @@ MODULE ServoDyn_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BlPitchCom !< Commanded blade pitch angles [radians] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BlAirfoilCom !< Commanded Airfoil UserProp for blade. Passed to AD15 for airfoil interpolation (must be same units as given in AD15 airfoil tables) [-] REAL(ReKi) :: YawMom = 0.0_ReKi !< Torque transmitted through the yaw bearing [N-m] + REAL(ReKi) :: YawPosCom = 0.0_ReKi !< Yaw command from controller (for SED module) [rad] + REAL(ReKi) :: YawRateCom = 0.0_ReKi !< Yaw rate command from controller (for SED module) [rad/s] REAL(ReKi) :: GenTrq = 0.0_ReKi !< Electrical generator torque [N-m] REAL(ReKi) :: HSSBrTrqC = 0.0_ReKi !< Commanded HSS brake torque [N-m] REAL(ReKi) :: ElecPwr = 0.0_ReKi !< Electrical power [W] @@ -6039,6 +6041,8 @@ subroutine SrvD_CopyOutput(SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrM DstOutputData%BlAirfoilCom = SrcOutputData%BlAirfoilCom end if DstOutputData%YawMom = SrcOutputData%YawMom + DstOutputData%YawPosCom = SrcOutputData%YawPosCom + DstOutputData%YawRateCom = SrcOutputData%YawRateCom DstOutputData%GenTrq = SrcOutputData%GenTrq DstOutputData%HSSBrTrqC = SrcOutputData%HSSBrTrqC DstOutputData%ElecPwr = SrcOutputData%ElecPwr @@ -6256,6 +6260,8 @@ subroutine SrvD_PackOutput(RF, Indata) call RegPackAlloc(RF, InData%BlPitchCom) call RegPackAlloc(RF, InData%BlAirfoilCom) call RegPack(RF, InData%YawMom) + call RegPack(RF, InData%YawPosCom) + call RegPack(RF, InData%YawRateCom) call RegPack(RF, InData%GenTrq) call RegPack(RF, InData%HSSBrTrqC) call RegPack(RF, InData%ElecPwr) @@ -6318,6 +6324,8 @@ subroutine SrvD_UnPackOutput(RF, OutData) call RegUnpackAlloc(RF, OutData%BlPitchCom); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BlAirfoilCom); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%YawMom); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawPosCom); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawRateCom); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%GenTrq); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%HSSBrTrqC); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%ElecPwr); if (RegCheckErr(RF, RoutineName)) return @@ -6857,6 +6865,8 @@ SUBROUTINE SrvD_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMs y_out%BlAirfoilCom = a1*y1%BlAirfoilCom + a2*y2%BlAirfoilCom END IF ! check if allocated y_out%YawMom = a1*y1%YawMom + a2*y2%YawMom + y_out%YawPosCom = a1*y1%YawPosCom + a2*y2%YawPosCom + y_out%YawRateCom = a1*y1%YawRateCom + a2*y2%YawRateCom y_out%GenTrq = a1*y1%GenTrq + a2*y2%GenTrq y_out%HSSBrTrqC = a1*y1%HSSBrTrqC + a2*y2%HSSBrTrqC y_out%ElecPwr = a1*y1%ElecPwr + a2*y2%ElecPwr @@ -6972,6 +6982,8 @@ SUBROUTINE SrvD_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, E y_out%BlAirfoilCom = a1*y1%BlAirfoilCom + a2*y2%BlAirfoilCom + a3*y3%BlAirfoilCom END IF ! check if allocated y_out%YawMom = a1*y1%YawMom + a2*y2%YawMom + a3*y3%YawMom + y_out%YawPosCom = a1*y1%YawPosCom + a2*y2%YawPosCom + a3*y3%YawPosCom + y_out%YawRateCom = a1*y1%YawRateCom + a2*y2%YawRateCom + a3*y3%YawRateCom y_out%GenTrq = a1*y1%GenTrq + a2*y2%GenTrq + a3*y3%GenTrq y_out%HSSBrTrqC = a1*y1%HSSBrTrqC + a2*y2%HSSBrTrqC + a3*y3%HSSBrTrqC y_out%ElecPwr = a1*y1%ElecPwr + a2*y2%ElecPwr + a3*y3%ElecPwr diff --git a/modules/simple-elastodyn/CMakeLists.txt b/modules/simple-elastodyn/CMakeLists.txt new file mode 100644 index 0000000000..ee5b439649 --- /dev/null +++ b/modules/simple-elastodyn/CMakeLists.txt @@ -0,0 +1,41 @@ +# +# Copyright 2024 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if (GENERATE_TYPES) + generate_f90_types(src/SED_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/SED_Types.f90) +endif() + +add_library(sedlib + src/SED_Types.f90 + src/SED_Output_Params.f90 + src/SED_IO.f90 + src/SED.f90 +) +target_link_libraries(sedlib nwtclibs) + +add_executable(sed_driver + src/driver/SED_Driver_Types.f90 + src/driver/SED_Driver_Subs.f90 + src/driver/SED_Driver.f90 +) +target_link_libraries(sed_driver sedlib versioninfolib ${CMAKE_DL_LIBS}) + +install(TARGETS sedlib sed_driver + EXPORT "${CMAKE_PROJECT_NAME}Libraries" + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/modules/simple-elastodyn/src/SED.f90 b/modules/simple-elastodyn/src/SED.f90 new file mode 100644 index 0000000000..fac980672e --- /dev/null +++ b/modules/simple-elastodyn/src/SED.f90 @@ -0,0 +1,1380 @@ +!********************************************************************************************************************************** +!> ## SED +!! The SED module solves a quasi-steady actuator disk representation of the rotor to calculate the 3 forces and 3 moments of +!! the rotor dependent on the tip-speed ratio (TSR), rotor speed (RotSpeed), relative wind velocity vector (VRel), and the rotor- +!! collective blade-pitch (BlPitch). +!! +! .................................................................................................................................. +!! ## LICENSING +!! Copyright (C) 2024 National Renewable Energy Laboratory +!! +!! This file is part of SED. +!! +!! Licensed under the Apache License, Version 2.0 (the "License"); +!! you may not use this file except in compliance with the License. +!! You may obtain a copy of the License at +!! +!! http://www.apache.org/licenses/LICENSE-2.0 +!! +!! Unless required by applicable law or agreed to in writing, software +!! distributed under the License is distributed on an "AS IS" BASIS, +!! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +!! See the License for the specific language governing permissions and +!! limitations under the License. +!********************************************************************************************************************************** +MODULE SED + + USE SED_Types + USE SED_IO + USE NWTC_Library + + implicit none + private + type(ProgDesc), parameter :: SED_Ver = ProgDesc( 'SED', 'v1.00.00', '15-Feb-2022' ) + + public :: SED_Init + public :: SED_End + public :: SED_UpdateStates + public :: SED_CalcOutput + public :: SED_CalcContStateDeriv + + ! Linearization is not supported by this module, so the following routines are omitted + !public :: SED_CalcConstrStateResidual + !public :: SED_UpdateDiscState + !public :: SED_JacobianPInput + !public :: SED_JacobianPContState + !public :: SED_JacobianPDiscState + !public :: SED_JacobianPConstrState + !public :: SED_GetOP + +CONTAINS + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Initialize the SED module: +!! - load settings (passed or from file) +!! - setup meshes +!! - initialize outputs and other data storage +SUBROUTINE SED_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) + type(SED_InitInputType), intent(in ) :: InitInp !< Input data for initialization routine + type(SED_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined + type(SED_ParameterType), intent( out) :: p !< Parameters + type(SED_ContinuousStateType), intent( out) :: x !< Initial continuous states + type(SED_DiscreteStateType), intent( out) :: xd !< Initial discrete states + type(SED_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states + type(SED_OtherStateType), intent( out) :: OtherState !< Initial other states (logical, etc) + type(SED_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated) + type(SED_MiscVarType), intent( out) :: m !< Misc variables for optimization (not copied in glue code) + real(DbKi), intent(inout) :: Interval !< Coupling interval in seconds: the rate that + type(SED_InitOutputType), intent( out) :: InitOut !< Output for initialization routine + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + type(SED_InputFile) :: InputFileData !< Data from input file as a string array + type(FileInfoType) :: FileInfo_In !< The derived type for holding the full input file for parsing -- we may pass this in the future + integer(IntKi) :: UnEc ! unit number for the echo file (-1 for not in use) + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'SED_Init' + + ! Initialize variables + ErrStat = ErrID_None + ErrMsg = "" + + ! Initialize the NWTC Subroutine Library + call NWTC_Init( ) + + ! Display the module information + call DispNVD( SED_Ver ) + + + ! set rootname + p%RootName = trim(InitInp%RootName) + + ! Get primary input file + if ( InitInp%UseInputFile ) then + CALL ProcessComFile( InitInp%InputFile, FileInfo_In, ErrStat2, ErrMsg2 ) + else + CALL NWTC_Library_CopyFileInfoType( InitInp%PassedFileData, FileInfo_In, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + endif + if (Failed()) return + + ! For diagnostic purposes, the following can be used to display the contents + ! of the FileInfo_In data structure. + !call Print_FileInfo_Struct( CU, FileInfo_In ) ! CU is the screen -- different number on different systems. + + ! Parse all SED-related input and populate the InputFileData structure + call SED_ParsePrimaryFileData( InitInp, p%RootName, Interval, FileInfo_In, InputFileData, UnEc, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + ! Verify all the necessary initialization and input file data + CALL SEDInput_ValidateInput( InitInp, InputFileData, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + ! This should be caught by glue code. Check it here after validation so we can + ! provide something meaningful in error messages about the input file + if (InitInp%Linearize) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = 'SED cannot perform linearization analysis.' + if (Failed()) return + end if + + ! Set parameters + CALL SED_SetParameters(ErrStat2,ErrMsg2); if (Failed()) return; + + ! Set States + call Init_States(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set inputs + call Init_U(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set Meshes + call Init_Mesh(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set miscvars (mesh mappings in here) + call Init_Misc(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set outputs + call Init_Y(ErrStat2,ErrMsg2); if (Failed()) return + + ! Set InitOutputs + call Init_InitY(ErrStat2,ErrMsg2); if (Failed()) return + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed + + !> Store parameters + subroutine SED_SetParameters(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + ! Set parameters + p%RootName = InitInp%RootName + p%DT = InputFileData%DT + Interval = p%DT ! Tell glue code what we want for DT + p%DT24 = p%DT/24.0_DbKi ! Time-step parameter needed for Solver(). + p%numOuts = InputFileData%NumOuts + p%IntMethod = InputFileData%IntMethod + p%GenDOF = InputFileData%GenDOF + p%YawDOF = InputFileData%YawDOF + p%InitYaw = InputFileData%NacYaw + p%InitAzimuth = InputFileData%Azimuth + + ! geometry + p%NumBl = InputFileData%NumBl + p%TipRad = InputFileData%TipRad + p%HubRad = InputFileData%HubRad + p%PreCone = InputFileData%PreCone + p%OverHang = InputFileData%OverHang + p%ShftTilt = InputFileData%ShftTilt + p%Twr2Shft = InputFileData%Twr2Shft + p%TowerHt = InputFileData%TowerHt + p%PtfmPitch = InputFileData%PtfmPitch + p%HubHt = p%TowerHt + p%Twr2Shft + p%OverHang*sin(p%ShftTilt) + !FIXME: Do we need to account for cone???? ED does not + p%BladeLength = p%TipRad - p%HubRad + + ! inertia / drivetrain + p%RotIner = InputFileData%RotIner + p%GenIner = InputFileData%GenIner + ! NOTE: since we do not calculate gearbox or tower top reaction loads, we don't care about the sign of the gearbox ratio (this simplifies our math) + p%GBoxRatio = abs(InputFileData%GBoxRatio) + + ! system inertia + p%J_DT = p%RotIner + p%GBoxRatio**2_IntKi * p%GenIner + + ! Set the outputs + call SetOutParam(InputFileData%OutList, p, ErrStat3, ErrMsg3 ) + end subroutine SED_SetParameters + + !> Initialize states + subroutine Init_States(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + integer(IntKi) :: I + ErrStat3 = ErrID_None + ErrMsg3 = "" + + ! Allocate states (only two states -- azimuth and rotor speed) + call AllocAry( x%QT, 1, 'x%QT', ErrStat3, ErrMsg3); if (ErrStat3 >= AbortErrLev) return + call AllocAry( x%QDT, 1, 'x%QDT', ErrStat3, ErrMsg3); if (ErrStat3 >= AbortErrLev) return + + ! Set initial conditions + x%QT( DOF_Az) = InputFileData%Azimuth + x%QDT(DOF_Az) = InputFileData%RotSpeed + + ! Unused states + xd%DummyDiscreteState = 0.0_ReKi + z%DummyConstrState = 0.0_ReKi + + ! Other states (for HSS brake) + OtherState%HSSBrTrq = 0.0_ReKi + OtherState%HSSBrTrqC = 0.0_ReKi + OtherState%SgnPrvLSTQ = 1 + OtherState%SgnLSTQ = 1 + OtherState%n = -1 ! we haven't updated OtherState%xdot, yet + + ! Now initialize the IC array = (/NMX, NMX-1, ... , 1 /) + ! this keeps track of the position in the array of continuous states (stored in other states) + OtherState%IC(1) = SED_NMX + do I = 2,SED_NMX + OtherState%IC(I) = OtherState%IC(I-1) - 1 + enddo + do i = lbound(OtherState%xdot,1), ubound(OtherState%xdot,1) + call SED_CopyContState( x, OtherState%xdot(i), MESH_NEWCOPY, ErrStat3, ErrMsg3) + if ( ErrStat3 >= AbortErrLev ) return + OtherState%xdot(i)%QT( DOF_Az) = x%QDT(DOF_Az) ! first derivative of azimuth state is rotor speed + OtherState%xdot(i)%QDT(DOF_Az) = 0.0_R8Ki ! assume no acceleration at start (brake torque not known) + enddo + end subroutine Init_States + + !> Initialize the meshes + subroutine Init_Mesh(ErrStat3,ErrMSg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + real(ReKi) :: Pos(3) + real(ReKi) :: Vec(3) + real(R8Ki) :: VecR8(3) + real(R8Ki) :: R33(3,3) + real(R8Ki) :: R33b(3,3) + real(R8Ki) :: R33c(3,3) + real(R8Ki) :: Orient(3,3) + real(R8Ki) :: RootAz + integer(IntKi) :: i + + !------------------------- + ! Set output platform mesh + call MeshCreate ( BlankMesh = y%PlatformPtMesh & + ,IOS = COMPONENT_OUTPUT & + ,Nnodes = 1 & + ,ErrStat = ErrStat3 & + ,ErrMess = ErrMsg3 & + ,Orientation = .true. & + ,TranslationDisp = .true. & + ,RotationVel = .false. & + ,TranslationVel = .false. & + ,RotationAcc = .false. & + ,TranslationAcc = .false. & + ) + if (errStat3 >= AbortErrLev) return + + ! Position/orientation of ref + Pos = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) + call Eye(Orient, ErrStat3, ErrMsg3); if (errStat3 >= AbortErrLev) return + call MeshPositionNode(y%PlatformPtMesh, 1, Pos, errStat3, errMsg3, Orient); if (errStat3 >= AbortErrLev) return + + ! Construct/commit + call MeshConstructElement( y%PlatformPtMesh, ELEMENT_POINT, errStat3, errMsg3, p1=1 ); if (errStat3 >= AbortErrLev) return + call MeshCommit(y%PlatformPtMesh, errStat3, errMsg3 ); if (errStat3 >= AbortErrLev) return + + + !----------------- + ! Set TowerLn2Mesh + call MeshCreate ( BlankMesh = y%TowerLn2Mesh & + ,IOS = COMPONENT_OUTPUT & + ,Nnodes = 2 & + ,ErrStat = ErrStat3 & + ,ErrMess = ErrMsg3 & + ,Orientation = .true. & + ,TranslationDisp = .true. & + ,RotationVel = .false. & + ,TranslationVel = .false. & + ,RotationAcc = .false. & + ,TranslationAcc = .false. & + ) + if (errStat3 >= AbortErrLev) return + + ! Position/orientation of tower base ref + Pos = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) + call Eye(Orient, ErrStat3, ErrMsg3); if (errStat3 >= AbortErrLev) return + call MeshPositionNode(y%TowerLn2Mesh, 1, Pos, errStat3, errMsg3, Orient); if (errStat3 >= AbortErrLev) return + + ! Position/orientation of tower top ref + Pos = (/ 0.0_ReKi, 0.0_ReKi, p%TowerHt /) + call Eye(Orient, ErrStat3, ErrMsg3); if (errStat3 >= AbortErrLev) return + call MeshPositionNode(y%TowerLn2Mesh, 2, Pos, errStat3, errMsg3, Orient); if (errStat3 >= AbortErrLev) return + + ! Construct/commit + call MeshConstructElement( y%TowerLn2Mesh, ELEMENT_LINE2, errStat3, errMsg3, p1=1, p2=2 ); if (errStat3 >= AbortErrLev) return + call MeshCommit(y%TowerLn2Mesh, errStat3, errMsg3 ); if (errStat3 >= AbortErrLev) return + + + !------------------------ + ! Set output nacelle mesh -- nacelle yaw dof exists, but no tower top motion + call MeshCreate ( BlankMesh = y%NacelleMotion & + ,IOS = COMPONENT_OUTPUT & + ,Nnodes = 1 & + ,ErrStat = ErrStat3 & + ,ErrMess = ErrMsg3 & + ,Orientation = .true. & + ,TranslationDisp = .true. & + ,RotationVel = .true. & + ,TranslationVel = .false. & + ,RotationAcc = .false. & + ,TranslationAcc = .false. & + ) + if (errStat3 >= AbortErrLev) return + + ! Position/orientation of ref + Pos = y%TowerLn2Mesh%Position(1:3,2) ! tower top + call Eye(Orient, ErrStat3, ErrMsg3); if (errStat3 >= AbortErrLev) return + call MeshPositionNode(y%NacelleMotion, 1, Pos, errStat3, errMsg3, Orient); if (errStat3 >= AbortErrLev) return + + ! Construct/commit + call MeshConstructElement( y%NacelleMotion, ELEMENT_POINT, errStat3, errMsg3, p1=1 ); if (errStat3 >= AbortErrLev) return + call MeshCommit(y%NacelleMotion, errStat3, errMsg3 ); if (errStat3 >= AbortErrLev) return + + + !-------------------------- + ! Set hub point motion mesh + call MeshCreate ( BlankMesh = y%HubPtMotion & + ,IOS = COMPONENT_OUTPUT & + ,Nnodes = 1 & + ,ErrStat = ErrStat3 & + ,ErrMess = ErrMsg3 & + ,Orientation = .true. & + ,TranslationDisp = .true. & + ,RotationVel = .true. & + ,TranslationVel = .true. & + ,RotationAcc = .true. & + ,TranslationAcc = .true. & ! gets set automatically + ) + if (errStat3 >= AbortErrLev) return + + ! Position/orientation of ref + Pos = y%NacelleMotion%Position(1:3,1) + (/ cos(p%ShftTilt) * p%OverHang, 0.0_ReKi, p%Twr2Shft + sin(p%ShftTilt) * p%OverHang/) + Orient = EulerConstruct( (/ 0.0_R8Ki, -real(p%ShftTilt,R8Ki), 0.0_R8Ki /) ) + call MeshPositionNode(y%HubPtMotion, 1, Pos, errStat3, errMsg3, Orient); if (errStat3 >= AbortErrLev) return + + ! Construct/commit + call MeshConstructElement( y%HubPtMotion, ELEMENT_POINT, errStat3, errMsg3, p1=1 ); if (errStat3 >= AbortErrLev) return + call MeshCommit(y%HubPtMotion, errStat3, errMsg3 ); if (errStat3 >= AbortErrLev) return + + + !-------------------- + ! Set BladeRootMotion + allocate( y%BladeRootMotion(p%NumBl), Stat=ErrStat3 ) + if (ErrStat3 /=0) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = "Could not allocate y%BladeRootMotion mesh" + return + endif + do i=1,p%NumBl + call MeshCreate ( BlankMesh = y%BladeRootMotion(i) & + ,IOS = COMPONENT_OUTPUT & + ,Nnodes = 1 & + ,ErrStat = ErrStat3 & + ,ErrMess = ErrMsg3 & + ,Orientation = .true. & + ,TranslationDisp = .true. & + ,RotationVel = .true. & + ,TranslationVel = .true. & + ,RotationAcc = .true. & + ,TranslationAcc = .true. & + ) + if (errStat3 >= AbortErrLev) return + + ! For blade 1, the reference orientation is the hub reference orientation + ! tilted about the hub y axis by the precone angle. Using the Rodrigues + ! formula for rotating about the hub y + R33(1:3,1:3) = SkewSymMat( y%HubPtMotion%RefOrientation(2,1:3,1) ) ! y axis + call Eye(R33b,ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + ! Rodrigues formula for rotation about a vector + R33b = R33b + sin(real(p%PreCone,R8Ki)) * R33 + (1-cos(real(p%PreCone,R8Ki))) * matmul(R33,R33) + ! apply to ref orientation of hub + Orient = matmul(y%HubPtMotion%RefOrientation(1:3,1:3,1),transpose(R33b)) + + ! now apply azimuth rotation about hub X + RootAz = real((i-1),R8Ki) * TwoPi_R8 / real(p%NumBl,R8Ki) + R33c(1:3,1:3) = SkewSymMat( y%HubPtMotion%RefOrientation(1,1:3,1) ) ! x axis + call Eye(R33b,ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + ! Rodrigues formula for rotation about a vector + R33b = R33b + sin(RootAz) * R33c + (1-cos(RootAz)) * matmul(R33c,R33c) + ! apply to orientation with cone + Orient = matmul(Orient,transpose(R33b)) + + ! for position, just locate along the Z axis + Pos = y%HubPtMotion%Position(1:3,1) + p%HubRad * real(Orient(3,1:3), ReKi) + + ! no blade pitch in the reference + call MeshPositionNode(y%BladeRootMotion(i), 1, Pos, errStat3, errMsg3, Orient); if (errStat3 >= AbortErrLev) return + ! Construct/commit + call MeshConstructElement( y%BladeRootMotion(i), ELEMENT_POINT, errStat3, errMsg3, p1=1 ); if (errStat3 >= AbortErrLev) return + call MeshCommit(y%BladeRootMotion(i), errStat3, errMsg3 ); if (errStat3 >= AbortErrLev) return + enddo + + ! set hub load input mesh + call MeshCopy ( SrcMesh = y%HubPtMotion & + , DestMesh = u%HubPtLoad & + , CtrlCode = MESH_SIBLING & + , IOS = COMPONENT_INPUT & + , Force = .TRUE. & + , Moment = .TRUE. & + , ErrStat = ErrStat3 & + , ErrMess = ErrMsg3 ) + if (ErrStat3 >= AbortErrLev) return + end subroutine Init_Mesh + + !> Initialize the inputs in u + subroutine Init_U(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + + u%GenTrq = 0.0_ReKi + call AllocAry( u%BlPitchCom, p%NumBl, 'u%BlPitchCom', ErrStat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + u%BlPitchCom= InputFileData%BlPitch + u%YawPosCom = InputFileData%NacYaw + u%YawRateCom= 0.0_ReKi + + return + end subroutine Init_U + + !> Initialize miscvars + subroutine Init_Misc(ErrStat3,ErRMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + integer(IntKi) :: i + ErrStat3 = ErrID_None + ErrMsg3 = "" + + !-------------- + ! Mesh mappings + ! These mesh mappings are only valid for the reference frames. During CalcOutput, we will use this mapping + ! to update the fields on the next connected mesh, then manually add values like yaw or pitch. Mapping to + ! the next connection point will propogate these changes forward. + + ! map platform to tower + ! NOTE: this mesh is never needed since Platform Pitch is constant + !call MeshMapCreate( y%PlatformPtMesh, y%TowerLn2Mesh, m%mapPtf2Twr, Errstat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + + ! map Tower to nacelle (does not account for yaw rotation, add manually at calcoutput) + ! NOTE: this mesh mapping is not actually needed since constant platform pitch and no tower flexibility + !call MeshMapCreate( y%TowerLn2Mesh, y%NacelleMotion, m%mapTwr2Nac, Errstat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + + ! map nacelle to hub (does not account for hub rotation, add manually at calcoutput) + call MeshMapCreate( y%NacelleMotion, y%HubPtMotion, m%mapNac2Hub, Errstat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + + ! map hub to blade roots (does not account for blade pitch, add manually at calcoutput) + allocate(m%mapHub2Root(p%NumBl),STAT=ErrStat3) + if (ErrStat3 /= 0) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = "Cannot allocate m%mapHub2Root" + return + endif + do i=1,p%NumBl + call MeshMapCreate( y%HubPtMotion, y%BladeRootMotion(i), m%mapHub2Root(i), Errstat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + enddo + + ! outputs + if (allocated(m%AllOuts)) deallocate(m%AllOuts) + allocate(m%AllOuts(0:MaxOutPts),STAT=ErrStat3) + if (ErrStat3 /= 0) then + ErrStat3 = ErrID_Fatal + ErrMsg3 = "Cannot allocate m%AllOuts" + return + endif + m%AllOuts = 0.0_SiKi + + ! 2nd derivative (acceleration matrix) -- used only in HSS Brake + call AllocAry( m%QD2T, 1, 'm%QD2T', ErrStat3, ErrMsg3); if (ErrStat3 >= AbortErrLev) return + m%QD2T = 0.0_R8Ki + end subroutine Init_Misc + + !> Initialize the InitOutput + subroutine Init_InitY(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + real(R8Ki) :: theta(3) + integer(IntKi) :: i + call AllocAry(InitOut%WriteOutputHdr,p%NumOuts,'WriteOutputHdr',ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + call AllocAry(InitOut%WriteOutputUnt,p%NumOuts,'WriteOutputUnt',ErrStat3,ErrMsg3); if (errStat3 >= AbortErrLev) return + do i=1,p%NumOuts + InitOut%WriteOutputHdr(i) = p%OutParam(i)%Name + InitOut%WriteOutputUnt(i) = p%OutParam(i)%Units + end do + ! Version + InitOut%Ver = SED_Ver + ! Turbine config + InitOut%NumBl = p%NumBl + InitOut%BladeLength = p%BladeLength + InitOut%TowerHt = p%TowerHt + InitOut%HubHt = p%HubHt + InitOut%HubRad = p%HubRad + InitOut%GenDOF = p%GenDOF + + call AllocAry( InitOut%BlPitch, p%NumBl, 'InitOut%BlPitch', ErrStat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + InitOut%BlPitch = InputFileData%BlPitch + InitOut%RotSpeed = x%QDT(DOF_Az) + InitOut%PlatformPos(1:3) = real(y%PlatformPtMesh%Position(1:3,1), ReKi) + real(y%PlatformPtMesh%TranslationDisp(1:3,1), ReKi) + theta(1:3) = GetSmllRotAngs(y%PlatformPtMesh%Orientation(1:3,1:3,1), ErrStat3, ErrMsg3); if (errStat3 >= AbortErrLev) return + InitOut%PlatformPos(4:6) = real(theta, ReKi) + end subroutine Init_InitY + + !> Initialize the outputs in Y + subroutine Init_Y(ErrStat3,ErrMSg3) + integer(IntKi), intent( out) :: ErrStat3 + character(*), intent( out) :: ErrMsg3 + !-------- + call AllocAry( y%BlPitch, p%NumBl, 'y%BlPitch', ErrStat3, ErrMsg3 ); if (errStat3 >= AbortErrLev) return + + !-------- + ! Outputs + call AllocAry(y%WriteOutput,p%NumOuts,'WriteOutput',Errstat3,ErrMsg3); if (ErrStat3 >= AbortErrLev) return + y%WriteOutput = 0.0_ReKi + + ! Set the meshes with initial conditions + call SED_CalcOutput( 0.0_DbKi, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) + end subroutine Init_Y + +END SUBROUTINE SED_Init + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine is called at the end of the simulation. +SUBROUTINE SED_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) + type(SED_InputType), intent(inout) :: u !< System inputs + type(SED_ParameterType), intent(inout) :: p !< Parameters + type(SED_ContinuousStateType), intent(inout) :: x !< Continuous states + type(SED_DiscreteStateType), intent(inout) :: xd !< Discrete states + type(SED_ConstraintStateType), intent(inout) :: z !< Constraint states + type(SED_OtherStateType), intent(inout) :: OtherState !< Other states + type(SED_OutputType), intent(inout) :: y !< System outputs + type(SED_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'SED_End' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + !! Place any last minute operations or calculations here: + + !! Close files here (but because of checkpoint-restart capability, it is not recommended to have files open during the simulation): + + ! Destroy the input data: + call SED_DestroyInput( u, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the parameter data: + call SED_DestroyParam( p, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the state data: + call SED_DestroyContState( x, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call SED_DestroyDiscState( xd, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call SED_DestroyConstrState( z, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + call SED_DestroyOtherState( OtherState, ErrStat2,ErrMsg2); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the output data: + call SED_DestroyOutput( y, ErrStat2, ErrMsg2 ); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + + ! Destroy the misc data: + call SED_DestroyMisc( m, ErrStat2, ErrMsg2 ); call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) +END SUBROUTINE SED_End + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a loose coupling routine for solving constraint states, integrating continuous states, and updating discrete and other +!! states. Continuous, constraint, discrete, and other states are updated to values at t + Interval. +SUBROUTINE SED_UpdateStates( t, n, u, uTimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< Current step of the simulation: t = n*Interval + type(SED_InputType), intent(inout) :: u(:) !< Inputs at InputTimes (output for mesh connect) + real(DbKi), intent(in ) :: uTimes(:) !< Times in seconds associated with Inputs + type(SED_ParameterType), intent(in ) :: p !< Parameters + type(SED_ContinuousStateType), intent(inout) :: x !< Input: Continuous states at t; + type(SED_DiscreteStateType), intent(inout) :: xd !< Input: Discrete states at t; + type(SED_ConstraintStateType), intent(inout) :: z !< Input: Constraint states at t; + type(SED_OtherStateType), intent(inout) :: OtherState !< Other states: Other states at t; + type(SED_MiscVarType), intent(inout) :: m !< Misc variables for optimization + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! Local variables + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'SED_UpdateStates' + + ! Initialize variables + ErrStat = ErrID_None ! no error has occurred + ErrMsg = "" + + ! Simple case of constant RPM + if (.not. p%GenDOF) then + + ! Azimuth angle -- step from n to n+1 + x%QT( DOF_Az) = p%InitAzimuth + x%QDT(DOF_Az) * real(n+1,R8Ki) * p%DT + ! Rotor speed: constant in this case + !x%QDT(DOF_Az) = x%QDT(DOF_Az) + + else + + select case (p%IntMethod) + case (Method_RK4) + call SED_RK4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2 ) + case (Method_AB4) + call SED_AB4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2 ) + case (Method_ABM4) + call SED_ABM4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2 ) + case default + ErrStat = ErrID_Fatal + ErrMsg = ' Error in SED_UpdateStates: p%method must be 1 (RK4), 2 (AB4), or 3 (ABM4)' + return + end select + + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + + endif +end subroutine SED_UpdateStates + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine implements the fourth-order Runge-Kutta Method (RK4) for numerically integrating ordinary differential equations: +!! +!! Let f(t, x) = xdot denote the time (t) derivative of the continuous states (x). +!! Define constants k1, k2, k3, and k4 as +!! k1 = dt * f(t , x_t ) +!! k2 = dt * f(t + dt/2 , x_t + k1/2 ) +!! k3 = dt * f(t + dt/2 , x_t + k2/2 ), and +!! k4 = dt * f(t + dt , x_t + k3 ). +!! Then the continuous states at t = t + dt are +!! x_(t+dt) = x_t + k1/6 + k2/3 + k3/3 + k4/6 + O(dt^5) +!! +!! For details, see: +!! Press, W. H.; Flannery, B. P.; Teukolsky, S. A.; and Vetterling, W. T. "Runge-Kutta Method" and "Adaptive Step Size Control for +!! Runge-Kutta." Sections 16.1 and 16.2 in Numerical Recipes in FORTRAN: The Art of Scientific Computing, 2nd ed. Cambridge, England: +!! Cambridge University Press, pp. 704-716, 1992. +subroutine SED_RK4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< time step number + type(SED_InputType), intent(inout) :: u(:) !< Inputs at t (out only for mesh record-keeping in ExtrapInterp routine) + real(DbKi), intent(in ) :: utimes(:) !< times of input + type(SED_ParameterType), intent(in ) :: p !< Parameters + type(SED_ContinuousStateType), intent(inout) :: x !< Continuous states at t on input at t + dt on output + type(SED_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(SED_ConstraintStateType), intent(in ) :: z !< Constraint states at t (possibly a guess) + type(SED_OtherStateType), intent(inout) :: OtherState !< Other states + type(SED_MiscVarType), intent(inout) :: m !< misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + type(SED_ContinuousStateType) :: xdot ! time derivatives of continuous states + type(SED_ContinuousStateType) :: k1 ! RK4 constant; see above + type(SED_ContinuousStateType) :: k2 ! RK4 constant; see above + type(SED_ContinuousStateType) :: k3 ! RK4 constant; see above + type(SED_ContinuousStateType) :: k4 ! RK4 constant; see above + type(SED_ContinuousStateType) :: x_tmp ! Holds temporary modification to x + type(SED_InputType) :: u_interp ! interpolated value of inputs + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message (ErrMsg) + character(*), parameter :: RoutineName = 'RK4' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + call SED_CopyContState( x, k1, MESH_NEWCOPY, ErrStat2, ErrMsg2 ); if (Failed()) return + call SED_CopyContState( x, k2, MESH_NEWCOPY, ErrStat2, ErrMsg2 ); if (Failed()) return + call SED_CopyContState( x, k3, MESH_NEWCOPY, ErrStat2, ErrMsg2 ); if (Failed()) return + call SED_CopyContState( x, k4, MESH_NEWCOPY, ErrStat2, ErrMsg2 ); if (Failed()) return + call SED_CopyContState( x, x_tmp, MESH_NEWCOPY, ErrStat2, ErrMsg2 ); if (Failed()) return + call SED_CopyInput( u(1), u_interp, MESH_NEWCOPY, ErrStat2, ErrMsg2 ); if (Failed()) return + + ! interpolate u to find u_interp = u(t) + call SED_Input_ExtrapInterp( u, utimes, u_interp, t, ErrStat2, ErrMsg2 ); if (Failed()) return + OtherState%HSSBrTrq = u_interp%HSSBrTrqC + + ! find xdot at t + call SED_CalcContStateDeriv( t, u_interp, p, x, xd, z, OtherState, m, xdot, ErrStat2, ErrMsg2 ) + if (Failed()) return + k1%qt = p%dt * xdot%qt + k1%qdt = p%dt * xdot%qdt + + x_tmp%qt = x%qt + 0.5 * k1%qt + x_tmp%qdt = x%qdt + 0.5 * k1%qdt + + ! interpolate u to find u_interp = u(t + dt/2) + call SED_Input_ExtrapInterp(u, utimes, u_interp, t+0.5*p%dt, ErrStat2, ErrMsg2) + if (Failed()) return + + ! find xdot at t + dt/2 + call SED_CalcContStateDeriv( t + 0.5*p%dt, u_interp, p, x_tmp, xd, z, OtherState, m, xdot, ErrStat2, ErrMsg2 ) + if (Failed()) return + + k2%qt = p%dt * xdot%qt + k2%qdt = p%dt * xdot%qdt + + x_tmp%qt = x%qt + 0.5 * k2%qt + x_tmp%qdt = x%qdt + 0.5 * k2%qdt + + ! find xdot at t + dt/2 + call SED_CalcContStateDeriv( t + 0.5*p%dt, u_interp, p, x_tmp, xd, z, OtherState, m, xdot, ErrStat2, ErrMsg2 ) + if (Failed()) return + + k3%qt = p%dt * xdot%qt + k3%qdt = p%dt * xdot%qdt + + x_tmp%qt = x%qt + k3%qt + x_tmp%qdt = x%qdt + k3%qdt + + ! interpolate u to find u_interp = u(t + dt) + CALL SED_Input_ExtrapInterp(u, utimes, u_interp, t + p%dt, ErrStat2, ErrMsg2) + if (Failed()) return + + ! find xdot at t + dt + call SED_CalcContStateDeriv( t + p%dt, u_interp, p, x_tmp, xd, z, OtherState, m, xdot, ErrStat2, ErrMsg2 ) + if (Failed()) return + + k4%qt = p%dt * xdot%qt + k4%qdt = p%dt * xdot%qdt + + x%qt = x%qt + ( k1%qt + 2. * k2%qt + 2. * k3%qt + k4%qt ) / 6. + x%qdt = x%qdt + ( k1%qdt + 2. * k2%qdt + 2. * k3%qdt + k4%qdt ) / 6. + + call Cleanup() + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine CleanUp() + integer(IntKi) :: ErrStat3 ! The error identifier (ErrStat) + character(ErrMsgLen) :: ErrMsg3 ! The error message (ErrMsg) + call SED_DestroyContState( xdot, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( k1, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( k2, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( k3, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( k4, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( x_tmp, ErrStat3, ErrMsg3 ) + call SED_DestroyInput( u_interp, ErrStat3, ErrMsg3 ) + end subroutine CleanUp +end subroutine SED_RK4 + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine is used to adjust the HSSBrTrq value if the absolute +!! magnitudue of the HSS brake torque was strong enough to reverse +!! the direction of the HSS, which is a physically impossible +!! situation. The problem arises since we are integrating in +!! discrete time, not continuous time. +subroutine FixHSSBrTq ( Integrator, u, p, x, OtherState, m, ErrStat, ErrMsg ) + type(SED_InputType), intent(in ) :: u !< Inputs at t + type(SED_ParameterType), intent(in ) :: p !< Parameters of the structural dynamics module + type(SED_OtherStateType), intent(inout) :: OtherState !< Other states of the structural dynamics module + type(SED_MiscVarType), intent(inout) :: m !< misc (optimization) variables + type(SED_ContinuousStateType), intent(inout) :: x !< Continuous states of the structural dynamics module at n+1 + character(1), intent(in ) :: Integrator !< A string holding the current integrator being used. + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + real(ReKi) :: RqdFrcAz ! The force term required to produce RqdQD2Az. + real(ReKi) :: RqdQD2Az ! The required QD2T(DOF_Az) to cause the HSS to stop rotating. + real(ReKi) :: GenTrqLSS ! Generator torque, expressed on LSS + real(ReKi) :: BrkTrqLSS ! HSS brake torque, expressed on LSS + real(ReKi) :: AeroTrq ! AeroDynamic torque -- passed in on HubPt + integer :: I ! Loops through all DOFs. + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'FixHSSBrTq' + + ErrStat = ErrID_None + ErrMsg = "" + + if ( (.not. p%GenDOF) .OR. EqualRealNos(OtherState%HSSBrTrqC, 0.0_ReKi ) ) return + + ! The absolute magnitude of the HSS brake must have been too great + ! that the HSS direction was reversed. What should have happened + ! is that the HSS should have stopped rotating. In other words, + ! QD(DOF_Az,IC(NMX)) should equal zero! Determining what + ! QD2T(DOF_Az) will make QD(DOF_Az,IC(NMX)) = 0, depends on + ! which integrator we are using. + select case (Integrator) + case ('C') ! Corrector + ! Find the required QD2T(DOF_Az) to cause the HSS to stop rotating (RqdQD2Az). + ! This is found by solving the corrector formula for QD2(DOF_Az,IC(NMX)) + ! when QD(DOF_Az,IC(NMX)) equals zero. + RqdQD2Az = ( - OtherState%xdot(OtherState%IC(1))%qt (DOF_Az)/ p%DT24 & + - 19.0*OtherState%xdot(OtherState%IC(1))%qdt(DOF_Az) & + + 5.0*OtherState%xdot(OtherState%IC(2))%qdt(DOF_Az) & + - OtherState%xdot(OtherState%IC(3))%qdt(DOF_Az) ) / 9.0 + + case ('P') ! Predictor + ! Find the required QD2T(DOF_Az) to cause the HSS to stop rotating (RqdQD2Az). + ! This is found by solving the predictor formula for QD2(DOF_Az,IC(1)) + ! when QD(DOF_Az,IC(NMX)) equals zero. + + RqdQD2Az = ( - OtherState%xdot(OtherState%IC(1))%qt( DOF_Az) / p%DT24 & + + 59.0*OtherState%xdot(OtherState%IC(2))%qdt(DOF_Az) & + - 37.0*OtherState%xdot(OtherState%IC(3))%qdt(DOF_Az) & + + 9.0*OtherState%xdot(OtherState%IC(4))%qdt(DOF_Az) )/55.0 + end select + + ! Rearrange the equations of motion to account + ! for the known acceleration of the azimuth DOF. To + ! do this, make the known inertia like an applied force to the + ! system. + !! + !! \f$ F = Q_a - \ddot{\psi} J_\text{DT} - Q_g - Q_b \f$ + !! + !! where + !! - \f$F\f$ is the additional force required to make the rotor stop + !! - \f$J_\text{DT}\f$ is the system inertia + !! - \f$Q_g = n_g Q_{g,\text{HSS}}\f$ is the generator torque projected to the LSS + !! - \f$Q_b = n_g Q_{b,\text{HSS}}\f$ is the HSS brake torque projected to the LSS + !! + + ! Find the force required to produce RqdQD2Az from the equations of + ! motion using the new accelerations: + GenTrqLSS = p%GBoxRatio * u%GenTrq + BrkTrqLSS = p%GBoxRatio * OtherState%HSSBrTrqC + AeroTrq = dot_product(u%HubPtLoad%Moment(:,1), m%HubPt_X(1:3)) ! torque about hub X + RqdFrcAz = RqdQD2Az * p%J_DT - AeroTrq + GenTrqLSS + BrkTrqLSS + + ! Find the HSSBrTrq necessary to bring about this force: + OtherState%HSSBrTrq = OtherState%HSSBrTrqC - RqdFrcAz/ABS(p%GBoxRatio) + + ! Make sure this new HSSBrTrq isn't larger in absolute magnitude than + ! the original HSSBrTrq. Indeed, the new HSSBrTrq can't be larger than + ! the old HSSBrTrq, since the old HSSBrTrq was found solely as a + ! function of time--and is thus the maximum possible at the current + ! time. If the new HSSBrTrq is larger, then the reversal in direction + ! was caused by factors other than the HSS brake--thus the original HSS + ! brake torque values were OK to begin with. Thus, restore the + ! variables changed by this subroutine, back to their original values: + if ( abs( OtherState%HSSBrTrq ) > abs( OtherState%HSSBrTrqC ) ) then + OtherState%HSSBrTrq = OtherState%HSSBrTrqC !OtherState%HSSBrTrqC = SIGN( u%HSSBrTrqC, x%QDT(DOF_Az) ) + else + ! overwrite QD2T with the new values + m%QD2T(DOF_Az) = RqdQD2Az + + ! Use the new accelerations to update the DOF values. Again, this + ! depends on the integrator type: + SELECT CASE (Integrator) + case ('C') ! Corrector + ! Update QD and QD2 with the new accelerations using the corrector. + ! This will make QD(DOF_Az,IC(NMX)) equal to zero and adjust all + ! of the other QDs as necessary. + ! The Q's are unnaffected by this change. + x%qdt = OtherState%xdot(OtherState%IC(1))%qt & ! qd at n + + p%DT24 * ( 9. * m%QD2T & ! the value we just changed + + 19. * OtherState%xdot(OtherState%IC(1))%qdt & + - 5. * OtherState%xdot(OtherState%IC(2))%qdt & + + 1. * OtherState%xdot(OtherState%IC(3))%qdt ) + case ('P') ! Predictor + ! Update QD and QD2 with the new accelerations using predictor. + x%qdt = OtherState%xdot(OtherState%IC(1))%qt + & ! qd at n + p%DT24 * ( 55.*m%QD2T & ! the value we just changed + - 59.*OtherState%xdot(OtherState%IC(2))%qdt & + + 37.*OtherState%xdot(OtherState%IC(3))%qdt & + - 9.*OtherState%xdot(OtherState%IC(4))%qdt ) + + OtherState%xdot ( OtherState%IC(1) )%qdt = m%QD2T ! fix the history + end select + endif + return +end subroutine FixHSSBrTq + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This function calculates the sign (+/-1) of the low-speed shaft torque for +!! this time step. MomLPRot is the moment on the +!! low-speed shaft at the teeter pin caused by the rotor. +function SignLSSTrq( u, p, m ) + type(SED_InputType), intent(in) :: u !< Inputs at t (out only for mesh record-keeping in ExtrapInterp routine) + type(SED_ParameterType), intent(in) :: p !< Parameters + type(SED_MiscVarType), intent(in) :: m !< Misc variables + integer(IntKi) :: SignLSSTrq !< The sign of the LSS_Trq, output from this function + real(ReKi) :: MomLPRot ! The total moment on the low-speed shaft at point P caused by the rotor. + real(ReKi) :: GenTrqLSS ! Generator torque, expressed on LSS + real(ReKi) :: BrkTrqLSS ! HSS brake torque, expressed on LSS + real(ReKi) :: AeroTrq ! AeroDynamic torque -- passed in on HubPt + + GenTrqLSS = p%GBoxRatio * u%GenTrq + BrkTrqLSS = p%GBoxRatio * u%HSSBrTrqC + AeroTrq = dot_product(u%HubPtLoad%Moment(:,1), m%HubPt_X(1:3)) ! torque about hub X + MomLPRot = AeroTrq - GenTrqLSS - BrkTrqLSS + + ! MomLProt has now been found. Now dot this with e1 to get the + ! low-speed shaft torque and take the SIGN of the result: + SignLSSTrq = nint( sign( 1.0_ReKi,MomLPRot )) +end function SignLSSTrq + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine implements the fourth-order Adams-Bashforth Method (AB4) for numerically integrating ordinary differential +!! equations: +!! +!! Let f(t, x) = xdot denote the time (t) derivative of the continuous states (x). +!! +!! x(t+dt) = x(t) + (dt / 24.) * ( 55.*f(t,x) - 59.*f(t-dt,x) + 37.*f(t-2.*dt,x) - 9.*f(t-3.*dt,x) ) +!! +!! See, e.g., +!! http://en.wikipedia.org/wiki/Linear_multistep_method +!! +!! or +!! +!! K. E. Atkinson, "An Introduction to Numerical Analysis", 1989, John Wiley & Sons, Inc, Second Edition. +subroutine SED_AB4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< time step number + type(SED_InputType), intent(inout) :: u(:) !< Inputs at t (out only for mesh record-keeping in ExtrapInterp routine) + real(DbKi), intent(in ) :: utimes(:) !< times of input + type(SED_ParameterType), intent(in ) :: p !< Parameters + type(SED_ContinuousStateType), intent(inout) :: x !< Continuous states at t on input at t + dt on output + type(SED_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(SED_ConstraintStateType), intent(in ) :: z !< Constraint states at t (possibly a guess) + type(SED_OtherStateType), intent(inout) :: OtherState !< Other states + type(SED_MiscVarType), intent(inout) :: m !< misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + type(SED_InputType) :: u_interp + type(SED_ContinuousStateType) :: xdot + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message (ErrMsg) + character(*), parameter :: RoutineName = 'AB4' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + if (OtherState%n .lt. n) then + OtherState%n = n + ! Update IC() index so IC(1) is the location of xdot values at n. + ! (this allows us to shift the indices into the array, not copy all of the values) + OtherState%IC = CSHIFT( OtherState%IC, -1 ) ! circular shift of all values to the right + elseif (OtherState%n .gt. n) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = ' Backing up in time is not supported with a multistep method.' + if (Failed()) return + endif + + ! Allocate the input arrays + call SED_CopyInput( u(1), u_interp, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + if (Failed()) return + + ! need xdot at t + call SED_Input_ExtrapInterp(u, utimes, u_interp, t, ErrStat2, ErrMsg2) + if (Failed()) return + + if (EqualRealNos( x%qdt(DOF_Az) ,0.0_R8Ki ) ) then + OtherState%HSSBrTrqC = u_interp%HSSBrTrqC + else + OtherState%HSSBrTrqC = SIGN( u_interp%HSSBrTrqC, real(x%qdt(DOF_Az),ReKi) ) ! hack for HSS brake (need correct sign) + endif + OtherState%HSSBrTrq = OtherState%HSSBrTrqC + OtherState%SgnPrvLSTQ = OtherState%SgnLSTQ(OtherState%IC(2)) + + call SED_CalcContStateDeriv( t, u_interp, p, x, xd, z, OtherState, m, xdot, ErrStat2, ErrMsg2 ) + if (Failed()) return + + call SED_CopyContState(xdot, OtherState%xdot ( OtherState%IC(1) ), MESH_NEWCOPY, ErrStat2, ErrMsg2) + if (Failed()) return + + if (n .le. 3) then ! to fully populate through IC(4), must use RK4 three times + call SED_RK4(t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat2, ErrMsg2 ) + if (Failed()) return + else + x%qt = x%qt + p%DT24 * ( 55.*OtherState%xdot(OtherState%IC(1))%qt - 59.*OtherState%xdot(OtherState%IC(2))%qt & + + 37.*OtherState%xdot(OtherState%IC(3))%qt - 9.*OtherState%xdot(OtherState%IC(4))%qt ) + + x%qdt = x%qdt + p%DT24 * ( 55.*OtherState%xdot(OtherState%IC(1))%qdt - 59.*OtherState%xdot(OtherState%IC(2))%qdt & + + 37.*OtherState%xdot(OtherState%IC(3))%qdt - 9.*OtherState%xdot(OtherState%IC(4))%qdt ) + + ! Make sure the HSS brake will not reverse the direction of the HSS + ! for the next time step. Do this by computing the predicted value + ! of x%qt(); QD(DOF_Az,IC(NMX)) as will be done during the next time step. + ! Only do this after the first few time steps since it doesn't work + ! for the Runga-Kutta integration scheme. + call FixHSSBrTq ( 'P', u_interp, p, x, OtherState, m, ErrStat2, ErrMsg2 ) + if (Failed()) return + endif + + OtherState%SgnPrvLSTQ = SignLSSTrq(u_interp, p, m) + OtherState%SgnLSTQ(OtherState%IC(1)) = OtherState%SgnPrvLSTQ + + call Cleanup() + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine CleanUp() + integer(IntKi) :: ErrStat3 ! The error identifier (ErrStat) + character(ErrMsgLen) :: ErrMsg3 ! The error message (ErrMsg) + call SED_DestroyInput( u_interp, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( xdot, ErrStat2, ErrMsg3 ) + end subroutine CleanUp +end subroutine SED_AB4 + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine implements the fourth-order Adams-Bashforth-Moulton Method (ABM4) for numerically integrating ordinary +!! differential equations: +!! +!! Let f(t, x) = xdot denote the time (t) derivative of the continuous states (x). +!! +!! Adams-Bashforth Predictor: \n +!! x^p(t+dt) = x(t) + (dt / 24.) * ( 55.*f(t,x) - 59.*f(t-dt,x) + 37.*f(t-2.*dt,x) - 9.*f(t-3.*dt,x) ) +!! +!! Adams-Moulton Corrector: \n +!! x(t+dt) = x(t) + (dt / 24.) * ( 9.*f(t+dt,x^p) + 19.*f(t,x) - 5.*f(t-dt,x) + 1.*f(t-2.*dt,x) ) +!! +!! See, e.g., +!! http://en.wikipedia.org/wiki/Linear_multistep_method +!! +!! or +!! +!! K. E. Atkinson, "An Introduction to Numerical Analysis", 1989, John Wiley & Sons, Inc, Second Edition. +subroutine SED_ABM4( t, n, u, utimes, p, x, xd, z, OtherState, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + integer(IntKi), intent(in ) :: n !< time step number + type(SED_InputType), intent(inout) :: u(:) !< Inputs at t (out only for mesh record-keeping in ExtrapInterp routine) + real(DbKi), intent(in ) :: utimes(:) !< times of input + type(SED_ParameterType), intent(in ) :: p !< Parameters + type(SED_ContinuousStateType), intent(inout) :: x !< Continuous states at t on input at t + dt on output + type(SED_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(SED_ConstraintStateType), intent(in ) :: z !< Constraint states at t (possibly a guess) + type(SED_OtherStateType), intent(inout) :: OtherState !< Other states + type(SED_MiscVarType), intent(inout) :: m !< misc/optimization variables + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + type(SED_InputType) :: u_interp ! Inputs at t + type(SED_ContinuousStateType) :: x_pred ! Continuous states at t + type(SED_ContinuousStateType) :: xdot_pred ! Derivative of continuous states at t + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message (ErrMsg) + character(*), parameter :: RoutineName = 'ABM4' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! Predict: + call SED_CopyContState(x, x_pred, MESH_NEWCOPY, ErrStat2, ErrMsg2) + if (Failed()) return + + call SED_AB4( t, n, u, utimes, p, x_pred, xd, z, OtherState, m, ErrStat2, ErrMsg2 ) + if (Failed()) return + + ! Correct: + if (n .gt. 2_IntKi) then + ! allocate the arrays in u_interp + call SED_CopyInput( u(1), u_interp, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + if (Failed()) return + + call SED_Input_ExtrapInterp(u, utimes, u_interp, t + p%dt, ErrStat2, ErrMsg2) + if (Failed()) return + + u_interp%HSSBrTrqC = max(0.0_ReKi, min(u_interp%HSSBrTrqC, ABS( OtherState%HSSBrTrqC) )) ! hack for extrapolation of limits (OtherState%HSSBrTrqC is HSSBrTrqC at t) + if (EqualRealNos( x_pred%qdt(DOF_Az) ,0.0_R8Ki ) ) then + OtherState%HSSBrTrqC = u_interp%HSSBrTrqC + else + OtherState%HSSBrTrqC = SIGN( u_interp%HSSBrTrqC, real(x_pred%qdt(DOF_Az),ReKi) ) ! hack for HSS brake (need correct sign) + endif + OtherState%HSSBrTrq = OtherState%HSSBrTrqC + + call SED_CalcContStateDeriv(t + p%dt, u_interp, p, x_pred, xd, z, OtherState, m, xdot_pred, ErrStat2, ErrMsg2 ) + if (Failed()) return + + x%qt = x%qt + p%DT24 * ( 9. * xdot_pred%qt + 19. * OtherState%xdot(OtherState%IC(1))%qt & + - 5. * OtherState%xdot(OtherState%IC(2))%qt & + + 1. * OtherState%xdot(OtherState%IC(3))%qt ) + + x%qdt = x%qdt + p%DT24 * ( 9. * xdot_pred%qdt + 19. * OtherState%xdot(OtherState%IC(1))%qdt & + - 5. * OtherState%xdot(OtherState%IC(2))%qdt & + + 1. * OtherState%xdot(OtherState%IC(3))%qdt ) + + ! Make sure the HSS brake has not reversed the direction of the HSS: + call FixHSSBrTq ( 'C', u_interp, p, x, OtherState, m, ErrStat2, ErrMsg2 ) + if (Failed()) return; + OtherState%SgnPrvLSTQ = SignLSSTrq(u_interp, p, m) + OtherState%SgnLSTQ(OtherState%IC(1)) = OtherState%SgnPrvLSTQ + else + x%qt = x_pred%qt + x%qdt = x_pred%qdt + endif + + call Cleanup() + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine CleanUp() + integer(IntKi) :: ErrStat3 ! The error identifier (ErrStat) + character(ErrMsgLen) :: ErrMsg3 ! The error message (ErrMsg) + call SED_DestroyContState( xdot_pred, ErrStat3, ErrMsg3 ) + call SED_DestroyContState( x_pred, ErrStat3, ErrMsg3 ) + call SED_DestroyInput( u_interp, ErrStat3, ErrMsg3 ) + end subroutine CleanUp +end subroutine SED_ABM4 + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a routine for computing outputs, used in both loose and tight coupling. +SUBROUTINE SED_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg, NeedWriteOutput ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(SED_InputType), intent(in ) :: u !< Inputs at t + type(SED_ParameterType), intent(in ) :: p !< Parameters + type(SED_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(SED_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(SED_ConstraintStateType), intent(in ) :: z !< Constraint states at t + type(SED_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(SED_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + type(SED_OutputType), intent(inout) :: y !< Outputs computed at t (Input only for mesh) + integer(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + logical, optional, intent(in ) :: NeedWriteOutput !< Flag to determine if WriteOutput values need to be calculated in this call + + ! local variables + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'SED_CalcOutput' + real(ReKi) :: Pos(3) + real(R8Ki) :: tmpR8(3) + real(R8Ki) :: R33(3,3) + real(R8Ki) :: R33b(3,3) + real(R8Ki) :: Orient(3,3) + real(ReKi) :: YawRotVel(3) + real(ReKi) :: YawAng + real(ReKi) :: AzRotVel(3) + integer(IntKi) :: i !< Generic counter + logical :: CalcWriteOutput + type(SED_ContinuousStateType) :: dxdt !< Derivatives of continuous states at t + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + m%AllOuts = 0.0_SiKi + + if (present(NeedWriteOutput)) then + CalcWriteOutput = NeedWriteOutput + else + CalcWriteOutput = .true. ! by default, calculate WriteOutput unless told that we do not need it + end if + + + !--------------------------------------------------------------------------- + ! Meshes + ! The mesh fields will be set explicitely here using the state and input information + + !------------------------- + ! Platform mesh (stionary) + y%PlatformPtMesh%TranslationDisp(1:3,1) = (/ 0.0_R8Ki, 0.0_R8Ki, 0.0_R8Ki /) + + ! Initial orientations + call SmllRotTrans( 'platform displacement (SED)', 0.0_R8Ki, real(p%PtfmPitch,R8Ki), 0.0_R8Ki, & + y%PlatformPtMesh%Orientation(:,:,1), errstat=ErrStat2, errmsg=ErrMsg2 ) + if (Failed()) return; + + + !------------------------- + ! TowerLn2Mesh mesh (stationary also) + ! The lower node stays at the PlatformPtMesh position, + ! Upper node is stationary, but we set it here just in case we later add this DOF + y%TowerLn2Mesh%TranslationDisp(1:3,1) = y%PlatformPtMesh%TranslationDisp(1:3,1) + Pos(1) = sin(p%PtfmPitch)*p%TowerHt + Pos(2) = 0.0_ReKi + Pos(3) = cos(p%PtfmPitch)*p%TowerHt + Pos = Pos - y%TowerLn2Mesh%Position(1:3,2) + y%TowerLn2Mesh%TranslationDisp(1:3,2) = real(Pos,R8Ki) + + ! Initial node orientations (same as ptfm) + y%TowerLn2Mesh%Orientation(:,:,1) = y%PlatformPtMesh%Orientation(:,:,1) + y%TowerLn2Mesh%Orientation(:,:,2) = y%PlatformPtMesh%Orientation(:,:,1) + + + !------------------------- + ! Nacelle mesh position + ! Yaw DOF will enable this to rotate + ! NOTE: we do not make any checks for consistency on the input for YawRate!!!! + y%NacelleMotion%TranslationDisp(1:3,1) = y%TowerLn2Mesh%TranslationDisp(1:3,2) + + if (p%YawDOF) then + YawAng = u%YawPosCom + YawRotVel = (/ 0.0_ReKi, 0.0_ReKi, u%YawRateCom /) ! Nacelle coordinate frame + else + YawAng = p%InitYaw + YawRotVel = (/ 0.0_ReKi, 0.0_ReKi, 0.0_Reki /) + endif + + ! Orientation (rotate about tower top (pitched position) + Orient = EulerConstruct( (/ 0.0_R8Ki, 0.0_R8Ki, real(YawAng,R8Ki) /) ) + y%NacelleMotion%Orientation(:,:,1) = matmul(Orient, y%TowerLn2Mesh%Orientation(:,:,2)) + + ! Nacelle motions + y%NacelleMotion%RotationVel(:,1) = matmul(YawRotVel,real(y%NacelleMotion%Orientation(:,:,1),ReKi)) + + + !-------------------------- + ! Hub point motion mesh + ! Transfer nacelle motions (does not include the hub rotation) + call Transfer_Point_to_Point( y%NacelleMotion, y%HubPtMotion, m%mapNac2Hub, ErrStat2, ErrMsg2 ); if (Failed()) return; + + ! include azimuth -- rotate about Hub_X + R33(1:3,1:3) = SkewSymMat( y%HubPtMotion%Orientation(1,1:3,1) ) ! hub x-axis + call Eye(Orient,ErrStat2,ErrMsg2); if (Failed()) return; + ! Rodrigues formula for rotation about a vector + Orient = Orient + sin(real(x%QT(DOF_Az),R8Ki)) * R33 + (1-cos(real(x%QT(DOF_Az),R8Ki))) * matmul(R33,R33) + y%HubPtMotion%Orientation(1:3,1:3,1) = matmul(y%HubPtMotion%Orientation(1:3,1:3,1),transpose(Orient)) + m%HubPt_X = real(y%HubPtMotion%Orientation(1,1:3,1),ReKi) ! Bit of hack, but storing this for use in FixHSSBrTq + + ! Now include the velocity terms from rotor rotation + AzRotVel = (/ real(x%QDT(DOF_Az),ReKi), 0.0_ReKi, 0.0_ReKi /) ! Hub coordinate frame + y%HubPtMotion%RotationVel(1:3,1) = y%HubPtMotion%RotationVel(1:3,1) + matmul(AzRotVel, real(y%HubPtMotion%Orientation(1:3,1:3,1),ReKi)) + + + !-------------------- + ! Set BladeRootMotion + do i=1,p%NumBl + ! Transfer hub motions (does not include the blade pitch) + call Transfer_Point_to_Point( y%HubPtMotion, y%BladeRootMotion(i), m%mapHub2Root(i), ErrStat2, ErrMsg2 ); if (Failed()) return; + + ! include blade pitch -- rotate about Blade_Z + R33(1:3,1:3) = SkewSymMat( y%BladeRootMotion(i)%Orientation(3,1:3,1) ) ! blade z-axis + call Eye(Orient,ErrStat2,ErrMsg2); if (Failed()) return; + ! Rodrigues formula for rotation about a vector (NOTE: BlPitch does not follow right hand rule) + Orient = Orient + sin(real(-u%BlPitchCom(i),R8Ki)) * R33 + (1-cos(real(-u%BlPitchCom(i),R8Ki))) * matmul(R33,R33) + y%BladeRootMotion(i)%Orientation(1:3,1:3,1) = matmul(y%BladeRootMotion(i)%Orientation(1:3,1:3,1),transpose(Orient)) + + ! We don't have a blade pitching rate, so we will not include it here + enddo + + !-------------------- + ! Get derivative of continuous states (need for RotTrq) + call SED_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat2, ErrMsg2 ); if (Failed()) return; + + !-------------------- + ! Other outputs + y%LSSTipPxa = x%QT( DOF_Az) + call Zero2TwoPi(y%LSSTipPxa) ! Modulo + y%RotSpeed = x%QDT(DOF_Az) + y%HSS_Spd = x%QDT(DOF_Az) * p%GBoxRatio + ! Rotor torque is the torque applied to the LSS shaft by the rotor. + ! NOTE: this is equivalent to the reactionary torque of the generator due to its torque and inertia. + y%RotTrq = p%GBoxRatio * u%GenTrq + dxdt%QDT(DOF_Az) * RPS2RPM * p%GBoxRatio * p%GenIner + p%GBoxRatio * OtherState%HSSBrTrq + ! y%RotTrq = dot_product(u%HubPtLoad%Moment(:,1), m%HubPt_X(1:3)) - dxdt%QDT(DOF_Az) * RPS2RPM * p%RotIner ! this equation is somehow wrong. + y%RotPwr = x%QDT(DOF_Az) * y%RotTrq + + ! Simply pass the yaw cammend through as the current yaw + y%Yaw = u%YawPosCom + y%YawRate = u%YawRateCom + y%BlPitch = u%BlPitchCom + + !--------------------------------------------------------------------------- + ! Compute outputs: + if (CalcWriteOutput) then + ! Set the outputs + call Calc_WriteOutput( u, p, x, dxdt, y, m, ErrStat2, ErrMsg2, CalcWriteOutput ); if (Failed()) return; + ! Place the selected output channels into the WriteOutput(:) + do i = 1,p%NumOuts ! Loop through all selected output channels + y%WriteOutput(i) = p%OutParam(i)%SignM * m%AllOuts( p%OutParam(i)%Indx ) + end do + endif + + return; +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine CleanUp() + y%WriteOutput = 0.0_ReKi ! clear any jibberish in outputs since they are not set + end subroutine CleanUp +END SUBROUTINE SED_CalcOutput + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> This is a tight coupling routine for computing derivatives of continuous states. +SUBROUTINE SED_CalcContStateDeriv( t, u, p, x, xd, z, OtherState, m, dxdt, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: t !< Current simulation time in seconds + type(SED_InputType), intent(in ) :: u !< Inputs at t + type(SED_ParameterType), intent(in ) :: p !< Parameters + type(SED_ContinuousStateType), intent(in ) :: x !< Continuous states at t + type(SED_DiscreteStateType), intent(in ) :: xd !< Discrete states at t + type(SED_ConstraintStateType), intent(in ) :: z !< Constraint states at t + type(SED_OtherStateType), intent(in ) :: OtherState !< Other states at t + type(SED_MiscVarType), intent(inout) :: m !< Misc variables for optimization (not copied in glue code) + type(SED_ContinuousStateType), intent( out) :: dxdt !< Continuous state derivatives at t + INTEGER(IntKi), intent( out) :: ErrStat !< Error status of the operation + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + ! local variables + integer(IntKi) :: ErrStat2 ! local error status + character(ErrMsgLen) :: ErrMsg2 ! local error message + character(*), parameter :: RoutineName = 'SED_CalcContStateDeriv' + real(ReKi) :: GenTrqLSS ! Generator torque, expressed on LSS + real(ReKi) :: BrkTrqLSS ! HSS brake torque, expressed on LSS + real(ReKi) :: AeroTrq ! AeroDynamic torque -- passed in on HubPt + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! Compute the first time derivatives of the continuous states here: + if (.not. allocated(dxdt%QT) ) then + call AllocAry( dxdt%QT, size(x%qt), 'dxdt%QT', ErrStat2, ErrMsg2 ) + if (Failed()) return; + dxdt%QT = 0.0_R8Ki + endif + + if (.not. allocated(dxdt%QDT) ) then + call AllocAry( dxdt%QDT, size(x%QDT), 'dxdt%QDT', ErrStat2, ErrMsg2 ) + if (Failed()) return; + dxdt%QDT = 0.0_R8Ki + endif + + + ! First derivative of azimuth is rotor speed, so copy over + dxdt%QT( DOF_Az) = x%QDT(DOF_Az) + + !> rotor acceleration -- only if Generator DOF is on + !! + !! \f$ \ddot{\psi} = \frac{1}{J_\text{DT}} \left( Q_a - Q_g - Q_b \right) \f$ + !! + !! where + !! - \f$J_\text{DT}\f$ is the system inertia + !! - \f$Q_g = n_g Q_{g,\text{HSS}}\f$ is the generator torque projected to the LSS + !! - \f$Q_b = n_g Q_{b,\text{HSS}}\f$ is the HSS brake torque projected to the LSS + !! + if (p%GenDOF) then + GenTrqLSS = p%GBoxRatio * u%GenTrq + BrkTrqLSS = p%GBoxRatio * OtherState%HSSBrTrq + AeroTrq = dot_product(u%HubPtLoad%Moment(:,1), m%HubPt_X(1:3)) ! torque about hub X + dxdt%QDT(DOF_Az) = real((AeroTrq - GenTrqLSS - BrkTrqLSS)/p%J_DT, R8Ki) + else + dxdt%QDT(DOF_Az) = 0.0_R8Ki + endif + +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + !if (Failed) call CleanUp() + end function Failed +END SUBROUTINE SED_CalcContStateDeriv + + +END MODULE SED +!********************************************************************************************************************************** diff --git a/modules/simple-elastodyn/src/SED_IO.f90 b/modules/simple-elastodyn/src/SED_IO.f90 new file mode 100644 index 0000000000..083de3e903 --- /dev/null +++ b/modules/simple-elastodyn/src/SED_IO.f90 @@ -0,0 +1,483 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2024 National Renewable Energy Laboratory +! +! This file is part of Simplified-ElastoDyn (SED) +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +MODULE SED_IO + + USE SED_Types + USE SED_Output_Params + USE NWTC_Library + + implicit none + + integer(IntKi), parameter :: Method_RK4 = 1 + integer(IntKi), parameter :: Method_AB4 = 2 + integer(IntKi), parameter :: Method_ABM4 = 3 + + + real(ReKi), parameter :: SmallAngleLimit_Deg = 15.0 ! Largest input angle considered "small" (used as a check on input data), degrees + integer(IntKi), parameter :: MaxBl = 3 ! Maximum number of blades allowed in simulation + + integer(IntKi), parameter :: DOF_Az = 1 ! Rotor azimuth + + + +contains + +!--------------------------------------------------------------- +!> Parse the input in the InFileInfo (FileInfo_Type data structure): +subroutine SED_ParsePrimaryFileData( InitInp, RootName, interval, FileInfo_In, InputFileData, UnEc, ErrStat, ErrMsg ) + type(SED_InitInputType), intent(in ) :: InitInp !< Input data for initialization routine + character(1024), intent(in ) :: RootName !< root name for summary file + real(DBKi), intent(in ) :: interval !< timestep + type(FileInfoType), intent(in ) :: FileInfo_In !< The input file stored in a data structure + type(SED_InputFile), intent(inout) :: InputFileData !< The data for initialization + integer(IntKi), intent( out) :: UnEc !< The local unit number for this module's echo file + integer(IntKi), intent( out) :: ErrStat !< Error status from this subroutine + character(*), intent( out) :: ErrMsg !< Error message from this subroutine + + ! local vars + integer(IntKi) :: CurLine !< current entry in FileInfo_In%Lines array + integer(IntKi) :: i !< generic counter + real(SiKi) :: TmpRe(10) !< temporary 10 number array for reading values in from table + integer(IntKi) :: ErrStat2 !< Temporary error status for subroutine and function calls + character(ErrMsgLen) :: ErrMsg2 !< Temporary error message for subroutine and function calls + character(*), parameter :: RoutineName="SED_ParsePrimaryFileData" + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + UnEc = -1 ! No file + + + CALL AllocAry( InputFileData%OutList, MaxOutPts, "Outlist", ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + !====== Simulation control ========================================================================= + CurLine = 4 ! Skip the first three lines as they are known to be header lines and separators + call ParseVar( FileInfo_In, CurLine, 'Echo', InputFileData%Echo, ErrStat2, ErrMsg2 ) + if (Failed()) return; + + if ( InputFileData%Echo ) then + CALL OpenEcho ( UnEc, TRIM(RootName)//'.ech', ErrStat2, ErrMsg2 ) + if (Failed()) return; + WRITE(UnEc, '(A)') 'Echo file for AeroDisk primary input file: '//trim(InitInp%InputFile) + ! Write the first three lines into the echo file + WRITE(UnEc, '(A)') FileInfo_In%Lines(1) + WRITE(UnEc, '(A)') FileInfo_In%Lines(2) + WRITE(UnEc, '(A)') FileInfo_In%Lines(3) + + CurLine = 4 + call ParseVar( FileInfo_In, CurLine, 'Echo', InputFileData%Echo, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + endif + + ! IntMethod - Integration method: {1: RK4, 2: AB4, or 3: ABM4} (-): + call ParseVar ( FileInfo_In, CurLine, "IntMethod", InputFileData%IntMethod, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! DT - Time interval for aerodynamic calculations {or default} (s): + call ParseVarWDefault ( FileInfo_In, CurLine, "DT", InputFileData%DT, interval, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + + !====== Degrees of Freedom ========================================================================= + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + ! GenDOF - Generator DOF (flag) + call ParseVar( FileInfo_In, CurLine, "GenDOF", InputFileData%GenDOF, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! YawDOF - Yaw DOF (flag) + call ParseVar( FileInfo_In, CurLine, "YawDOF", InputFileData%YawDOF, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + + !====== Iniital Conditions ========================================================================= + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! Azimuth - Initial azimuth angle for blades (degrees) + call ParseVar( FileInfo_In, CurLine, "Azimuth", InputFileData%Azimuth, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%Azimuth = InputFileData%Azimuth * D2R + + ! BlPitch - Blades initial pitch (degrees) + call ParseVar( FileInfo_In, CurLine, "BlPitch", InputFileData%BlPitch, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%BlPitch = InputFileData%BlPitch * D2R + + ! RotSpeed - Initial or fixed rotor speed (rpm) + call ParseVar( FileInfo_In, CurLine, "RotSpeed", InputFileData%RotSpeed, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%RotSpeed = InputFileData%RotSpeed * RPM2RPS + + ! NacYaw - Initial or fixed nacelle-yaw angle (degrees) + call ParseVar( FileInfo_In, CurLine, "NacYaw", InputFileData%NacYaw, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%NacYaw = InputFileData%NacYaw * D2R + + ! PtfmPitch - Fixed pitch tilt rotational displacement of platform (degrees) + call ParseVar( FileInfo_In, CurLine, "PtfmPitch", InputFileData%PtfmPitch, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%PtfmPitch = InputFileData%PtfmPitch * D2R + + + !====== Turbine Configuration ====================================================================== + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! NumBl - Number of blades (-) + call ParseVar( FileInfo_In, CurLine, "NumBl", InputFileData%NumBl, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! TipRad - The distance from the rotor apex to the blade tip (meters) + call ParseVar( FileInfo_In, CurLine, "TipRad", InputFileData%TipRad, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! HubRad - The distance from the rotor apex to the blade root (meters) + call ParseVar( FileInfo_In, CurLine, "HubRad", InputFileData%HubRad, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! PreCone - Blades cone angle (degrees) + call ParseVar( FileInfo_In, CurLine, "PreCone", InputFileData%PreCone, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%PreCone = InputFileData%PreCone * D2R + + ! OverHang - Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades] (meters) + call ParseVar( FileInfo_In, CurLine, "OverHang", InputFileData%OverHang, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! ShftTilt - Rotor shaft tilt angle (degrees) + call ParseVar( FileInfo_In, CurLine, "ShftTilt", InputFileData%ShftTilt, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + InputFileData%ShftTilt = InputFileData%ShftTilt * D2R + + ! Twr2Shft - Vertical distance from the tower-top to the rotor shaft (meters) + call ParseVar( FileInfo_In, CurLine, "Twr2Shft", InputFileData%Twr2Shft, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! TowerHt - Height of tower above ground level [onshore] or MSL [offshore] (meters) + call ParseVar( FileInfo_In, CurLine, "TowerHt", InputFileData%TowerHt, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + + !====== Mass and Inertia =========================================================================== + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! RotIner - Rot inertia about rotor axis [blades + hub] (kg m^2) + call ParseVar( FileInfo_In, CurLine, "RotIner", InputFileData%RotIner, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + ! GenIner - Generator inertia about HSS (kg m^2) + call ParseVar( FileInfo_In, CurLine, "GenIner", InputFileData%GenIner, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + + !====== Drivetrain ================================================================================= + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! GBoxRatio - Gearbox ratio (-) + call ParseVar( FileInfo_In, CurLine, "GBoxRatio", InputFileData%GBoxRatio, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return + + + !====== Outputs ==================================================================================== + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 +! ! SumPrint - Generate a summary file listing input options and interpolated properties to ".AD.sum"? (flag) +! call ParseVar( FileInfo_In, CurLine, "SumPrint", InputFileData%SumPrint, ErrStat2, ErrMsg2, UnEc ) +! if (Failed()) return + + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + call ReadOutputListFromFileInfo( FileInfo_In, CurLine, InputFileData%OutList, & + InputFileData%NumOuts, ErrStat2, ErrMsg2, UnEc ) + if (Failed()) return; +contains + logical function Failed() + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + Failed = ErrStat >= AbortErrLev + if (Failed) call CleanUp() + end function Failed + subroutine Cleanup() + ! Only do this on a fault. Leave open for calling routine in case we want to write anything else. + if (UnEc > 0_IntKi) close(UnEc) + end subroutine Cleanup +end subroutine SED_ParsePrimaryFileData + + +!> Check inputdata +subroutine SEDInput_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg ) + type(SED_InitInputType), intent(in ) :: InitInp !< Input data for initialization + type(SED_InputFile), intent(in ) :: InputFileData !< The data for initialization + integer(IntKi), intent( out) :: ErrStat !< Error status from this subroutine + character(*), intent( out) :: ErrMsg !< Error message from this subroutine + character(*), parameter :: RoutineName="SEDInput_ValidateInput" + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = "" + + ! InitInput checks + if (InitInp%Linearize) call SetErrStat(ErrID_Fatal,'AeroDisk cannot perform linearization analysis.',ErrStat,ErrMsg,RoutineName) + if (InputFileData%DT <= 0.0_DbKi) call SetErrStat(ErrID_Fatal,'DT must not be negative.', ErrStat,ErrMsg,RoutineName) + if ((InputFileData%IntMethod /= Method_RK4) .and. (InputFileData%IntMethod /= Method_AB4) .and. (InputFileData%IntMethod /= Method_ABM4)) & + call SetErrStat(ErrID_Fatal,'IntMethod must be '//trim(Num2LStr(Method_RK4))//': RK4, '//trim(Num2LStr(Method_AB4))//': AB4, or '//trim(Num2LStr(Method_ABM4))//': ABM4', ErrStat,ErrMsg,RoutineName) + + ! initial settings check + if (abs(InputFileData%Azimuth) > TwoPi) & + call SetErrStat(ErrID_Fatal,'Starting Azimuth must be between -360 and 360 degrees.', ErrStat,ErrMsg,RoutineName) + if ((InputFileData%BlPitch <= -pi ) .or. (InputFileData%BlPitch > pi)) & + call SetErrStat( ErrID_Fatal, 'BlPitch must be greater than -pi radians and '// & + 'less than or equal to pi radians (i.e., in the range (-180, 180] degrees).',ErrStat,ErrMsg,RoutineName) + if (InputFileData%RotSpeed < 0_ReKi) call SetErrStat(ErrID_Fatal,'RotSpeed must not be negative', ErrStat,ErrMsg,RoutineName) + IF ((InputFileData%NacYaw <= -pi) .or. (InputFileData%NacYaw > pi)) & + call SetErrStat( ErrID_Fatal, 'NacYaw must be in the range (-pi, pi] radians (i.e., (-180, 180] degrees).',ErrStat,ErrMsg,RoutineName) + if ( ABS( InputFileData%PtfmPitch ) > SmallAngleLimit_Deg*D2R ) & + call SetErrStat( ErrID_Fatal, 'PtfmPitch must be between -'//TRIM(Num2LStr(SmallAngleLimit_Deg))//' and ' & + //TRIM(Num2LStr(SmallAngleLimit_Deg))//' degrees.',ErrStat,ErrMsg,RoutineName) + + ! turbine configuration + if ((InputFileData%NumBl < 1) .or. (InputFileData%NumBl > MaxBl)) & + call SetErrStat( ErrID_Fatal, 'NumBl must be 1, 2, or 3.',ErrStat,ErrMsg,RoutineName) + if (InputFileData%TipRad < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'TipRad must not be negative.',ErrStat,ErrMsg,RoutineName) + if (InputFileData%HubRad < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'HubRad must not be negative.',ErrStat,ErrMsg,RoutineName) + if (abs(InputFileData%PreCone) >= PiBy2) & + call SetErrStat( ErrID_Fatal, 'PreCone must be in the range (-pi/2, pi/2) '//& + 'radians (i.e., (-90, 90) degrees).',ErrStat,ErrMsg,RoutineName) + if (abs(InputFileData%OverHang) > InputFileData%TipRad) & + call SetErrStat( ErrID_Fatal, 'Overhang larger than tip-radius. Does your model make sense?',ErrStat,ErrMsg,RoutineName) + if (abs(InputFileData%ShftTilt) > PiBy2) & + call SetErrStat(ErrID_Fatal,'ShftTilt must be between -pi/2 and pi/2 radians (i.e., in the range [-90, 90] degrees).',ErrStat,ErrMsg,RoutineName) + if (InputFileData%Twr2Shft < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'Twr2Shft must not be negative.',ErrStat,ErrMsg,RoutineName) + if ( InputFileData%TowerHt <= 0.0_ReKi) call SetErrStat( ErrID_Fatal, 'TowerHt must be greater than zero.',ErrStat,ErrMsg,RoutineName ) + + if ( InputFileData%TowerHt + InputFileData%Twr2Shft + InputFileData%OverHang*SIN(InputFileData%ShftTilt) <= InputFileData%TipRad ) & + call SetErrStat( ErrID_Fatal, 'TowerHt + Twr2Shft + OverHang*SIN(ShftTilt) must be greater than TipRad.',ErrStat,ErrMsg,RoutineName) + + ! inertias + if (InputFileData%RotIner < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'RotIner must not be negative.',ErrStat,ErrMsg,RoutineName) + if (InputFileData%GenIner < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'GenIner must not be negative.',ErrStat,ErrMsg,RoutineName) + + !GBRatio -- no sanity checks on this +end subroutine SEDInput_ValidateInput + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> this routine fills the AllOuts array, which is used to send data to the glue code to be written to an output file. +!! NOTE: AllOuts is ReKi, but most calculations in this module are in single precision. This requires a bunch of conversions at this +!! stage. +subroutine Calc_WriteOutput( u, p, x, dxdt, y, m, ErrStat, ErrMsg, CalcWriteOutput ) + type(SED_InputType), intent(in ) :: u !< The inputs at time T + type(SED_ParameterType), intent(in ) :: p !< The module parameters + type(SED_ContinuousStateType),intent(in ) :: x !< Continuous states at t + type(SED_ContinuousStateType),intent(in ) :: dxdt !< Derivative of continuous states at t + type(SED_OutputType), intent(in ) :: y !< outputs + type(SED_MiscVarType), intent(inout) :: m !< misc/optimization variables (for computing mesh transfers) + integer(IntKi), intent( out) :: ErrStat !< The error status code + character(*), intent( out) :: ErrMsg !< The error message, if an error occurred + logical, intent(in ) :: CalcWriteOutput !< flag that determines if we need to compute AllOuts (or just the reaction loads that get returned to ServoDyn) + ! local variables + character(*), parameter :: RoutineName = 'Calc_WriteOutput' + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + real(ReKi) :: Tmp3(3) + real(ReKi) :: Rxyz(3,3) !< rotation matrix for x,y,z of local coordinates + + ! Initialize + ErrStat = ErrID_None + ErrMsg = "" + + ! return if we are not providing outputs + if (.not. CalcWriteOutput) return + + ! Azimuth + m%AllOuts( Azimuth ) = x%QT( DOF_Az) + call Zero2TwoPi(m%AllOuts( Azimuth )) ! modulo + m%AllOuts( Azimuth ) = m%AllOuts( Azimuth ) * R2D + + ! speed + m%AllOuts( RotSpeed ) = x%QDT(DOF_Az) * RPS2RPM + m%AllOuts( GenSpeed ) = x%QDT(DOF_Az) * RPS2RPM * p%GBoxRatio + + ! accel + m%AllOuts( RotAcc ) = dxdt%QDT(DOF_Az) * RPS2RPM + m%AllOuts( GenAcc ) = dxdt%QDT(DOF_Az) * RPS2RPM * p%GBoxRatio + + ! Yaw commands + m%AllOuts( Yaw ) = y%Yaw * R2D + m%AllOuts( YawRate ) = y%YawRate * R2D + + ! BlPitch1 + m%AllOuts( BlPitch1 ) = u%BlPitchCom(1) * R2D + if (p%NumBl > 1) m%AllOuts( BlPitch2 ) = u%BlPitchCom(2) * R2D + if (p%NumBl > 2) m%AllOuts( BlPitch3 ) = u%BlPitchCom(3) * R2D + + ! LLS torqque and power + m%AllOuts( RotTorq ) = y%RotTrq * 0.001_ReKi ! LSShftTq (kN-m) + m%AllOuts( RotPwr ) = y%RotPwr * 0.001_ReKi ! LSShftPwr (kN-m) +end subroutine Calc_WriteOutput + + +!********************************************************************************************************************************** +! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m" +! using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these +! lines should be modified in the Matlab script and/or Excel worksheet as necessary. +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine checks to see if any requested output channel names (stored in the OutList(:)) are invalid. It returns a +!! warning if any of the channels are not available outputs from the module. +!! It assigns the settings for OutParam(:) (i.e, the index, name, and units of the output channels, WriteOutput(:)). +!! the sign is set to 0 if the channel is invalid. +!! It sets assumes the value p%NumOuts has been set before this routine has been called, and it sets the values of p%OutParam here. +!! +!! This routine was generated by Write_ChckOutLst.m using the parameters listed in OutListParameters.xlsx at 10-Aug-2022 08:44:32. +SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg ) +!.................................................................................................................................. + + IMPLICIT NONE + + ! Passed variables + + CHARACTER(ChanLen), INTENT(IN) :: OutList(:) !< The list out user-requested outputs + TYPE(SED_ParameterType), INTENT(INOUT) :: p !< The module parameters + INTEGER(IntKi), INTENT(OUT) :: ErrStat !< The error status code + CHARACTER(*), INTENT(OUT) :: ErrMsg !< The error message, if an error occurred + + ! Local variables + + INTEGER :: ErrStat2 ! temporary (local) error status + INTEGER :: I ! Generic loop-counting index + INTEGER :: J ! Generic loop-counting index + INTEGER :: INDX ! Index for valid arrays + + LOGICAL :: CheckOutListAgain ! Flag used to determine if output parameter starting with "M" is valid (or the negative of another parameter) + LOGICAL :: InvalidOutput(0:MaxOutPts) ! This array determines if the output channel is valid for this configuration + CHARACTER(ChanLen) :: OutListTmp ! A string to temporarily hold OutList(I) + CHARACTER(*), PARAMETER :: RoutineName = "SetOutParam" + + CHARACTER(OutStrLenM1), PARAMETER :: ValidParamAry(25) = (/ & ! This lists the names of the allowed parameters, which must be sorted alphabetically + "AZIMUTH ","BLDPITCH1","BLDPITCH2","BLDPITCH3","BLPITCH1 ","BLPITCH2 ","BLPITCH3 ","GENACC ", & + "GENSPEED ","HSSHFTA ","HSSHFTV ","LSSHFTPWR","LSSHFTTQ ","LSSTIPA ","LSSTIPAXA","LSSTIPAXS", & + "LSSTIPV ","LSSTIPVXA","LSSTIPVXS","ROTACC ","ROTPWR ","ROTSPEED ","ROTTORQ ","YAW ", & + "YAWRATE "/) + INTEGER(IntKi), PARAMETER :: ParamIndxAry(25) = (/ & ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:) + Azimuth , BlPitch1 , BlPitch2 , BlPitch3 , BlPitch1 , BlPitch2 , BlPitch3 , GenAcc , & + GenSpeed , GenAcc , GenSpeed , RotPwr , RotTorq , RotAcc , RotAcc , RotAcc , & + RotSpeed , RotSpeed , RotSpeed , RotAcc , RotPwr , RotSpeed , RotTorq , Yaw , & + YawRate /) + CHARACTER(ChanLen), PARAMETER :: ParamUnitsAry(25) = (/ & ! This lists the units corresponding to the allowed parameters + "(deg) ","(deg) ","(deg) ","(deg) ","(deg) ","(deg) ","(deg) ","(deg/s^2)", & + "(rpm) ","(deg/s^2)","(rpm) ","(kW) ","(kN-m) ","(deg/s^2)","(deg/s^2)","(deg/s^2)", & + "(rpm) ","(rpm) ","(rpm) ","(deg/s^2)","(kW) ","(rpm) ","(kN-m) ","(deg) ", & + "(deg/s) "/) + + + ! Initialize values + ErrStat = ErrID_None + ErrMsg = "" + InvalidOutput = .FALSE. + + if (p%NumBl < 3) InvalidOutput( BlPitch3 ) = .true. + if (p%NumBl < 2) InvalidOutput( BlPitch2 ) = .true. + + !------------------------------------------------------------------------------------------------- + ! Allocate and set index, name, and units for the output channels + ! If a selected output channel is not available in this module, set error flag. + !------------------------------------------------------------------------------------------------- + + ALLOCATE ( p%OutParam(0:p%NumOuts) , STAT=ErrStat2 ) + IF ( ErrStat2 /= 0_IntKi ) THEN + CALL SetErrStat( ErrID_Fatal,"Error allocating memory for the SimpleElastoDyn OutParam array.", ErrStat, ErrMsg, RoutineName ) + RETURN + ENDIF + + ! Set index, name, and units for the time output channel: + + p%OutParam(0)%Indx = Time + p%OutParam(0)%Name = "Time" ! OutParam(0) is the time channel by default. + p%OutParam(0)%Units = "(s)" + p%OutParam(0)%SignM = 1 + + + ! Set index, name, and units for all of the output channels. + ! If a selected output channel is not available by this module set ErrStat = ErrID_Warn. + + DO I = 1,p%NumOuts + + p%OutParam(I)%Name = OutList(I) + OutListTmp = OutList(I) + + ! Reverse the sign (+/-) of the output channel if the user prefixed the + ! channel name with a "-", "_", "m", or "M" character indicating "minus". + + + CheckOutListAgain = .FALSE. + + IF ( INDEX( "-_", OutListTmp(1:1) ) > 0 ) THEN + p%OutParam(I)%SignM = -1 ! ex, "-TipDxc1" causes the sign of TipDxc1 to be switched. + OutListTmp = OutListTmp(2:) + ELSE IF ( INDEX( "mM", OutListTmp(1:1) ) > 0 ) THEN ! We'll assume this is a variable name for now, (if not, we will check later if OutListTmp(2:) is also a variable name) + CheckOutListAgain = .TRUE. + p%OutParam(I)%SignM = 1 + ELSE + p%OutParam(I)%SignM = 1 + END IF + + CALL Conv2UC( OutListTmp ) ! Convert OutListTmp to upper case + + + Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry ) + + + ! If it started with an "M" (CheckOutListAgain) we didn't find the value in our list (Indx < 1) + + IF ( CheckOutListAgain .AND. Indx < 1 ) THEN ! Let's assume that "M" really meant "minus" and then test again + p%OutParam(I)%SignM = -1 ! ex, "MTipDxc1" causes the sign of TipDxc1 to be switched. + OutListTmp = OutListTmp(2:) + + Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry ) + END IF + + + IF ( Indx > 0 ) THEN ! we found the channel name + IF ( InvalidOutput( ParamIndxAry(Indx) ) ) THEN ! but, it isn't valid for these settings + p%OutParam(I)%Indx = 0 ! pick any valid channel (I just picked "Time=0" here because it's universal) + p%OutParam(I)%Units = "INVALID" + p%OutParam(I)%SignM = 0 + ELSE + p%OutParam(I)%Indx = ParamIndxAry(Indx) + p%OutParam(I)%Units = ParamUnitsAry(Indx) ! it's a valid output + END IF + ELSE ! this channel isn't valid + p%OutParam(I)%Indx = 0 ! pick any valid channel (I just picked "Time=0" here because it's universal) + p%OutParam(I)%Units = "INVALID" + p%OutParam(I)%SignM = 0 ! multiply all results by zero + + CALL SetErrStat(ErrID_Fatal, TRIM(p%OutParam(I)%Name)//" is not an available output channel.",ErrStat,ErrMsg,RoutineName) + END IF + + END DO + + RETURN +END SUBROUTINE SetOutParam +!---------------------------------------------------------------------------------------------------------------------------------- +!End of code generated by Matlab script +!********************************************************************************************************************************** +END MODULE SED_IO diff --git a/modules/simple-elastodyn/src/SED_Output_Params.f90 b/modules/simple-elastodyn/src/SED_Output_Params.f90 new file mode 100644 index 0000000000..7d64c07ea0 --- /dev/null +++ b/modules/simple-elastodyn/src/SED_Output_Params.f90 @@ -0,0 +1,52 @@ +!> The parameters in this code are from the MATLAB autogeneration scripts. Do not manually edit unless also editing the OutListParamters.xls SED tab. +module SED_Output_Params + use NWTC_Library + +! =================================================================================================== +! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m" +! using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these +! lines should be modified in the Matlab script and/or Excel worksheet as necessary. +! =================================================================================================== +! This code was generated by Write_ChckOutLst.m at 10-Aug-2022 08:44:32. + + + ! Indices for computing output channels: + ! NOTES: + ! (1) These parameters are in the order stored in "OutListParameters.xlsx" + ! (2) Array AllOuts() must be dimensioned to the value of the largest output parameter + + ! Time: + + INTEGER(IntKi), PARAMETER :: Time = 0 + + + ! Outputs: + + INTEGER(IntKi), PARAMETER :: Azimuth = 1 + INTEGER(IntKi), PARAMETER :: RotSpeed = 2 + INTEGER(IntKi), PARAMETER :: RotAcc = 3 + INTEGER(IntKi), PARAMETER :: GenSpeed = 4 + INTEGER(IntKi), PARAMETER :: GenAcc = 5 + INTEGER(IntKi), PARAMETER :: Yaw = 6 + INTEGER(IntKi), PARAMETER :: YawRate = 7 + + + ! Blade Pitch Motions: + + INTEGER(IntKi), PARAMETER :: BlPitch1 = 8 + INTEGER(IntKi), PARAMETER :: BlPitch2 = 9 + INTEGER(IntKi), PARAMETER :: BlPitch3 = 10 + + + ! Hub and Rotor Loads: + + INTEGER(IntKi), PARAMETER :: RotTorq = 11 + INTEGER(IntKi), PARAMETER :: RotPwr = 12 + + + ! The maximum number of output channels which can be output by the code. + INTEGER(IntKi), PARAMETER :: MaxOutPts = 12 + +!End of code generated by Matlab script +! =================================================================================================== +end module SED_Output_Params diff --git a/modules/simple-elastodyn/src/SED_Registry.txt b/modules/simple-elastodyn/src/SED_Registry.txt new file mode 100644 index 0000000000..691d108a97 --- /dev/null +++ b/modules/simple-elastodyn/src/SED_Registry.txt @@ -0,0 +1,159 @@ +################################################################################################################################### +# Registry for Simplified ElastoDyn in the FAST Modularization Framework +# This Registry file is used to create MODULE SED_Types which contains all of the user-defined types needed in Simplified ElastoDyn. +# It also contains copy, destroy, pack, and unpack routines associated with each defined data types. +# See the NWTC Programmer's Handbook for further information on the format/contents of this file. +# +# Entries are of the form +# +# +# Use ^ as a shortcut for the value in the same column from the previous line. +################################################################################################################################### +# ...... Include files (definitions from NWTC Library) ............................................................................ +include Registry_NWTC_Library.txt + +param SED/SED - IntKi SED_NMX - 4 - "Used in updating predictor-corrector values (size of state history)" - + +# ..... Initialization data ....................................................................................................... +# SED input file +typedef SED/SED SED_InputFile LOGICAL Echo - - - "Echo the input file" - +typedef ^ SED_InputFile DBKi DT - - - "Time step for module time integration" s +typedef ^ SED_InputFile IntKi IntMethod - - - "Integration method {1: RK4, 2: AB4, or 3: ABM4}" - +typedef ^ SED_InputFile LOGICAL GenDOF - - - "whether the generator is fixed or free" - +typedef ^ SED_InputFile LOGICAL YawDOF - - - "Yaw controlled by controller, or fixed" - +typedef ^ SED_InputFile R8Ki Azimuth - - - "Initial azimuth angle for blade 1" deg +typedef ^ SED_InputFile ReKi BlPitch - - - "Initial blade pitch angles" radians +typedef ^ SED_InputFile ReKi RotSpeed - - - "Initial or fixed rotor speed" RPM +typedef ^ SED_InputFile ReKi NacYaw - - - "Initial or fixed nacelle yaw" deg +typedef ^ SED_InputFile ReKi PtfmPitch - - - "Fixed pitch tilt rotational displacement of platform" deg +typedef ^ SED_InputFile IntKi NumBl - - - "Number of blades on the turbine" - +typedef ^ SED_InputFile ReKi TipRad - - - "Preconed blade-tip radius (distance from the rotor apex to the blade tip)" m +typedef ^ SED_InputFile ReKi HubRad - - - "Preconed hub radius (distance from the rotor apex to the blade root)" m +typedef ^ SED_InputFile ReKi PreCone - - - "Rotor precone angles" deg +typedef ^ SED_InputFile ReKi OverHang - - - "Distance from yaw axis to rotor apex or teeter pin" m +typedef ^ SED_InputFile ReKi ShftTilt - - - "Rotor shaft tilt angle" deg +typedef ^ SED_InputFile ReKi Twr2Shft - - - "Vertical distance from the tower-top to the rotor shaft" m +typedef ^ SED_InputFile ReKi TowerHt - - - "Height of tower above ground level [onshore] or MSL [offshore]" m +typedef ^ SED_InputFile ReKi RotIner - - - "Hub inertia about teeter axis (2-blader) or rotor axis (3-blader)" "kg m^2" +typedef ^ SED_InputFile ReKi GenIner - - - "Generator inertia about HSS" "kg m^2" +typedef ^ SED_InputFile ReKi GBoxRatio - - - "Gearbox ratio" - +typedef ^ SED_InputFile LOGICAL SumPrint - - - "Print summary data to .sum" - +typedef ^ SED_InputFile IntKi NumOuts - - - "Number of outputs" - +typedef ^ SED_InputFile CHARACTER(ChanLen) OutList : - - "List of user-requested output channels" - + + +# ..... Initialization data ....................................................................................................... +# inputs for initialization: +typedef SED/SED InitInputType CHARACTER(1024) InputFile - - - "Name of the input file" - +typedef ^ InitInputType CHARACTER(1024) RootName - - - "RootName for writing output files" - +typedef ^ InitInputType LOGICAL Linearize - .false. - "this module cannot be linearized at present" - +typedef ^ InitInputType LOGICAL UseInputFile - .TRUE. - "Supplied by Driver: .TRUE. if using a input file, .FALSE. if all inputs are being passed in by the caller" - +typedef ^ InitInputType FileInfoType PassedFileData - - - "If we don't use the input file, pass everything through this" - + + +# outputs from initialization: +typedef ^ InitOutputType CHARACTER(ChanLen) WriteOutputHdr {:} - - "Names of the output-to-file channels" - +typedef ^ InitOutputType CHARACTER(ChanLen) WriteOutputUnt {:} - - "Units of the output-to-file channels" - +typedef ^ InitOutputType ProgDesc Ver - - - "This module's name, version, and date" - +typedef ^ InitOutputType IntKi NumBl - - - "Number of blades on the turbine" - +typedef ^ InitOutputType ReKi BlPitch {:} - - "Initial blade pitch angles" radians +typedef ^ InitOutputType ReKi BladeLength - - - "Blade length (for AeroDyn)" meters +typedef ^ InitOutputType ReKi TowerHt - - - "Tower Height" meters +typedef ^ InitOutputType ReKi HubHt - - - "Height of the hub" meters +typedef ^ InitOutputType ReKi PlatformPos {6} - - "Initial platform position (6 DOFs)" +typedef ^ InitOutputType ReKi HubRad - - - "Preconed hub radius (distance from the rotor apex to the blade root)" m +typedef ^ InitOutputType ReKi RotSpeed - - - "Initial or fixed rotor speed" rad/s +typedef ^ InitOutputType LOGICAL GenDOF - - - "whether the generator DOF is on (true) or off (false)" - + + +# ..... Inputs .................................................................................................................... +# inputs on meshes: +typedef ^ InputType MeshType HubPtLoad - - - "AeroDisk maps load to hub" - +# inputs not on meshes: +typedef ^ InputType ReKi HSSBrTrqC - - - "Commanded HSS brake torque" N-m +typedef ^ InputType ReKi GenTrq - - - "Electrical generator torque" N-m +typedef ^ InputType ReKi BlPitchCom {:} - 2pi "Commanded blade pitch angles" radians +typedef ^ InputType ReKi YawPosCom - - - "Yaw angle commanded" rad +typedef ^ InputType ReKi YawRateCom - - - "Yaw rate commanded" rad/s + + +# ..... Outputs ................................................................................................................... +# outputs on meshes: +typedef ^ OutputType MeshType BladeRootMotion {:} - - "For AeroDyn/BeamDyn: motions at the blade roots" - +typedef ^ OutputType MeshType HubPtMotion - - - "For AeroDyn and Lidar(InflowWind): motions of the hub" - +typedef ^ OutputType MeshType NacelleMotion - - - "For AeroDyn14 & ServoDyn/TMD: motions of the nacelle." - +typedef ^ OutputType MeshType TowerLn2Mesh - - - "Tower line2 mesh with positions/orientations/velocities/accelerations" - +typedef ^ OutputType MeshType PlatformPtMesh - - - "Platform reference point positions/orientations/velocities/accelerations" - +#TODO: any mesh for visualization of blades/rotor disk? +# outputs not on meshes: +typedef ^ OutputType ReKi LSSTipPxa - - 2pi "Rotor azimuth angle (position)" radians +typedef ^ OutputType ReKi RotSpeed - - - "Rotor azimuth angular speed" rad/s +typedef ^ OutputType ReKi RotPwr - - - "Rotor power" W +typedef ^ OutputType ReKi RotTrq - - - "Rotor torque" N-m +typedef ^ OutputType ReKi HSS_Spd - - - "High-speed shaft (HSS) speed" rad/s +# This is a feed through -- SrvD requires knowledge of current actual yaw +typedef ^ OutputType ReKi Yaw - - - "Yaw angle" rad +typedef ^ OutputType ReKi YawRate - - - "Yaw rate" rad/s +typedef ^ OutputType ReKi BlPitch {:} - 2pi "Actual blade pitch" radians +typedef ^ OutputType ReKi WriteOutput {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" + + +# ..... States .................................................................................................................... +# continuous (differentiable) states: +typedef ^ ContinuousStateType R8Ki QT {:} - - "Current estimate of Q (displacement matrix) for each degree of freedom" - +typedef ^ ContinuousStateType ^ QDT {:} - - "Current estimate of QD (velocity matrix) for each degree of freedom" + +# Define discrete (nondifferentiable) states here: +typedef ^ DiscreteStateType ReKi DummyDiscreteState - - - "" - + +# Define constraint states here: +typedef ^ ConstraintStateType ReKi DummyConstrState - - - "" - + +# any other states +typedef ^ OtherStateType IntKi n - - - "tracks time step for which OtherState was updated" - +typedef ^ OtherStateType SED_ContinuousStateType xdot {SED_NMX} - - "previous state deriv for multi-step" - +typedef ^ OtherStateType IntKi IC {SED_NMX} - - "Array which stores pointers to predictor-corrector results" - +typedef ^ OtherStateType ReKi HSSBrTrq - - - "HSSBrTrq from update states; a hack to get this working with a single integrator" - +typedef ^ OtherStateType ReKi HSSBrTrqC - - - "Commanded HSS brake torque (adjusted for sign)" N-m +typedef ^ OtherStateType IntKi SgnPrvLSTQ - - - "The sign of the low-speed shaft torque from the previous call to RtHS(). NOTE: The low-speed shaft torque is assumed to be positive at the beginning of the run!" - +typedef ^ OtherStateType IntKi SgnLSTQ {SED_NMX} - - "history of sign of LSTQ" + + + +# ..... Parameters................................................................................................................. +# unchanging parameters: +typedef ^ ParameterType CHARACTER(1024) RootName - - - "RootName for writing output files" - +typedef ^ ParameterType LOGICAL GenDOF - - - "whether the generator DOF is on (free) or off (fixed)" - +typedef ^ ParameterType LOGICAL YawDOF - - - "Yaw controlled by controller, or fixed" - +typedef ^ ParameterType DbKi DT - - - "Time step for module time integration" s +typedef ^ ParameterType DbKi DT24 - - - "Time step for solver" s +typedef ^ ParameterType IntKi IntMethod - - - "Integration method {1: RK4, 2: AB4, or 3: ABM4}" - +typedef ^ ParameterType ReKi J_DT - - - "Drivetrain inertia (blades+hub+shaft+generator)" "kgm^2" +typedef ^ ParameterType ReKi PtfmPitch - - - "Static platform tilt angle" rad +typedef ^ ParameterType ReKi InitYaw - - - "Initial or fixed nacelle yaw -- store in case YawDOF is off" deg +typedef ^ ParameterType R8Ki InitAzimuth - - - "Initial azimuth angle for blade 1" deg +typedef ^ ParameterType ReKi RotIner - - - "Hub inertia about teeter axis (2-blader) or rotor axis (3-blader)" "kg m^2" +typedef ^ ParameterType ReKi GenIner - - - "Generator inertia about HSS" "kg m^2" +typedef ^ ParameterType ReKi GBoxRatio - - - "Gearbox ratio" - +typedef ^ ParameterType IntKi NumBl - - - "Number of blades on the turbine" - +typedef ^ ParameterType ReKi TipRad - - - "Preconed blade-tip radius (distance from the rotor apex to the blade tip)" m +typedef ^ ParameterType ReKi HubRad - - - "Preconed hub radius (distance from the rotor apex to the blade root)" m +typedef ^ ParameterType ReKi BladeLength - - - "Length of blades" m +typedef ^ ParameterType ReKi PreCone - - - "Rotor precone angles" deg +typedef ^ ParameterType ReKi OverHang - - - "Distance from yaw axis to rotor apex or teeter pin" m +typedef ^ ParameterType ReKi ShftTilt - - - "Rotor shaft tilt angle" deg +typedef ^ ParameterType ReKi Twr2Shft - - - "Vertical distance from the tower-top to the rotor shaft" m +typedef ^ ParameterType ReKi TowerHt - - - "Height of tower above ground level [onshore] or MSL [offshore]" m +typedef ^ ParameterType ReKi HubHt - - - "Height of hub center above ground level [onshore] or MSL [offshore]" m +typedef ^ ParameterType IntKi NumOuts - - - "Number of outputs" - +typedef ^ ParameterType OutParmType OutParam {:} - - "Names and units (and other characteristics) of all requested output parameters" - + +# ..... Misc/Optimization variables................................................................................................. +typedef ^ MiscVarType ReKi AllOuts {:} - - "Array of all outputs" - +#typedef ^ MiscVarType MeshMapType mapPtf2Twr - - - "Mesh mapping from Ptfm to Tower line" - +#typedef ^ MiscVarType MeshMapType mapTwr2Nac - - - "Mesh mapping from Tower to Nacelle" - +typedef ^ MiscVarType MeshMapType mapNac2Hub - - - "Mesh mapping from Nacelle to Hub" - +typedef ^ MiscVarType MeshMapType mapHub2Root {:} - - "Mesh mapping from Hub to BladeRootMotion" - +typedef ^ MiscVarType R8Ki QD2T {:} - - "Current estimate of first derivative of QD (acceleration matrix) for each degree of freedom" +typedef ^ MiscVarType ReKi HubPt_X {3} - - "X orientation of hub calculated in CalcOutput -- saving so we don't recalculate a bunch of things to get it in UpdateStates" + diff --git a/modules/simple-elastodyn/src/SED_Types.f90 b/modules/simple-elastodyn/src/SED_Types.f90 new file mode 100644 index 0000000000..bc97de1b88 --- /dev/null +++ b/modules/simple-elastodyn/src/SED_Types.f90 @@ -0,0 +1,1690 @@ +!STARTOFREGISTRYGENERATEDFILE 'SED_Types.f90' +! +! WARNING This file is generated automatically by the FAST registry. +! Do not edit. Your changes to this file will be lost. +! +! FAST Registry +!********************************************************************************************************************************* +! SED_Types +!................................................................................................................................. +! This file is part of SED. +! +! Copyright (C) 2012-2016 National Renewable Energy Laboratory +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +! +! W A R N I N G : This file was automatically generated from the FAST registry. Changes made to this file may be lost. +! +!********************************************************************************************************************************* +!> This module contains the user-defined types needed in SED. It also contains copy, destroy, pack, and +!! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. +MODULE SED_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE NWTC_Library +IMPLICIT NONE + INTEGER(IntKi), PUBLIC, PARAMETER :: SED_NMX = 4 ! Used in updating predictor-corrector values (size of state history) [-] +! ========= SED_InputFile ======= + TYPE, PUBLIC :: SED_InputFile + LOGICAL :: Echo = .false. !< Echo the input file [-] + REAL(DbKi) :: DT = 0.0_R8Ki !< Time step for module time integration [s] + INTEGER(IntKi) :: IntMethod = 0_IntKi !< Integration method {1: RK4, 2: AB4, or 3: ABM4} [-] + LOGICAL :: GenDOF = .false. !< whether the generator is fixed or free [-] + LOGICAL :: YawDOF = .false. !< Yaw controlled by controller, or fixed [-] + REAL(R8Ki) :: Azimuth = 0.0_R8Ki !< Initial azimuth angle for blade 1 [deg] + REAL(ReKi) :: BlPitch = 0.0_ReKi !< Initial blade pitch angles [radians] + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Initial or fixed rotor speed [RPM] + REAL(ReKi) :: NacYaw = 0.0_ReKi !< Initial or fixed nacelle yaw [deg] + REAL(ReKi) :: PtfmPitch = 0.0_ReKi !< Fixed pitch tilt rotational displacement of platform [deg] + INTEGER(IntKi) :: NumBl = 0_IntKi !< Number of blades on the turbine [-] + REAL(ReKi) :: TipRad = 0.0_ReKi !< Preconed blade-tip radius (distance from the rotor apex to the blade tip) [m] + REAL(ReKi) :: HubRad = 0.0_ReKi !< Preconed hub radius (distance from the rotor apex to the blade root) [m] + REAL(ReKi) :: PreCone = 0.0_ReKi !< Rotor precone angles [deg] + REAL(ReKi) :: OverHang = 0.0_ReKi !< Distance from yaw axis to rotor apex or teeter pin [m] + REAL(ReKi) :: ShftTilt = 0.0_ReKi !< Rotor shaft tilt angle [deg] + REAL(ReKi) :: Twr2Shft = 0.0_ReKi !< Vertical distance from the tower-top to the rotor shaft [m] + REAL(ReKi) :: TowerHt = 0.0_ReKi !< Height of tower above ground level [onshore] or MSL [offshore] [m] + REAL(ReKi) :: RotIner = 0.0_ReKi !< Hub inertia about teeter axis (2-blader) or rotor axis (3-blader) [kg m^2] + REAL(ReKi) :: GenIner = 0.0_ReKi !< Generator inertia about HSS [kg m^2] + REAL(ReKi) :: GBoxRatio = 0.0_ReKi !< Gearbox ratio [-] + LOGICAL :: SumPrint = .false. !< Print summary data to .sum [-] + INTEGER(IntKi) :: NumOuts = 0_IntKi !< Number of outputs [-] + CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: OutList !< List of user-requested output channels [-] + END TYPE SED_InputFile +! ======================= +! ========= SED_InitInputType ======= + TYPE, PUBLIC :: SED_InitInputType + CHARACTER(1024) :: InputFile !< Name of the input file [-] + CHARACTER(1024) :: RootName !< RootName for writing output files [-] + LOGICAL :: Linearize = .false. !< this module cannot be linearized at present [-] + LOGICAL :: UseInputFile = .TRUE. !< Supplied by Driver: .TRUE. if using a input file, .FALSE. if all inputs are being passed in by the caller [-] + TYPE(FileInfoType) :: PassedFileData !< If we don't use the input file, pass everything through this [-] + END TYPE SED_InitInputType +! ======================= +! ========= SED_InitOutputType ======= + TYPE, PUBLIC :: SED_InitOutputType + CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr !< Names of the output-to-file channels [-] + CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt !< Units of the output-to-file channels [-] + TYPE(ProgDesc) :: Ver !< This module's name, version, and date [-] + INTEGER(IntKi) :: NumBl = 0_IntKi !< Number of blades on the turbine [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BlPitch !< Initial blade pitch angles [radians] + REAL(ReKi) :: BladeLength = 0.0_ReKi !< Blade length (for AeroDyn) [meters] + REAL(ReKi) :: TowerHt = 0.0_ReKi !< Tower Height [meters] + REAL(ReKi) :: HubHt = 0.0_ReKi !< Height of the hub [meters] + REAL(ReKi) , DIMENSION(1:6) :: PlatformPos = 0.0_ReKi !< Initial platform position (6 DOFs) [-] + REAL(ReKi) :: HubRad = 0.0_ReKi !< Preconed hub radius (distance from the rotor apex to the blade root) [m] + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Initial or fixed rotor speed [rad/s] + LOGICAL :: GenDOF = .false. !< whether the generator DOF is on (true) or off (false) [-] + END TYPE SED_InitOutputType +! ======================= +! ========= SED_InputType ======= + TYPE, PUBLIC :: SED_InputType + TYPE(MeshType) :: HubPtLoad !< AeroDisk maps load to hub [-] + REAL(ReKi) :: HSSBrTrqC = 0.0_ReKi !< Commanded HSS brake torque [N-m] + REAL(ReKi) :: GenTrq = 0.0_ReKi !< Electrical generator torque [N-m] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BlPitchCom !< Commanded blade pitch angles [radians] + REAL(ReKi) :: YawPosCom = 0.0_ReKi !< Yaw angle commanded [rad] + REAL(ReKi) :: YawRateCom = 0.0_ReKi !< Yaw rate commanded [rad/s] + END TYPE SED_InputType +! ======================= +! ========= SED_OutputType ======= + TYPE, PUBLIC :: SED_OutputType + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeRootMotion !< For AeroDyn/BeamDyn: motions at the blade roots [-] + TYPE(MeshType) :: HubPtMotion !< For AeroDyn and Lidar(InflowWind): motions of the hub [-] + TYPE(MeshType) :: NacelleMotion !< For AeroDyn14 & ServoDyn/TMD: motions of the nacelle. [-] + TYPE(MeshType) :: TowerLn2Mesh !< Tower line2 mesh with positions/orientations/velocities/accelerations [-] + TYPE(MeshType) :: PlatformPtMesh !< Platform reference point positions/orientations/velocities/accelerations [-] + REAL(ReKi) :: LSSTipPxa = 0.0_ReKi !< Rotor azimuth angle (position) [radians] + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Rotor azimuth angular speed [rad/s] + REAL(ReKi) :: RotPwr = 0.0_ReKi !< Rotor power [W] + REAL(ReKi) :: RotTrq = 0.0_ReKi !< Rotor torque [N-m] + REAL(ReKi) :: HSS_Spd = 0.0_ReKi !< High-speed shaft (HSS) speed [rad/s] + REAL(ReKi) :: Yaw = 0.0_ReKi !< Yaw angle [rad] + REAL(ReKi) :: YawRate = 0.0_ReKi !< Yaw rate [rad/s] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BlPitch !< Actual blade pitch [radians] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: WriteOutput !< Data to be written to an output file: see WriteOutputHdr for names of each variable [see WriteOutputUnt] + END TYPE SED_OutputType +! ======================= +! ========= SED_ContinuousStateType ======= + TYPE, PUBLIC :: SED_ContinuousStateType + REAL(R8Ki) , DIMENSION(:), ALLOCATABLE :: QT !< Current estimate of Q (displacement matrix) for each degree of freedom [-] + REAL(R8Ki) , DIMENSION(:), ALLOCATABLE :: QDT !< Current estimate of QD (velocity matrix) for each degree of freedom [-] + END TYPE SED_ContinuousStateType +! ======================= +! ========= SED_DiscreteStateType ======= + TYPE, PUBLIC :: SED_DiscreteStateType + REAL(ReKi) :: DummyDiscreteState = 0.0_ReKi !< [-] + END TYPE SED_DiscreteStateType +! ======================= +! ========= SED_ConstraintStateType ======= + TYPE, PUBLIC :: SED_ConstraintStateType + REAL(ReKi) :: DummyConstrState = 0.0_ReKi !< [-] + END TYPE SED_ConstraintStateType +! ======================= +! ========= SED_OtherStateType ======= + TYPE, PUBLIC :: SED_OtherStateType + INTEGER(IntKi) :: n = 0_IntKi !< tracks time step for which OtherState was updated [-] + TYPE(SED_ContinuousStateType) , DIMENSION(1:SED_NMX) :: xdot !< previous state deriv for multi-step [-] + INTEGER(IntKi) , DIMENSION(1:SED_NMX) :: IC = 0_IntKi !< Array which stores pointers to predictor-corrector results [-] + REAL(ReKi) :: HSSBrTrq = 0.0_ReKi !< HSSBrTrq from update states; a hack to get this working with a single integrator [-] + REAL(ReKi) :: HSSBrTrqC = 0.0_ReKi !< Commanded HSS brake torque (adjusted for sign) [N-m] + INTEGER(IntKi) :: SgnPrvLSTQ = 0_IntKi !< The sign of the low-speed shaft torque from the previous call to RtHS(). NOTE: The low-speed shaft torque is assumed to be positive at the beginning of the run! [-] + INTEGER(IntKi) , DIMENSION(1:SED_NMX) :: SgnLSTQ = 0_IntKi !< history of sign of LSTQ [-] + END TYPE SED_OtherStateType +! ======================= +! ========= SED_ParameterType ======= + TYPE, PUBLIC :: SED_ParameterType + CHARACTER(1024) :: RootName !< RootName for writing output files [-] + LOGICAL :: GenDOF = .false. !< whether the generator DOF is on (free) or off (fixed) [-] + LOGICAL :: YawDOF = .false. !< Yaw controlled by controller, or fixed [-] + REAL(DbKi) :: DT = 0.0_R8Ki !< Time step for module time integration [s] + REAL(DbKi) :: DT24 = 0.0_R8Ki !< Time step for solver [s] + INTEGER(IntKi) :: IntMethod = 0_IntKi !< Integration method {1: RK4, 2: AB4, or 3: ABM4} [-] + REAL(ReKi) :: J_DT = 0.0_ReKi !< Drivetrain inertia (blades+hub+shaft+generator) [kgm^2] + REAL(ReKi) :: PtfmPitch = 0.0_ReKi !< Static platform tilt angle [rad] + REAL(ReKi) :: InitYaw = 0.0_ReKi !< Initial or fixed nacelle yaw -- store in case YawDOF is off [deg] + REAL(R8Ki) :: InitAzimuth = 0.0_R8Ki !< Initial azimuth angle for blade 1 [deg] + REAL(ReKi) :: RotIner = 0.0_ReKi !< Hub inertia about teeter axis (2-blader) or rotor axis (3-blader) [kg m^2] + REAL(ReKi) :: GenIner = 0.0_ReKi !< Generator inertia about HSS [kg m^2] + REAL(ReKi) :: GBoxRatio = 0.0_ReKi !< Gearbox ratio [-] + INTEGER(IntKi) :: NumBl = 0_IntKi !< Number of blades on the turbine [-] + REAL(ReKi) :: TipRad = 0.0_ReKi !< Preconed blade-tip radius (distance from the rotor apex to the blade tip) [m] + REAL(ReKi) :: HubRad = 0.0_ReKi !< Preconed hub radius (distance from the rotor apex to the blade root) [m] + REAL(ReKi) :: BladeLength = 0.0_ReKi !< Length of blades [m] + REAL(ReKi) :: PreCone = 0.0_ReKi !< Rotor precone angles [deg] + REAL(ReKi) :: OverHang = 0.0_ReKi !< Distance from yaw axis to rotor apex or teeter pin [m] + REAL(ReKi) :: ShftTilt = 0.0_ReKi !< Rotor shaft tilt angle [deg] + REAL(ReKi) :: Twr2Shft = 0.0_ReKi !< Vertical distance from the tower-top to the rotor shaft [m] + REAL(ReKi) :: TowerHt = 0.0_ReKi !< Height of tower above ground level [onshore] or MSL [offshore] [m] + REAL(ReKi) :: HubHt = 0.0_ReKi !< Height of hub center above ground level [onshore] or MSL [offshore] [m] + INTEGER(IntKi) :: NumOuts = 0_IntKi !< Number of outputs [-] + TYPE(OutParmType) , DIMENSION(:), ALLOCATABLE :: OutParam !< Names and units (and other characteristics) of all requested output parameters [-] + END TYPE SED_ParameterType +! ======================= +! ========= SED_MiscVarType ======= + TYPE, PUBLIC :: SED_MiscVarType + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AllOuts !< Array of all outputs [-] + TYPE(MeshMapType) :: mapNac2Hub !< Mesh mapping from Nacelle to Hub [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: mapHub2Root !< Mesh mapping from Hub to BladeRootMotion [-] + REAL(R8Ki) , DIMENSION(:), ALLOCATABLE :: QD2T !< Current estimate of first derivative of QD (acceleration matrix) for each degree of freedom [-] + REAL(ReKi) , DIMENSION(1:3) :: HubPt_X = 0.0_ReKi !< X orientation of hub calculated in CalcOutput -- saving so we don't recalculate a bunch of things to get it in UpdateStates [-] + END TYPE SED_MiscVarType +! ======================= +CONTAINS + +subroutine SED_CopyInputFile(SrcInputFileData, DstInputFileData, CtrlCode, ErrStat, ErrMsg) + type(SED_InputFile), intent(in) :: SrcInputFileData + type(SED_InputFile), intent(inout) :: DstInputFileData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'SED_CopyInputFile' + ErrStat = ErrID_None + ErrMsg = '' + DstInputFileData%Echo = SrcInputFileData%Echo + DstInputFileData%DT = SrcInputFileData%DT + DstInputFileData%IntMethod = SrcInputFileData%IntMethod + DstInputFileData%GenDOF = SrcInputFileData%GenDOF + DstInputFileData%YawDOF = SrcInputFileData%YawDOF + DstInputFileData%Azimuth = SrcInputFileData%Azimuth + DstInputFileData%BlPitch = SrcInputFileData%BlPitch + DstInputFileData%RotSpeed = SrcInputFileData%RotSpeed + DstInputFileData%NacYaw = SrcInputFileData%NacYaw + DstInputFileData%PtfmPitch = SrcInputFileData%PtfmPitch + DstInputFileData%NumBl = SrcInputFileData%NumBl + DstInputFileData%TipRad = SrcInputFileData%TipRad + DstInputFileData%HubRad = SrcInputFileData%HubRad + DstInputFileData%PreCone = SrcInputFileData%PreCone + DstInputFileData%OverHang = SrcInputFileData%OverHang + DstInputFileData%ShftTilt = SrcInputFileData%ShftTilt + DstInputFileData%Twr2Shft = SrcInputFileData%Twr2Shft + DstInputFileData%TowerHt = SrcInputFileData%TowerHt + DstInputFileData%RotIner = SrcInputFileData%RotIner + DstInputFileData%GenIner = SrcInputFileData%GenIner + DstInputFileData%GBoxRatio = SrcInputFileData%GBoxRatio + DstInputFileData%SumPrint = SrcInputFileData%SumPrint + DstInputFileData%NumOuts = SrcInputFileData%NumOuts + if (allocated(SrcInputFileData%OutList)) then + LB(1:1) = lbound(SrcInputFileData%OutList, kind=B8Ki) + UB(1:1) = ubound(SrcInputFileData%OutList, kind=B8Ki) + if (.not. allocated(DstInputFileData%OutList)) then + allocate(DstInputFileData%OutList(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInputFileData%OutList.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInputFileData%OutList = SrcInputFileData%OutList + end if +end subroutine + +subroutine SED_DestroyInputFile(InputFileData, ErrStat, ErrMsg) + type(SED_InputFile), intent(inout) :: InputFileData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'SED_DestroyInputFile' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(InputFileData%OutList)) then + deallocate(InputFileData%OutList) + end if +end subroutine + +subroutine SED_PackInputFile(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_InputFile), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackInputFile' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Echo) + call RegPack(RF, InData%DT) + call RegPack(RF, InData%IntMethod) + call RegPack(RF, InData%GenDOF) + call RegPack(RF, InData%YawDOF) + call RegPack(RF, InData%Azimuth) + call RegPack(RF, InData%BlPitch) + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%NacYaw) + call RegPack(RF, InData%PtfmPitch) + call RegPack(RF, InData%NumBl) + call RegPack(RF, InData%TipRad) + call RegPack(RF, InData%HubRad) + call RegPack(RF, InData%PreCone) + call RegPack(RF, InData%OverHang) + call RegPack(RF, InData%ShftTilt) + call RegPack(RF, InData%Twr2Shft) + call RegPack(RF, InData%TowerHt) + call RegPack(RF, InData%RotIner) + call RegPack(RF, InData%GenIner) + call RegPack(RF, InData%GBoxRatio) + call RegPack(RF, InData%SumPrint) + call RegPack(RF, InData%NumOuts) + call RegPackAlloc(RF, InData%OutList) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackInputFile(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_InputFile), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackInputFile' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Echo); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%IntMethod); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GenDOF); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawDOF); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Azimuth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BlPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacYaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumBl); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TipRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PreCone); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OverHang); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ShftTilt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Twr2Shft); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotIner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GenIner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GBoxRatio); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%SumPrint); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumOuts); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutList); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrStat, ErrMsg) + type(SED_InitInputType), intent(in) :: SrcInitInputData + type(SED_InitInputType), intent(inout) :: DstInitInputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyInitInput' + ErrStat = ErrID_None + ErrMsg = '' + DstInitInputData%InputFile = SrcInitInputData%InputFile + DstInitInputData%RootName = SrcInitInputData%RootName + DstInitInputData%Linearize = SrcInitInputData%Linearize + DstInitInputData%UseInputFile = SrcInitInputData%UseInputFile + call NWTC_Library_CopyFileInfoType(SrcInitInputData%PassedFileData, DstInitInputData%PassedFileData, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine SED_DestroyInitInput(InitInputData, ErrStat, ErrMsg) + type(SED_InitInputType), intent(inout) :: InitInputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyInitInput' + ErrStat = ErrID_None + ErrMsg = '' + call NWTC_Library_DestroyFileInfoType(InitInputData%PassedFileData, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine SED_PackInitInput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_InitInputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackInitInput' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%InputFile) + call RegPack(RF, InData%RootName) + call RegPack(RF, InData%Linearize) + call RegPack(RF, InData%UseInputFile) + call NWTC_Library_PackFileInfoType(RF, InData%PassedFileData) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackInitInput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_InitInputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackInitInput' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RootName); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Linearize); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%UseInputFile); if (RegCheckErr(RF, RoutineName)) return + call NWTC_Library_UnpackFileInfoType(RF, OutData%PassedFileData) ! PassedFileData +end subroutine + +subroutine SED_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) + type(SED_InitOutputType), intent(in) :: SrcInitOutputData + type(SED_InitOutputType), intent(inout) :: DstInitOutputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyInitOutput' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(SrcInitOutputData%WriteOutputHdr)) then + LB(1:1) = lbound(SrcInitOutputData%WriteOutputHdr, kind=B8Ki) + UB(1:1) = ubound(SrcInitOutputData%WriteOutputHdr, kind=B8Ki) + if (.not. allocated(DstInitOutputData%WriteOutputHdr)) then + allocate(DstInitOutputData%WriteOutputHdr(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitOutputData%WriteOutputHdr.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitOutputData%WriteOutputHdr = SrcInitOutputData%WriteOutputHdr + end if + if (allocated(SrcInitOutputData%WriteOutputUnt)) then + LB(1:1) = lbound(SrcInitOutputData%WriteOutputUnt, kind=B8Ki) + UB(1:1) = ubound(SrcInitOutputData%WriteOutputUnt, kind=B8Ki) + if (.not. allocated(DstInitOutputData%WriteOutputUnt)) then + allocate(DstInitOutputData%WriteOutputUnt(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitOutputData%WriteOutputUnt.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitOutputData%WriteOutputUnt = SrcInitOutputData%WriteOutputUnt + end if + call NWTC_Library_CopyProgDesc(SrcInitOutputData%Ver, DstInitOutputData%Ver, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstInitOutputData%NumBl = SrcInitOutputData%NumBl + if (allocated(SrcInitOutputData%BlPitch)) then + LB(1:1) = lbound(SrcInitOutputData%BlPitch, kind=B8Ki) + UB(1:1) = ubound(SrcInitOutputData%BlPitch, kind=B8Ki) + if (.not. allocated(DstInitOutputData%BlPitch)) then + allocate(DstInitOutputData%BlPitch(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitOutputData%BlPitch.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitOutputData%BlPitch = SrcInitOutputData%BlPitch + end if + DstInitOutputData%BladeLength = SrcInitOutputData%BladeLength + DstInitOutputData%TowerHt = SrcInitOutputData%TowerHt + DstInitOutputData%HubHt = SrcInitOutputData%HubHt + DstInitOutputData%PlatformPos = SrcInitOutputData%PlatformPos + DstInitOutputData%HubRad = SrcInitOutputData%HubRad + DstInitOutputData%RotSpeed = SrcInitOutputData%RotSpeed + DstInitOutputData%GenDOF = SrcInitOutputData%GenDOF +end subroutine + +subroutine SED_DestroyInitOutput(InitOutputData, ErrStat, ErrMsg) + type(SED_InitOutputType), intent(inout) :: InitOutputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyInitOutput' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(InitOutputData%WriteOutputHdr)) then + deallocate(InitOutputData%WriteOutputHdr) + end if + if (allocated(InitOutputData%WriteOutputUnt)) then + deallocate(InitOutputData%WriteOutputUnt) + end if + call NWTC_Library_DestroyProgDesc(InitOutputData%Ver, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(InitOutputData%BlPitch)) then + deallocate(InitOutputData%BlPitch) + end if +end subroutine + +subroutine SED_PackInitOutput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_InitOutputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackInitOutput' + if (RF%ErrStat >= AbortErrLev) return + call RegPackAlloc(RF, InData%WriteOutputHdr) + call RegPackAlloc(RF, InData%WriteOutputUnt) + call NWTC_Library_PackProgDesc(RF, InData%Ver) + call RegPack(RF, InData%NumBl) + call RegPackAlloc(RF, InData%BlPitch) + call RegPack(RF, InData%BladeLength) + call RegPack(RF, InData%TowerHt) + call RegPack(RF, InData%HubHt) + call RegPack(RF, InData%PlatformPos) + call RegPack(RF, InData%HubRad) + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%GenDOF) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackInitOutput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_InitOutputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackInitOutput' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt); if (RegCheckErr(RF, RoutineName)) return + call NWTC_Library_UnpackProgDesc(RF, OutData%Ver) ! Ver + call RegUnpack(RF, OutData%NumBl); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BlPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BladeLength); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PlatformPos); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GenDOF); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyInput(SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg) + type(SED_InputType), intent(inout) :: SrcInputData + type(SED_InputType), intent(inout) :: DstInputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyInput' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcInputData%HubPtLoad, DstInputData%HubPtLoad, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstInputData%HSSBrTrqC = SrcInputData%HSSBrTrqC + DstInputData%GenTrq = SrcInputData%GenTrq + if (allocated(SrcInputData%BlPitchCom)) then + LB(1:1) = lbound(SrcInputData%BlPitchCom, kind=B8Ki) + UB(1:1) = ubound(SrcInputData%BlPitchCom, kind=B8Ki) + if (.not. allocated(DstInputData%BlPitchCom)) then + allocate(DstInputData%BlPitchCom(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInputData%BlPitchCom.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInputData%BlPitchCom = SrcInputData%BlPitchCom + end if + DstInputData%YawPosCom = SrcInputData%YawPosCom + DstInputData%YawRateCom = SrcInputData%YawRateCom +end subroutine + +subroutine SED_DestroyInput(InputData, ErrStat, ErrMsg) + type(SED_InputType), intent(inout) :: InputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyInput' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( InputData%HubPtLoad, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(InputData%BlPitchCom)) then + deallocate(InputData%BlPitchCom) + end if +end subroutine + +subroutine SED_PackInput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_InputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackInput' + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%HubPtLoad) + call RegPack(RF, InData%HSSBrTrqC) + call RegPack(RF, InData%GenTrq) + call RegPackAlloc(RF, InData%BlPitchCom) + call RegPack(RF, InData%YawPosCom) + call RegPack(RF, InData%YawRateCom) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackInput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_InputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackInput' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%HubPtLoad) ! HubPtLoad + call RegUnpack(RF, OutData%HSSBrTrqC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GenTrq); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BlPitchCom); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawPosCom); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawRateCom); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyOutput(SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg) + type(SED_OutputType), intent(inout) :: SrcOutputData + type(SED_OutputType), intent(inout) :: DstOutputData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyOutput' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(SrcOutputData%BladeRootMotion)) then + LB(1:1) = lbound(SrcOutputData%BladeRootMotion, kind=B8Ki) + UB(1:1) = ubound(SrcOutputData%BladeRootMotion, kind=B8Ki) + if (.not. allocated(DstOutputData%BladeRootMotion)) then + allocate(DstOutputData%BladeRootMotion(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%BladeRootMotion.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call MeshCopy(SrcOutputData%BladeRootMotion(i1), DstOutputData%BladeRootMotion(i1), CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call MeshCopy(SrcOutputData%HubPtMotion, DstOutputData%HubPtMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcOutputData%NacelleMotion, DstOutputData%NacelleMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcOutputData%TowerLn2Mesh, DstOutputData%TowerLn2Mesh, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcOutputData%PlatformPtMesh, DstOutputData%PlatformPtMesh, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + DstOutputData%LSSTipPxa = SrcOutputData%LSSTipPxa + DstOutputData%RotSpeed = SrcOutputData%RotSpeed + DstOutputData%RotPwr = SrcOutputData%RotPwr + DstOutputData%RotTrq = SrcOutputData%RotTrq + DstOutputData%HSS_Spd = SrcOutputData%HSS_Spd + DstOutputData%Yaw = SrcOutputData%Yaw + DstOutputData%YawRate = SrcOutputData%YawRate + if (allocated(SrcOutputData%BlPitch)) then + LB(1:1) = lbound(SrcOutputData%BlPitch, kind=B8Ki) + UB(1:1) = ubound(SrcOutputData%BlPitch, kind=B8Ki) + if (.not. allocated(DstOutputData%BlPitch)) then + allocate(DstOutputData%BlPitch(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%BlPitch.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstOutputData%BlPitch = SrcOutputData%BlPitch + end if + if (allocated(SrcOutputData%WriteOutput)) then + LB(1:1) = lbound(SrcOutputData%WriteOutput, kind=B8Ki) + UB(1:1) = ubound(SrcOutputData%WriteOutput, kind=B8Ki) + if (.not. allocated(DstOutputData%WriteOutput)) then + allocate(DstOutputData%WriteOutput(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstOutputData%WriteOutput.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstOutputData%WriteOutput = SrcOutputData%WriteOutput + end if +end subroutine + +subroutine SED_DestroyOutput(OutputData, ErrStat, ErrMsg) + type(SED_OutputType), intent(inout) :: OutputData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyOutput' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(OutputData%BladeRootMotion)) then + LB(1:1) = lbound(OutputData%BladeRootMotion, kind=B8Ki) + UB(1:1) = ubound(OutputData%BladeRootMotion, kind=B8Ki) + do i1 = LB(1), UB(1) + call MeshDestroy( OutputData%BladeRootMotion(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(OutputData%BladeRootMotion) + end if + call MeshDestroy( OutputData%HubPtMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( OutputData%NacelleMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( OutputData%TowerLn2Mesh, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( OutputData%PlatformPtMesh, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(OutputData%BlPitch)) then + deallocate(OutputData%BlPitch) + end if + if (allocated(OutputData%WriteOutput)) then + deallocate(OutputData%WriteOutput) + end if +end subroutine + +subroutine SED_PackOutput(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_OutputType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackOutput' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, allocated(InData%BladeRootMotion)) + if (allocated(InData%BladeRootMotion)) then + call RegPackBounds(RF, 1, lbound(InData%BladeRootMotion, kind=B8Ki), ubound(InData%BladeRootMotion, kind=B8Ki)) + LB(1:1) = lbound(InData%BladeRootMotion, kind=B8Ki) + UB(1:1) = ubound(InData%BladeRootMotion, kind=B8Ki) + do i1 = LB(1), UB(1) + call MeshPack(RF, InData%BladeRootMotion(i1)) + end do + end if + call MeshPack(RF, InData%HubPtMotion) + call MeshPack(RF, InData%NacelleMotion) + call MeshPack(RF, InData%TowerLn2Mesh) + call MeshPack(RF, InData%PlatformPtMesh) + call RegPack(RF, InData%LSSTipPxa) + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%RotPwr) + call RegPack(RF, InData%RotTrq) + call RegPack(RF, InData%HSS_Spd) + call RegPack(RF, InData%Yaw) + call RegPack(RF, InData%YawRate) + call RegPackAlloc(RF, InData%BlPitch) + call RegPackAlloc(RF, InData%WriteOutput) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackOutput(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_OutputType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackOutput' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + if (allocated(OutData%BladeRootMotion)) deallocate(OutData%BladeRootMotion) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%BladeRootMotion(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%BladeRootMotion.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call MeshUnpack(RF, OutData%BladeRootMotion(i1)) ! BladeRootMotion + end do + end if + call MeshUnpack(RF, OutData%HubPtMotion) ! HubPtMotion + call MeshUnpack(RF, OutData%NacelleMotion) ! NacelleMotion + call MeshUnpack(RF, OutData%TowerLn2Mesh) ! TowerLn2Mesh + call MeshUnpack(RF, OutData%PlatformPtMesh) ! PlatformPtMesh + call RegUnpack(RF, OutData%LSSTipPxa); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotPwr); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotTrq); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HSS_Spd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Yaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawRate); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BlPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutput); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyContState(SrcContStateData, DstContStateData, CtrlCode, ErrStat, ErrMsg) + type(SED_ContinuousStateType), intent(in) :: SrcContStateData + type(SED_ContinuousStateType), intent(inout) :: DstContStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'SED_CopyContState' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(SrcContStateData%QT)) then + LB(1:1) = lbound(SrcContStateData%QT, kind=B8Ki) + UB(1:1) = ubound(SrcContStateData%QT, kind=B8Ki) + if (.not. allocated(DstContStateData%QT)) then + allocate(DstContStateData%QT(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstContStateData%QT.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstContStateData%QT = SrcContStateData%QT + end if + if (allocated(SrcContStateData%QDT)) then + LB(1:1) = lbound(SrcContStateData%QDT, kind=B8Ki) + UB(1:1) = ubound(SrcContStateData%QDT, kind=B8Ki) + if (.not. allocated(DstContStateData%QDT)) then + allocate(DstContStateData%QDT(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstContStateData%QDT.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstContStateData%QDT = SrcContStateData%QDT + end if +end subroutine + +subroutine SED_DestroyContState(ContStateData, ErrStat, ErrMsg) + type(SED_ContinuousStateType), intent(inout) :: ContStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'SED_DestroyContState' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(ContStateData%QT)) then + deallocate(ContStateData%QT) + end if + if (allocated(ContStateData%QDT)) then + deallocate(ContStateData%QDT) + end if +end subroutine + +subroutine SED_PackContState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_ContinuousStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackContState' + if (RF%ErrStat >= AbortErrLev) return + call RegPackAlloc(RF, InData%QT) + call RegPackAlloc(RF, InData%QDT) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackContState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_ContinuousStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackContState' + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpackAlloc(RF, OutData%QT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%QDT); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg) + type(SED_DiscreteStateType), intent(in) :: SrcDiscStateData + type(SED_DiscreteStateType), intent(inout) :: DstDiscStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'SED_CopyDiscState' + ErrStat = ErrID_None + ErrMsg = '' + DstDiscStateData%DummyDiscreteState = SrcDiscStateData%DummyDiscreteState +end subroutine + +subroutine SED_DestroyDiscState(DiscStateData, ErrStat, ErrMsg) + type(SED_DiscreteStateType), intent(inout) :: DiscStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'SED_DestroyDiscState' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine SED_PackDiscState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_DiscreteStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackDiscState' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DummyDiscreteState) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackDiscState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_DiscreteStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackDiscState' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DummyDiscreteState); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyConstrState(SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg) + type(SED_ConstraintStateType), intent(in) :: SrcConstrStateData + type(SED_ConstraintStateType), intent(inout) :: DstConstrStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'SED_CopyConstrState' + ErrStat = ErrID_None + ErrMsg = '' + DstConstrStateData%DummyConstrState = SrcConstrStateData%DummyConstrState +end subroutine + +subroutine SED_DestroyConstrState(ConstrStateData, ErrStat, ErrMsg) + type(SED_ConstraintStateType), intent(inout) :: ConstrStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'SED_DestroyConstrState' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine SED_PackConstrState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_ConstraintStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackConstrState' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DummyConstrState) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackConstrState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_ConstraintStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackConstrState' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DummyConstrState); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyOtherState(SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg) + type(SED_OtherStateType), intent(in) :: SrcOtherStateData + type(SED_OtherStateType), intent(inout) :: DstOtherStateData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyOtherState' + ErrStat = ErrID_None + ErrMsg = '' + DstOtherStateData%n = SrcOtherStateData%n + LB(1:1) = lbound(SrcOtherStateData%xdot, kind=B8Ki) + UB(1:1) = ubound(SrcOtherStateData%xdot, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_CopyContState(SrcOtherStateData%xdot(i1), DstOtherStateData%xdot(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + DstOtherStateData%IC = SrcOtherStateData%IC + DstOtherStateData%HSSBrTrq = SrcOtherStateData%HSSBrTrq + DstOtherStateData%HSSBrTrqC = SrcOtherStateData%HSSBrTrqC + DstOtherStateData%SgnPrvLSTQ = SrcOtherStateData%SgnPrvLSTQ + DstOtherStateData%SgnLSTQ = SrcOtherStateData%SgnLSTQ +end subroutine + +subroutine SED_DestroyOtherState(OtherStateData, ErrStat, ErrMsg) + type(SED_OtherStateType), intent(inout) :: OtherStateData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyOtherState' + ErrStat = ErrID_None + ErrMsg = '' + LB(1:1) = lbound(OtherStateData%xdot, kind=B8Ki) + UB(1:1) = ubound(OtherStateData%xdot, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_DestroyContState(OtherStateData%xdot(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do +end subroutine + +subroutine SED_PackOtherState(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_OtherStateType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackOtherState' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%n) + LB(1:1) = lbound(InData%xdot, kind=B8Ki) + UB(1:1) = ubound(InData%xdot, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_PackContState(RF, InData%xdot(i1)) + end do + call RegPack(RF, InData%IC) + call RegPack(RF, InData%HSSBrTrq) + call RegPack(RF, InData%HSSBrTrqC) + call RegPack(RF, InData%SgnPrvLSTQ) + call RegPack(RF, InData%SgnLSTQ) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackOtherState(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_OtherStateType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackOtherState' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%n); if (RegCheckErr(RF, RoutineName)) return + LB(1:1) = lbound(OutData%xdot, kind=B8Ki) + UB(1:1) = ubound(OutData%xdot, kind=B8Ki) + do i1 = LB(1), UB(1) + call SED_UnpackContState(RF, OutData%xdot(i1)) ! xdot + end do + call RegUnpack(RF, OutData%IC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HSSBrTrq); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HSSBrTrqC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%SgnPrvLSTQ); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%SgnLSTQ); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) + type(SED_ParameterType), intent(in) :: SrcParamData + type(SED_ParameterType), intent(inout) :: DstParamData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyParam' + ErrStat = ErrID_None + ErrMsg = '' + DstParamData%RootName = SrcParamData%RootName + DstParamData%GenDOF = SrcParamData%GenDOF + DstParamData%YawDOF = SrcParamData%YawDOF + DstParamData%DT = SrcParamData%DT + DstParamData%DT24 = SrcParamData%DT24 + DstParamData%IntMethod = SrcParamData%IntMethod + DstParamData%J_DT = SrcParamData%J_DT + DstParamData%PtfmPitch = SrcParamData%PtfmPitch + DstParamData%InitYaw = SrcParamData%InitYaw + DstParamData%InitAzimuth = SrcParamData%InitAzimuth + DstParamData%RotIner = SrcParamData%RotIner + DstParamData%GenIner = SrcParamData%GenIner + DstParamData%GBoxRatio = SrcParamData%GBoxRatio + DstParamData%NumBl = SrcParamData%NumBl + DstParamData%TipRad = SrcParamData%TipRad + DstParamData%HubRad = SrcParamData%HubRad + DstParamData%BladeLength = SrcParamData%BladeLength + DstParamData%PreCone = SrcParamData%PreCone + DstParamData%OverHang = SrcParamData%OverHang + DstParamData%ShftTilt = SrcParamData%ShftTilt + DstParamData%Twr2Shft = SrcParamData%Twr2Shft + DstParamData%TowerHt = SrcParamData%TowerHt + DstParamData%HubHt = SrcParamData%HubHt + DstParamData%NumOuts = SrcParamData%NumOuts + if (allocated(SrcParamData%OutParam)) then + LB(1:1) = lbound(SrcParamData%OutParam, kind=B8Ki) + UB(1:1) = ubound(SrcParamData%OutParam, kind=B8Ki) + if (.not. allocated(DstParamData%OutParam)) then + allocate(DstParamData%OutParam(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%OutParam.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyOutParmType(SrcParamData%OutParam(i1), DstParamData%OutParam(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if +end subroutine + +subroutine SED_DestroyParam(ParamData, ErrStat, ErrMsg) + type(SED_ParameterType), intent(inout) :: ParamData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyParam' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(ParamData%OutParam)) then + LB(1:1) = lbound(ParamData%OutParam, kind=B8Ki) + UB(1:1) = ubound(ParamData%OutParam, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyOutParmType(ParamData%OutParam(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(ParamData%OutParam) + end if +end subroutine + +subroutine SED_PackParam(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_ParameterType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackParam' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%RootName) + call RegPack(RF, InData%GenDOF) + call RegPack(RF, InData%YawDOF) + call RegPack(RF, InData%DT) + call RegPack(RF, InData%DT24) + call RegPack(RF, InData%IntMethod) + call RegPack(RF, InData%J_DT) + call RegPack(RF, InData%PtfmPitch) + call RegPack(RF, InData%InitYaw) + call RegPack(RF, InData%InitAzimuth) + call RegPack(RF, InData%RotIner) + call RegPack(RF, InData%GenIner) + call RegPack(RF, InData%GBoxRatio) + call RegPack(RF, InData%NumBl) + call RegPack(RF, InData%TipRad) + call RegPack(RF, InData%HubRad) + call RegPack(RF, InData%BladeLength) + call RegPack(RF, InData%PreCone) + call RegPack(RF, InData%OverHang) + call RegPack(RF, InData%ShftTilt) + call RegPack(RF, InData%Twr2Shft) + call RegPack(RF, InData%TowerHt) + call RegPack(RF, InData%HubHt) + call RegPack(RF, InData%NumOuts) + call RegPack(RF, allocated(InData%OutParam)) + if (allocated(InData%OutParam)) then + call RegPackBounds(RF, 1, lbound(InData%OutParam, kind=B8Ki), ubound(InData%OutParam, kind=B8Ki)) + LB(1:1) = lbound(InData%OutParam, kind=B8Ki) + UB(1:1) = ubound(InData%OutParam, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_PackOutParmType(RF, InData%OutParam(i1)) + end do + end if + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackParam(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_ParameterType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackParam' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%RootName); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GenDOF); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%YawDOF); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DT24); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%IntMethod); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%J_DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%InitYaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%InitAzimuth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotIner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GenIner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GBoxRatio); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumBl); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TipRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BladeLength); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PreCone); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OverHang); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ShftTilt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Twr2Shft); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumOuts); if (RegCheckErr(RF, RoutineName)) return + if (allocated(OutData%OutParam)) deallocate(OutData%OutParam) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%OutParam(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%OutParam.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackOutParmType(RF, OutData%OutParam(i1)) ! OutParam + end do + end if +end subroutine + +subroutine SED_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) + type(SED_MiscVarType), intent(inout) :: SrcMiscData + type(SED_MiscVarType), intent(inout) :: DstMiscData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_CopyMisc' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(SrcMiscData%AllOuts)) then + LB(1:1) = lbound(SrcMiscData%AllOuts, kind=B8Ki) + UB(1:1) = ubound(SrcMiscData%AllOuts, kind=B8Ki) + if (.not. allocated(DstMiscData%AllOuts)) then + allocate(DstMiscData%AllOuts(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%AllOuts.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%AllOuts = SrcMiscData%AllOuts + end if + call NWTC_Library_CopyMeshMapType(SrcMiscData%mapNac2Hub, DstMiscData%mapNac2Hub, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMiscData%mapHub2Root)) then + LB(1:1) = lbound(SrcMiscData%mapHub2Root, kind=B8Ki) + UB(1:1) = ubound(SrcMiscData%mapHub2Root, kind=B8Ki) + if (.not. allocated(DstMiscData%mapHub2Root)) then + allocate(DstMiscData%mapHub2Root(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%mapHub2Root.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcMiscData%mapHub2Root(i1), DstMiscData%mapHub2Root(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcMiscData%QD2T)) then + LB(1:1) = lbound(SrcMiscData%QD2T, kind=B8Ki) + UB(1:1) = ubound(SrcMiscData%QD2T, kind=B8Ki) + if (.not. allocated(DstMiscData%QD2T)) then + allocate(DstMiscData%QD2T(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%QD2T.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%QD2T = SrcMiscData%QD2T + end if + DstMiscData%HubPt_X = SrcMiscData%HubPt_X +end subroutine + +subroutine SED_DestroyMisc(MiscData, ErrStat, ErrMsg) + type(SED_MiscVarType), intent(inout) :: MiscData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'SED_DestroyMisc' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(MiscData%AllOuts)) then + deallocate(MiscData%AllOuts) + end if + call NWTC_Library_DestroyMeshMapType(MiscData%mapNac2Hub, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MiscData%mapHub2Root)) then + LB(1:1) = lbound(MiscData%mapHub2Root, kind=B8Ki) + UB(1:1) = ubound(MiscData%mapHub2Root, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(MiscData%mapHub2Root(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MiscData%mapHub2Root) + end if + if (allocated(MiscData%QD2T)) then + deallocate(MiscData%QD2T) + end if +end subroutine + +subroutine SED_PackMisc(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SED_MiscVarType), intent(in) :: InData + character(*), parameter :: RoutineName = 'SED_PackMisc' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call RegPackAlloc(RF, InData%AllOuts) + call NWTC_Library_PackMeshMapType(RF, InData%mapNac2Hub) + call RegPack(RF, allocated(InData%mapHub2Root)) + if (allocated(InData%mapHub2Root)) then + call RegPackBounds(RF, 1, lbound(InData%mapHub2Root, kind=B8Ki), ubound(InData%mapHub2Root, kind=B8Ki)) + LB(1:1) = lbound(InData%mapHub2Root, kind=B8Ki) + UB(1:1) = ubound(InData%mapHub2Root, kind=B8Ki) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%mapHub2Root(i1)) + end do + end if + call RegPackAlloc(RF, InData%QD2T) + call RegPack(RF, InData%HubPt_X) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_UnPackMisc(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SED_MiscVarType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'SED_UnPackMisc' + integer(B8Ki) :: i1 + integer(B8Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpackAlloc(RF, OutData%AllOuts); if (RegCheckErr(RF, RoutineName)) return + call NWTC_Library_UnpackMeshMapType(RF, OutData%mapNac2Hub) ! mapNac2Hub + if (allocated(OutData%mapHub2Root)) deallocate(OutData%mapHub2Root) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%mapHub2Root(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%mapHub2Root.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%mapHub2Root(i1)) ! mapHub2Root + end do + end if + call RegUnpackAlloc(RF, OutData%QD2T); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubPt_X); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine SED_Input_ExtrapInterp(u, t, u_out, t_out, ErrStat, ErrMsg) + ! + ! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time + ! values of u (which has values associated with times in t). Order of the interpolation is given by the size of u + ! + ! expressions below based on either + ! + ! f(t) = a + ! f(t) = a + b * t, or + ! f(t) = a + b * t + c * t**2 + ! + ! where a, b and c are determined as the solution to + ! f(t1) = u1, f(t2) = u2, f(t3) = u3 (as appropriate) + ! + !---------------------------------------------------------------------------------------------------------------------------------- + + type(SED_InputType), intent(inout) :: u(:) ! Input at t1 > t2 > t3 + real(DbKi), intent(in ) :: t(:) ! Times associated with the Inputs + type(SED_InputType), intent(inout) :: u_out ! Input at tin_out + real(DbKi), intent(in ) :: t_out ! time to be extrap/interp'd to + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + integer(IntKi) :: order ! order of polynomial fit (max 2) + integer(IntKi) :: ErrStat2 ! local errors + character(ErrMsgLen) :: ErrMsg2 ! local errors + character(*), PARAMETER :: RoutineName = 'SED_Input_ExtrapInterp' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + if (size(t) /= size(u)) then + call SetErrStat(ErrID_Fatal, 'size(t) must equal size(u)', ErrStat, ErrMsg, RoutineName) + return + endif + order = size(u) - 1 + select case (order) + case (0) + call SED_CopyInput(u(1), u_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (1) + call SED_Input_ExtrapInterp1(u(1), u(2), t, u_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (2) + call SED_Input_ExtrapInterp2(u(1), u(2), u(3), t, u_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case default + call SetErrStat(ErrID_Fatal, 'size(u) must be less than 4 (order must be less than 3).', ErrStat, ErrMsg, RoutineName) + return + end select +end subroutine + +SUBROUTINE SED_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = u1, f(t2) = u2 +! +!.................................................................................................................................. + + TYPE(SED_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 + TYPE(SED_InputType), INTENT(INOUT) :: u2 ! Input at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Inputs + TYPE(SED_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'SED_Input_ExtrapInterp1' + REAL(DbKi) :: a1, a2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i1 ! dim1 counter variable for arrays + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF (EqualRealNos(t(1), t(2))) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg, RoutineName) + RETURN + END IF + + ! Calculate weighting factors from Lagrange polynomial + a1 = -(t_out - t(2))/t(2) + a2 = t_out/t(2) + + CALL MeshExtrapInterp1(u1%HubPtLoad, u2%HubPtLoad, tin, u_out%HubPtLoad, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + u_out%HSSBrTrqC = a1*u1%HSSBrTrqC + a2*u2%HSSBrTrqC + u_out%GenTrq = a1*u1%GenTrq + a2*u2%GenTrq + IF (ALLOCATED(u_out%BlPitchCom) .AND. ALLOCATED(u1%BlPitchCom)) THEN + DO i1 = LBOUND(u_out%BlPitchCom,1, kind=B8Ki),UBOUND(u_out%BlPitchCom,1, kind=B8Ki) + CALL Angles_ExtrapInterp( u1%BlPitchCom(i1), u2%BlPitchCom(i1), tin, u_out%BlPitchCom(i1), tin_out ) + END DO + END IF ! check if allocated + u_out%YawPosCom = a1*u1%YawPosCom + a2*u2%YawPosCom + u_out%YawRateCom = a1*u1%YawRateCom + a2*u2%YawRateCom +END SUBROUTINE + +SUBROUTINE SED_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Input u_out at time t_out, from previous/future time +! values of u (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = u1, f(t2) = u2, f(t3) = u3 +! +!.................................................................................................................................. + + TYPE(SED_InputType), INTENT(INOUT) :: u1 ! Input at t1 > t2 > t3 + TYPE(SED_InputType), INTENT(INOUT) :: u2 ! Input at t2 > t3 + TYPE(SED_InputType), INTENT(INOUT) :: u3 ! Input at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Inputs + TYPE(SED_InputType), INTENT(INOUT) :: u_out ! Input at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Inputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: a1,a2,a3 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'SED_Input_ExtrapInterp2' + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i1 ! dim1 counter variable for arrays + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + + ! Calculate Lagrange polynomial coefficients + a1 = (t_out - t(2))*(t_out - t(3))/((t(1) - t(2))*(t(1) - t(3))) + a2 = (t_out - t(1))*(t_out - t(3))/((t(2) - t(1))*(t(2) - t(3))) + a3 = (t_out - t(1))*(t_out - t(2))/((t(3) - t(1))*(t(3) - t(2))) + CALL MeshExtrapInterp2(u1%HubPtLoad, u2%HubPtLoad, u3%HubPtLoad, tin, u_out%HubPtLoad, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + u_out%HSSBrTrqC = a1*u1%HSSBrTrqC + a2*u2%HSSBrTrqC + a3*u3%HSSBrTrqC + u_out%GenTrq = a1*u1%GenTrq + a2*u2%GenTrq + a3*u3%GenTrq + IF (ALLOCATED(u_out%BlPitchCom) .AND. ALLOCATED(u1%BlPitchCom)) THEN + DO i1 = LBOUND(u_out%BlPitchCom,1, kind=B8Ki),UBOUND(u_out%BlPitchCom,1, kind=B8Ki) + CALL Angles_ExtrapInterp( u1%BlPitchCom(i1), u2%BlPitchCom(i1), u3%BlPitchCom(i1), tin, u_out%BlPitchCom(i1), tin_out ) + END DO + END IF ! check if allocated + u_out%YawPosCom = a1*u1%YawPosCom + a2*u2%YawPosCom + a3*u3%YawPosCom + u_out%YawRateCom = a1*u1%YawRateCom + a2*u2%YawRateCom + a3*u3%YawRateCom +END SUBROUTINE + +subroutine SED_Output_ExtrapInterp(y, t, y_out, t_out, ErrStat, ErrMsg) + ! + ! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time + ! values of y (which has values associated with times in t). Order of the interpolation is given by the size of y + ! + ! expressions below based on either + ! + ! f(t) = a + ! f(t) = a + b * t, or + ! f(t) = a + b * t + c * t**2 + ! + ! where a, b and c are determined as the solution to + ! f(t1) = y1, f(t2) = y2, f(t3) = y3 (as appropriate) + ! + !---------------------------------------------------------------------------------------------------------------------------------- + + type(SED_OutputType), intent(inout) :: y(:) ! Output at t1 > t2 > t3 + real(DbKi), intent(in ) :: t(:) ! Times associated with the Outputs + type(SED_OutputType), intent(inout) :: y_out ! Output at tin_out + real(DbKi), intent(in ) :: t_out ! time to be extrap/interp'd to + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + integer(IntKi) :: order ! order of polynomial fit (max 2) + integer(IntKi) :: ErrStat2 ! local errors + character(ErrMsgLen) :: ErrMsg2 ! local errors + character(*), PARAMETER :: RoutineName = 'SED_Output_ExtrapInterp' + + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + if (size(t) /= size(y)) then + call SetErrStat(ErrID_Fatal, 'size(t) must equal size(y)', ErrStat, ErrMsg, RoutineName) + return + endif + order = size(y) - 1 + select case (order) + case (0) + call SED_CopyOutput(y(1), y_out, MESH_UPDATECOPY, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (1) + call SED_Output_ExtrapInterp1(y(1), y(2), t, y_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case (2) + call SED_Output_ExtrapInterp2(y(1), y(2), y(3), t, y_out, t_out, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + case default + call SetErrStat(ErrID_Fatal, 'size(y) must be less than 4 (order must be less than 3).', ErrStat, ErrMsg, RoutineName) + return + end select +end subroutine + +SUBROUTINE SED_Output_ExtrapInterp1(y1, y2, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 1. +! +! f(t) = a + b * t, or +! +! where a and b are determined as the solution to +! f(t1) = y1, f(t2) = y2 +! +!.................................................................................................................................. + + TYPE(SED_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 + TYPE(SED_OutputType), INTENT(INOUT) :: y2 ! Output at t2 + REAL(DbKi), INTENT(IN ) :: tin(2) ! Times associated with the Outputs + TYPE(SED_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(2) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + CHARACTER(*), PARAMETER :: RoutineName = 'SED_Output_ExtrapInterp1' + REAL(DbKi) :: a1, a2 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i1 ! dim1 counter variable for arrays + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF (EqualRealNos(t(1), t(2))) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg, RoutineName) + RETURN + END IF + + ! Calculate weighting factors from Lagrange polynomial + a1 = -(t_out - t(2))/t(2) + a2 = t_out/t(2) + + IF (ALLOCATED(y_out%BladeRootMotion) .AND. ALLOCATED(y1%BladeRootMotion)) THEN + DO i1 = LBOUND(y_out%BladeRootMotion,1, kind=B8Ki),UBOUND(y_out%BladeRootMotion,1, kind=B8Ki) + CALL MeshExtrapInterp1(y1%BladeRootMotion(i1), y2%BladeRootMotion(i1), tin, y_out%BladeRootMotion(i1), tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + END DO + END IF ! check if allocated + CALL MeshExtrapInterp1(y1%HubPtMotion, y2%HubPtMotion, tin, y_out%HubPtMotion, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL MeshExtrapInterp1(y1%NacelleMotion, y2%NacelleMotion, tin, y_out%NacelleMotion, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL MeshExtrapInterp1(y1%TowerLn2Mesh, y2%TowerLn2Mesh, tin, y_out%TowerLn2Mesh, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL MeshExtrapInterp1(y1%PlatformPtMesh, y2%PlatformPtMesh, tin, y_out%PlatformPtMesh, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL Angles_ExtrapInterp( y1%LSSTipPxa, y2%LSSTipPxa, tin, y_out%LSSTipPxa, tin_out ) + y_out%RotSpeed = a1*y1%RotSpeed + a2*y2%RotSpeed + y_out%RotPwr = a1*y1%RotPwr + a2*y2%RotPwr + y_out%RotTrq = a1*y1%RotTrq + a2*y2%RotTrq + y_out%HSS_Spd = a1*y1%HSS_Spd + a2*y2%HSS_Spd + y_out%Yaw = a1*y1%Yaw + a2*y2%Yaw + y_out%YawRate = a1*y1%YawRate + a2*y2%YawRate + IF (ALLOCATED(y_out%BlPitch) .AND. ALLOCATED(y1%BlPitch)) THEN + DO i1 = LBOUND(y_out%BlPitch,1, kind=B8Ki),UBOUND(y_out%BlPitch,1, kind=B8Ki) + CALL Angles_ExtrapInterp( y1%BlPitch(i1), y2%BlPitch(i1), tin, y_out%BlPitch(i1), tin_out ) + END DO + END IF ! check if allocated + IF (ALLOCATED(y_out%WriteOutput) .AND. ALLOCATED(y1%WriteOutput)) THEN + y_out%WriteOutput = a1*y1%WriteOutput + a2*y2%WriteOutput + END IF ! check if allocated +END SUBROUTINE + +SUBROUTINE SED_Output_ExtrapInterp2(y1, y2, y3, tin, y_out, tin_out, ErrStat, ErrMsg ) +! +! This subroutine calculates a extrapolated (or interpolated) Output y_out at time t_out, from previous/future time +! values of y (which has values associated with times in t). Order of the interpolation is 2. +! +! expressions below based on either +! +! f(t) = a + b * t + c * t**2 +! +! where a, b and c are determined as the solution to +! f(t1) = y1, f(t2) = y2, f(t3) = y3 +! +!.................................................................................................................................. + + TYPE(SED_OutputType), INTENT(INOUT) :: y1 ! Output at t1 > t2 > t3 + TYPE(SED_OutputType), INTENT(INOUT) :: y2 ! Output at t2 > t3 + TYPE(SED_OutputType), INTENT(INOUT) :: y3 ! Output at t3 + REAL(DbKi), INTENT(IN ) :: tin(3) ! Times associated with the Outputs + TYPE(SED_OutputType), INTENT(INOUT) :: y_out ! Output at tin_out + REAL(DbKi), INTENT(IN ) :: tin_out ! time to be extrap/interp'd to + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! Error status of the operation + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + ! local variables + REAL(DbKi) :: t(3) ! Times associated with the Outputs + REAL(DbKi) :: t_out ! Time to which to be extrap/interpd + INTEGER(IntKi) :: order ! order of polynomial fit (max 2) + REAL(DbKi) :: a1,a2,a3 ! temporary for extrapolation/interpolation + INTEGER(IntKi) :: ErrStat2 ! local errors + CHARACTER(ErrMsgLen) :: ErrMsg2 ! local errors + CHARACTER(*), PARAMETER :: RoutineName = 'SED_Output_ExtrapInterp2' + INTEGER :: i01 ! dim1 level 0 counter variable for arrays of ddts + INTEGER :: i1 ! dim1 counter variable for arrays + ! Initialize ErrStat + ErrStat = ErrID_None + ErrMsg = '' + ! we'll subtract a constant from the times to resolve some + ! numerical issues when t gets large (and to simplify the equations) + t = tin - tin(1) + t_out = tin_out - tin(1) + + IF ( EqualRealNos( t(1), t(2) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(2) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(2), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(2) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + ELSE IF ( EqualRealNos( t(1), t(3) ) ) THEN + CALL SetErrStat(ErrID_Fatal, 't(1) must not equal t(3) to avoid a division-by-zero error.', ErrStat, ErrMsg,RoutineName) + RETURN + END IF + + ! Calculate Lagrange polynomial coefficients + a1 = (t_out - t(2))*(t_out - t(3))/((t(1) - t(2))*(t(1) - t(3))) + a2 = (t_out - t(1))*(t_out - t(3))/((t(2) - t(1))*(t(2) - t(3))) + a3 = (t_out - t(1))*(t_out - t(2))/((t(3) - t(1))*(t(3) - t(2))) + IF (ALLOCATED(y_out%BladeRootMotion) .AND. ALLOCATED(y1%BladeRootMotion)) THEN + DO i1 = LBOUND(y_out%BladeRootMotion,1, kind=B8Ki),UBOUND(y_out%BladeRootMotion,1, kind=B8Ki) + CALL MeshExtrapInterp2(y1%BladeRootMotion(i1), y2%BladeRootMotion(i1), y3%BladeRootMotion(i1), tin, y_out%BladeRootMotion(i1), tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + END DO + END IF ! check if allocated + CALL MeshExtrapInterp2(y1%HubPtMotion, y2%HubPtMotion, y3%HubPtMotion, tin, y_out%HubPtMotion, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL MeshExtrapInterp2(y1%NacelleMotion, y2%NacelleMotion, y3%NacelleMotion, tin, y_out%NacelleMotion, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL MeshExtrapInterp2(y1%TowerLn2Mesh, y2%TowerLn2Mesh, y3%TowerLn2Mesh, tin, y_out%TowerLn2Mesh, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL MeshExtrapInterp2(y1%PlatformPtMesh, y2%PlatformPtMesh, y3%PlatformPtMesh, tin, y_out%PlatformPtMesh, tin_out, ErrStat2, ErrMsg2) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg,RoutineName) + CALL Angles_ExtrapInterp( y1%LSSTipPxa, y2%LSSTipPxa, y3%LSSTipPxa, tin, y_out%LSSTipPxa, tin_out ) + y_out%RotSpeed = a1*y1%RotSpeed + a2*y2%RotSpeed + a3*y3%RotSpeed + y_out%RotPwr = a1*y1%RotPwr + a2*y2%RotPwr + a3*y3%RotPwr + y_out%RotTrq = a1*y1%RotTrq + a2*y2%RotTrq + a3*y3%RotTrq + y_out%HSS_Spd = a1*y1%HSS_Spd + a2*y2%HSS_Spd + a3*y3%HSS_Spd + y_out%Yaw = a1*y1%Yaw + a2*y2%Yaw + a3*y3%Yaw + y_out%YawRate = a1*y1%YawRate + a2*y2%YawRate + a3*y3%YawRate + IF (ALLOCATED(y_out%BlPitch) .AND. ALLOCATED(y1%BlPitch)) THEN + DO i1 = LBOUND(y_out%BlPitch,1, kind=B8Ki),UBOUND(y_out%BlPitch,1, kind=B8Ki) + CALL Angles_ExtrapInterp( y1%BlPitch(i1), y2%BlPitch(i1), y3%BlPitch(i1), tin, y_out%BlPitch(i1), tin_out ) + END DO + END IF ! check if allocated + IF (ALLOCATED(y_out%WriteOutput) .AND. ALLOCATED(y1%WriteOutput)) THEN + y_out%WriteOutput = a1*y1%WriteOutput + a2*y2%WriteOutput + a3*y3%WriteOutput + END IF ! check if allocated +END SUBROUTINE +END MODULE SED_Types +!ENDOFREGISTRYGENERATEDFILE diff --git a/modules/simple-elastodyn/src/driver/SED_Driver.f90 b/modules/simple-elastodyn/src/driver/SED_Driver.f90 new file mode 100644 index 0000000000..900c7ea938 --- /dev/null +++ b/modules/simple-elastodyn/src/driver/SED_Driver.f90 @@ -0,0 +1,373 @@ +!********************************************************************************************************************************** +!> ## SED_DriverCode: This code tests the SED module +!!.................................................................................................................................. +!! LICENSING +!! Copyright (C) 2024 National Renewable Energy Laboratory +!! +!! This file is part of SED. +!! +!! Licensed under the Apache License, Version 2.0 (the "License"); +!! you may not use this file except in compliance with the License. +!! You may obtain a copy of the License at +!! +!! http://www.apache.org/licenses/LICENSE-2.0 +!! +!! Unless required by applicable law or agreed to in writing, software +!! distributed under the License is distributed on an "AS IS" BASIS, +!! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +!! See the License for the specific language governing permissions and +!! limitations under the License. +!********************************************************************************************************************************** +PROGRAM SED_Driver + + USE NWTC_Library + USE VersionInfo + USE SED + USE SED_IO + USE SED_Types + USE SED_Driver_Subs + USE SED_Driver_Types + + IMPLICIT NONE + + TYPE( ProgDesc ), PARAMETER :: ProgInfo = ProgDesc("SED_Driver","","") + INTEGER(IntKi) :: SEDDriver_Verbose = 5 ! Verbose level. 0 = none, 5 = some, 10 = lots + + + + integer(IntKi), parameter :: NumInp = 3 !< Number of inputs sent to SED_UpdateStates (InterpOrder+1) + + ! Program variables + real(DbKi) :: T !< Variable for storing time, in seconds + real(DbKi) :: TimeInterval !< Interval between time steps, in seconds + real(DbKi) :: TStart !< Time to start + real(DbKi) :: TMax !< Maximum time if found by default + integer(IntKi) :: NumTSteps !< number of timesteps + logical :: TimeIntervalFound !< Interval between time steps, in seconds + real(DbKi) :: uTimes(NumInp) !< Variable for storing time associated with inputs, in seconds + real(DbKi), allocatable :: CaseTime(:) !< Timestamps for the case data + real(ReKi), allocatable :: CaseData(:,:) !< Data for the case. Corresponds to CaseTime + + type(SED_InitInputType) :: InitInData !< Input data for initialization + type(SED_InitOutputType) :: InitOutData !< Output data from initialization + + type(SED_ContinuousStateType) :: x !< Continuous states + type(SED_DiscreteStateType) :: xd !< Discrete states + type(SED_ConstraintStateType) :: z !< Constraint states + type(SED_ConstraintStateType) :: Z_residual !< Residual of the constraint state functions (Z) + type(SED_OtherStateType) :: OtherState !< Other states + type(SED_MiscVarType) :: misc !< Optimization variables + + type(SED_ParameterType) :: p !< Parameters + type(SED_InputType) :: u(NumInp) !< System inputs + type(SED_OutputType) :: y !< System outputs + + ! Local variables for this code + TYPE(SEDDriver_Flags) :: CLSettingsFlags ! Flags indicating which command line arguments were specified + TYPE(SEDDriver_Settings) :: CLSettings ! Command line arguments passed in + TYPE(SEDDriver_Flags) :: SettingsFlags ! Flags indicating which settings were specified (includes CL and ipt file) + TYPE(SEDDriver_Settings) :: Settings ! Driver settings + REAL(DbKi) :: Timer(1:2) ! Keep track of how long this takes to run + type(FileInfoType) :: DvrFileInfo ! Input file stored in FileInfoType structure + + + ! Data transfer + real(ReKi) :: AeroTrq !< AeroTrq read from table -- must be put onto mesh for passing + + INTEGER(IntKi) :: n !< Loop counter (for time step) + integer(IntKi) :: i !< generic loop counter + integer(IntKi) :: DimIdx !< Index of current dimension + integer(IntKi) :: TmpIdx !< Index of last point accessed by dimension + INTEGER(IntKi) :: ErrStat !< Status of error message + CHARACTER(ErrMsgLen) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + CHARACTER(200) :: git_commit ! String containing the current git commit hash + TYPE(ProgDesc), PARAMETER :: version = ProgDesc( 'SED Driver', '', '' ) ! The version number of this program. + integer(IntKi) :: DvrOut + character(1024) :: OutputFileRootName + + + ! initialize library + call NWTC_Init + call DispNVD(ProgInfo) + DvrOut=-1 ! Set output unit to negative + + ! Display the copyright notice + CALL DispCopyrightLicense( version%Name ) + ! Obtain OpenFAST git commit hash + git_commit = QueryGitVersion() + ! Tell our users what they're running + CALL WrScr( ' Running '//GetNVD( version )//' a part of OpenFAST - '//TRIM(git_Commit)//NewLine//' linked with '//TRIM( GetNVD( NWTC_Ver ))//NewLine ) + + ! Start the timer + call CPU_TIME( Timer(1) ) + + ! Initialize the driver settings to their default values (same as the CL -- command line -- values) + call InitSettingsFlags( ProgInfo, CLSettings, CLSettingsFlags ) + Settings = CLSettings + SettingsFlags = CLSettingsFlags + + ! Parse the input line + call RetrieveArgs( CLSettings, CLSettingsFlags, ErrStat, ErrMsg ) + call CheckErr('') + + ! Check if we are doing verbose error reporting + IF ( CLSettingsFlags%VVerbose ) SEDDriver_Verbose = 10_IntKi + IF ( CLSettingsFlags%Verbose ) SEDDriver_Verbose = 7_IntKi + + ! Verbose error reporting + IF ( SEDDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr('--- Settings from the command line: ---') + CALL printSettings( CLSettingsFlags, CLSettings ) + CALL WrSCr(NewLine) + ENDIF + + ! Verbose error reporting + IF ( SEDDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr('--- Driver settings (before reading driver ipt file): ---') + CALL printSettings( SettingsFlags, Settings ) + CALL WrScr(NewLine) + ENDIF + + + ! Copy the input file information from the CLSettings to the Settings. + ! At this point only one input file type can be set. + IF ( CLSettingsFlags%DvrIptFile ) THEN + SettingsFlags%DvrIptFile = CLSettingsFlags%DvrIptFile + Settings%DvrIptFileName = CLSettings%DvrIptFileName + ELSE + SettingsFlags%SEDIptFile = CLSettingsFlags%SEDIptFile + Settings%SEDIptFileName = CLSettings%SEDIptFileName + ENDIF + + ! If the filename given was not the SED input file (-ifw option), then it is treated + ! as the driver input file (flag should be set correctly by RetrieveArgs). So, we must + ! open this. + IF ( SettingsFlags%DvrIptFile ) THEN + + ! Read the driver input file + CALL ProcessComFile( CLSettings%DvrIptFileName, DvrFileInfo, ErrStat, ErrMsg ) + call CheckErr('') + + ! For diagnostic purposes, the following can be used to display the contents + ! of the DvrFileInfo data structure. + ! call Print_FileInfo_Struct( CU, DvrFileInfo ) ! CU is the screen -- different number on different systems. + + ! Parse the input file + CALL ParseDvrIptFile( CLSettings%DvrIptFileName, DvrFileInfo, SettingsFlags, Settings, ProgInfo, CaseTime, CaseData, ErrStat, ErrMsg ) + call CheckErr('') + + ! VVerbose error reporting + IF ( SEDDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr(NewLine//'--- Driver settings after reading the driver ipt file: ---') + CALL printSettings( SettingsFlags, Settings ) + CALL WrScr(NewLine) + ENDIF + + ! VVerbose error reporting + IF ( SEDDriver_Verbose >= 10_IntKi ) CALL WrScr('Updating driver settings with command line arguments') + + ELSE + + ! VVerbose error reporting + IF ( SEDDriver_Verbose >= 10_IntKi ) CALL WrScr('No driver input file used. Updating driver settings with command line arguments') + + ENDIF + + ! Since there were no settings picked up from the driver input file, we need to copy over all + ! the CLSettings into the regular Settings. The SettingsFlags%DvrIptFile is a flag indicating + ! if the driver input file read. + CALL UpdateSettingsWithCL( SettingsFlags, Settings, CLSettingsFlags, CLSettings, SettingsFlags%DvrIptFile, ErrStat, ErrMsg ) + call CheckErr('') + + ! Verbose error reporting + IF ( SEDDriver_Verbose >= 10_IntKi ) THEN + CALL WrScr(NewLine//'--- Driver settings after copying over CL settings: ---') + CALL printSettings( SettingsFlags, Settings ) + CALL WrScr(NewLine) + ENDIF + + + + !------------------------------------------ + ! Logic for timestep and total time for sim. + !------------------------------------------ + if ( SettingsFlags%TStart ) then + TStart = Settings%TStart + else + TStart = 0.0_DbKi + ! TODO: if using the input file, could start at the initial time given there (set the TStart with a "default" input option) + endif + + + TimeIntervalFound=.true. ! If specified or default value set + ! DT - timestep. If default was specified, then calculate default level. + if ( SettingsFlags%DTdefault ) then + ! Set a value to start with (something larger than any expected DT). + TimeIntervalFound=.false. + TimeInterval=1000.0_DbKi + ! Step through all lines to get smallest DT + do n=min(2,size(CaseTime)),size(CaseTime) ! Start at 2nd point (min to avoid stepping over end for single line files) + TimeInterval=min(TimeInterval, real(CaseTime(n)-CaseTime(n-1), DbKi)) + TimeIntervalFound=.true. + enddo + if (TimeIntervalFound) then + call WrScr('Using smallest DT from data file: '//trim(Num2LStr(TimeInterval))//' seconds.') + else + call WrScr('No time timesteps found in input displacement file. Using only one timestep.') + endif + endif + + + ! TMax and NumTSteps from input file or from the value specified (specified overrides) + if ( SettingsFlags%NumTimeStepsDefault ) then + TMax = CaseTime(size(CaseTime)) + NumTSteps = ceiling( TMax / TimeInterval ) + elseif ( SettingsFlags%NumTimeSteps ) then ! Override with number of timesteps + TMax = TimeInterval * Settings%NumTimeSteps + TStart + NumTSteps = Settings%NumTimeSteps + else + NumTSteps = 1_IntKi + TMax = TimeInterval * NumTSteps + endif + Settings%NumTimeSteps = NumTSteps + + + ! Routines called in initialization + !............................................................................................................................... + + InitInData%InputFile = Settings%SEDIptFileName + InitInData%RootName = Settings%OutRootName + + ! Initialize the module + CALL SED_Init( InitInData, u(1), p, x, xd, z, OtherState, y, misc, TimeInterval, InitOutData, ErrStat, ErrMsg ) + call CheckErr('After Init: '); + + ! Make sure don't use a High-speed brake with RK4 method + if (.not. EqualRealNos( maxval(abs(CaseData(2,:))), 0.0_ReKi)) then + if (p%IntMethod == Method_RK4) then ! only works with AB4 or ABM4 + ErrStat = ErrID_Fatal + ErrMsg = 'Simplified-ElastoDyn (SED) must use the AB4 or ABM4 integration method to implement high-speed shaft braking.' + call CheckErr('') + endif + endif + + do i = 2, NumInp + call SED_CopyInput(u(1),u(i),MESH_NEWCOPY, ErrStat, ErrMsg) + call CheckErr('CopyInput') + enddo + + ! Set the output file + call GetRoot(Settings%OutRootName,OutputFileRootName) + call Dvr_InitializeOutputFile(DvrOut, InitOutData, OutputFileRootName, ErrStat, ErrMsg) + call CheckErr('Setting output file'); + + ! Destroy initialization data + CALL SED_DestroyInitInput( InitInData, ErrStat, ErrMsg ) + CALL SED_DestroyInitOutput( InitOutData, ErrStat, ErrMsg ) + + n = 0 + if (Settings%WrVTK > 0_IntKi) then + call WrVTK_refMeshes(Settings, p, y, ErrStat,ErrMsg) + call CheckErr('After WrVTK_refMeshes: '); + endif + + + ! Routines called in loose coupling -- the glue code may implement this in various ways + !............................................................................................................................... + + + T = TStart + + ! Get values from table + do i = 1, NumInp-1 !u(NumInp) is overwritten in time-sim loop, so no need to init here + AeroTrq = InterpStp( real(T,ReKi), real(CaseTime(:),ReKi), CaseData(1,:), TmpIdx, size(CaseTime) ) + u(i)%HubPtLoad%Moment(1:3,1) = y%HubPtMotion%Orientation(1,1:3,1) * AeroTrq + u(i)%HSSBrTrqC = InterpStp( real(T,ReKi), real(CaseTime(:),ReKi), CaseData(2,:), TmpIdx, size(CaseTime) ) + u(i)%GenTrq = InterpStp( real(T,ReKi), real(CaseTime(:),ReKi), CaseData(3,:), TmpIdx, size(CaseTime) ) + u(i)%BlPitchCom = InterpStp( real(T,ReKi), real(CaseTime(:),ReKi), CaseData(4,:), TmpIdx, size(CaseTime) ) + u(i)%YawPosCom = InterpStp( real(T,ReKi), real(CaseTime(:),ReKi), CaseData(5,:), TmpIdx, size(CaseTime) ) + u(i)%YawRateCom = InterpStp( real(T,ReKi), real(CaseTime(:),ReKi), CaseData(6,:), TmpIdx, size(CaseTime) ) + uTimes(i) = TStart - real((i-1),R8Ki) * TimeInterval + enddo + + + + + ! Calculate outputs at TStart + CALL SED_CalcOutput( T, u(1), p, x, xd, z, OtherState, y, misc, ErrStat, ErrMsg ); + call CheckErr('After CalcOutput: '); + + if (Settings%WrVTK > 1_IntKi) then + call WrVTK_Meshes(Settings, p, y, n, ErrStat,ErrMsg) + call CheckErr('Time: '//trim(Num2LStr(T))//'After WrVTK_Meshes: '); + endif + + call Dvr_WriteOutputLine(TStart,DvrOut,"ES20.12E2",y) + + TmpIdx = 0_IntKi + DO n = 1,NumTSteps + ! Step the states back one step + do i = NumInp-1, 1, -1 + u( i+1) = u( i) + uTimes(i+1) = uTimes(i) + enddo + + uTimes(1) = n*TimeInterval+TStart + + ! InterpStpReal( T, Tary, Vary, TmpIdx, size) + AeroTrq = InterpStp( real(T+TimeInterval,ReKi), real(CaseTime(:),ReKi), CaseData(1,:), TmpIdx, size(CaseTime) ) + u(1)%HubPtLoad%Moment(1:3,1) = y%HubPtMotion%Orientation(1,1:3,1) * AeroTrq + u(1)%HSSBrTrqC = InterpStp( real(T+TimeInterval,ReKi), real(CaseTime(:),ReKi), CaseData(2,:), TmpIdx, size(CaseTime) ) + u(1)%GenTrq = InterpStp( real(T+TimeInterval,ReKi), real(CaseTime(:),ReKi), CaseData(3,:), TmpIdx, size(CaseTime) ) + u(1)%BlPitchCom = InterpStp( real(T+TimeInterval,ReKi), real(CaseTime(:),ReKi), CaseData(4,:), TmpIdx, size(CaseTime) ) + u(1)%YawPosCom = InterpStp( real(T+TimeInterval,ReKi), real(CaseTime(:),ReKi), CaseData(5,:), TmpIdx, size(CaseTime) ) + u(1)%YawRateCom = InterpStp( real(T+TimeInterval,ReKi), real(CaseTime(:),ReKi), CaseData(6,:), TmpIdx, size(CaseTime) ) + + ! There are no states to update in SED, but for completeness we add this. + ! Get state variables at next step: INPUT at step n, OUTPUT at step n + 1 + CALL SED_UpdateStates( uTimes(2), n, u, uTimes, p, x, xd, z, OtherState, misc, ErrStat, ErrMsg ); + call CheckErr(''); + + ! Calculate outputs at n + CALL SED_CalcOutput( uTimes(1), u(1), p, x, xd, z, OtherState, y, misc, ErrStat, ErrMsg ); + call CheckErr('After CalcOutput: '); + + if (Settings%WrVTK > 1_IntKi) then + call WrVTK_Meshes(Settings, p, y, n, ErrStat,ErrMsg) + call CheckErr('Time: '//trim(Num2LStr(uTimes(1)))//'After WrVTK_Meshes: '); + endif + + call Dvr_WriteOutputLine(uTimes(1),DvrOut,"ES20.12E2",y) + END DO + + + + + !............................................................................................................................... + ! Routine to terminate program execution + !............................................................................................................................... + if (DvrOut>0) close(DvrOut) + CALL SED_End( u(1), p, x, xd, z, OtherState, y, misc, ErrStat, ErrMsg ) + + IF ( ErrStat /= ErrID_None ) THEN + CALL WrScr( 'After End: '//ErrMsg ) + ELSE + call WrSCr( 'Simple-ElastoDyn completed' ) + END IF + +CONTAINS + subroutine CheckErr(Text) + character(*), intent(in) :: Text + IF ( ErrStat /= ErrID_None ) THEN ! Check if there was an error and do something about it if necessary + CALL WrScr( Text//trim(ErrMsg) ) + if ( ErrStat >= AbortErrLev ) call ProgEnd() + END IF + end subroutine CheckErr + subroutine ProgEnd() + ! Placeholder for moment + if (DvrOut>0) close(DvrOut) + CALL SED_End( u(1), p, x, xd, z, OtherState, y, misc, ErrStat, ErrMsg ) + Call ProgAbort('Fatal error encountered. Ending.') + end subroutine ProgEnd +END PROGRAM SED_Driver diff --git a/modules/simple-elastodyn/src/driver/SED_Driver_Subs.f90 b/modules/simple-elastodyn/src/driver/SED_Driver_Subs.f90 new file mode 100644 index 0000000000..554526d1ff --- /dev/null +++ b/modules/simple-elastodyn/src/driver/SED_Driver_Subs.f90 @@ -0,0 +1,850 @@ +!********************************************************************************************************************************** +! +! MODULE: SED_Driver_Subs - This module contains subroutines used by the SED Driver program +! +!********************************************************************************************************************************** +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2020 National Renewable Energy Laboratory +! +! This file is part of SED. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +!********************************************************************************************************************************** +MODULE SED_Driver_Subs + + USE NWTC_Library + USE SED_Driver_Types + IMPLICIT NONE + +! NOTE: This is loosely based on the InflowWind driver code. + + +CONTAINS +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!> Print out help information +SUBROUTINE DispHelpText() + ! Statement about usage + CALL WrScr("") + CALL WrScr(" Syntax: SED_Driver [options]") + CALL WrScr("") + CALL WrScr(" where: -- Name of driver input file to use") + CALL WrScr(" options: "//SWChar//"sed -- treat as name of SED input file") + CALL WrScr(" (no driver input file)") + CALL WrScr("") + CALL WrScr(" The following options will overwrite values in the driver input file:") + CALL WrScr(" "//SwChar//"DT[#] -- timestep ") + CALL WrScr(" "//SwChar//"TStart[#] -- start time ") + CALL WrScr(" "//SwChar//"TSteps[#] -- number of timesteps ") + CALL WrScr(" "//SwChar//"v -- verbose output ") + CALL WrScr(" "//SwChar//"vv -- very verbose output ") + CALL WrScr(" "//SwChar//"NonLinear -- only return non-linear portion of reaction force") + CALL WrScr(" "//SwChar//"help -- print this help menu and exit") + CALL WrScr("") + CALL WrScr(" Notes:") + CALL WrScr(" -- Options are not case sensitive.") + CALL WrScr("") +!FIXME: update this +END SUBROUTINE DispHelpText + + +subroutine InitSettingsFlags( ProgInfo, CLSettings, CLFlags ) + implicit none + ! Storing the arguments + type( ProgDesc ), intent(in ) :: ProgInfo + type( SEDDriver_Settings ), intent( out) :: CLSettings !< Command line arguments passed in + type( SEDDriver_Flags ), intent( out) :: CLFlags !< Flags indicating which command line arguments were specified + + ! Set some CLSettings to null/default values + CLSettings%DvrIptFileName = "" ! No input name name until set + CLSettings%SEDIptFileName = "" ! No SED input file name until set + CLSettings%NumTimeSteps = 0_IntKi + CLSettings%DT = 0.0_DbKi + CLSettings%TStart = 0.0_ReKi + CLSettings%ProgInfo = ProgInfo ! Driver info + + ! Set some CLFlags to null/default values + CLFlags%DvrIptFile = .FALSE. ! Driver input filename given as command line argument + CLFlags%SEDIptFile = .FALSE. ! SED input filename given as command line argument + CLFlags%TStart = .FALSE. ! specified time to start at + CLFlags%NumTimeSteps = .FALSE. ! specified a number of timesteps + CLFlags%NumTimeStepsDefault = .FALSE. ! specified 'DEFAULT' for number of timesteps + CLFlags%DT = .FALSE. ! specified a resolution in time + CLFlags%DTDefault = .FALSE. ! specified 'DEFAULT' for resolution in time + CLFlags%Verbose = .FALSE. ! Turn on verbose error reporting? + CLFlags%VVerbose = .FALSE. ! Turn on very verbose error reporting? + +end subroutine InitSettingsFlags + +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!> This subroutine retrieves the command line arguments and passes them to the +!! SED_driver_subs::parsearg routine for processing. +SUBROUTINE RetrieveArgs( CLSettings, CLFlags, ErrStat, ErrMsg ) + ! Storing the arguments + type( SEDDriver_Flags ), intent( out) :: CLFlags !< Flags indicating which command line arguments were specified + type( SEDDriver_Settings ), intent( out) :: CLSettings !< Command line arguments passed in + integer(IntKi), intent( out) :: ErrStat + CHARACTER(*), intent( out) :: ErrMsg + + ! Local variable + integer(IntKi) :: i !< Generic counter + character(1024) :: Arg !< argument given + character(1024) :: ArgUC !< Upper case argument to check + integer(IntKi) :: NumInputArgs !< Number of argements passed in from command line + logical :: sedFlag !< The -sed flag was set + character(1024) :: FileName !< Filename from the command line. + logical :: FileNameGiven !< Flag indicating if a filename was given. + integer(IntKi) :: ErrStatTmp !< Temporary error status (for calls) + character(1024) :: ErrMsgTmp !< Temporary error message (for calls) + + ! initialize some things + CLFlags%DvrIptFile = .FALSE. + ErrStat = ErrID_None + ErrStatTmp = ErrID_None + ErrMsg = '' + ErrMsgTmp = '' + sedFlag = .FALSE. + FileNameGiven = .FALSE. + FileName = '' + + ! Check how many arguments are passed in + NumInputArgs = COMMAND_ARGUMENT_COUNT() + + ! exit if we don't have enough + IF (NumInputArgs == 0) THEN + CALL SetErrStat(ErrID_Fatal," Insufficient Arguments. Use option "//SwChar//"help for help menu.", & + ErrStat,ErrMsg,'RetrieveArgs') + RETURN + ENDIF + + + ! Loop through all the arguments, and store them + DO i=1,NumInputArgs + ! get the ith argument + CALL get_command_argument(i, Arg) + ArgUC = Arg + + ! convert to uppercase + CALL Conv2UC( ArgUC ) + + ! Check to see if it is a control parameter or the filename + IF ( INDEX( SwChar, ArgUC(1:1) ) > 0 ) THEN + + ! check to see if we asked for help + IF ( ArgUC(2:5) == "HELP" ) THEN + CALL DispHelpText() + CALL ProgExit(0) + ENDIF + + + ! Check the argument and put it where it belongs + ! chop the SwChar off before passing the argument + CALL ParseArg( CLSettings, CLFlags, ArgUC(2:), Arg(2:), sedFlag, ErrStatTmp, ErrMsgTmp ) + CALL SetErrStat(ErrStatTmp,ErrMsgTmp,ErrStat,ErrMsg,'RetrieveArgs') + IF (ErrStat>AbortErrLev) RETURN + + ELSE + + ! since there is no switch character, assume it is the filename, unless we already set one + IF ( FileNameGiven ) THEN + CALL SetErrStat(ErrID_Fatal," Multiple driver input filenames given: "//TRIM(FileName)//", "//TRIM(Arg), & + ErrStat,ErrMsg,'RetrieveArgs') + RETURN + ELSE + FileName = TRIM(Arg) + FileNameGiven = .TRUE. + ENDIF + + ENDIF + END DO + + + ! Was a filename given? + IF ( .NOT. FileNameGiven ) THEN + CALL SetErrStat( ErrID_Fatal, " No filename given.", ErrStat, ErrMsg, 'RetrieveArgs' ) + RETURN + ENDIF + + ! Was the -sed flag set? If so, the filename is the SED input file. Otherwise + ! it is the driver input file. + IF ( sedFlag ) THEN + CLSettings%SEDIptFileName = TRIM(FileName) + CLFlags%SEDIptFile = .TRUE. + call GetRoot( CLSettings%SEDIptFileName, CLSettings%OutRootName ) + CLFlags%OutRootName = .TRUE. + ELSE + CLSettings%DvrIptFileName = TRIM(FileName) + CLFlags%DvrIptFile = .TRUE. + ENDIF + + + + !------------------------------------------------------------------------------- + !------------------------------------------------------------------------------- + CONTAINS + + + !------------------------------------------------------------------------------- + !> Convert a string to a real number + FUNCTION StringToReal( StringIn, ErrStat ) + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT(IN ) :: StringIn + + REAL(ReKi) :: StringToReal + INTEGER(IntKi) :: ErrStatTmp ! Temporary variable to hold the error status + + read( StringIn, *, iostat=ErrStatTmp) StringToReal + + ! If that isn't a number, only warn since we can continue by skipping this value + IF ( ErrStatTmp .ne. 0 ) ErrStat = ErrID_Warn + + END FUNCTION StringToReal + + + + !------------------------------------------------------------------------------- + SUBROUTINE ParseArg( CLSettings, CLFlags, ThisArgUC, ThisArg, sedFlagSet, ErrStat, ErrMsg ) + !-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-! + ! Parse and store the input argument ! + !-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-! + + USE NWTC_Library + USE SED_Driver_Types + USE SED_Types + + IMPLICIT NONE + + ! Storing the arguments + TYPE( SEDDriver_Flags ), INTENT(INOUT) :: CLFlags ! Flags indicating which arguments were specified + TYPE( SEDDriver_Settings ), INTENT(INOUT) :: CLSettings ! Arguments passed in + + CHARACTER(*), INTENT(IN ) :: ThisArgUC ! The current argument (upper case for testing) + CHARACTER(*), INTENT(IN ) :: ThisArg ! The current argument (as passed in for error messages) + LOGICAL, INTENT(INOUT) :: sedFlagSet ! Was the -sed flag given? + + ! Error Handling + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + + + ! local variables + INTEGER(IntKi) :: Delim1 ! where the [ is + INTEGER(IntKi) :: Delim2 ! where the ] is + INTEGER(IntKi) :: DelimSep ! where the : is + REAL(ReKi) :: TempReal ! temp variable to hold a real + + INTEGER(IntKi) :: ErrStatTmp ! Temporary error status for calls + + + + ! Initialize some things + ErrStat = ErrID_None + ErrStatTmp = ErrID_None + ErrMsg = '' + + ! Get the delimiters -- returns 0 if there isn't one + Delim1 = INDEX(ThisArgUC,'[') + Delim2 = INDEX(ThisArgUC,']') + DelimSep = INDEX(ThisArgUC,':') + + + ! check that if there is an opening bracket, then there is a closing one + IF ( (Delim1 > 0_IntKi ) .and. (Delim2 < Delim1) ) THEN + CALL SetErrStat(ErrID_Warn," Syntax error in option: '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArg') + RETURN + ENDIF + + ! check that if there is a colon, then there are brackets + IF ( (DelimSep > 0_IntKi) .and. (Delim1 == 0_IntKi) ) THEN + CALL SetErrStat(ErrID_Warn," Syntax error in option: '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArg') + RETURN + ENDIF + + + ! If no delimeters were given, than this option is simply a flag + IF ( Delim1 == 0_IntKi ) THEN + ! check to see if the filename is the name of the SED input file + IF ( ThisArgUC(1:4) == "SED" ) THEN + sedFlagSet = .TRUE. ! More logic in the routine that calls this one to set things. + RETURN + ELSEIF ( ThisArgUC(1:2) == "VV" ) THEN + CLFlags%VVerbose = .TRUE. + RETURN + ELSEIF ( ThisArgUC(1:1) == "V" ) THEN + CLFlags%Verbose = .TRUE. + RETURN + ELSE + CALL SetErrStat( ErrID_Warn," Unrecognized option '"//SwChar//TRIM(ThisArg)//"'. Ignoring. Use option "//SwChar//"help for list of options.", & + ErrStat,ErrMsg,'ParseArg') + ENDIF + + ENDIF + + + ! "DT[#]" + IF( ThisArgUC(1:Delim1) == "DT[" ) THEN + TempReal = StringToReal( ThisArgUC(Delim1+1:Delim2-1), ErrStat ) + IF ( ErrStat == ErrID_None ) THEN + CLFlags%Dt = .TRUE. + CLSettings%DT = abs(TempReal) + ELSE + CLFlags%Dt = .FALSE. + IF ( ErrStat == ErrID_Warn ) THEN + CALL SetErrStat(ErrStatTmp," Invalid number in option '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArgs') + ELSE + CALL SetErrStat( ErrID_Fatal," Something failed in parsing option '"//SwChar//TRIM(ThisArg)//"'.", & + ErrStat, ErrMsg, 'ParseArg') + ENDIF + RETURN + ENDIF + + + ! "TSTEPS[#]" + ELSEIF( ThisArgUC(1:Delim1) == "TSTEPS[" ) THEN + TempReal = StringToReal( ThisArgUC(Delim1+1:Delim2-1), ErrStat ) + IF ( ErrStat == ErrID_None ) THEN + CLFlags%NumTimeSteps = .TRUE. + CLSettings%NumTimeSteps = nint(abs(TempReal)) + ELSE + CLFlags%NumTimeSteps = .FALSE. + CLSettings%NumTimeSteps = 1_IntKi + IF ( ErrStat == ErrID_Warn ) THEN + CALL SetErrStat(ErrStatTmp," Invalid number in option '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArgs') + ELSE + CALL SetErrStat( ErrID_Fatal," Something failed in parsing option '"//SwChar//TRIM(ThisArg)//"'.", & + ErrStat, ErrMsg, 'ParseArg') + ENDIF + RETURN + ENDIF + + + + ! "TSTART[#]" + ELSEIF( ThisArgUC(1:Delim1) == "TSTART[" ) THEN + TempReal = StringToReal( ThisArgUC(Delim1+1:Delim2-1), ErrStat ) + IF ( ErrStat == ErrID_None ) THEN + CLFlags%TStart = .TRUE. + CLSettings%TStart = abs(TempReal) + ELSE + CLFlags%TStart = .FALSE. + IF ( ErrStat == ErrID_Warn ) THEN + CALL SetErrStat(ErrStatTmp," Invalid number in option '"//SwChar//TRIM(ThisArg)//"'. Ignoring.", & + ErrStat,ErrMsg,'ParseArgs') + ELSE + CALL SetErrStat( ErrID_Fatal," Something failed in parsing option '"//SwChar//TRIM(ThisArg)//"'.", & + ErrStat, ErrMsg, 'ParseArg') + ENDIF + RETURN + ENDIF +!FIXME: add in the other inputs here. + + ELSE + ErrMsg = " Unrecognized option: '"//SwChar//TRIM(ThisArg)//"'. Ignoring. Use option "//SwChar//"help for list of options." + ErrStat = ErrID_Warn + ENDIF + + END SUBROUTINE ParseArg + !------------------------------------------------------------------------------- + +END SUBROUTINE RetrieveArgs + + +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +!> This subroutine reads the driver input file and sets up the flags and settings +!! for the driver code. Any settings from the command line options will override +!! this. +SUBROUTINE ParseDvrIptFile( DvrFileName, DvrFileInfo, DvrFlags, DvrSettings, ProgInfo, CaseTime, CaseData, ErrStat, ErrMsg ) + + CHARACTER(1024), INTENT(IN ) :: DvrFileName + type(FileInfoType), INTENT(IN ) :: DvrFileInfo ! Input file stored in FileInfoType structure + TYPE(SEDDriver_Flags), INTENT(INOUT) :: DvrFlags + TYPE(SEDDriver_Settings), INTENT(INOUT) :: DvrSettings + TYPE(ProgDesc), INTENT(IN ) :: ProgInfo + real(DbKi), allocatable, intent( out) :: CaseTime(:) + real(ReKi), allocatable, intent( out) :: CaseData(:,:) + INTEGER(IntKi), INTENT( OUT) :: ErrStat ! returns a non-zero value when an error occurs + CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None + + ! Local variables + INTEGER(IntKi) :: CurLine ! Current line in parsing + INTEGER(IntKi) :: TabLines ! Number of lines in the table + integer(IntKi) :: i !< generic loop counter + real(DbKi) :: TmpDb7(7) !< temporary real array + CHARACTER(1024) :: RootName ! Root name of AeroDisk driver input file + + ! Input file echoing + LOGICAL :: EchoFileContents ! Do we echo the driver file out or not? + INTEGER(IntKi) :: UnEc ! The local unit number for this module's echo file + CHARACTER(1024) :: EchoFileName ! Name of SED driver echo file + + ! Time steps + CHARACTER(1024) :: InputChr ! Character string for timesteps and input file names (to handle DEFAULT or NONE value) + + ! Local error handling + INTEGER(IntKi) :: ios !< I/O status + INTEGER(IntKi) :: ErrStatTmp !< Temporary error status for calls + CHARACTER(1024) :: ErrMsgTmp !< Temporary error messages for calls + + + ! Initialize the echo file unit to -1 which is the default to prevent echoing, we will alter this based on user input + UnEc = -1 + + call GetRoot( DvrFileName, RootName ) + + !====== General ==================================================================================== + CurLine = 4 ! Skip the first three lines as they are known to be header lines and separators + call ParseVar( DvrFileInfo, CurLine, 'Echo', EchoFileContents, ErrStatTmp, ErrMsgTmp ) + if (Failed()) return; + + if ( EchoFileContents ) then + CALL OpenEcho ( UnEc, TRIM(RootName)//'.ech', ErrStatTmp, ErrMsgTmp ) + if (Failed()) return; + WRITE(UnEc, '(A)') 'Echo file for AeroDisk driver input file: '//trim(DvrFileName) + ! Write the first three lines into the echo file + WRITE(UnEc, '(A)') DvrFileInfo%Lines(1) + WRITE(UnEc, '(A)') DvrFileInfo%Lines(2) + WRITE(UnEc, '(A)') DvrFileInfo%Lines(3) + + CurLine = 4 + call ParseVar( DvrFileInfo, CurLine, 'Echo', EchoFileContents, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + endif + + !====== Primary file and rootname ================================================================== + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! AeroDisk input file + call ParseVar( DvrFileInfo, CurLine, "SEDIptFile", DvrSettings%SEDIptFileName, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%SEDIptFile = .TRUE. + + ! AeroDisk output root name + call ParseVar( DvrFileInfo, CurLine, "OutRootName", DvrSettings%OutRootName, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%OutRootName = .TRUE. + + + !====== Output ====================================================================================== + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! WrVTK -- writing VTK visualization files must be [0: none, 1: init only, 2: animation] + call ParseVar( DvrFileInfo, CurLine, "WrVTK", DvrSettings%WrVTK, ErrStatTmp, ErrMsgTmp, UnEc ) + + + !====== Case analysis =============================================================================== + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) ! Write section break to echo + CurLine = CurLine + 1 + + ! TStart -- start time + call ParseVar( DvrFileInfo, CurLine, "TStart", DvrSettings%TStart, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + DvrFlags%TStart = .TRUE. + + + ! DT -- Timestep size for the driver to take (or DEFAULT for what the file contains) + call ParseVar( DvrFileInfo, CurLine, "DT", InputChr, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + + ! Check if we asked for the DEFAULT (use what is in the file) + CALL Conv2UC( InputChr ) + IF ( TRIM(InputChr) == 'DEFAULT' ) THEN ! we asked for the default value + DvrFlags%DT = .TRUE. + DvrFlags%DTDefault = .TRUE. ! This flag tells us to use the inflow wind file values + ELSE + ! We probably have a number if it isn't 'DEFAULT', so do an internal read and check to + ! make sure that it was appropriately interpretted. + READ (InputChr,*,IOSTAT=IOS) DvrSettings%DT + IF ( IOS /= 0 ) THEN ! problem in the read, so parse the error. + CALL CheckIOS ( IOS, '', 'DT',NumType, ErrStatTmp, ErrMsgTmp ) + if (Failed()) return + ELSE ! Was ok, so set the flags + DvrFlags%DT = .TRUE. + DvrFlags%DTDefault = .FALSE. + ENDIF + ENDIF + + + ! Number of timesteps + call ParseVar( DvrFileInfo, CurLine, "NumTimeSteps", InputChr, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return + + ! Check if we asked for the DEFAULT (use what is in the file) + CALL Conv2UC( InputChr ) + IF ( TRIM(InputChr) == 'DEFAULT' ) THEN ! we asked for the default value + DvrFlags%NumTimeSteps = .FALSE. + DvrFlags%NumTimeStepsDefault = .TRUE. ! This flag tells us to use the inflow wind file values + ELSE + ! We probably have a number if it isn't 'DEFAULT', so do an internal read and check to + ! make sure that it was appropriately interpretted. + READ (InputChr,*,IOSTAT=IOS) DvrSettings%NumTimeSteps + IF ( IOS /= 0 ) THEN ! problem in the read, so parse the error. + CALL CheckIOS ( IOS, '', 'NumTimeSteps',NumType, ErrStatTmp, ErrMsgTmp ) + if (Failed()) return + ELSE ! Was ok, so set the flags + DvrFlags%NumTimeSteps = .TRUE. + DvrFlags%NumTimeStepsDefault = .FALSE. + ENDIF + ENDIF + + + ! Column headers + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) + CurLine = CurLine + 1 + if ( EchoFileContents ) WRITE(UnEc, '(A)') DvrFileInfo%Lines(CurLine) + CurLine = CurLine + 1 + + + ! Last line of table is assumed to be last line in file (or included file) + TabLines = DvrFileInfo%NumLines - CurLine + 1 + call AllocAry( CaseTime, TabLines, 'CaseTime', ErrStatTmp, ErrMsgTmp ); if (Failed()) return; + call AllocAry( CaseData, 6, TabLines, 'CaseData', ErrStatTmp, ErrMsgTmp ); if (Failed()) return; + do i=1,Tablines + call ParseAry ( DvrFileInfo, CurLine, 'Coordinates', TmpDb7, 7, ErrStatTmp, ErrMsgTmp, UnEc ) + if (Failed()) return; + ! Set Time_(s) + CaseTime(i) = TmpDb7(1) + ! Set AerTrq_(N-m) HSSBrTrqC_(N-m) GenTrq_(N-m) + CaseData(1,i) = real(TmpDb7(2),ReKi) + CaseData(2,i) = abs(real(TmpDb7(3),ReKi)) ! HSSBrTrqC should be positive vlaued + CaseData(3,i) = real(TmpDb7(4),ReKi) + ! Set BlPitchCom (deg -> rad) + CaseData(4,i) = real(TmpDb7(5),ReKi) * D2R + ! Set Yaw (deg -> rad) + CaseData(5,i) = real(TmpDb7(6),ReKi) * D2R + ! Set YawRate (deg/s -> rad/s) + CaseData(6,i) = real(TmpDb7(7),ReKi) * D2R + enddo + + + ! Close the echo and input file + CALL CleanupEchoFile( EchoFileContents, UnEc ) + + +CONTAINS + + !> Set error status, close stuff, and return + logical function Failed() + CALL SetErrStat(ErrStatTmp,ErrMsgTmp,ErrStat,ErrMsg,'ParseDvrIptFile') + if (ErrStat >= AbortErrLev) then + CALL CleanupEchoFile( EchoFileContents, UnEc ) + endif + Failed = ErrStat >= AbortErrLev + end function Failed + + !> Clean up the module echo file + subroutine CleanupEchoFile( EchoFlag, UnEcho) + logical, intent(in ) :: EchoFlag ! local version of echo flag + integer(IntKi), intent(in ) :: UnEcho ! echo unit number + + ! Close this module's echo file + if ( EchoFlag ) then + close(UnEcho) + endif + END SUBROUTINE CleanupEchoFile + +END SUBROUTINE ParseDvrIptFile + + +!> This subroutine copies an command line (CL) settings over to the program settings. Warnings are +!! issued if anything is changed from what the driver input file requested. +SUBROUTINE UpdateSettingsWithCL( DvrFlags, DvrSettings, CLFlags, CLSettings, DVRIPT, ErrStat, ErrMsg ) + + TYPE(SEDDriver_Flags), INTENT(INOUT) :: DvrFlags + TYPE(SEDDriver_Settings), INTENT(INOUT) :: DvrSettings + TYPE(SEDDriver_Flags), INTENT(IN ) :: CLFlags + TYPE(SEDDriver_Settings), INTENT(IN ) :: CLSettings + LOGICAL, INTENT(IN ) :: DVRIPT + INTEGER(IntKi), INTENT( OUT) :: ErrStat + CHARACTER(*), INTENT( OUT) :: ErrMsg + + + ! Local variables + INTEGER(IntKi) :: ErrStatTmp !< Temporary error status for calls + CHARACTER(1024) :: ErrMsgTmp !< Temporary error status for calls + LOGICAL :: WindGridModify !< Did we modify any of the WindGrid related settings? + character(*), parameter :: RoutineName = 'UpdateSettingsWithCL' + + ! Initialization + WindGridModify = .FALSE. + + ! Initialize the error handling + ErrStat = ErrID_None + ErrMsg = '' + ErrStatTmp = ErrID_None + ErrMsgTmp = '' + + + !-------------------------------------------- + ! Did we change any time information? + !-------------------------------------------- + + ! Check TStart + IF ( CLFlags%TStart ) THEN + IF ( DvrFlags%TStart .AND. ( .NOT. EqualRealNos(DvrSettings%TStart, CLSettings%TStart) ) ) THEN + CALL SetErrStat( ErrID_Warn, ' Overriding driver input value for TStart with '//TRIM(Num2LStr(CLSettings%TStart))//'.', & + ErrStat,ErrMsg,RoutineName) + ELSE + DvrFlags%TStart = .TRUE. + ENDIF + DvrSettings%TStart = CLSettings%TStart + ENDIF + + ! Check DT + IF ( CLFlags%DT ) THEN + IF ( DvrFlags%DT .AND. ( .NOT. EqualRealNos(DvrSettings%DT, CLSettings%DT) ) ) THEN + CALL SetErrStat( ErrID_Warn, ' Overriding driver input value for DT with '//TRIM(Num2LStr(CLSettings%DT))//'.', & + ErrStat,ErrMsg,RoutineName) + ELSE + DvrFlags%DT = .TRUE. + ENDIF + DvrSettings%DT = CLSettings%DT + DvrFlags%DTDefault = .FALSE. + ENDIF + + ! Check NumTimeSteps + IF ( CLFlags%NumTimeSteps ) THEN + IF ( DvrFlags%NumTimeSteps .AND. ( DvrSettings%NumTimeSteps /= CLSettings%NumTimeSteps ) ) THEN + CALL SetErrStat( ErrID_Warn, ' Overriding driver input value for NumTimeSteps with '// & + TRIM(Num2LStr(CLSettings%NumTimeSteps))//'.',& + ErrStat,ErrMsg,RoutineName) + ELSE + DvrFlags%NumTimeSteps = .TRUE. + ENDIF + DvrSettings%NumTimeSteps = CLSettings%NumTimeSteps + DvrFlags%NumTimeStepsDefault = .FALSE. + ENDIF + + ! Make sure there is at least one timestep + DvrSettings%NumTimeSteps = MAX(DvrSettings%NumTimeSteps,1_IntKi) + + + !-------------------------------------------- + ! If there was no driver input file, we need to set a few things. + !-------------------------------------------- + + IF ( .NOT. DVRIPT ) THEN + + ! Do we need to set the NumTimeStepsDefault flag? + IF ( .NOT. DvrFlags%NumTimeSteps ) THEN + DvrFlags%NumTimeStepsDefault = .TRUE. + CALL SetErrStat( ErrID_Info,' The number of timesteps is not specified. Defaulting to what is in the input series file.', & + ErrStat,ErrMsg,RoutineName) + ENDIF + ENDIF + + +!FIXME: remove this after parsing rest of input file. + ! If no DT value has been set (DEFAULT requested), we need to set a default to pass into SED + IF ( .NOT. DvrFlags%DT ) THEN + DvrSettings%DT = 0.025_DbKi ! This value gets passed into the SED_Init routine, so something must be set. + ENDIF + + +END SUBROUTINE UpdateSettingsWithCL + + + +!> This routine exists only to support the development of the module. It will not be needed after the module is complete. +SUBROUTINE printSettings( DvrFlags, DvrSettings ) + ! The arguments + TYPE( SEDDriver_Flags ), INTENT(IN ) :: DvrFlags !< Flags indicating which settings were set + TYPE( SEDDriver_Settings ), INTENT(IN ) :: DvrSettings !< Stored settings + + CALL WrsCr(TRIM(GetNVD(DvrSettings%ProgInfo))) + CALL WrScr(' DvrIptFile: '//FLAG(DvrFlags%DvrIptFile)// ' '//TRIM(DvrSettings%DvrIptFileName)) + CALL WrScr(' SEDIptFile: '//FLAG(DvrFlags%SEDIptFile)// ' '//TRIM(DvrSettings%SEDIptFileName)) + CALL WrScr(' TStart: '//FLAG(DvrFlags%TStart)// ' '//TRIM(Num2LStr(DvrSettings%TStart))) + IF ( DvrFlags%DTDefault) THEN + CALL WrScr(' DT: '//FLAG(DvrFlags%DT)// ' DEFAULT') + ELSE + CALL WrScr(' DT: '//FLAG(DvrFlags%DT)// ' '//TRIM(Num2LStr(DvrSettings%DT))) + ENDIF + IF ( DvrFlags%NumTimeStepsDefault) THEN + CALL WrScr(' NumTimeSteps: '//FLAG(DvrFlags%NumTimeSteps)// ' DEFAULT') + ELSE + CALL WrScr(' NumTimeSteps: '//FLAG(DvrFlags%NumTimeSteps)// ' '//TRIM(Num2LStr(DvrSettings%NumTimeSteps))) + ENDIF + CALL WrScr(' Verbose: '//FLAG(DvrFlags%Verbose)) + CALL WrScr(' VVerbose: '//FLAG(DvrFlags%VVerbose)) + RETURN +END SUBROUTINE printSettings + + +!> This routine exists only to support the development of the module. It will not be kept after the module is complete. +!! This routine takes a flag setting (LOGICAL) and exports either 'T' or '-' for T/F (respectively) +FUNCTION FLAG(flagval) + LOGICAL, INTENT(IN ) :: flagval !< Value of the flag + CHARACTER(1) :: FLAG !< character interpretation (for prettiness when printing) + IF ( flagval ) THEN + FLAG = 'T' + ELSE + FLAG = '-' + ENDIF + RETURN +END FUNCTION FLAG + + +SUBROUTINE Dvr_InitializeOutputFile(OutUnit,IntOutput,RootName,ErrStat,ErrMsg) + integer(IntKi), intent( out):: OutUnit + type(SED_InitOutputType), intent(in ):: IntOutput ! Output for initialization routine + integer(IntKi), intent( out):: ErrStat ! Error status of the operation + character(*), intent( out):: ErrMsg ! Error message if ErrStat /= ErrID_None + character(*), intent(in ):: RootName + integer(IntKi) :: i + integer(IntKi) :: numOuts + integer(IntKi) :: ErrStat2 ! Temporary Error status + character(ErrMsgLen) :: ErrMsg2 ! Temporary Error message + character(*), parameter :: RoutineName = 'Dvr_InitializeOutputFile' + character(ChanLen) :: TmpChar + + ErrStat = ErrID_none + ErrMsg = "" + + CALL GetNewUnit(OutUnit,ErrStat2,ErrMsg2) + CALL OpenFOutFile ( OutUnit, trim(RootName)//'.out', ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (ErrStat >= AbortErrLev) return + + write (OutUnit,'(/,A)') 'Predictions were generated on '//CurDate()//' at '//CurTime()//' using '//trim(GetNVD(IntOutput%Ver)) + write (OutUnit,'()' ) !print a blank line + + numOuts = size(IntOutput%WriteOutputHdr) + !...................................................... + ! Write the names of the output parameters on one line: + !...................................................... + + write (OutUnit,'()') + write (OutUnit,'()') + write (OutUnit,'()') + + TmpChar = 'Time' + call WrFileNR ( OutUnit, TmpChar ) + + do i=1,NumOuts + call WrFileNR ( OutUnit, tab//IntOutput%WriteOutputHdr(i) ) + end do ! i + + write (OutUnit,'()') + + !...................................................... + ! Write the units of the output parameters on one line: + !...................................................... + + TmpChar = '(s)' + call WrFileNR ( OutUnit, TmpChar ) + + do i=1,NumOuts + call WrFileNR ( Outunit, tab//IntOutput%WriteOutputUnt(i) ) + end do ! i + + write (OutUnit,'()') + + +END SUBROUTINE Dvr_InitializeOutputFile + +!---------------------------------------------------------------------------------------------------------------------------------- +SUBROUTINE Dvr_WriteOutputLine(t,OutUnit, OutFmt, Output) + real(DbKi) , intent(in ) :: t ! simulation time (s) + integer(IntKi) , intent(in ) :: OutUnit ! Status of error message + character(*) , intent(in ) :: OutFmt + type(SED_OutputType), intent(in ) :: Output + integer(IntKi) :: errStat ! Status of error message (we're going to ignore errors in writing to the file) + character(ErrMsgLen) :: errMsg ! Error message if ErrStat /= ErrID_None + character(200) :: frmt ! A string to hold a format specifier + character(15) :: tmpStr ! temporary string to print the time output as text + + frmt = '"'//tab//'"'//trim(OutFmt) ! format for array elements from individual modules + + ! time + write( tmpStr, '(F15.6)' ) t + call WrFileNR( OutUnit, tmpStr ) + call WrNumAryFileNR ( OutUnit, Output%WriteOutput, frmt, errStat, errMsg ) + + ! write a new line (advance to the next line) + write (OutUnit,'()') +end subroutine Dvr_WriteOutputLine + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Write VTK reference meshes and setup directory if needed. +subroutine WrVTK_refMeshes(DvrSettings, p, y, ErrStat,ErrMsg) + type( SEDDriver_Settings ), intent(inout) :: DvrSettings !< Stored settings + type(SED_ParameterType), intent(in ) :: p !< System parameters + type(SED_OutputType), intent(in ) :: y !< System outputs + integer(IntKi), intent( out) :: ErrStat !< error status + character(ErrMsgLen), intent( out) :: ErrMsg !< error message + integer(IntKi) :: i + character(1024) :: TmpFileName + + ! get the name of the output directory for vtk files (in a subdirectory called "vtk" of the output directory), and + ! create the VTK directory if it does not exist + call GetPath ( DvrSettings%OutRootName, DvrSettings%VTK_OutFileRoot, TmpFileName ) ! the returned VTK_OutFileRoot includes a file separator character at the end + DvrSettings%VTK_OutFileRoot = trim(DvrSettings%VTK_OutFileRoot) // 'vtk-SED' + call MKDIR( trim(DvrSettings%VTK_OutFileRoot) ) + DvrSettings%VTK_OutFileRoot = trim( DvrSettings%VTK_OutFileRoot ) // PathSep // trim(TmpFileName) + + ! calculate the number of digits in 'y_FAST%NOutSteps' (Maximum number of output steps to be written) + ! this will be used to pad the write-out step in the VTK filename with zeros in calls to MeshWrVTK() + DvrSettings%VTK_tWidth = CEILING( log10( real(DvrSettings%NumTimeSteps+1,ReKi) ) ) + 1 + + ! Write reference meshes + call MeshWrVTKreference((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%PlatformPtMesh, trim(DvrSettings%VTK_OutFileRoot)//'.PlatformPtMesh', ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + call MeshWrVTKreference((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%TowerLn2Mesh, trim(DvrSettings%VTK_OutFileRoot)//'.TowerLn2Mesh', ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + call MeshWrVTKreference((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%NacelleMotion, trim(DvrSettings%VTK_OutFileRoot)//'.NacelleMotion', ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + call MeshWrVTKreference((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%HubPtMotion, trim(DvrSettings%VTK_OutFileRoot)//'.HubPtMotion', ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + do i=1,p%NumBl + call MeshWrVTKreference((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%BladeRootMotion(i), trim(DvrSettings%VTK_OutFileRoot)//'.BladeRootMotion'//trim(Num2LStr(i)), ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + enddo +end subroutine WrVTK_refMeshes + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Write VTK reference meshes and setup directory if needed. +subroutine WrVTK_Meshes(DvrSettings, p, y, N_Global, ErrStat,ErrMsg) + type( SEDDriver_Settings ), intent(inout) :: DvrSettings !< Stored settings + type(SED_ParameterType), intent(in ) :: p !< System parameters + type(SED_OutputType), intent(in ) :: y !< System outputs + integer(IntKi), intent(in ) :: N_Global !< System timestep number + integer(IntKi), intent( out) :: ErrStat !< error status + character(ErrMsgLen), intent( out) :: ErrMsg !< error message + integer(IntKi) :: i + character(1024) :: TmpFileName + + ! Write meshes + call MeshWrVTK((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%PlatformPtMesh, trim(DvrSettings%VTK_OutFileRoot)//'.PlatformPtMesh', N_Global, .true., ErrStat, ErrMsg, DvrSettings%VTK_tWidth) + if (ErrStat >= AbortErrLev) return + call MeshWrVTK((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%TowerLn2Mesh, trim(DvrSettings%VTK_OutFileRoot)//'.TowerLn2Mesh', N_Global, .true., ErrStat, ErrMsg, DvrSettings%VTK_tWidth) + if (ErrStat >= AbortErrLev) return + call MeshWrVTK((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%NacelleMotion, trim(DvrSettings%VTK_OutFileRoot)//'.NacelleMotion', N_Global, .true., ErrStat, ErrMsg, DvrSettings%VTK_tWidth) + if (ErrStat >= AbortErrLev) return + call MeshWrVTK((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%HubPtMotion, trim(DvrSettings%VTK_OutFileRoot)//'.HubPtMotion', N_Global, .true., ErrStat, ErrMsg, DvrSettings%VTK_tWidth) + if (ErrStat >= AbortErrLev) return + do i=1,p%NumBl + call MeshWrVTK((/0.0_SiKi,0.0_SiKi,0.0_SiKi/), y%BladeRootMotion(i), trim(DvrSettings%VTK_OutFileRoot)//'.BladeRootMotion'//trim(Num2LStr(i)), N_Global, .true., ErrStat, ErrMsg, DvrSettings%VTK_tWidth) + if (ErrStat >= AbortErrLev) return + enddo +end subroutine WrVTK_Meshes + +END MODULE SED_Driver_Subs diff --git a/modules/simple-elastodyn/src/driver/SED_Driver_Types.f90 b/modules/simple-elastodyn/src/driver/SED_Driver_Types.f90 new file mode 100644 index 0000000000..65b6ed4167 --- /dev/null +++ b/modules/simple-elastodyn/src/driver/SED_Driver_Types.f90 @@ -0,0 +1,73 @@ +!********************************************************************************************************************************** +! +! MODULE: SED_Driver_Types - This module contains types used by the SED Driver program to store arguments passed in +! +! The types listed here are used within the SED Driver program to store the settings. These settings are read in as +! command line arguments, then stored within these types. +! +!********************************************************************************************************************************** +! +!.................................................................................................................................. +! LICENSING +! Copyright (C) 2015 National Renewable Energy Laboratory +! +! This file is part of SED. +! +! SED is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as +! published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +! of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License along with SED. +! If not, see . +! +!********************************************************************************************************************************** +module SED_Driver_Types + + use NWTC_Library + use SED_Types + + implicit none + + !> This contains flags to note if the settings were made. This same data structure is + !! used both during the driver input file and the command line options. + !! + !! NOTE: The WindFileType is only set if it is given as a command line option. Otherwise + !! it is handled internally by InflowWInd. + !! + !! NOTE: The wind direction is specified by the SED input file. + type :: SEDDriver_Flags + logical :: DvrIptFile = .FALSE. !< Was an input file name given on the command line? + logical :: SEDIptFile = .FALSE. !< Was an SED input file requested? + logical :: OutRootName = .FALSE. !< Was an AeroDisk output rootname + logical :: TStart = .FALSE. !< specified a start time + logical :: NumTimeSteps = .FALSE. !< specified a number of timesteps to process + logical :: NumTimeStepsDefault = .FALSE. !< specified a 'DEFAULT' for number of timesteps to process + logical :: DT = .FALSE. !< specified a resolution in time + logical :: DTDefault = .FALSE. !< specified a 'DEFAULT' for the time resolution + logical :: Verbose = .FALSE. !< Verbose error reporting + logical :: VVerbose = .FALSE. !< Very Verbose error reporting + end type SEDDriver_Flags + + + ! This contains all the settings (possible passed in arguments). + type :: SEDDriver_Settings + character(1024) :: DvrIptFileName !< Driver input file name + character(1024) :: SEDIptFileName !< Filename of SED input file to read (if no driver input file) + character(1024) :: OutRootName !< Output root name + + real(DbKi) :: TStart !< Start time + integer(IntKi) :: NumTimeSteps !< Number of timesteps + real(DbKi) :: DT !< resolution of time + + type(ProgDesc) :: ProgInfo !< Program info + type(ProgDesc) :: SEDProgInfo !< Program info for SED + + integer(IntKi) :: WrVTK !< Write VTK outputs [0: none, 1: init only, 2: animation] + integer(IntKi) :: VTK_tWidth !< width of the time field in the VTK + character(1024) :: VTK_OutFileRoot !< Output root name for VTK + end type SEDDriver_Settings + + +end module SED_Driver_Types diff --git a/reg_tests/CMakeLists.txt b/reg_tests/CMakeLists.txt index 9d51bb2620..26cbe15910 100644 --- a/reg_tests/CMakeLists.txt +++ b/reg_tests/CMakeLists.txt @@ -74,6 +74,12 @@ set(CTEST_MOORDYN_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/moordyn/moordyn_driver # Set the SeaState executable configuration option and default set(CTEST_SEASTATE_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/seastate/seastate_driver" CACHE FILEPATH "Specify the SeaState driver executable to use in testing.") +# Set the AeroDisk executable configuration option and default +set(CTEST_AERODISK_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/aerodisk/aerodisk_driver" CACHE FILEPATH "Specify the AeroDisk driver executable to use in testing.") + +# Set the SED executable configuration option and default +set(CTEST_SED_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/simple-elastodyn/sed_driver" CACHE FILEPATH "Specify the SED driver executable to use in testing.") + # Set the testing tolerance set(CTEST_RTEST_RTOL "2" CACHE STRING "Sets the relative orders of magnitude to allow to deviate from the baseline.") diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index 01f0f91dbe..0ee94bda6b 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -267,6 +267,24 @@ function(py_md_regression TESTNAME LABEL) regression(${TEST_SCRIPT} ${MOORDYN_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") endfunction(py_md_regression) +# aerodisk +function(adsk_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeAerodiskRegressionCase.py") + set(AERODISK_EXECUTABLE "${CTEST_AERODISK_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/modules/aerodisk") + regression(${TEST_SCRIPT} ${AERODISK_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") +endfunction(adsk_regression) + +# simple-elastodyn +function(sed_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeSimpleElastodynRegressionCase.py") + set(SED_EXECUTABLE "${CTEST_SED_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/modules/simple-elastodyn") + regression(${TEST_SCRIPT} ${SED_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") +endfunction(sed_regression) + # # Python-based OpenFAST Library tests # function(py_openfast_library_regression TESTNAME LABEL) # set(test_module "${CMAKE_SOURCE_DIR}/modules/openfast-library/tests/test_openfast_library.py") @@ -280,45 +298,45 @@ endfunction(py_md_regression) #=============================================================================== # OpenFAST regression tests -of_regression("AWT_YFix_WSt" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AWT_WSt_StartUp_HighSpShutDown" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AWT_YFree_WSt" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AWT_YFree_WTurb" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AWT_WSt_StartUpShutDown" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AOC_WSt" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AOC_YFree_WTurb" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("AOC_YFix_WSt" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("UAE_Dnwind_YRamp_WSt" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("UAE_Upwind_Rigid_WRamp_PwrCurve" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("WP_VSP_WTurb_PitchFail" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("WP_VSP_ECD" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("WP_VSP_WTurb" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("SWRT_YFree_VS_EDG01" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("SWRT_YFree_VS_EDC01" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("SWRT_YFree_VS_WTurb" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("5MW_Land_DLL_WTurb" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("5MW_Land_DLL_WTurb_wNacDrag" "openfast;elastodyn;aerodyn15;servodyn") -of_regression("5MW_OC3Mnpl_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;subdyn;offshore") -of_regression("5MW_OC3Mnpl_DLL_WTurb_WavesIrr_Restart" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;subdyn;offshore;restart") -of_regression("5MW_OC3Trpd_DLL_WSt_WavesReg" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;subdyn;offshore") -of_regression("5MW_OC4Jckt_DLL_WTurb_WavesIrr_MGrowth" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;subdyn;offshore") -of_regression("5MW_ITIBarge_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;map;offshore") -of_regression("5MW_TLP_DLL_WTurb_WavesIrr_WavesMulti" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;map;offshore") -of_regression("5MW_OC3Spar_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;map;offshore") -of_regression("5MW_OC4Semi_WSt_WavesWN" "openfast;elastodyn;aerodyn15;servodyn;hydrodyn;moordyn;offshore") -of_regression("5MW_Land_BD_DLL_WTurb" "openfast;beamdyn;aerodyn15;servodyn") -of_regression("5MW_Land_BD_Init" "openfast;beamdyn;aerodyn15;servodyn") +of_regression("AWT_YFix_WSt" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AWT_WSt_StartUp_HighSpShutDown" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AWT_YFree_WSt" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AWT_YFree_WTurb" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AWT_WSt_StartUpShutDown" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AOC_WSt" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AOC_YFree_WTurb" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AOC_YFix_WSt" "openfast;elastodyn;aerodyn;servodyn") +of_regression("UAE_Dnwind_YRamp_WSt" "openfast;elastodyn;aerodyn;servodyn") +of_regression("UAE_Upwind_Rigid_WRamp_PwrCurve" "openfast;elastodyn;aerodyn;servodyn") +of_regression("WP_VSP_WTurb_PitchFail" "openfast;elastodyn;aerodyn;servodyn") +of_regression("WP_VSP_ECD" "openfast;elastodyn;aerodyn;servodyn") +of_regression("WP_VSP_WTurb" "openfast;elastodyn;aerodyn;servodyn") +of_regression("SWRT_YFree_VS_EDG01" "openfast;elastodyn;aerodyn;servodyn") +of_regression("SWRT_YFree_VS_EDC01" "openfast;elastodyn;aerodyn;servodyn") +of_regression("SWRT_YFree_VS_WTurb" "openfast;elastodyn;aerodyn;servodyn") +of_regression("5MW_Land_DLL_WTurb" "openfast;elastodyn;aerodyn;servodyn") +of_regression("5MW_Land_DLL_WTurb_wNacDrag" "openfast;elastodyn;aerodyn;servodyn") +of_regression("5MW_OC3Mnpl_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;subdyn;offshore") +of_regression("5MW_OC3Mnpl_DLL_WTurb_WavesIrr_Restart" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;subdyn;offshore;restart") +of_regression("5MW_OC3Trpd_DLL_WSt_WavesReg" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;subdyn;offshore") +of_regression("5MW_OC4Jckt_DLL_WTurb_WavesIrr_MGrowth" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;subdyn;offshore") +of_regression("5MW_ITIBarge_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;map;offshore") +of_regression("5MW_TLP_DLL_WTurb_WavesIrr_WavesMulti" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;map;offshore") +of_regression("5MW_OC3Spar_DLL_WTurb_WavesIrr" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;map;offshore") +of_regression("5MW_OC4Semi_WSt_WavesWN" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;moordyn;offshore") +of_regression("5MW_Land_BD_DLL_WTurb" "openfast;beamdyn;aerodyn;servodyn") +of_regression("5MW_Land_BD_Init" "openfast;beamdyn;aerodyn;servodyn") of_regression("5MW_OC4Jckt_ExtPtfm" "openfast;elastodyn;extptfm") -of_regression("HelicalWake_OLAF" "openfast;aerodyn15;olaf") -of_regression("EllipticalWing_OLAF" "openfast;aerodyn15;olaf") +of_regression("HelicalWake_OLAF" "openfast;aerodyn;olaf") +of_regression("EllipticalWing_OLAF" "openfast;aerodyn;olaf") of_regression("StC_test_OC4Semi" "openfast;servodyn;hydrodyn;moordyn;offshore;stc") -of_regression("MHK_RM1_Fixed" "openfast;elastodyn;aerodyn15;mhk") -of_regression("MHK_RM1_Floating" "openfast;elastodyn;aerodyn15;hydrodyn;moordyn;mhk") -of_regression("MHK_RM1_Floating_wNacDrag" "openfast;elastodyn;aerodyn15;hydrodyn;moordyn;mhk") -of_regression("Tailfin_FreeYaw1DOF_PolarBased" "openfast;elastodyn;aerodyn15") -of_regression("Tailfin_FreeYaw1DOF_Unsteady" "openfast;elastodyn;aerodyn15") +of_regression("MHK_RM1_Fixed" "openfast;elastodyn;aerodyn;mhk") +of_regression("MHK_RM1_Floating" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk") +of_regression("MHK_RM1_Floating_wNacDrag" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk") +of_regression("Tailfin_FreeYaw1DOF_PolarBased" "openfast;elastodyn;aerodyn") +of_regression("Tailfin_FreeYaw1DOF_Unsteady" "openfast;elastodyn;aerodyn") -of_aeromap_regression("5MW_Land_AeroMap" "aeromap;elastodyn;aerodyn15") +of_aeromap_regression("5MW_Land_AeroMap" "aeromap;elastodyn;aerodyn") # OpenFAST C++ API test if(BUILD_OPENFAST_CPP_DRIVER) @@ -329,21 +347,21 @@ endif() # OpenFAST C++ Driver test for OpenFAST Library # This tests the FAST Library and FAST_Library.h if(BUILD_OPENFAST_CPP_DRIVER) - of_fastlib_regression("AWT_YFree_WSt" "fastlib;elastodyn;aerodyn15;servodyn") + of_fastlib_regression("AWT_YFree_WSt" "fastlib;elastodyn;aerodyn;servodyn") endif(BUILD_OPENFAST_CPP_DRIVER) # OpenFAST Python API test -of_regression_py("5MW_Land_DLL_WTurb_py" "openfast;fastlib;python;elastodyn;aerodyn15;servodyn") -of_regression_py("5MW_ITIBarge_DLL_WTurb_WavesIrr_py" "openfast;fastlib;python;elastodyn;aerodyn15;servodyn;hydrodyn;map;offshore") -of_regression_py("5MW_TLP_DLL_WTurb_WavesIrr_WavesMulti_py" "openfast;fastlib;python;elastodyn;aerodyn15;servodyn;hydrodyn;map;offshore") -of_regression_py("5MW_OC3Spar_DLL_WTurb_WavesIrr_py" "openfast;fastlib;python;elastodyn;aerodyn15;servodyn;hydrodyn;map;offshore") -of_regression_py("5MW_OC4Semi_WSt_WavesWN_py" "openfast;fastlib;python;elastodyn;aerodyn15;servodyn;hydrodyn;moordyn;offshore") -of_regression_py("5MW_Land_BD_DLL_WTurb_py" "openfast;fastlib;python;beamdyn;aerodyn15;servodyn") -of_regression_py("HelicalWake_OLAF_py" "openfast;fastlib;python;aerodyn15;olaf") -of_regression_py("EllipticalWing_OLAF_py" "openfast;fastlib;python;aerodyn15;olaf") +of_regression_py("5MW_Land_DLL_WTurb_py" "openfast;fastlib;python;elastodyn;aerodyn;servodyn") +of_regression_py("5MW_ITIBarge_DLL_WTurb_WavesIrr_py" "openfast;fastlib;python;elastodyn;aerodyn;servodyn;hydrodyn;map;offshore") +of_regression_py("5MW_TLP_DLL_WTurb_WavesIrr_WavesMulti_py" "openfast;fastlib;python;elastodyn;aerodyn;servodyn;hydrodyn;map;offshore") +of_regression_py("5MW_OC3Spar_DLL_WTurb_WavesIrr_py" "openfast;fastlib;python;elastodyn;aerodyn;servodyn;hydrodyn;map;offshore") +of_regression_py("5MW_OC4Semi_WSt_WavesWN_py" "openfast;fastlib;python;elastodyn;aerodyn;servodyn;hydrodyn;moordyn;offshore") +of_regression_py("5MW_Land_BD_DLL_WTurb_py" "openfast;fastlib;python;beamdyn;aerodyn;servodyn") +of_regression_py("HelicalWake_OLAF_py" "openfast;fastlib;python;aerodyn;olaf") +of_regression_py("EllipticalWing_OLAF_py" "openfast;fastlib;python;aerodyn;olaf") # AeroAcoustic regression test -of_regression_aeroacoustic("IEA_LB_RWT-AeroAcoustics" "openfast;aerodyn15;aeroacoustics") +of_regression_aeroacoustic("IEA_LB_RWT-AeroAcoustics" "openfast;aerodyn;aeroacoustics") # Linearized OpenFAST regression tests of_regression_linear("Fake5MW_AeroLin_B1_UA4_DBEMT3" "-highpass=0.05" "openfast;linear;elastodyn;aerodyn") @@ -370,6 +388,7 @@ if(BUILD_FASTFARM) # ff_regression("Uninflow_curl" "fastfarm") ff_regression("TSinflow_curl" "fastfarm") ff_regression("ModAmb_3" "fastfarm") + ff_regression("TSinflowADskSED" "fastfarm;aerodisk;simple-elastodyn") endif() # AeroDyn regression tests @@ -483,3 +502,10 @@ md_regression("md_vertical" "moordyn") py_md_regression("py_md_5MW_OC4Semi" "moordyn;python") # the following tests are excessively slow in double precision, so skip these in normal testing #md_regression("md_Single_Line_Quasi_Static_Test" "moordyn") + +# AeroDisk regression tests +adsk_regression("adsk_timeseries_shutdown" "aerodisk") + +# SimplifiedElastoDyn regression tests +sed_regression("sed_test_HSSbrk" "simple-elastodyn") +sed_regression("sed_test_freewheel" "simple-elastodyn") diff --git a/reg_tests/executeAerodiskRegressionCase.py b/reg_tests/executeAerodiskRegressionCase.py new file mode 100644 index 0000000000..ae1dcd5eec --- /dev/null +++ b/reg_tests/executeAerodiskRegressionCase.py @@ -0,0 +1,133 @@ +# +# Copyright 2024 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes AeroDisk and a regression test for a single test case. + The test data is contained in a git submodule, r-test, which must be initialized + prior to running. See the r-test README or OpenFAST documentation for more info. + + Get usage with: `executeAeroDiskRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import numpy as np +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes OpenFAST and a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="AeroDisk-Driver", type=str, nargs=1, help="The path to the AeroDisk driver executable.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("rtol", metavar="Relative-Tolerance", type=float, nargs=1, help="Relative tolerance to allow the solution to deviate; expressed as order of magnitudes less than baseline.") +parser.add_argument("atol", metavar="Absolute-Tolerance", type=float, nargs=1, help="Absolute tolerance to allow small values to pass; expressed as order of magnitudes less than baseline.") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +rtol = args.rtol[0] +atol = args.atol[0] +plotError = args.plot +noExec = args.noExec +verbose = args.verbose + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory, exist_ok = True) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "modules", "aerodisk") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +rtl.copyTree(inputsDirectory, testBuildDirectory, renameDict={'adsk_driver.outb':'adsk_driver_ref.outb'}) + +### Run aerodisk on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "adsk_driver.dvr") + returnCode = openfastDrivers.runAerodiskDriverCase(caseInputFile, executable, verbose=verbose) + if returnCode != 0: + sys.exit(returnCode*10) + +### Build the filesystem navigation variables for running the regression test +localOutFile = os.path.join(testBuildDirectory, "adsk_driver.out") +baselineOutFile = os.path.join(targetOutputDirectory, "adsk_driver.out") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, _ = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) + +passing_channels = pass_fail.passing_channels(testData.T, baselineData.T, rtol, atol) +passing_channels = passing_channels.T + +norms = pass_fail.calculateNorms(testData, baselineData) + +# export all case summaries +channel_names = testInfo["attribute_names"] +exportCaseSummary(testBuildDirectory, caseName, channel_names, passing_channels, norms) + +# passing case +if np.all(passing_channels): + sys.exit(0) + +# failing case +if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + for channel in testInfo["attribute_names"]: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel, rtol, atol) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error)) + finalizePlotDirectory(localOutFile, testInfo["attribute_names"], caseName) + +sys.exit(1) diff --git a/reg_tests/executeSimpleElastodynRegressionCase.py b/reg_tests/executeSimpleElastodynRegressionCase.py new file mode 100644 index 0000000000..e271872574 --- /dev/null +++ b/reg_tests/executeSimpleElastodynRegressionCase.py @@ -0,0 +1,133 @@ +# +# Copyright 2024 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes Simple-ElastoDyn and a regression test for a single test case. + The test data is contained in a git submodule, r-test, which must be initialized + prior to running. See the r-test README or OpenFAST documentation for more info. + + Get usage with: `executeSimpleElastoDynRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import numpy as np +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes OpenFAST and a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="SED-Driver", type=str, nargs=1, help="The path to the SED driver executable.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("rtol", metavar="Relative-Tolerance", type=float, nargs=1, help="Relative tolerance to allow the solution to deviate; expressed as order of magnitudes less than baseline.") +parser.add_argument("atol", metavar="Absolute-Tolerance", type=float, nargs=1, help="Absolute tolerance to allow small values to pass; expressed as order of magnitudes less than baseline.") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +rtol = args.rtol[0] +atol = args.atol[0] +plotError = args.plot +noExec = args.noExec +verbose = args.verbose + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory, exist_ok = True) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "modules", "simple-elastodyn") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +rtl.copyTree(inputsDirectory, testBuildDirectory, renameDict={'sed_driver.outb':'sed_driver_ref.outb'}) + +### Run SED on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "sed_driver.dvr") + returnCode = openfastDrivers.runSimpleElastodynDriverCase(caseInputFile, executable, verbose=verbose) + if returnCode != 0: + rtl.exitWithError("") + +### Build the filesystem navigation variables for running the regression test +localOutFile = os.path.join(testBuildDirectory, "sed_driver.out") +baselineOutFile = os.path.join(targetOutputDirectory, "sed_driver.out") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, _ = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) + +passing_channels = pass_fail.passing_channels(testData.T, baselineData.T, rtol, atol) +passing_channels = passing_channels.T + +norms = pass_fail.calculateNorms(testData, baselineData) + +# export all case summaries +channel_names = testInfo["attribute_names"] +exportCaseSummary(testBuildDirectory, caseName, channel_names, passing_channels, norms) + +# passing case +if np.all(passing_channels): + sys.exit(0) + +# failing case +if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + for channel in testInfo["attribute_names"]: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel, rtol, atol) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error)) + finalizePlotDirectory(localOutFile, testInfo["attribute_names"], caseName) + +sys.exit(1) diff --git a/reg_tests/lib/openfastDrivers.py b/reg_tests/lib/openfastDrivers.py index 1173fe5aed..5323ad818b 100644 --- a/reg_tests/lib/openfastDrivers.py +++ b/reg_tests/lib/openfastDrivers.py @@ -118,8 +118,17 @@ def runMoordynDriverCase(inputFile, executable, verbose=False): os.chdir(caseDirectory) return _runGenericCase(inputFile, executable, verbose) - def runSeaStateDriverCase(inputFile, executable, verbose=False): caseDirectory = os.path.sep.join(inputFile.split(os.path.sep)[:-1]) os.chdir(caseDirectory) return _runGenericCase(inputFile, executable, verbose) + +def runAerodiskDriverCase(inputFile, executable, verbose=False): + caseDirectory = os.path.sep.join(inputFile.split(os.path.sep)[:-1]) + os.chdir(caseDirectory) + return _runGenericCase(inputFile, executable, verbose) + +def runSimpleElastodynDriverCase(inputFile, executable, verbose=False): + caseDirectory = os.path.sep.join(inputFile.split(os.path.sep)[:-1]) + os.chdir(caseDirectory) + return _runGenericCase(inputFile, executable, verbose) diff --git a/reg_tests/r-test b/reg_tests/r-test index c976254a1b..b074a96dbe 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit c976254a1b7771181cc0d027ea59f330b7273e62 +Subproject commit b074a96dbeff873af30c415942590cd50b7e026f diff --git a/vs-build/AeroDisk/AeroDisk_Driver.sln b/vs-build/AeroDisk/AeroDisk_Driver.sln new file mode 100644 index 0000000000..be507fe647 --- /dev/null +++ b/vs-build/AeroDisk/AeroDisk_Driver.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{38C3D685-B817-4E00-A9A9-F3898DC01990}") = "AeroDisk_Driver", "AeroDisk_Driver.vfproj", "{C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}" + ProjectSection(ProjectDependencies) = postProject + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} = {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAST_Registry", "..\Registry\FAST_Registry.vcxproj", "{DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_Double|Win32 = Debug_Double|Win32 + Debug_Double|x64 = Debug_Double|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release_Double|Win32 = Release_Double|Win32 + Release_Double|x64 = Release_Double|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug_Double|Win32.ActiveCfg = Debug_Double|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug_Double|Win32.Build.0 = Debug_Double|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug_Double|x64.ActiveCfg = Debug_Double|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug_Double|x64.Build.0 = Debug_Double|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug|Win32.ActiveCfg = Debug|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug|Win32.Build.0 = Debug|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug|x64.ActiveCfg = Debug|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Debug|x64.Build.0 = Debug|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release_Double|Win32.ActiveCfg = Release_Double|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release_Double|Win32.Build.0 = Release_Double|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release_Double|x64.ActiveCfg = Release_Double|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release_Double|x64.Build.0 = Release_Double|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release|Win32.ActiveCfg = Release|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release|Win32.Build.0 = Release|Win32 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release|x64.ActiveCfg = Release|x64 + {C5F53AB0-6FE8-405C-A7F6-59BBF5E6D5C2}.Release|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.ActiveCfg = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.ActiveCfg = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vs-build/AeroDisk/AeroDisk_Driver.vfproj b/vs-build/AeroDisk/AeroDisk_Driver.vfproj new file mode 100644 index 0000000000..db745ceac6 --- /dev/null +++ b/vs-build/AeroDisk/AeroDisk_Driver.vfproj @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vs-build/FASTlib/FASTlib.vfproj b/vs-build/FASTlib/FASTlib.vfproj index 33763541da..a5594307e3 100644 --- a/vs-build/FASTlib/FASTlib.vfproj +++ b/vs-build/FASTlib/FASTlib.vfproj @@ -462,7 +462,50 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2273,6 +2316,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vs-build/RunRegistry.bat b/vs-build/RunRegistry.bat index dfb6402e2b..97657031e1 100644 --- a/vs-build/RunRegistry.bat +++ b/vs-build/RunRegistry.bat @@ -25,7 +25,7 @@ SET Modules_Loc=%Root_Loc%\modules SET Registry=..\..\build\bin\Registry.exe SET FAST_Loc=%Modules_Loc%\openfast-library\src SET ED_Loc=%Modules_Loc%\elastodyn\src -SET AD14_Loc=%Modules_Loc%\aerodyn14\src +SET SED_Loc=%Modules_Loc%\simple-elastodyn\src SET IfW_Loc=%Modules_Loc%\inflowwind\src SET HD_Loc=%Modules_Loc%\hydrodyn\src SET SEAST_Loc=%Modules_Loc%\seastate\src @@ -44,6 +44,7 @@ SET AD_Loc=%Modules_Loc%\aerodyn\src SET SrvD_Loc=%Modules_Loc%\servodyn\src SET BD_Loc=%Modules_Loc%\beamdyn\src SET SC_Loc=%Modules_Loc%\supercontroller\src +SET ADsk_Loc=%Modules_Loc%\aerodisk\src SET LD_Loc=%Modules_Loc%\lindyn\src @@ -51,8 +52,8 @@ SET AWAE_Loc=%Modules_Loc%\awae\src SET WD_Loc=%Modules_Loc%\wakedynamics\src SET Farm_Loc=%Root_Loc%\glue-codes\fast-farm\src -SET ALL_FAST_Includes=-I "%FAST_Loc%" -I "%NWTC_Lib_Loc%" -I "%ED_Loc%" -I "%SrvD_Loc%" -I "%AD14_Loc%" -I^ - "%AD_Loc%" -I "%BD_Loc%" -I "%SC_Loc%" -I^ +SET ALL_FAST_Includes=-I "%FAST_Loc%" -I "%NWTC_Lib_Loc%" -I "%ED_Loc%" -I "%SED_Loc%" -I^ + "%SrvD_Loc%" -I "%AD_Loc%" -I "%ADsk_Loc%" -I "%BD_Loc%" -I "%SC_Loc%" -I^ "%IfW_Loc%" -I "%SD_Loc%" -I "%HD_Loc%" -I "%SEAST_Loc%" -I "%MAP_Loc%" -I "%FEAM_Loc%" -I^ "%IceF_Loc%" -I "%IceD_Loc%" -I "%MD_Loc%" -I "%ExtInfw_Loc%" -I "%Orca_Loc%" -I "%ExtPtfm_Loc%" -I "%ExtLoads_Loc%" @@ -114,11 +115,17 @@ SET Output_Loc=%CURR_LOC% %REGISTRY% "%CURR_LOC%\%ModuleName%_Registry.txt" -I "%NWTC_Lib_Loc%" -O "%Output_Loc%" GOTO checkError +:SimpleElastoDyn +SET CURR_LOC=%SED_Loc% +SET Output_Loc=%CURR_LOC% +%REGISTRY% "%CURR_LOC%\SED_Registry.txt" -I "%NWTC_Lib_Loc%" -O "%Output_Loc%" +GOTO checkError + :StrucCtrl :ServoDyn SET CURR_LOC=%SrvD_Loc% SET Output_Loc=%CURR_LOC% -%REGISTRY% "%CURR_LOC%\%ModuleName%_Registry.txt" -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -O "%Output_Loc%" +%REGISTRY% "%CURR_LOC%\SED_Registry.txt" -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -O "%Output_Loc%" GOTO checkError :Lidar @@ -204,18 +211,6 @@ SET Output_Loc=%CURR_LOC% %REGISTRY% "%CURR_LOC%\AeroAcoustics_Registry.txt" -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -O "%Output_Loc%" -noextrap GOTO checkError -:AeroDyn14 -SET CURR_LOC=%AD14_Loc% -SET Output_Loc=%CURR_LOC% -%REGISTRY% "%CURR_LOC%\Registry-AD14.txt" -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -I "%IfW_Loc%" -O "%Output_Loc%" -GOTO checkError - -:DWM -SET CURR_LOC=%AD14_Loc% -SET Output_Loc=%CURR_LOC% -%REGISTRY% "%CURR_LOC%\Registry-DWM.txt" -I "%NWTC_Lib_Loc%" -I "%IfW_Loc%" -O "%Output_Loc%" -GOTO checkError - :HydroDyn :SS_Excitation :SS_Radiation @@ -305,6 +300,12 @@ SET Output_Loc=%CURR_LOC% %REGISTRY% "%CURR_LOC%\AWAE_Registry.txt" -I %NWTC_Lib_Loc% -I %IfW_Loc% -noextrap -O "%Output_Loc%" GOTO checkError +:AeroDisk +SET CURR_LOC=%ADsk_Loc% +SET Output_Loc=%CURR_LOC% +%REGISTRY% "%CURR_LOC%\AeroDisk_Registry.txt" -I %NWTC_Lib_Loc% -I %IfW_Loc% -I "%CURR_LOC%" -O "%Output_Loc%" +GOTO checkError + :Version DEL "%Root_Loc%\VersionInfo.obj" "%Root_Loc%\versioninfo.mod" GOTO end @@ -336,8 +337,8 @@ SET FAST_Loc= SET Registry= SET ED_Loc= +SET SED_Loc= SET BD_Loc= -SET AD14_Loc= SET IfW_Loc= SET HD_Loc= SET SD_Loc= @@ -351,6 +352,7 @@ SET Orca_Loc= SET NWTC_Lib_Loc= SET ExtPtfm_Loc= SET AD_Loc= +SET ADsk_Loc= SET SrvD_Loc= SET MAP_Loc= diff --git a/vs-build/SimpleElastoDyn/SimpleElastoDyn_Driver.sln b/vs-build/SimpleElastoDyn/SimpleElastoDyn_Driver.sln new file mode 100644 index 0000000000..956126eccf --- /dev/null +++ b/vs-build/SimpleElastoDyn/SimpleElastoDyn_Driver.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2EF6BE3F-01AD-4236-81A5-69E813EF3897}") = "SimpleElastoDyn_Driver", "SimpleElastoDyn_Driver.vfproj", "{ABF8EC9F-A94B-460C-8AB5-17733D51820B}" + ProjectSection(ProjectDependencies) = postProject + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} = {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAST_Registry", "..\Registry\FAST_Registry.vcxproj", "{DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_Double|Win32 = Debug_Double|Win32 + Debug_Double|x64 = Debug_Double|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release_Double|Win32 = Release_Double|Win32 + Release_Double|x64 = Release_Double|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug_Double|Win32.ActiveCfg = Debug_Double|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug_Double|Win32.Build.0 = Debug_Double|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug_Double|x64.ActiveCfg = Debug_Double|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug_Double|x64.Build.0 = Debug_Double|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug|Win32.ActiveCfg = Debug|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug|Win32.Build.0 = Debug|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug|x64.ActiveCfg = Debug|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Debug|x64.Build.0 = Debug|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release_Double|Win32.ActiveCfg = Release_Double|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release_Double|Win32.Build.0 = Release_Double|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release_Double|x64.ActiveCfg = Release_Double|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release_Double|x64.Build.0 = Release_Double|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release|Win32.ActiveCfg = Release|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release|Win32.Build.0 = Release|Win32 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release|x64.ActiveCfg = Release|x64 + {ABF8EC9F-A94B-460C-8AB5-17733D51820B}.Release|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.ActiveCfg = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.ActiveCfg = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vs-build/SimpleElastoDyn/SimpleElastoDyn_Driver.vfproj b/vs-build/SimpleElastoDyn/SimpleElastoDyn_Driver.vfproj new file mode 100644 index 0000000000..80e3d4e568 --- /dev/null +++ b/vs-build/SimpleElastoDyn/SimpleElastoDyn_Driver.vfproj @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +