@@ -35,6 +35,7 @@ MODULE AeroDyn_Inflow_C_BINDING
3535
3636 PUBLIC :: ADI_C_Init
3737 PUBLIC :: ADI_C_CalcOutput
38+ PUBLIC :: ADI_C_CalcOutput_and_AddedMass ! CalcOutput plus instantaneous blade added-mass matrices (MHK)
3839 PUBLIC :: ADI_C_UpdateStates
3940 PUBLIC :: ADI_C_End
4041 PUBLIC :: ADI_C_PreInit ! Initial call to setup number of turbines
@@ -1175,6 +1176,214 @@ end function Failed
11751176! FIXME: add a showpassed/return routine
11761177END SUBROUTINE ADI_C_CalcOutput
11771178
1179+ ! ===============================================================================================================
1180+ !- ----------------------------------- AeroDyn CalcOutput and AddedMass -----------------------------------------
1181+ ! ===============================================================================================================
1182+ ! > This routine calculates the outputs at Time_C and the instantaneous added-mass matrices for the blade
1183+ ! ! structural mesh points of all rotors. On return, ADI%y holds the remainder loads (loads computed with zero
1184+ ! ! structural acceleration), so a subsequent call to ADI_C_GetRotorLoads for each rotor returns those remainder
1185+ ! ! loads (i.e. the loads on the blades excluding the added-mass terms).
1186+ ! ! NOTE: make sure to call ADI_C_SetRotorMotion for every rotor before calling this routine.
1187+ ! ! NOTE: AeroDyn added mass is only nonzero for MHK turbines (p%MHK /= MHK_None); otherwise the matrix is zero.
1188+ ! ! NOTE: the returned per-node 6x6 blocks are a diagonal (block) approximation of the true (banded) structural
1189+ ! ! added-mass matrix -- all structural nodes are perturbed simultaneously, so each block is the row-sum of
1190+ ! ! the corresponding matrix rows. This conserves rigid-body totals and mirrors the strip-theory approach
1191+ ! ! used in HydroDyn_C_CalcOutput_and_AddedMass.
1192+ SUBROUTINE ADI_C_CalcOutput_and_AddedMass (Time_C , &
1193+ OutputChannelValues_C , NodeAdm_C , ErrStat_C , ErrMsg_C ) BIND (C, NAME= ' ADI_C_CalcOutput_and_AddedMass' )
1194+ implicit none
1195+ #ifndef IMPLICIT_DLLEXPORT
1196+ ! DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_CalcOutput_and_AddedMass
1197+ ! GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_CalcOutput_and_AddedMass
1198+ #endif
1199+ real (c_double), intent (in ) :: Time_C
1200+ real (c_float), intent ( out ) :: OutputChannelValues_C(ADI% p% NumOuts)
1201+ real (c_float), intent ( out ) :: NodeAdm_C(* ) ! < 6x6 added-mass block per structural mesh point, concatenated by rotor then mesh point, each block column-major (global frame). Sized 36*sum(NumMeshPts).
1202+ integer (c_int), intent ( out ) :: ErrStat_C
1203+ character (kind= c_char), intent ( out ) :: ErrMsg_C(ErrMsgLen_C)
1204+
1205+ ! Local variables
1206+ real (DbKi) :: Time
1207+ integer (IntKi) :: iWT ! < current rotor/turbine
1208+ integer (IntKi) :: i,j,k,g,pt ! < generic index variables
1209+ integer (IntKi) :: nWT ! < number of turbines
1210+ integer (IntKi) :: NT ! < total number of structural mesh points across all rotors
1211+ integer (IntKi), allocatable :: globOffset(:) ! < global mesh-point offset for each rotor
1212+ real (ReKi), allocatable :: NodeAdm(:,:,:) ! < (6,6,NT) added-mass blocks
1213+ real (ReKi), allocatable :: baseLoad(:,:) ! < (6,NT) remainder loads at zero structural acceleration
1214+ integer (IntKi) :: ErrStat_F ! < aggregated error status
1215+ character (ErrMsgLen) :: ErrMsg_F ! < aggregated error message
1216+ integer (IntKi) :: ErrStat_F2 ! < temporary error status from a call
1217+ character (ErrMsgLen) :: ErrMsg_F2 ! < temporary error message from a call
1218+ character (* ), parameter :: RoutineName = ' ADI_C_CalcOutput_and_AddedMass' ! < for error handling
1219+
1220+ ! Initialize error handling
1221+ ErrStat_F = ErrID_None
1222+ ErrMsg_F = " "
1223+
1224+ ! Convert the inputs from C to Fortran
1225+ Time = REAL (Time_C,DbKi)
1226+
1227+ ! Global mesh-point bookkeeping (rotors in order, within a rotor by mesh-point index)
1228+ nWT = Sim% NumTurbines
1229+ call AllocAry(globOffset, nWT, " globOffset" , ErrStat_F2, ErrMsg_F2); if (Failed()) return
1230+ NT = 0
1231+ do iWT= 1 ,nWT
1232+ globOffset(iWT) = NT
1233+ NT = NT + NumMeshPts(iWT)
1234+ enddo
1235+ call AllocAry(NodeAdm, 6 , 6 , NT, " NodeAdm" , ErrStat_F2, ErrMsg_F2); if (Failed()) return
1236+ call AllocAry(baseLoad, 6 , NT, " baseLoad" , ErrStat_F2, ErrMsg_F2); if (Failed()) return
1237+ NodeAdm = 0.0_ReKi
1238+ baseLoad = 0.0_ReKi
1239+
1240+ ! Copy new inputs over once; probes/baseline only override the blade structural acceleration
1241+ call ADI_CopyInput (ADI_u, ADI% u(1 ), MESH_UPDATECOPY, ErrStat_F2, ErrMsg_F2)
1242+ if (Failed()) return
1243+
1244+ !- ------------------------------------------------------
1245+ ! Probe the added-mass response: perturb all blade structural nodes with a unit negative acceleration in
1246+ ! each of the 6 DOFs. Perturbing all nodes at once gives the row-sum (diagonal-block) approximation.
1247+ !- ------------------------------------------------------
1248+ do k= 1 ,6
1249+ call SetBladeStrAccel(k, - 1.0_ReKi ) ! DOF k acceleration = -1 on all blade structural nodes
1250+ do iWT= 1 ,nWT
1251+ call AD_SetBladeMotion(iWT, ErrStat_F2, ErrMsg_F2); if (Failed()) return
1252+ enddo
1253+ CALL ADI_CalcOutput( Time, ADI% u(1 ), ADI% p, ADI% x(STATE_CURR), ADI% xd(STATE_CURR), ADI% z(STATE_CURR), ADI% OtherState(STATE_CURR), ADI% y, ADI% m, ErrStat_F2, ErrMsg_F2 )
1254+ if (Failed()) return
1255+ do iWT= 1 ,nWT
1256+ call AD_TransferLoads( iWT, ADI% u(1 ), ADI% y, ErrStat_F2, ErrMsg_F2 ); if (Failed()) return
1257+ call Set_OutputLoadArray(iWT)
1258+ do i= 1 ,Sim% WT(iWT)% NumBlades
1259+ do j= 1 ,StrucPts_2_Bld_Map(iWT)% NumMeshPtsPerBlade(i)
1260+ pt = StrucPts_2_Bld_Map(iWT)% BladeNode_2_MeshPt(i)% BladeNodeToMeshPoint(j)
1261+ g = globOffset(iWT) + pt
1262+ NodeAdm(1 :6 ,k,g) = StrucPts_2_Bld_Map(iWT)% BladeStrMeshCoords(i)% Force(1 :6 ,j)
1263+ enddo
1264+ enddo
1265+ enddo
1266+ enddo
1267+
1268+ !- ------------------------------------------------------
1269+ ! Baseline (zero structural acceleration). Done last so that ADI%y holds the remainder loads on return,
1270+ ! ready for subsequent ADI_C_GetRotorLoads calls.
1271+ !- ------------------------------------------------------
1272+ call SetBladeStrAccel(0 , 0.0_ReKi ) ! zero acceleration on all blade structural nodes
1273+ do iWT= 1 ,nWT
1274+ call AD_SetBladeMotion(iWT, ErrStat_F2, ErrMsg_F2); if (Failed()) return
1275+ enddo
1276+ CALL ADI_CalcOutput( Time, ADI% u(1 ), ADI% p, ADI% x(STATE_CURR), ADI% xd(STATE_CURR), ADI% z(STATE_CURR), ADI% OtherState(STATE_CURR), ADI% y, ADI% m, ErrStat_F2, ErrMsg_F2 )
1277+ if (Failed()) return
1278+ do iWT= 1 ,nWT
1279+ call AD_TransferLoads( iWT, ADI% u(1 ), ADI% y, ErrStat_F2, ErrMsg_F2 ); if (Failed()) return
1280+ call Set_OutputLoadArray(iWT)
1281+ do i= 1 ,Sim% WT(iWT)% NumBlades
1282+ do j= 1 ,StrucPts_2_Bld_Map(iWT)% NumMeshPtsPerBlade(i)
1283+ pt = StrucPts_2_Bld_Map(iWT)% BladeNode_2_MeshPt(i)% BladeNodeToMeshPoint(j)
1284+ g = globOffset(iWT) + pt
1285+ baseLoad(1 :6 ,g) = StrucPts_2_Bld_Map(iWT)% BladeStrMeshCoords(i)% Force(1 :6 ,j)
1286+ enddo
1287+ enddo
1288+ enddo
1289+
1290+ ! Subtract baseline: each block is F(-1) - F(0). Added-mass load is F = -M*accel, so probing with
1291+ ! accel = -1 gives the added-mass matrix M directly.
1292+ do g= 1 ,NT
1293+ do k= 1 ,6
1294+ NodeAdm(1 :6 ,k,g) = NodeAdm(1 :6 ,k,g) - baseLoad(1 :6 ,g)
1295+ enddo
1296+ enddo
1297+
1298+ ! Pack into the C return array (per node: 6x6 block in column-major order)
1299+ do g= 1 ,NT
1300+ NodeAdm_C(36 * (g-1 )+ 1 :36 * g) = reshape ( real (NodeAdm(1 :6 ,1 :6 ,g), c_float), (/ 36 / ) )
1301+ enddo
1302+
1303+ ! Get the output channel info out of y (reflects the remainder / zero-acceleration state)
1304+ OutputChannelValues_C = REAL (ADI% y% WriteOutput, C_FLOAT)
1305+
1306+ !- ------------------------------------------------------
1307+ ! write outputs (mirror ADI_C_CalcOutput)
1308+ !- ------------------------------------------------------
1309+ if (WrOutputsData% WrVTK > 1_IntKi ) then
1310+ VTKn_Global = nint (Time_C / WrOutputsData% VTK_dt )
1311+ if (VTKn_Global /= VTKn_last) then
1312+ VTKn_last = VTKn_Global
1313+ call WrVTK_Meshes(ADI% u(1 )% AD% rotors(:),(/ 0.0_SiKi ,0.0_SiKi ,0.0_SiKi / ),ErrStat_F2,ErrMsg_F2)
1314+ if (Failed()) return
1315+ endif
1316+ endif
1317+
1318+ if (WrOutputsData% fileFmt > idFmtNone) then
1319+ call Dvr_WriteOutputs(n_Global+1 , ADI% InputTimes(INPUT_CURR), Sim, WrOutputsData, ADI% y, SeaSt, ErrStat_F2, ErrMsg_F2); if (Failed()) return
1320+ endif
1321+
1322+ ! Set error status
1323+ call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C)
1324+
1325+ ! Store info what time we just ran calcs for
1326+ InputTimePrev_Calc = Time
1327+
1328+ if (allocated (globOffset)) deallocate (globOffset)
1329+ if (allocated (NodeAdm)) deallocate (NodeAdm)
1330+ if (allocated (baseLoad)) deallocate (baseLoad)
1331+
1332+ CONTAINS
1333+ ! > Set the acceleration on all blade structural motion mesh nodes (all rotors, all blades).
1334+ ! ! dof = 0 zeroes all 6 DOFs; dof = 1..3 sets that translational DOF; dof = 4..6 sets that rotational DOF.
1335+ subroutine SetBladeStrAccel (dof , val )
1336+ integer (IntKi), intent (in ) :: dof
1337+ real (ReKi), intent (in ) :: val
1338+ integer (IntKi) :: iWTl, iBl, jn
1339+ do iWTl= 1 ,Sim% NumTurbines
1340+ do iBl= 1 ,Sim% WT(iWTl)% NumBlades
1341+ do jn= 1 ,StrucPts_2_Bld_Map(iWTl)% NumMeshPtsPerBlade(iBl)
1342+ BldStrMotionMesh(iWTl)% BldMesh(iBl)% TranslationAcc(1 :3 ,jn) = 0.0_ReKi
1343+ BldStrMotionMesh(iWTl)% BldMesh(iBl)% RotationAcc( 1 :3 ,jn) = 0.0_ReKi
1344+ if (dof >= 1 .and. dof <= 3 ) then
1345+ BldStrMotionMesh(iWTl)% BldMesh(iBl)% TranslationAcc(dof,jn) = val
1346+ elseif (dof >= 4 .and. dof <= 6 ) then
1347+ BldStrMotionMesh(iWTl)% BldMesh(iBl)% RotationAcc(dof-3 ,jn) = val
1348+ endif
1349+ enddo
1350+ enddo
1351+ enddo
1352+ end subroutine SetBladeStrAccel
1353+
1354+ ! > Transfer the blade structural motion mesh to the AD blade motion mesh for a single rotor. Only the blade
1355+ ! ! motion is (re)transferred; hub/nacelle/root motions already present in ADI%u(1) are left unchanged.
1356+ subroutine AD_SetBladeMotion (iWTl , ErrStat3 , ErrMsg3 )
1357+ integer (IntKi), intent (in ) :: iWTl
1358+ integer (IntKi), intent ( out ) :: ErrStat3
1359+ character (ErrMsgLen), intent ( out ) :: ErrMsg3
1360+ integer (IntKi) :: iBl
1361+ integer (IntKi) :: n_elems
1362+ ErrStat3 = 0_IntKi
1363+ ErrMsg3 = ' '
1364+ do iBl= 1 ,Sim% WT(iWTl)% NumBlades
1365+ n_elems = size (BldStrMotionMesh(iWTl)% BldMesh(iBl)% Position, 2 )
1366+ if ( ADI% u(1 )% AD% rotors(iWTl)% BladeMotion(iBl)% Committed .and. (n_elems > 0 )) then
1367+ if (PointLoadOutput) then
1368+ call Transfer_Point_to_Line2(BldStrMotionMesh(iWTl)% BldMesh(iBl), ADI% u(1 )% AD% rotors(iWTl)% BladeMotion(iBl), Map_BldStrMotion_2_AD_Blade(iBl,iWTl), ErrStat3, ErrMsg3)
1369+ else
1370+ call Transfer_Line2_to_Line2(BldStrMotionMesh(iWTl)% BldMesh(iBl), ADI% u(1 )% AD% rotors(iWTl)% BladeMotion(iBl), Map_BldStrMotion_2_AD_Blade(iBl,iWTl), ErrStat3, ErrMsg3)
1371+ endif
1372+ if (ErrStat3 >= AbortErrLev) return
1373+ endif
1374+ enddo
1375+ end subroutine AD_SetBladeMotion
1376+
1377+ logical function Failed ()
1378+ CALL SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName )
1379+ Failed = ErrStat_F >= AbortErrLev
1380+ if (Failed) then
1381+ call ClearTmpStorage()
1382+ call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C)
1383+ endif
1384+ end function Failed
1385+ END SUBROUTINE ADI_C_CalcOutput_and_AddedMass
1386+
11781387! ===============================================================================================================
11791388!- -------------------------------------------- AeroDyn UpdateStates -------------------------------------------
11801389! ===============================================================================================================
@@ -1687,7 +1896,7 @@ subroutine SetupMotionMesh()
16871896 ErrMess = ErrMsg_F2 , &
16881897 TranslationDisp = .TRUE. , Orientation = .TRUE. , &
16891898 TranslationVel = .TRUE. , RotationVel = .TRUE. , &
1690- TranslationAcc = .TRUE. , RotationAcc = .FALSE. )
1899+ TranslationAcc = .TRUE. , RotationAcc = .TRUE. )
16911900 if (Failed()) return
16921901 enddo
16931902
@@ -2126,6 +2335,7 @@ subroutine Set_MotionMesh(iWT, ErrStat3, ErrMsg3)
21262335 BldStrMotionMesh(iWT)% BldMesh(iBlade)% TranslationVel( 1 :3 ,j) = StrucPts_2_Bld_Map(iWT)% BladeStrMeshCoords(iBlade)% Velocity(1 :3 ,j)
21272336 BldStrMotionMesh(iWT)% BldMesh(iBlade)% RotationVel( 1 :3 ,j) = StrucPts_2_Bld_Map(iWT)% BladeStrMeshCoords(iBlade)% Velocity(4 :6 ,j)
21282337 BldStrMotionMesh(iWT)% BldMesh(iBlade)% TranslationAcc( 1 :3 ,j) = StrucPts_2_Bld_Map(iWT)% BladeStrMeshCoords(iBlade)% Accln(1 :3 ,j)
2338+ BldStrMotionMesh(iWT)% BldMesh(iBlade)% RotationAcc( 1 :3 ,j) = StrucPts_2_Bld_Map(iWT)% BladeStrMeshCoords(iBlade)% Accln(4 :6 ,j)
21292339 call OrientRemap(BldStrMotionMesh(iWT)% BldMesh(iBlade)% Orientation(1 :3 ,1 :3 ,j))
21302340 if (TransposeDCM) then
21312341 BldStrMotionMesh(iWT)% BldMesh(iBlade)% Orientation(1 :3 ,1 :3 ,j) = transpose (BldStrMotionMesh(iWT)% BldMesh(iBlade)% Orientation(1 :3 ,1 :3 ,j))
@@ -2210,9 +2420,9 @@ subroutine AD_SetInputMotion( iWT, u_local, &
22102420 n_elems = size (BldStrMotionMesh(iWT)% BldMesh(iBlade)% Position, 2 )
22112421 if (( u_local% AD% rotors(iWT)% BladeMotion(iBlade)% Committed ) .and. (n_elems > 0 )) then
22122422 if (PointLoadOutput) then
2213- call Transfer_Point_to_Line2(BldStrMotionMesh(iWT)% BldMesh(iBlade), u_local% AD% rotors(iWT)% BladeMotion(iBlade), Map_BldStrMotion_2_AD_Blade(i ,iWT), ErrStat, ErrMsg)
2423+ call Transfer_Point_to_Line2(BldStrMotionMesh(iWT)% BldMesh(iBlade), u_local% AD% rotors(iWT)% BladeMotion(iBlade), Map_BldStrMotion_2_AD_Blade(iBlade ,iWT), ErrStat, ErrMsg)
22142424 else
2215- call Transfer_Line2_to_Line2(BldStrMotionMesh(iWT)% BldMesh(iBlade), u_local% AD% rotors(iWT)% BladeMotion(iBlade), Map_BldStrMotion_2_AD_Blade(i ,iWT), ErrStat, ErrMsg)
2425+ call Transfer_Line2_to_Line2(BldStrMotionMesh(iWT)% BldMesh(iBlade), u_local% AD% rotors(iWT)% BladeMotion(iBlade), Map_BldStrMotion_2_AD_Blade(iBlade ,iWT), ErrStat, ErrMsg)
22162426 u_local% AD% rotors(iWT)% BladeMotion(iBlade)% RemapFlag = .false.
22172427 end if
22182428 if (ErrStat >= AbortErrLev) return
0 commit comments