From 9f20236f22933ee18197ec3045514edd3f5a781d Mon Sep 17 00:00:00 2001 From: willwiley Date: Tue, 25 Aug 2026 13:17:47 -0600 Subject: [PATCH 1/8] KC dependent drag for Morison members --- modules/hydrodyn/src/HydroDyn_Input.f90 | 194 ++++++++++++++++++------ modules/hydrodyn/src/Morison.f90 | 166 ++++++++++++++++++-- modules/hydrodyn/src/Morison.txt | 14 +- 3 files changed, 310 insertions(+), 64 deletions(-) diff --git a/modules/hydrodyn/src/HydroDyn_Input.f90 b/modules/hydrodyn/src/HydroDyn_Input.f90 index b091062a9f..ee1a2826f4 100644 --- a/modules/hydrodyn/src/HydroDyn_Input.f90 +++ b/modules/hydrodyn/src/HydroDyn_Input.f90 @@ -81,12 +81,22 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi INTEGER :: UnEc ! The local unit number for this module's echo file CHARACTER(1024) :: EchoFile ! Name of HydroDyn echo file CHARACTER(MaxFileInfoLineLen) :: Line ! String to temporarially hold value of read line + CHARACTER(8) :: KCFile1 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(8) :: KCFile2 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(1024) :: KCPath ! String to temporarilly hold path name of KC-Cd function file + TYPE(FileInfoType) :: FileInfo_KC ! Temporary derived type to hold KC file information INTEGER :: NDOF ! Number of DOF in each WAMIT module real(ReKi), ALLOCATABLE :: tmpVec1(:), tmpVec2(:) ! Temporary arrays for WAMIT data integer(IntKi) :: startIndx, endIndx ! indices into working arrays INTEGER, ALLOCATABLE :: tmpArray(:) ! Temporary array storage of the joint output list REAL(ReKi), ALLOCATABLE :: tmpReArray(:) ! Temporary array storage of the joint output list INTEGER(IntKi) :: CurLine !< Current entry in FileInfo_In%Lines array + INTEGER(IntKi) :: CurRow ! Current line in secondary file + INTEGER(IntKi) :: NKCCd ! Temporary number of provided KC-Cd functions + INTEGER(IntKi) :: NKC ! Temporary number of lines in a KC-Cd function file + INTEGER(IntKi) :: totalNKC ! Temporary counter for the total number of lines in concatenated KC table + REAL(ReKi), DIMENSION(2) :: tmpKCCd ! Temporary array for reading row of KC-Cd function files + INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'HydroDyn_ParaseInput' @@ -401,26 +411,41 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi END IF DO I = 1,InputFileData%Morison%NAxCoefs - ! read the table entries AxCoefID, AxCd, AxCa, AxCp, AxFdMod, AxVnCOff, AxFDLoFSc in the HydroDyn input file - ! Try reading in 7 entries first - call ParseAry( FileInfo_In, CurLine, ' axial coefficients line '//trim( Int2LStr(I)), tmpReArray, size(tmpReArray), ErrStat2, ErrMsg2, UnEc ) - if ( ErrStat2 /= ErrID_None ) then ! Try reading in 5 entries - tmpReArray(6) = -1.0 ! AxVnCoff - tmpReArray(7) = 1.0 ! AxFDLoFSc - call ParseAry( FileInfo_In, CurLine, ' axial coefficients line '//trim( Int2LStr(I)), tmpReArray(1:5), 5, ErrStat2, ErrMsg2, UnEc ) - if ( ErrStat2 /= ErrID_None ) then ! Try reading in 4 entries - tmpReArray(5) = 0.0 ! AxFdMod - call ParseAry( FileInfo_In, CurLine, ' axial coefficients line '//trim( Int2LStr(I)), tmpReArray(1:4), 4, ErrStat2, ErrMsg2, UnEc ) + + ! Can't use ParseAry since cd could contain string for KC-Cd function + Line = FileInfo_In%Lines(CurLine) + READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%AxialCoefs(I)%AxCoefID, KCFile1, & + InputFileData%Morison%AxialCoefs(I)%AxCa, InputFileData%Morison%AxialCoefs(I)%AxCp, & + InputFileData%Morison%AxialCoefs(I)%AxFDMod, InputFileData%Morison%AxialCoefs(I)%AxVnCOff, & + InputFileData%Morison%AxialCoefs(I)%AxFDLoFSc + if (IS_IOSTAT_EOR(ErrStat2)) then + InputFileData%Morison%AxialCoefs(I)%AxVnCOff = -1.0 + InputFileData%Morison%AxialCoefs(I)%AxFDLoFSc = 1.0 + READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%AxialCoefs(I)%AxCoefID, KCFile1, & + InputFileData%Morison%AxialCoefs(I)%AxCa, InputFileData%Morison%AxialCoefs(I)%AxCp, & + InputFileData%Morison%AxialCoefs(I)%AxFDMod + if (IS_IOSTAT_EOR(ErrStat2)) then + InputFileData%Morison%AxialCoefs(I)%AxFDMod = 0.0 + InputFileData%Morison%AxialCoefs(I)%AxVnCOff = -1.0 + InputFileData%Morison%AxialCoefs(I)%AxFDLoFSc = 1.0 + READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%AxialCoefs(I)%AxCoefID, KCFile1, & + InputFileData%Morison%AxialCoefs(I)%AxCa, InputFileData%Morison%AxialCoefs(I)%AxCp if (Failed()) return; end if end if - InputFileData%Morison%AxialCoefs(I)%AxCoefID = NINT(tmpReArray(1)) - InputFileData%Morison%AxialCoefs(I)%AxCd = tmpReArray(2) - InputFileData%Morison%AxialCoefs(I)%AxCa = tmpReArray(3) - InputFileData%Morison%AxialCoefs(I)%AxCp = tmpReArray(4) - InputFileData%Morison%AxialCoefs(I)%AxFDMod = NINT(tmpReArray(5)) - InputFileData%Morison%AxialCoefs(I)%AxVnCOff = tmpReArray(6) - InputFileData%Morison%AxialCoefs(I)%AxFDLoFSc = tmpReArray(7) + if ( KCFile1(1:2) == "KC" ) then + InputFileData%Morison%AxialCoefs(I)%AxCd = 0.0 + READ( KCFile1(3:), '(I10)', iostat=ErrStat2 ) InputFileData%Morison%AxialCoefs(I)%AxiKC + else + READ(KCFile1,*,IOSTAT=ErrStat2) InputFileData%Morison%AxialCoefs(I)%AxCd + InputFileData%Morison%AxialCoefs(I)%AxiKC = 0 + if (ErrStat2 /= 0) then + ErrMsg2 = "AxCd input needs to either be a real number or a string starting with 'KC' followed by an integer between 1 and NKCCd" + end if + if (Failed()) return + end if + CurLine = CurLine + 1 + END DO if (allocated(tmpReArray)) deallocate(tmpReArray) @@ -789,40 +814,48 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi END IF DO I = 1,InputFileData%Morison%NCoefMembersCyl - - CALL ParseRAryWKywrd( FileInfo_In, CurLine, 'Member-based cylindrical member hydrodynamic coefficients table row '//trim( Int2LStr(I)), tmpReArray, size(tmpReArray), & - 'MCF', 1.0_ReKi, (/10,11,12,13/), InputFileData%Morison%CoefMembersCyl(I)%MemberMCF, ErrStat2, ErrMsg2, UnEc ) + + Line = FileInfo_In%Lines(CurLine) + READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberID, & + KCFile1, KCFile2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCa1, InputFileData%Morison%CoefMembersCyl(I)%MemberCa2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCp1, InputFileData%Morison%CoefMembersCyl(I)%MemberCp2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCb1, InputFileData%Morison%CoefMembersCyl(I)%MemberCb2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG2 + + if ( KCFile1(1:2) == "KC" ) then + InputFileData%Morison%CoefMembersCyl(I)%MemberCd1 = 0.0 + InputFileData%Morison%CoefMembersCyl(I)%MemberCd2 = 0.0 + if ( trim(KCFile1) /= trim(KCFile2) ) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "MemberCd1 and MemberCd2 must be the same KC file reference when using KC-based input (e.g., KC1 and KC1)." + if (Failed()) return + end if + READ( KCFile1(3:), '(I10)', iostat=ErrStat2 ) InputFileData%Morison%CoefMembersCyl(I)%MemberiKC + else + READ(KCFile1,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberCd1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCd1 input needs to either be a real number or a string starting with 'KC' followed by an integer between 1 and NKCCd" + end if + READ(KCFile2,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberCd2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCd2 input needs to either be a real number or the same string as MemberCd1, starting with 'KC' followed by an integer between 1 and NKCCd" + end if + InputFileData%Morison%CoefMembersCyl(I)%MemberiKC = 0 if (Failed()) return + end if + + CurLine = CurLine + 1 - InputFileData%Morison%CoefMembersCyl(I)%MemberID = NINT(tmpReArray( 1)) - InputFileData%Morison%CoefMembersCyl(I)%MemberCd1 = tmpReArray( 2) - InputFileData%Morison%CoefMembersCyl(I)%MemberCd2 = tmpReArray( 3) - InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG1 = tmpReArray( 4) - InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG2 = tmpReArray( 5) - InputFileData%Morison%CoefMembersCyl(I)%MemberCa1 = tmpReArray( 6) - InputFileData%Morison%CoefMembersCyl(I)%MemberCa2 = tmpReArray( 7) - InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG1 = tmpReArray( 8) - InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG2 = tmpReArray( 9) - InputFileData%Morison%CoefMembersCyl(I)%MemberCp1 = tmpReArray(10) - InputFileData%Morison%CoefMembersCyl(I)%MemberCp2 = tmpReArray(11) - InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG1 = tmpReArray(12) - InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG2 = tmpReArray(13) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd1 = tmpReArray(14) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd2 = tmpReArray(15) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG1 = tmpReArray(16) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG2 = tmpReArray(17) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa1 = tmpReArray(18) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa2 = tmpReArray(19) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG1 = tmpReArray(20) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG2 = tmpReArray(21) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp1 = tmpReArray(22) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp2 = tmpReArray(23) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG1 = tmpReArray(24) - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG2 = tmpReArray(25) - InputFileData%Morison%CoefMembersCyl(I)%MemberCb1 = tmpReArray(26) - InputFileData%Morison%CoefMembersCyl(I)%MemberCb2 = tmpReArray(27) - InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG1 = tmpReArray(28) - InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG2 = tmpReArray(29) END DO if (allocated(tmpReArray)) deallocate(tmpReArray) @@ -1086,6 +1119,69 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi if (allocated(tmpReArray)) deallocate(tmpReArray) END IF + !------------------------------------------------------------------------------------------------- + ! KC-Cd function paths + !------------------------------------------------------------------------------------------------- + !call ParseVar( FileInfo_In, CurLine+1, 'NKCCd', NKCCd, ErrStat2, ErrMsg2, UnEc ) + ! if (Failed()) return; + + CurLine = CurLine + 1 + call ParseVar( FileInfo_In, CurLine, 'NKCCd', NKCCd, ErrStat2, ErrMsg2, UnEc ) + IF ( ErrStat2 == 0 ) THEN + + if ( InputFileData%Echo ) WRITE(UnEc, '(A)') trim(FileInfo_In%Lines(CurLine)) ! Write section break to echo + + ALLOCATE(InputFileData%Morison%iKCstart(NKCCd,2)) + + ! First loop to check number of rows and allocate concatenated array + totalNKC = 0_IntKi + DO I = 1,NKCCd + CALL ParseVar( FileInfo_In, CurLine, "", KCPath, ErrStat2, ErrMsg2, UnEc ) + CALL ProcessComFile ( KCPath, FileInfo_KC, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat >= AbortErrLev) THEN + CALL Cleanup() + RETURN + END IF + ! Assumed 1 header line -> NKC line -> column name line -> array + CurRow = 1_IntKi + CALL ParseVar ( FileInfo_KC, CurRow, 'NKC', NKC, ErrStat2, ErrMsg2, UnEc ) + InputFileData%Morison%iKCstart(I,1) = totalNKC + 1_IntKi + InputFileData%Morison%iKCstart(I,2) = totalNKC + NKC + totalNKC = totalNKC + NKC + END DO + ALLOCATE (InputFileData%Morison%KCCd(totalNKC,2)) + CurLine = CurLine - NKCCd + + ! Second loop to fill the concatenated array + DO I = 1,NKCCd + CALL ParseVar( FileInfo_In, CurLine, "", KCPath, ErrStat2, ErrMsg2, UnEc ) + CALL ProcessComFile ( KCPath, FileInfo_KC, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat >= AbortErrLev) THEN + CALL Cleanup() + RETURN + END IF + ! Assumed 1 header line -> NKC line -> column name line -> array + CurRow = 1_IntKi + CALL ParseVar ( FileInfo_KC, CurRow, 'NKC', NKC, ErrStat2, ErrMsg2, UnEc ) + CurRow = 2_IntKi + DO J=1,NKC + CALL ParseAry ( FileInfo_KC, CurRow, "", tmpKCCd, 2, ErrStat2, ErrMsg2, UnEc ) + InputFileData%Morison%KCCd(InputFileData%Morison%iKCstart(I,1)+J-1,1) = tmpKCCd(1) + InputFileData%Morison%KCCd(InputFileData%Morison%iKCstart(I,1)+J-1,2) = tmpKCCd(2) + ! Add check that all KC are ascending with error message if not + ! IF ( J > 1 ) THEN + ! IF ( tmpKCCd(1) <= InputFileData%Morison%KCCD(InputFileData%Morison%iKCstart(I,1)+J-2,1) ) THEN + ! Error Message + ! END IF + ! END IF + END DO ! J + END DO + + ELSE + CurLine = CurLine - 1 + END IF !------------------------------------------------------------------------------------------------- ! Member Output List Section diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index 4dfe7e438e..29468092b9 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -153,7 +153,6 @@ SUBROUTINE Morison_DirCosMtrx_noSpin( pos0, pos1, DirCos ) END SUBROUTINE Morison_DirCosMtrx_noSpin - SUBROUTINE GetDisplacedNodePosition( u, p, forceDisplaced, pos ) TYPE(Morison_InputType), INTENT(IN ) :: u !< Inputs at Time TYPE(Morison_ParameterType), INTENT(IN ) :: p !< Parameters @@ -1568,6 +1567,7 @@ SUBROUTINE SetExternalHydroCoefs_Cyl( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC ! Pull member end-node data from the tables and then linearly interpolate it onto the interior member nodes s = (real(i,ReKi)-1.0) / real(member%NElements,ReKi) if (member%flipped) s = 1.0-s + if (member%flipped) s = 1.0-s if ( member%tMG(i) > 0.0_ReKi ) then member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdMG2 *s member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaMG2 *s @@ -1576,6 +1576,7 @@ SUBROUTINE SetExternalHydroCoefs_Cyl( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG2*s member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG2*s member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG2*s + member%iKC (i) = CoefMembers(MmbrCoefIDIndx)%MemberiKC else member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCd1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCd2 *s member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCa1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCa2 *s @@ -1584,6 +1585,7 @@ SUBROUTINE SetExternalHydroCoefs_Cyl( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCd1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCd2 *s member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCa1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCa2 *s member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCp1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCp2 *s + member%iKC (i) = CoefMembers(MmbrCoefIDIndx)%MemberiKC end if end do member%propMCF = CoefMembers(MmbrCoefIDIndx)%MemberMCF @@ -1812,6 +1814,7 @@ subroutine AllocateMemberDataArrays( member, memberLoads, errStat, errMsg ) call AllocAry(member%Rin , member%NElements+1, 'member%Rin ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry(member%Cd , member%NElements+1, 'member%Cd ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry(member%Ca , member%NElements+1, 'member%Ca ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) + call AllocAry(member%iKC , member%NElements+1, 'member%iKC ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) else if (member%MSecGeom == MSecGeom_Rec) then call AllocAry(member%dSadl_mg , member%NElements, 'member%dSadl_mg' , errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry(member%dSadl_mg_b , member%NElements, 'member%dSadl_mg_b' , errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) @@ -1894,6 +1897,7 @@ subroutine AllocateMemberDataArrays( member, memberLoads, errStat, errMsg ) member%Rin = 0.0_ReKi member%Cd = 0.0_ReKi member%Ca = 0.0_ReKi + member%iKC = 0_IntKi else if (member%MSecGeom == MSecGeom_Rec) then member%dSadl_mg = 0.0_ReKi member%dSadl_mg_b = 0.0_ReKi @@ -2864,6 +2868,7 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In InitInp%Nodes(i)%JAxFDMod = InitInp%AxialCoefs(InitInp%InpJoints(i)%JointAxIDIndx)%AxFDMod InitInp%Nodes(i)%JAxVnCOff = InitInp%AxialCoefs(InitInp%InpJoints(i)%JointAxIDIndx)%AxVnCOff InitInp%Nodes(i)%JAxFDLoFSc = InitInp%AxialCoefs(InitInp%InpJoints(i)%JointAxIDIndx)%AxFDLoFSc + InitInp%Nodes(i)%JAxiKC = InitInp%AxialCoefs(InitInp%InpJoints(i)%JointAxIDIndx)%AxiKC ! Redundant work (these are already assigned to the member data arrays, ! but is needed on the joint data because we report the tMG, and MGDensity at each Joint node in the Summary File @@ -2966,6 +2971,7 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In x%DummyContState = 0 + ALLOCATE ( xd%V_rel_n_FiltStat(p%NJoints), STAT = ErrStat ) IF ( ErrStat /= ErrID_None ) THEN ErrMsg = ' Error allocating space for V_rel_n_FiltStat array.' @@ -2974,6 +2980,37 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In END IF xd%V_rel_n_FiltStat = 0.0_ReKi + ALLOCATE ( xd%AKC(p%NNodes), STAT = ErrStat ) + IF ( ErrStat /= ErrID_None ) THEN + ErrMsg = ' Error allocating space for AKC array.' + ErrStat = ErrID_Fatal + RETURN + END IF + xd%AKC = 0.0_ReKi + + ALLOCATE ( xd%vrel_rad_prev(3,p%NNodes), STAT = ErrStat ) + IF ( ErrStat /= ErrID_None ) THEN + ErrMsg = ' Error allocating space for vrel_rad_prev array.' + ErrStat = ErrID_Fatal + RETURN + END IF + xd%vrel_rad_prev = 0.0_ReKi + + ALLOCATE ( xd%vrel_ax_prev(3,p%NNodes), STAT = ErrStat ) + IF ( ErrStat /= ErrID_None ) THEN + ErrMsg = ' Error allocating space for vrel_ax_prev array.' + ErrStat = ErrID_Fatal + RETURN + END IF + xd%vrel_ax_prev = 0.0_ReKi + ALLOCATE ( xd%MV_rel_n_FiltStat(4,p%NNodes), STAT = ErrStat ) + IF ( ErrStat /= ErrID_None ) THEN + ErrMsg = ' Error allocating space for MV_rel_n_FiltStat array.' + ErrStat = ErrID_Fatal + RETURN + END IF + xd%MV_rel_n_FiltStat = 0.0_ReKi + ALLOCATE ( xd%MV_rel_n_FiltStat(4,p%NNodes), STAT = ErrStat ) IF ( ErrStat /= ErrID_None ) THEN ErrMsg = ' Error allocating space for MV_rel_n_FiltStat array.' @@ -3134,6 +3171,8 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In p%DragLoFSc_End (i) = InitInp%Nodes(i)%JAxFDLoFSc END IF + p%DragAxiKC(i) = InitInp%Nodes(i)%JAxiKC + END DO ! looping through nodes that are joints, i ! Copy ballast group information to parameters @@ -3165,6 +3204,10 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In END DO END DO + ! KC-Cd table + p%KCCd = InitInp%KCCd + p%iKCstart = InitInp%iKCstart + ! Define initial guess for the system inputs here: ! u%DummyInput = 0 ! Define system output initializations (set up mesh) here: @@ -3433,6 +3476,7 @@ SUBROUTINE AllocateNodeLoadVariables(InitInp, p, m, NNodes, errStat, errMsg ) call AllocAry( m%V_rel_n_HiPass , p%NJoints, 'm%V_rel_n_HiPass', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry( m%zFillGroup , p%NFillGroups, 'm%zFillGroup' , errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry( p%DragMod_End , p%NJoints, 'p%DragMod_End' , errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) + call AllocAry( p%DragAxiKC , p%NJoints, 'p%DragAxiKC' , errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry( p%DragLoFSc_End , p%NJoints, 'p%DragLoFSc_End' , errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry( p%VRelNFiltConst , p%NJoints, 'p%VRelNFiltConst', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) @@ -3528,6 +3572,9 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, REAL(ReKi) :: h, h_c_AM, deltal_AM REAL(ReKi) :: F_WMG(6), F_IMG(6), F_If(6), F_B0(6), F_B1(6), F_B2(6), F_B_End(6) REAL(ReKi) :: AM_End(3,3), An_End(3), DP_Const_End(3), I_MG_End(3,3) + REAL(ReKi) :: Cd_End_KC + REAL(ReKi) :: Cd_KC + INTEGER(IntKi) :: ILo ! Dummy starting index needed for interpbin ! Local variables needed for wave stretching and load smoothing/redistribution INTEGER(IntKi) :: FSElem @@ -4073,9 +4120,22 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, !-------------------- hydrodynamic drag loads: sides: Section 7.1.2 ------------------------! vec = matmul( mem%Ak,m%vrel(:,mem%NodeIndx(i)) ) IF (mem%MSecGeom==MSecGeom_Cyl) THEN - f_hydro = mem%Cd(i)*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part + IF ( mem%iKC(i) > 0_IntKi ) THEN + ILo = 1_IntKi + Cd_KC = InterpBin( xd%AKC(mem%NodeIndx(i))*2*Pi/(mem%RMG(i)*2), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),1), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),2), Ilo, & + (p%iKCstart(mem%iKC(i),2)-p%iKCstart(mem%iKC(i),1)+1) ) + END IF + IF ( mem%iKC(i) > 0_IntKi ) THEN + f_hydro = Cd_KC*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part 0.5*mem%AxCd(i)*p%WaveField%WtrDens * pi*mem%RMG(i)*dRdl_p * & ! axial part abs(dot_product( mem%k, m%vrel(:,mem%NodeIndx(i)) )) * matmul( mem%kkt, m%vrel(:,mem%NodeIndx(i)) ) ! axial part cont'd + ELSE + f_hydro = mem%Cd(i)*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part + 0.5*mem%AxCd(i)*p%WaveField%WtrDens * pi*mem%RMG(i)*dRdl_p * & ! axial part + abs(dot_product( mem%k, m%vrel(:,mem%NodeIndx(i)) )) * matmul( mem%kkt, m%vrel(:,mem%NodeIndx(i)) ) ! axial part cont'd + END IF ELSE IF (mem%MSecGeom==MSecGeom_Rec) THEN Call GetDistDrag_Rec(p, m, u, xd, Time,mem,i,dSadl_p,dSbdl_p,f_hydro,ErrStat2,ErrMsg2); CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END IF @@ -4440,9 +4500,20 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, !--------------------- hydrodynamic drag loads: sides: Section 7.1.2 --------------------------------! vec = matmul( mem%Ak,m%vrel(:,mem%NodeIndx(i)) ) IF (mem%MSecGeom==MSecGeom_Cyl) THEN - f_hydro = mem%Cd(i)*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part - 0.5*mem%AxCd(i)*p%WaveField%WtrDens*pi*mem%RMG(i)*dRdl_p * & ! axial part + IF ( mem%iKC(i) > 0_IntKi ) THEN + ILo = 1_IntKi + Cd_KC = InterpBin( xd%AKC(mem%NodeIndx(i))*2*Pi/(mem%RMG(i)*2), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),1), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),2), Ilo, & + (p%iKCstart(mem%iKC(i),2)-p%iKCstart(mem%iKC(i),1)+1) ) + f_hydro = Cd_KC*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part + 0.5*mem%AxCd(i)*p%WaveField%WtrDens * pi*mem%RMG(i)*dRdl_p * & ! axial part abs(dot_product( mem%k, m%vrel(:,mem%NodeIndx(i)) )) * matmul( mem%kkt, m%vrel(:,mem%NodeIndx(i)) ) ! axial part cont'd + ELSE + f_hydro = mem%Cd(i)*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part + 0.5*mem%AxCd(i)*p%WaveField%WtrDens * pi*mem%RMG(i)*dRdl_p * & ! axial part + abs(dot_product( mem%k, m%vrel(:,mem%NodeIndx(i)) )) * matmul( mem%kkt, m%vrel(:,mem%NodeIndx(i)) ) ! axial part cont'd + END IF ELSE IF (mem%MSecGeom==MSecGeom_Rec) THEN Call GetDistDrag_Rec(p, m, u, xd, Time,mem,i,dSadl_p,dSbdl_p,f_hydro,ErrStat2,ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -4761,6 +4832,16 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, ! High-pass filtering vmagf = p%VRelNFiltConst(J) * (vmag + xd%v_rel_n_FiltStat(J)) + ! Drag coefficient if instantaneous KC dependent drag is enabled + Cd_End_KC = 0.0_ReKi + IF ( p%DragAxiKC(J) > 0_IntKi ) THEN + ILo = 1_IntKi + Cd_End_KC = InterpBin( xd%AKC(J)*2*Pi/(sqrt(sqrt(dot_product(An_End,An_End))/Pi)), & + p%KCCd(p%iKCstart(p%DragAxiKC(J),1):p%iKCstart(p%DragAxiKC(J),2),1), & + p%KCCd(p%iKCstart(p%DragAxiKC(J),1):p%iKCstart(p%DragAxiKC(J),2),2), Ilo, & + (p%iKCstart(p%DragAxiKC(J),2)-p%iKCstart(p%DragAxiKC(J),1)+1) ) + END IF + ! Record most up-to-date vmagf and vmag at join J m%v_rel_n(j) = vmag m%v_rel_n_HiPass(j) = vmagf @@ -4770,24 +4851,37 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, IF (I < 4 ) THEN ! Three force components IF ( p%DragMod_End(J) .EQ. 0_IntKi ) THEN ! Note: vmag is zero if node is not in the water - m%F_D_End(i,j) = (1.0_ReKi - p%DragLoFSc_End(j)) * An_End(i) * p%DragConst_End(j) * abs(vmagf)*vmagf & - + p%DragLoFSc_End(j) * An_End(i) * p%DragConst_End(j) * abs(vmag )*vmag + IF ( p%DragAxiKC(J) > 0_IntKi ) THEN + m%F_D_End(i,j) = 0.25_ReKi * p%WaveField%WtrDens * Cd_End_KC * An_End(i) * abs(vmag)*vmag / dot_product(An_End,An_End) + ELSE + m%F_D_End(i,j) = (1.0_ReKi - p%DragLoFSc_End(j)) * An_End(i) * p%DragConst_End(j) * abs(vmagf)*vmagf & + + p%DragLoFSc_End(j) * An_End(i) * p%DragConst_End(j) * abs(vmag )*vmag + END IF ELSE IF (p%DragMod_End(J) .EQ. 1_IntKi) THEN ! Note: vmag is zero if node is not in the water - m%F_D_End(i,j) = (1.0_ReKi - p%DragLoFSc_End(j)) * An_End(i) * p%DragConst_End(j) * abs(vmagf)*max(vmagf,0.0_ReKi) & - + p%DragLoFSc_End(j) * An_End(i) * p%DragConst_End(j) * abs(vmag) *max(vmag, 0.0_ReKi) - m%F_D_End(i,j) = 2.0_ReKi * m%F_D_End(i,j) + IF ( p%DragAxiKC(J) > 0_IntKi ) THEN + m%F_D_End(i,j) = 0.5_ReKi * p%WaveField%WtrDens * Cd_End_KC * An_End(i) * abs(vmag)*vmag / dot_product(An_End,An_End) + ELSE + m%F_D_End(i,j) = (1.0_ReKi - p%DragLoFSc_End(j)) * An_End(i) * p%DragConst_End(j) * abs(vmagf)*max(vmagf,0.0_ReKi) & + + p%DragLoFSc_End(j) * An_End(i) * p%DragConst_End(j) * abs(vmag) *max(vmag, 0.0_ReKi) + m%F_D_End(i,j) = 2.0_ReKi * m%F_D_End(i,j) + END IF END IF m%F_tot_End(i,j) = m%F_D_End(i,j) + m%F_I_End(i,j) + p%F_WMG_End(i,j) + m%F_B_End(i,j) + m%F_BF_End(i,j) + m%F_A_End(i,j) + m%F_IMG_End(i,j) y%Mesh%Force(i,j) = y%Mesh%Force(i,j) + m%F_tot_End(i,j) ELSE ! Three moment components m%F_tot_End(i,j) = m%F_B_End(i,j) + m%F_BF_End(i,j) + m%F_IMG_End(i,j) y%Mesh%Moment(i-3,j) = y%Mesh%Moment(i-3,j) + m%F_tot_End(i,j) + m%F_tot_End(i,j) = m%F_B_End(i,j) + m%F_BF_End(i,j) + m%F_IMG_End(i,j) + y%Mesh%Moment(i-3,j) = y%Mesh%Moment(i-3,j) + m%F_tot_End(i,j) END IF END DO ! I=1,6 ! Compute and save the joint total moment about PRP for output file m%F_tot_End(4:6,j) = m%F_tot_End(4:6,j) + cross_product( u%Mesh%Position(:,j)+u%Mesh%TranslationDisp(:,j)-u%PRP, m%F_tot_End(1:3,j)) + ! Compute and save the joint total moment about PRP for output file + m%F_tot_End(4:6,j) = m%F_tot_End(4:6,j) + cross_product( u%Mesh%Position(:,j)+u%Mesh%TranslationDisp(:,j)-u%PRP, m%F_tot_End(1:3,j)) + END DO ! J = 1, p%NJoints !---------------------------------------------------------------------------------------------------------------! @@ -6450,13 +6544,10 @@ SUBROUTINE Morison_UpdateDiscState( Time, u, p, x, xd, z, OtherState, m, errStat TYPE(Morison_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 - INTEGER(IntKi) :: I, J, im, N - INTEGER(IntKi) :: nodeInWater, tmpInt - REAL(ReKi) :: pos(3), vrel(3), FV(3), vmag, vmagf, An_End(3) - REAL(ReKi) :: posFC(3), SVFC(3), vrelFC, vrelFCf + INTEGER(IntKi) :: I, J, im, N, nodeIndx, nodeInWater, tmpInt + REAL(ReKi) :: pos(3), vrel(3), FV(3), vmag, vmagf, An_End(3), vrel_rad(3), posFC(3), SVFC(3), vrelFC, vrelFCf REAL(SiKi) :: FVTmp(3),FATmp(3) TYPE(Morison_MemberType) :: mem !< Current member - INTEGER(IntKi) :: errStat2 CHARACTER(ErrMsgLen) :: errMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'Morison_UpdateDiscState' @@ -6491,8 +6582,55 @@ SUBROUTINE Morison_UpdateDiscState( Time, u, p, x, xd, z, OtherState, m, errStat ! Update relative normal velocity filter state for joint J xd%V_rel_n_FiltStat(J) = vmagf-vmag + ! Update amplitude for instantaneous axial KC number + vrel_rad = (An_End/TwoNorm(An_End)) * vrel + + IF ( p%DragAxiKC(J) > 0 ) THEN + IF (m%nodeInWater(j) == 0) THEN + xd%AKC(J) = 0 + ELSE + IF ( dot_product(vrel_rad, xd%vrel_ax_prev(:,J)) <= 0.0_ReKi ) THEN + xd%AKC(J) = 0 + END IF + xd%AKC(J) = xd%AKC(J) + TwoNorm(vrel_rad)*p%DT + END IF + WRITE(*,*) "Joint ", J, " vmag = ", TwoNorm(vrel_rad), " vmag_previous = ", TwoNorm(xd%vrel_ax_prev(:,J)), " AKC = ", xd%AKC(J) + END IF + + xd%vrel_ax_prev(:,J) = vrel_rad + END DO ! J = 1, p%NJoints + ! Update state of the relative radial velocity at each member + DO im = 1, p%NMembers + N = p%Members(im)%NElements + mem = p%Members(im) + DO i = mem%i_floor+1,N+1 + nodeIndx = mem%NodeIndx(i) + pos = m%DispNodePosHdn(:, nodeIndx) + CALL WaveField_GetNodeWaveVel( p%WaveField, m%WaveField_m, Time, pos, .FALSE., .TRUE., nodeInWater, FVTmp, ErrStat2, ErrMsg2 ) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + FV = REAL(FVTmp, ReKi) + vrel = ( FV - u%Mesh%TranslationVel(:,nodeIndx) ) * nodeInWater + + ! Relative radial velocity for this member node (normal to the member axis). + vrel_rad = matmul(mem%Ak, vrel) + + IF ( mem%iKC(i) > 0_IntKi ) THEN + IF (nodeInWater == 0_IntKi) THEN + xd%AKC(nodeIndx) = 0.0_ReKi + ELSE + IF ( dot_product(vrel_rad, xd%vrel_rad_prev(:,nodeIndx)) <= 0.0_ReKi ) THEN + xd%AKC(nodeIndx) = 0.0_ReKi + END IF + xd%AKC(nodeIndx) = xd%AKC(nodeIndx) + TwoNorm(vrel_rad)*p%DT + END IF + END IF + + xd%vrel_rad_prev(:,nodeIndx) = vrel_rad + END DO ! i = 1,N+1 ! loop through member nodes + END DO ! im + ! Update state of the relative normal velocity high-pass filter for each rectangular member DO im = 1, p%NMembers IF ( (p%Members(im)%MSecGeom == MSecGeom_Rec) .and. (p%Members(im)%FDMod > 0_IntKi) ) THEN diff --git a/modules/hydrodyn/src/Morison.txt b/modules/hydrodyn/src/Morison.txt index f8da63bbea..b43f4c5c42 100644 --- a/modules/hydrodyn/src/Morison.txt +++ b/modules/hydrodyn/src/Morison.txt @@ -80,7 +80,8 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi AxCp - - - "Axial Cp" - typedef ^ ^ ReKi AxVnCOff - - - "High-pass cut-off frequency for normal velocity when computing axial drag force" - typedef ^ ^ ReKi AxFDLoFSc - - - "Scaling factor for low frequency axial drag force" - -typedef ^ ^ IntKi AxFDMod - - - "Switch for the axial drag formulation {0: original formulation, 1: Away from member only}" - +typedef ^ ^ IntKi AxFDMod - - - "Switch for the axial drag formulation {0: original formulation, 1: Away from member only}" +typedef ^ ^ INTEGER AxiKC - - - "Integer of KC-Cd file to use if instantaneous KC dependent drag is enabled (0 if constant drag coefficient is used)" # typedef ^ Morison_MemberInputType INTEGER MemberID - - - "User-supplied integer ID for this member" - typedef ^ ^ INTEGER NodeIndx {:} - - "Index of each of the member's nodes in the master node list" - @@ -121,6 +122,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi JAxCp - - - "Nodal lumped (joint) axial Ca" - typedef ^ ^ ReKi JAxVnCOff - - - "High-pass cut-off frequency for normal velocity when computing axial drag force" - typedef ^ ^ ReKi JAxFDLoFSc - - - "Scaling factor for low frequency axial drag force" - +typedef ^ ^ INTEGER JAxiKC - - - "Integer of KC-Cd file to use if instantaneous KC dependent drag is enabled (0 if constant drag coefficient is used)" typedef ^ ^ IntKi JAxFDMod - - - "Switch for the axial drag formulation {0: original formulation, 1: Away from member only}" - typedef ^ ^ ReKi FillDensity - - - "Fill fluid density" kg/m^3 typedef ^ ^ ReKi tMG - - - "Nodal thickness with marine growth " m @@ -177,6 +179,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi alpha_fb {:} - - "relative volume centroid of each element's flooded ballast, from node i to node i+1" - typedef ^ ^ ReKi alpha_fb_star {:} - - "load distribution factor for each element after adjusting alpha_fb for node reference depths" - typedef ^ ^ ReKi Cd {:} - - "Member Cd at each node" - +typedef ^ ^ INTEGER iKC {:} - - "Member KC-Cd function index at each node" - typedef ^ ^ ReKi Ca {:} - - "Member Ca at each node" - typedef ^ ^ ReKi CdA {:} - - "Member Cd normal to side A at each node" - typedef ^ ^ ReKi CaA {:} - - "Member Ca normal to side A at each node" - @@ -254,6 +257,7 @@ typedef ^ ^ ReKi typedef ^ Morison_CoefMembersCyl INTEGER MemberID - - - "User-specified integer id for the Member-based coefs" - typedef ^ ^ ReKi MemberCd1 - - - "Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - typedef ^ ^ ReKi MemberCd2 - - - "Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - +typedef ^ ^ INTEGER MemberiKC - - - "Cylindrical member-based coefs, same iKC for both ends - typedef ^ ^ ReKi MemberCdMG1 - - - "Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - typedef ^ ^ ReKi MemberCdMG2 - - - "Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - typedef ^ ^ ReKi MemberCa1 - - - "Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - @@ -412,6 +416,8 @@ typedef ^ ^ INTEGER typedef ^ ^ SeaSt_WaveFieldType *WaveField - - - "Pointer to SeaState wave field" - typedef ^ ^ logical VisMeshes - .false. - "Output visualization meshes" - typedef ^ ^ INTEGER PtfmYMod - - - "Large yaw model" - +typedef ^ ^ ReKi KCCd {:}{:} - - "Concatenated array of KC-Cd functions for KC-dependent drag" +typedef ^ ^ INTEGER iKCstart {:}{:} - - "Start and stop indices for each KC-Cd function within the concatenated 'KCCd' array" # # # Define outputs from the initialization routine here: @@ -430,6 +436,9 @@ typedef ^ ContinuousStateType SiKi # Define discrete (nondifferentiable) states here: # typedef ^ DiscreteStateType ReKi V_rel_n_FiltStat {:} - - "State of the high-pass filter for the joint relative normal velocity" m/s +typedef ^ DiscreteStateType ReKi AKC {:} - - "State of the KC number amplitude for instantaneous KC dependent drag" +typedef ^ DiscreteStateType ReKi vrel_ax_prev {:}{:} - - "Relative axial velocity vector from previous time step" +typedef ^ DiscreteStateType ReKi vrel_rad_prev {:}{:} - - "Relative radial velocity vector from previous time step" typedef ^ DiscreteStateType ReKi MV_rel_n_FiltStat {:}{:} - - "State of the high-pass filter for the rectangular member relative normal velocity" m/s # # @@ -487,6 +496,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi DragConst_End {:} - - "" - typedef ^ ^ ReKi VRelNFiltConst {:} - - "" - typedef ^ ^ IntKi DragMod_End {:} - - "" - +typedef ^ ^ IntKi DragAxiKC {:} - - "" typedef ^ ^ ReKi DragLoFSc_End {:} - - "" - typedef ^ ^ ReKi F_WMG_End {:}{:} - - "Joint marine growth weight loads, constant for all t" N typedef ^ ^ ReKi DP_Const_End {:}{:} - - "Constant part of Joint dynamic pressure term" N @@ -504,6 +514,8 @@ typedef ^ ^ logical typedef ^ ^ INTEGER PtfmYMod - - - "Large yaw model" - typedef ^ ^ INTEGER NFillGroups - - - "" - typedef ^ ^ Morison_FilledGroupType FilledGroups {:} - - "" - +typedef ^ ^ ReKi KCCd {:}{:} - - "" +typedef ^ ^ INTEGER iKCstart {:}{:} - - "" # # # ..... Inputs .................................................................................................................... From 6f12b8e5532ee90e839407b631e4d2b098bd0bbf Mon Sep 17 00:00:00 2001 From: willwiley Date: Tue, 25 Aug 2026 13:42:12 -0600 Subject: [PATCH 2/8] KC dependent drag with types update --- modules/hydrodyn/src/Morison_Types.f90 | 190 +++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 8 deletions(-) diff --git a/modules/hydrodyn/src/Morison_Types.f90 b/modules/hydrodyn/src/Morison_Types.f90 index 3ab02c2918..9e5007f97f 100644 --- a/modules/hydrodyn/src/Morison_Types.f90 +++ b/modules/hydrodyn/src/Morison_Types.f90 @@ -125,6 +125,7 @@ MODULE Morison_Types REAL(ReKi) :: AxVnCOff = 0.0_ReKi !< High-pass cut-off frequency for normal velocity when computing axial drag force [-] REAL(ReKi) :: AxFDLoFSc = 0.0_ReKi !< Scaling factor for low frequency axial drag force [-] INTEGER(IntKi) :: AxFDMod = 0_IntKi !< Switch for the axial drag formulation {0: original formulation, 1: Away from member only} [-] + INTEGER(IntKi) :: AxiKC = 0_IntKi !< Integer of KC-Cd file to use if instantaneous KC dependent drag is enabled (0 if constant drag coefficient is used) [-] END TYPE Morison_AxialCoefType ! ======================= ! ========= Morison_MemberInputType ======= @@ -171,6 +172,7 @@ MODULE Morison_Types REAL(ReKi) :: JAxCp = 0.0_ReKi !< Nodal lumped (joint) axial Ca [-] REAL(ReKi) :: JAxVnCOff = 0.0_ReKi !< High-pass cut-off frequency for normal velocity when computing axial drag force [-] REAL(ReKi) :: JAxFDLoFSc = 0.0_ReKi !< Scaling factor for low frequency axial drag force [-] + INTEGER(IntKi) :: JAxiKC = 0_IntKi !< Integer of KC-Cd file to use if instantaneous KC dependent drag is enabled (0 if constant drag coefficient is used) [-] INTEGER(IntKi) :: JAxFDMod = 0_IntKi !< Switch for the axial drag formulation {0: original formulation, 1: Away from member only} [-] REAL(ReKi) :: FillDensity = 0.0_ReKi !< Fill fluid density [kg/m^3] REAL(ReKi) :: tMG = 0.0_ReKi !< Nodal thickness with marine growth [m] @@ -230,6 +232,7 @@ MODULE Morison_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: alpha_fb !< relative volume centroid of each element's flooded ballast, from node i to node i+1 [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: alpha_fb_star !< load distribution factor for each element after adjusting alpha_fb for node reference depths [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Cd !< Member Cd at each node [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: iKC !< Member KC-Cd function index at each node [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Ca !< Member Ca at each node [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: CdA !< Member Cd normal to side A at each node [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: CaA !< Member Ca normal to side A at each node [-] @@ -306,6 +309,7 @@ MODULE Morison_Types INTEGER(IntKi) :: MemberID = 0_IntKi !< User-specified integer id for the Member-based coefs [-] REAL(ReKi) :: MemberCd1 = 0.0_ReKi !< Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] REAL(ReKi) :: MemberCd2 = 0.0_ReKi !< Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] + INTEGER(IntKi) :: MemberiKC = 0_IntKi REAL(ReKi) :: MemberCdMG1 = 0.0_ReKi !< Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] REAL(ReKi) :: MemberCdMG2 = 0.0_ReKi !< Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] REAL(ReKi) :: MemberCa1 = 0.0_ReKi !< Cylindrical member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] @@ -480,6 +484,8 @@ MODULE Morison_Types TYPE(SeaSt_WaveFieldType) , POINTER :: WaveField => NULL() !< Pointer to SeaState wave field [-] LOGICAL :: VisMeshes = .false. !< Output visualization meshes [-] INTEGER(IntKi) :: PtfmYMod = 0_IntKi !< Large yaw model [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: KCCd !< Concatenated array of KC-Cd functions for KC-dependent drag [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: iKCstart !< Start and stop indices for each KC-Cd function within the concatenated 'KCCd' array [-] END TYPE Morison_InitInputType ! ======================= ! ========= Morison_InitOutputType ======= @@ -497,6 +503,9 @@ MODULE Morison_Types ! ========= Morison_DiscreteStateType ======= TYPE, PUBLIC :: Morison_DiscreteStateType REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: V_rel_n_FiltStat !< State of the high-pass filter for the joint relative normal velocity [m/s] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AKC !< State of the KC number amplitude for instantaneous KC dependent drag [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: vrel_ax_prev !< Relative axial velocity vector from previous time step [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: vrel_rad_prev !< Relative radial velocity vector from previous time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MV_rel_n_FiltStat !< State of the high-pass filter for the rectangular member relative normal velocity [m/s] END TYPE Morison_DiscreteStateType ! ======================= @@ -554,6 +563,7 @@ MODULE Morison_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: DragConst_End !< [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: VRelNFiltConst !< [-] INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: DragMod_End !< [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: DragAxiKC !< [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: DragLoFSc_End !< [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: F_WMG_End !< Joint marine growth weight loads, constant for all t [N] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: DP_Const_End !< Constant part of Joint dynamic pressure term [N] @@ -571,6 +581,8 @@ MODULE Morison_Types INTEGER(IntKi) :: PtfmYMod = 0_IntKi !< Large yaw model [-] INTEGER(IntKi) :: NFillGroups = 0_IntKi !< [-] TYPE(Morison_FilledGroupType) , DIMENSION(:), ALLOCATABLE :: FilledGroups !< [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: KCCd !< [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: iKCstart !< [-] END TYPE Morison_ParameterType ! ======================= ! ========= Morison_InputType ======= @@ -1010,6 +1022,7 @@ subroutine Morison_CopyAxialCoefType(SrcAxialCoefTypeData, DstAxialCoefTypeData, DstAxialCoefTypeData%AxVnCOff = SrcAxialCoefTypeData%AxVnCOff DstAxialCoefTypeData%AxFDLoFSc = SrcAxialCoefTypeData%AxFDLoFSc DstAxialCoefTypeData%AxFDMod = SrcAxialCoefTypeData%AxFDMod + DstAxialCoefTypeData%AxiKC = SrcAxialCoefTypeData%AxiKC end subroutine subroutine Morison_DestroyAxialCoefType(AxialCoefTypeData, ErrStat, ErrMsg) @@ -1033,6 +1046,7 @@ subroutine Morison_PackAxialCoefType(RF, Indata) call RegPack(RF, InData%AxVnCOff) call RegPack(RF, InData%AxFDLoFSc) call RegPack(RF, InData%AxFDMod) + call RegPack(RF, InData%AxiKC) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1048,6 +1062,7 @@ subroutine Morison_UnPackAxialCoefType(RF, OutData) call RegUnpack(RF, OutData%AxVnCOff); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%AxFDLoFSc); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%AxFDMod); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%AxiKC); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine Morison_CopyMemberInputType(SrcMemberInputTypeData, DstMemberInputTypeData, CtrlCode, ErrStat, ErrMsg) @@ -1205,6 +1220,7 @@ subroutine Morison_CopyNodeType(SrcNodeTypeData, DstNodeTypeData, CtrlCode, ErrS DstNodeTypeData%JAxCp = SrcNodeTypeData%JAxCp DstNodeTypeData%JAxVnCOff = SrcNodeTypeData%JAxVnCOff DstNodeTypeData%JAxFDLoFSc = SrcNodeTypeData%JAxFDLoFSc + DstNodeTypeData%JAxiKC = SrcNodeTypeData%JAxiKC DstNodeTypeData%JAxFDMod = SrcNodeTypeData%JAxFDMod DstNodeTypeData%FillDensity = SrcNodeTypeData%FillDensity DstNodeTypeData%tMG = SrcNodeTypeData%tMG @@ -1236,6 +1252,7 @@ subroutine Morison_PackNodeType(RF, Indata) call RegPack(RF, InData%JAxCp) call RegPack(RF, InData%JAxVnCOff) call RegPack(RF, InData%JAxFDLoFSc) + call RegPack(RF, InData%JAxiKC) call RegPack(RF, InData%JAxFDMod) call RegPack(RF, InData%FillDensity) call RegPack(RF, InData%tMG) @@ -1259,6 +1276,7 @@ subroutine Morison_UnPackNodeType(RF, OutData) call RegUnpack(RF, OutData%JAxCp); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%JAxVnCOff); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%JAxFDLoFSc); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%JAxiKC); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%JAxFDMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%FillDensity); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%tMG); if (RegCheckErr(RF, RoutineName)) return @@ -1646,6 +1664,18 @@ subroutine Morison_CopyMemberType(SrcMemberTypeData, DstMemberTypeData, CtrlCode end if DstMemberTypeData%Cd = SrcMemberTypeData%Cd end if + if (allocated(SrcMemberTypeData%iKC)) then + LB(1:1) = lbound(SrcMemberTypeData%iKC) + UB(1:1) = ubound(SrcMemberTypeData%iKC) + if (.not. allocated(DstMemberTypeData%iKC)) then + allocate(DstMemberTypeData%iKC(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMemberTypeData%iKC.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMemberTypeData%iKC = SrcMemberTypeData%iKC + end if if (allocated(SrcMemberTypeData%Ca)) then LB(1:1) = lbound(SrcMemberTypeData%Ca) UB(1:1) = ubound(SrcMemberTypeData%Ca) @@ -2168,6 +2198,9 @@ subroutine Morison_DestroyMemberType(MemberTypeData, ErrStat, ErrMsg) if (allocated(MemberTypeData%Cd)) then deallocate(MemberTypeData%Cd) end if + if (allocated(MemberTypeData%iKC)) then + deallocate(MemberTypeData%iKC) + end if if (allocated(MemberTypeData%Ca)) then deallocate(MemberTypeData%Ca) end if @@ -2328,6 +2361,7 @@ subroutine Morison_PackMemberType(RF, Indata) call RegPackAlloc(RF, InData%alpha_fb) call RegPackAlloc(RF, InData%alpha_fb_star) call RegPackAlloc(RF, InData%Cd) + call RegPackAlloc(RF, InData%iKC) call RegPackAlloc(RF, InData%Ca) call RegPackAlloc(RF, InData%CdA) call RegPackAlloc(RF, InData%CaA) @@ -2442,6 +2476,7 @@ subroutine Morison_UnPackMemberType(RF, OutData) call RegUnpackAlloc(RF, OutData%alpha_fb); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%alpha_fb_star); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%Cd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%iKC); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%Ca); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%CdA); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%CaA); if (RegCheckErr(RF, RoutineName)) return @@ -2755,6 +2790,7 @@ subroutine Morison_CopyCoefMembersCyl(SrcCoefMembersCylData, DstCoefMembersCylDa DstCoefMembersCylData%MemberID = SrcCoefMembersCylData%MemberID DstCoefMembersCylData%MemberCd1 = SrcCoefMembersCylData%MemberCd1 DstCoefMembersCylData%MemberCd2 = SrcCoefMembersCylData%MemberCd2 + DstCoefMembersCylData%MemberiKC = SrcCoefMembersCylData%MemberiKC DstCoefMembersCylData%MemberCdMG1 = SrcCoefMembersCylData%MemberCdMG1 DstCoefMembersCylData%MemberCdMG2 = SrcCoefMembersCylData%MemberCdMG2 DstCoefMembersCylData%MemberCa1 = SrcCoefMembersCylData%MemberCa1 @@ -2801,6 +2837,7 @@ subroutine Morison_PackCoefMembersCyl(RF, Indata) call RegPack(RF, InData%MemberID) call RegPack(RF, InData%MemberCd1) call RegPack(RF, InData%MemberCd2) + call RegPack(RF, InData%MemberiKC) call RegPack(RF, InData%MemberCdMG1) call RegPack(RF, InData%MemberCdMG2) call RegPack(RF, InData%MemberCa1) @@ -2839,6 +2876,7 @@ subroutine Morison_UnPackCoefMembersCyl(RF, OutData) call RegUnpack(RF, OutData%MemberID); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCd1); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCd2); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MemberiKC); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdMG1); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdMG2); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCa1); if (RegCheckErr(RF, RoutineName)) return @@ -3259,8 +3297,8 @@ subroutine Morison_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, E integer(IntKi), intent(in ) :: CtrlCode integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg - integer(B4Ki) :: i1 - integer(B4Ki) :: LB(1), UB(1) + integer(B4Ki) :: i1, i2 + integer(B4Ki) :: LB(2), UB(2) integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'Morison_CopyInitInput' @@ -3562,14 +3600,38 @@ subroutine Morison_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, E DstInitInputData%WaveField => SrcInitInputData%WaveField DstInitInputData%VisMeshes = SrcInitInputData%VisMeshes DstInitInputData%PtfmYMod = SrcInitInputData%PtfmYMod + if (allocated(SrcInitInputData%KCCd)) then + LB(1:2) = lbound(SrcInitInputData%KCCd) + UB(1:2) = ubound(SrcInitInputData%KCCd) + if (.not. allocated(DstInitInputData%KCCd)) then + allocate(DstInitInputData%KCCd(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%KCCd.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitInputData%KCCd = SrcInitInputData%KCCd + end if + if (allocated(SrcInitInputData%iKCstart)) then + LB(1:2) = lbound(SrcInitInputData%iKCstart) + UB(1:2) = ubound(SrcInitInputData%iKCstart) + if (.not. allocated(DstInitInputData%iKCstart)) then + allocate(DstInitInputData%iKCstart(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%iKCstart.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitInputData%iKCstart = SrcInitInputData%iKCstart + end if end subroutine subroutine Morison_DestroyInitInput(InitInputData, ErrStat, ErrMsg) type(Morison_InitInputType), intent(inout) :: InitInputData integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg - integer(B4Ki) :: i1 - integer(B4Ki) :: LB(1), UB(1) + integer(B4Ki) :: i1, i2 + integer(B4Ki) :: LB(2), UB(2) integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'Morison_DestroyInitInput' @@ -3705,14 +3767,20 @@ subroutine Morison_DestroyInitInput(InitInputData, ErrStat, ErrMsg) deallocate(InitInputData%OutList) end if nullify(InitInputData%WaveField) + if (allocated(InitInputData%KCCd)) then + deallocate(InitInputData%KCCd) + end if + if (allocated(InitInputData%iKCstart)) then + deallocate(InitInputData%iKCstart) + end if end subroutine subroutine Morison_PackInitInput(RF, Indata) type(RegFile), intent(inout) :: RF type(Morison_InitInputType), intent(in) :: InData character(*), parameter :: RoutineName = 'Morison_PackInitInput' - integer(B4Ki) :: i1 - integer(B4Ki) :: LB(1), UB(1) + integer(B4Ki) :: i1, i2 + integer(B4Ki) :: LB(2), UB(2) logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return call RegPack(RF, InData%Gravity) @@ -3908,6 +3976,8 @@ subroutine Morison_PackInitInput(RF, Indata) end if call RegPack(RF, InData%VisMeshes) call RegPack(RF, InData%PtfmYMod) + call RegPackAlloc(RF, InData%KCCd) + call RegPackAlloc(RF, InData%iKCstart) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -3915,8 +3985,8 @@ subroutine Morison_UnPackInitInput(RF, OutData) type(RegFile), intent(inout) :: RF type(Morison_InitInputType), intent(inout) :: OutData character(*), parameter :: RoutineName = 'Morison_UnPackInitInput' - integer(B4Ki) :: i1 - integer(B4Ki) :: LB(1), UB(1) + integer(B4Ki) :: i1, i2 + integer(B4Ki) :: LB(2), UB(2) integer(IntKi) :: stat logical :: IsAllocAssoc integer(B8Ki) :: PtrIdx @@ -4182,6 +4252,8 @@ subroutine Morison_UnPackInitInput(RF, OutData) end if call RegUnpack(RF, OutData%VisMeshes); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PtfmYMod); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%KCCd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%iKCstart); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine Morison_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) @@ -4336,6 +4408,42 @@ subroutine Morison_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, E end if DstDiscStateData%V_rel_n_FiltStat = SrcDiscStateData%V_rel_n_FiltStat end if + if (allocated(SrcDiscStateData%AKC)) then + LB(1:1) = lbound(SrcDiscStateData%AKC) + UB(1:1) = ubound(SrcDiscStateData%AKC) + if (.not. allocated(DstDiscStateData%AKC)) then + allocate(DstDiscStateData%AKC(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%AKC.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstDiscStateData%AKC = SrcDiscStateData%AKC + end if + if (allocated(SrcDiscStateData%vrel_ax_prev)) then + LB(1:2) = lbound(SrcDiscStateData%vrel_ax_prev) + UB(1:2) = ubound(SrcDiscStateData%vrel_ax_prev) + if (.not. allocated(DstDiscStateData%vrel_ax_prev)) then + allocate(DstDiscStateData%vrel_ax_prev(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%vrel_ax_prev.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstDiscStateData%vrel_ax_prev = SrcDiscStateData%vrel_ax_prev + end if + if (allocated(SrcDiscStateData%vrel_rad_prev)) then + LB(1:2) = lbound(SrcDiscStateData%vrel_rad_prev) + UB(1:2) = ubound(SrcDiscStateData%vrel_rad_prev) + if (.not. allocated(DstDiscStateData%vrel_rad_prev)) then + allocate(DstDiscStateData%vrel_rad_prev(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%vrel_rad_prev.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstDiscStateData%vrel_rad_prev = SrcDiscStateData%vrel_rad_prev + end if if (allocated(SrcDiscStateData%MV_rel_n_FiltStat)) then LB(1:2) = lbound(SrcDiscStateData%MV_rel_n_FiltStat) UB(1:2) = ubound(SrcDiscStateData%MV_rel_n_FiltStat) @@ -4360,6 +4468,15 @@ subroutine Morison_DestroyDiscState(DiscStateData, ErrStat, ErrMsg) if (allocated(DiscStateData%V_rel_n_FiltStat)) then deallocate(DiscStateData%V_rel_n_FiltStat) end if + if (allocated(DiscStateData%AKC)) then + deallocate(DiscStateData%AKC) + end if + if (allocated(DiscStateData%vrel_ax_prev)) then + deallocate(DiscStateData%vrel_ax_prev) + end if + if (allocated(DiscStateData%vrel_rad_prev)) then + deallocate(DiscStateData%vrel_rad_prev) + end if if (allocated(DiscStateData%MV_rel_n_FiltStat)) then deallocate(DiscStateData%MV_rel_n_FiltStat) end if @@ -4371,6 +4488,9 @@ subroutine Morison_PackDiscState(RF, Indata) character(*), parameter :: RoutineName = 'Morison_PackDiscState' if (RF%ErrStat >= AbortErrLev) return call RegPackAlloc(RF, InData%V_rel_n_FiltStat) + call RegPackAlloc(RF, InData%AKC) + call RegPackAlloc(RF, InData%vrel_ax_prev) + call RegPackAlloc(RF, InData%vrel_rad_prev) call RegPackAlloc(RF, InData%MV_rel_n_FiltStat) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -4384,6 +4504,9 @@ subroutine Morison_UnPackDiscState(RF, OutData) logical :: IsAllocAssoc if (RF%ErrStat /= ErrID_None) return call RegUnpackAlloc(RF, OutData%V_rel_n_FiltStat); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%AKC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%vrel_ax_prev); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%vrel_rad_prev); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%MV_rel_n_FiltStat); if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -5027,6 +5150,18 @@ subroutine Morison_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrM end if DstParamData%DragMod_End = SrcParamData%DragMod_End end if + if (allocated(SrcParamData%DragAxiKC)) then + LB(1:1) = lbound(SrcParamData%DragAxiKC) + UB(1:1) = ubound(SrcParamData%DragAxiKC) + if (.not. allocated(DstParamData%DragAxiKC)) then + allocate(DstParamData%DragAxiKC(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%DragAxiKC.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstParamData%DragAxiKC = SrcParamData%DragAxiKC + end if if (allocated(SrcParamData%DragLoFSc_End)) then LB(1:1) = lbound(SrcParamData%DragLoFSc_End) UB(1:1) = ubound(SrcParamData%DragLoFSc_End) @@ -5159,6 +5294,30 @@ subroutine Morison_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrM if (ErrStat >= AbortErrLev) return end do end if + if (allocated(SrcParamData%KCCd)) then + LB(1:2) = lbound(SrcParamData%KCCd) + UB(1:2) = ubound(SrcParamData%KCCd) + if (.not. allocated(DstParamData%KCCd)) then + allocate(DstParamData%KCCd(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%KCCd.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstParamData%KCCd = SrcParamData%KCCd + end if + if (allocated(SrcParamData%iKCstart)) then + LB(1:2) = lbound(SrcParamData%iKCstart) + UB(1:2) = ubound(SrcParamData%iKCstart) + if (.not. allocated(DstParamData%iKCstart)) then + allocate(DstParamData%iKCstart(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%iKCstart.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstParamData%iKCstart = SrcParamData%iKCstart + end if end subroutine subroutine Morison_DestroyParam(ParamData, ErrStat, ErrMsg) @@ -5196,6 +5355,9 @@ subroutine Morison_DestroyParam(ParamData, ErrStat, ErrMsg) if (allocated(ParamData%DragMod_End)) then deallocate(ParamData%DragMod_End) end if + if (allocated(ParamData%DragAxiKC)) then + deallocate(ParamData%DragAxiKC) + end if if (allocated(ParamData%DragLoFSc_End)) then deallocate(ParamData%DragLoFSc_End) end if @@ -5248,6 +5410,12 @@ subroutine Morison_DestroyParam(ParamData, ErrStat, ErrMsg) end do deallocate(ParamData%FilledGroups) end if + if (allocated(ParamData%KCCd)) then + deallocate(ParamData%KCCd) + end if + if (allocated(ParamData%iKCstart)) then + deallocate(ParamData%iKCstart) + end if end subroutine subroutine Morison_PackParam(RF, Indata) @@ -5280,6 +5448,7 @@ subroutine Morison_PackParam(RF, Indata) call RegPackAlloc(RF, InData%DragConst_End) call RegPackAlloc(RF, InData%VRelNFiltConst) call RegPackAlloc(RF, InData%DragMod_End) + call RegPackAlloc(RF, InData%DragAxiKC) call RegPackAlloc(RF, InData%DragLoFSc_End) call RegPackAlloc(RF, InData%F_WMG_End) call RegPackAlloc(RF, InData%DP_Const_End) @@ -5335,6 +5504,8 @@ subroutine Morison_PackParam(RF, Indata) call Morison_PackFilledGroupType(RF, InData%FilledGroups(i1)) end do end if + call RegPackAlloc(RF, InData%KCCd) + call RegPackAlloc(RF, InData%iKCstart) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -5375,6 +5546,7 @@ subroutine Morison_UnPackParam(RF, OutData) call RegUnpackAlloc(RF, OutData%DragConst_End); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%VRelNFiltConst); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%DragMod_End); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%DragAxiKC); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%DragLoFSc_End); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%F_WMG_End); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%DP_Const_End); if (RegCheckErr(RF, RoutineName)) return @@ -5457,6 +5629,8 @@ subroutine Morison_UnPackParam(RF, OutData) call Morison_UnpackFilledGroupType(RF, OutData%FilledGroups(i1)) ! FilledGroups end do end if + call RegUnpackAlloc(RF, OutData%KCCd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%iKCstart); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine Morison_CopyInput(SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg) From 94cb65f4204c90392c14f18db15df3ad95c345fb Mon Sep 17 00:00:00 2001 From: willwiley Date: Tue, 25 Aug 2026 14:29:56 -0600 Subject: [PATCH 3/8] KC dependent drag with types update and allocate fix --- modules/hydrodyn/src/Morison.f90 | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index 29468092b9..16a87f3465 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -3003,13 +3003,6 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In RETURN END IF xd%vrel_ax_prev = 0.0_ReKi - ALLOCATE ( xd%MV_rel_n_FiltStat(4,p%NNodes), STAT = ErrStat ) - IF ( ErrStat /= ErrID_None ) THEN - ErrMsg = ' Error allocating space for MV_rel_n_FiltStat array.' - ErrStat = ErrID_Fatal - RETURN - END IF - xd%MV_rel_n_FiltStat = 0.0_ReKi ALLOCATE ( xd%MV_rel_n_FiltStat(4,p%NNodes), STAT = ErrStat ) IF ( ErrStat /= ErrID_None ) THEN From 3f9deced0f90870ade15a2eb391899f20de0d025 Mon Sep 17 00:00:00 2001 From: willwiley Date: Thu, 27 Aug 2026 14:26:59 -0600 Subject: [PATCH 4/8] update KC-Cd function input to assign dummy values values if no table is present --- modules/hydrodyn/src/HydroDyn_Input.f90 | 17 ++++++++++++----- modules/hydrodyn/src/Morison.f90 | 3 +-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/hydrodyn/src/HydroDyn_Input.f90 b/modules/hydrodyn/src/HydroDyn_Input.f90 index ee1a2826f4..adc28aaa48 100644 --- a/modules/hydrodyn/src/HydroDyn_Input.f90 +++ b/modules/hydrodyn/src/HydroDyn_Input.f90 @@ -1171,16 +1171,23 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi InputFileData%Morison%KCCd(InputFileData%Morison%iKCstart(I,1)+J-1,1) = tmpKCCd(1) InputFileData%Morison%KCCd(InputFileData%Morison%iKCstart(I,1)+J-1,2) = tmpKCCd(2) ! Add check that all KC are ascending with error message if not - ! IF ( J > 1 ) THEN - ! IF ( tmpKCCd(1) <= InputFileData%Morison%KCCD(InputFileData%Morison%iKCstart(I,1)+J-2,1) ) THEN - ! Error Message - ! END IF - ! END IF + IF ( J > 1 ) THEN + IF ( tmpKCCd(1) <= InputFileData%Morison%KCCd(InputFileData%Morison%iKCstart(I,1)+J-2,1) ) THEN + CALL SetErrStat( ErrID_Fatal, 'All KC values in a KC-Cd input file must be in ascending order.', ErrStat, ErrMsg, RoutineName ) + CALL Cleanup() + RETURN + END IF + END IF END DO ! J END DO ELSE + + ! If no KC-Cd function paths are provided, allocate empty arrays and reset CurLine CurLine = CurLine - 1 + ALLOCATE(InputFileData%Morison%iKCstart(0,2)) + ALLOCATE(InputFileData%Morison%KCCd(0,2)) + END IF !------------------------------------------------------------------------------------------------- diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index 16a87f3465..528f3d1f0b 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -3003,7 +3003,7 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In RETURN END IF xd%vrel_ax_prev = 0.0_ReKi - + ALLOCATE ( xd%MV_rel_n_FiltStat(4,p%NNodes), STAT = ErrStat ) IF ( ErrStat /= ErrID_None ) THEN ErrMsg = ' Error allocating space for MV_rel_n_FiltStat array.' @@ -6587,7 +6587,6 @@ SUBROUTINE Morison_UpdateDiscState( Time, u, p, x, xd, z, OtherState, m, errStat END IF xd%AKC(J) = xd%AKC(J) + TwoNorm(vrel_rad)*p%DT END IF - WRITE(*,*) "Joint ", J, " vmag = ", TwoNorm(vrel_rad), " vmag_previous = ", TwoNorm(xd%vrel_ax_prev(:,J)), " AKC = ", xd%AKC(J) END IF xd%vrel_ax_prev(:,J) = vrel_rad From 03dd647f5612e12b7ffcb3796195e2c5e68f7709 Mon Sep 17 00:00:00 2001 From: willwiley Date: Mon, 31 Aug 2026 16:36:00 -0600 Subject: [PATCH 5/8] Remove duplicated lines and fix input mistake that didn't set MCF --- modules/hydrodyn/src/HydroDyn_Input.f90 | 46 ++++++++++++++++++++++--- modules/hydrodyn/src/Morison.f90 | 11 ++---- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/modules/hydrodyn/src/HydroDyn_Input.f90 b/modules/hydrodyn/src/HydroDyn_Input.f90 index adc28aaa48..1597ff723e 100644 --- a/modules/hydrodyn/src/HydroDyn_Input.f90 +++ b/modules/hydrodyn/src/HydroDyn_Input.f90 @@ -81,8 +81,9 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi INTEGER :: UnEc ! The local unit number for this module's echo file CHARACTER(1024) :: EchoFile ! Name of HydroDyn echo file CHARACTER(MaxFileInfoLineLen) :: Line ! String to temporarially hold value of read line - CHARACTER(8) :: KCFile1 ! String to temporarilly hold value of KC-Cd function name and number - CHARACTER(8) :: KCFile2 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(16) :: KCFile1 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(16) :: KCFile2 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(8) :: CpMCF1, CpMCF2, CpMCFMG1, CpMCFMG2 ! Strings to temporarilly hold MemberCp entries, which may be the 'MCF' keyword CHARACTER(1024) :: KCPath ! String to temporarilly hold path name of KC-Cd function file TYPE(FileInfoType) :: FileInfo_KC ! Temporary derived type to hold KC file information INTEGER :: NDOF ! Number of DOF in each WAMIT module @@ -96,7 +97,6 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi INTEGER(IntKi) :: NKC ! Temporary number of lines in a KC-Cd function file INTEGER(IntKi) :: totalNKC ! Temporary counter for the total number of lines in concatenated KC table REAL(ReKi), DIMENSION(2) :: tmpKCCd ! Temporary array for reading row of KC-Cd function files - INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'HydroDyn_ParaseInput' @@ -821,8 +821,8 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG2, & InputFileData%Morison%CoefMembersCyl(I)%MemberCa1, InputFileData%Morison%CoefMembersCyl(I)%MemberCa2, & InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCp1, InputFileData%Morison%CoefMembersCyl(I)%MemberCp2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG2, & + CpMCF1, CpMCF2, & + CpMCFMG1, CpMCFMG2, & InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd2, & InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG2, & InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa2, & @@ -854,6 +854,42 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi if (Failed()) return end if + ! MCF keyword must appear in all four of MemberCp1, MemberCp2, MemberCpMG1, MemberCpMG2, or none of them + IF ( trim(CpMCF1) == 'MCF' .OR. trim(CpMCF2) == 'MCF' .OR. trim(CpMCFMG1) == 'MCF' .OR. trim(CpMCFMG2) == 'MCF' ) THEN + IF ( trim(CpMCF1) /= 'MCF' .OR. trim(CpMCF2) /= 'MCF' .OR. trim(CpMCFMG1) /= 'MCF' .OR. trim(CpMCFMG2) /= 'MCF' ) THEN + ErrStat2 = ErrID_Fatal + ErrMsg2 = "When parsing member-based cylindrical member hydrodynamic coefficients table, MCF is used at some but not all of MemberCp1, MemberCp2, MemberCpMG1, MemberCpMG2." + if (Failed()) return + END IF + InputFileData%Morison%CoefMembersCyl(I)%MemberMCF = .true. + InputFileData%Morison%CoefMembersCyl(I)%MemberCp1 = 1.0_ReKi + InputFileData%Morison%CoefMembersCyl(I)%MemberCp2 = 1.0_ReKi + InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG1 = 1.0_ReKi + InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG2 = 1.0_ReKi + ELSE + InputFileData%Morison%CoefMembersCyl(I)%MemberMCF = .false. + READ(CpMCF1, *,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberCp1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCp1 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + READ(CpMCF2, *,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberCp2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCp2 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + READ(CpMCFMG1,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCpMG1 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + READ(CpMCFMG2,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberCpMG2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCpMG2 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + END IF + CurLine = CurLine + 1 END DO diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index 528f3d1f0b..ef433cc33a 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -153,6 +153,7 @@ SUBROUTINE Morison_DirCosMtrx_noSpin( pos0, pos1, DirCos ) END SUBROUTINE Morison_DirCosMtrx_noSpin + SUBROUTINE GetDisplacedNodePosition( u, p, forceDisplaced, pos ) TYPE(Morison_InputType), INTENT(IN ) :: u !< Inputs at Time TYPE(Morison_ParameterType), INTENT(IN ) :: p !< Parameters @@ -1567,7 +1568,6 @@ SUBROUTINE SetExternalHydroCoefs_Cyl( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC ! Pull member end-node data from the tables and then linearly interpolate it onto the interior member nodes s = (real(i,ReKi)-1.0) / real(member%NElements,ReKi) if (member%flipped) s = 1.0-s - if (member%flipped) s = 1.0-s if ( member%tMG(i) > 0.0_ReKi ) then member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdMG2 *s member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaMG2 *s @@ -4119,8 +4119,6 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),1), & p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),2), Ilo, & (p%iKCstart(mem%iKC(i),2)-p%iKCstart(mem%iKC(i),1)+1) ) - END IF - IF ( mem%iKC(i) > 0_IntKi ) THEN f_hydro = Cd_KC*p%WaveField%WtrDens*mem%RMG(i)*TwoNorm(vec)*vec + & ! radial part 0.5*mem%AxCd(i)*p%WaveField%WtrDens * pi*mem%RMG(i)*dRdl_p * & ! axial part abs(dot_product( mem%k, m%vrel(:,mem%NodeIndx(i)) )) * matmul( mem%kkt, m%vrel(:,mem%NodeIndx(i)) ) ! axial part cont'd @@ -4864,17 +4862,12 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, y%Mesh%Force(i,j) = y%Mesh%Force(i,j) + m%F_tot_End(i,j) ELSE ! Three moment components m%F_tot_End(i,j) = m%F_B_End(i,j) + m%F_BF_End(i,j) + m%F_IMG_End(i,j) - y%Mesh%Moment(i-3,j) = y%Mesh%Moment(i-3,j) + m%F_tot_End(i,j) - m%F_tot_End(i,j) = m%F_B_End(i,j) + m%F_BF_End(i,j) + m%F_IMG_End(i,j) - y%Mesh%Moment(i-3,j) = y%Mesh%Moment(i-3,j) + m%F_tot_End(i,j) + y%Mesh%Moment(i-3,j) = y%Mesh%Moment(i-3,j) + m%F_tot_End(i,j) END IF END DO ! I=1,6 ! Compute and save the joint total moment about PRP for output file m%F_tot_End(4:6,j) = m%F_tot_End(4:6,j) + cross_product( u%Mesh%Position(:,j)+u%Mesh%TranslationDisp(:,j)-u%PRP, m%F_tot_End(1:3,j)) - ! Compute and save the joint total moment about PRP for output file - m%F_tot_End(4:6,j) = m%F_tot_End(4:6,j) + cross_product( u%Mesh%Position(:,j)+u%Mesh%TranslationDisp(:,j)-u%PRP, m%F_tot_End(1:3,j)) - END DO ! J = 1, p%NJoints !---------------------------------------------------------------------------------------------------------------! From e1c491e7df255df8dcf1d9e24cb9f2eaf79e805a Mon Sep 17 00:00:00 2001 From: willwiley Date: Mon, 31 Aug 2026 16:59:32 -0600 Subject: [PATCH 6/8] Add check on update discrete state for KC amplitude only for cylindrical members --- modules/hydrodyn/src/Morison.f90 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index ef433cc33a..a63e4ca460 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -6601,14 +6601,16 @@ SUBROUTINE Morison_UpdateDiscState( Time, u, p, x, xd, z, OtherState, m, errStat ! Relative radial velocity for this member node (normal to the member axis). vrel_rad = matmul(mem%Ak, vrel) - IF ( mem%iKC(i) > 0_IntKi ) THEN - IF (nodeInWater == 0_IntKi) THEN - xd%AKC(nodeIndx) = 0.0_ReKi - ELSE - IF ( dot_product(vrel_rad, xd%vrel_rad_prev(:,nodeIndx)) <= 0.0_ReKi ) THEN + IF ( mem%MSecGeom == MSecGeom_Cyl ) THEN + IF ( mem%iKC(i) > 0_IntKi ) THEN + IF (nodeInWater == 0_IntKi) THEN xd%AKC(nodeIndx) = 0.0_ReKi + ELSE + IF ( dot_product(vrel_rad, xd%vrel_rad_prev(:,nodeIndx)) <= 0.0_ReKi ) THEN + xd%AKC(nodeIndx) = 0.0_ReKi + END IF + xd%AKC(nodeIndx) = xd%AKC(nodeIndx) + TwoNorm(vrel_rad)*p%DT END IF - xd%AKC(nodeIndx) = xd%AKC(nodeIndx) + TwoNorm(vrel_rad)*p%DT END IF END IF From 4a91ff8e0f32b01178f3939c1b4d06474e89b86c Mon Sep 17 00:00:00 2001 From: willwiley Date: Tue, 6 Oct 2026 11:23:36 -0600 Subject: [PATCH 7/8] Addition of KC dependent Cd for rectangular members --- modules/hydrodyn/src/HydroDyn_Input.f90 | 204 +++++++++++++++++------- modules/hydrodyn/src/Morison.f90 | 94 +++++++++-- modules/hydrodyn/src/Morison.txt | 5 + modules/hydrodyn/src/Morison_Types.f90 | 62 +++++++ 4 files changed, 300 insertions(+), 65 deletions(-) diff --git a/modules/hydrodyn/src/HydroDyn_Input.f90 b/modules/hydrodyn/src/HydroDyn_Input.f90 index 1597ff723e..8a0593bbc4 100644 --- a/modules/hydrodyn/src/HydroDyn_Input.f90 +++ b/modules/hydrodyn/src/HydroDyn_Input.f90 @@ -83,6 +83,8 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi CHARACTER(MaxFileInfoLineLen) :: Line ! String to temporarially hold value of read line CHARACTER(16) :: KCFile1 ! String to temporarilly hold value of KC-Cd function name and number CHARACTER(16) :: KCFile2 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(16) :: KCFile3 ! String to temporarilly hold value of KC-Cd function name and number + CHARACTER(16) :: KCFile4 ! String to temporarilly hold value of KC-Cd function name and number CHARACTER(8) :: CpMCF1, CpMCF2, CpMCFMG1, CpMCFMG2 ! Strings to temporarilly hold MemberCp entries, which may be the 'MCF' keyword CHARACTER(1024) :: KCPath ! String to temporarilly hold path name of KC-Cd function file TYPE(FileInfoType) :: FileInfo_KC ! Temporary derived type to hold KC file information @@ -816,21 +818,35 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi DO I = 1,InputFileData%Morison%NCoefMembersCyl Line = FileInfo_In%Lines(CurLine) - READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberID, & - KCFile1, KCFile2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCa1, InputFileData%Morison%CoefMembersCyl(I)%MemberCa2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG2, & - CpMCF1, CpMCF2, & - CpMCFMG1, CpMCFMG2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCb1, InputFileData%Morison%CoefMembersCyl(I)%MemberCb2, & - InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG1, InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG2 + READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersCyl(I)%MemberID, & + KCFile1, & + KCFile2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCdMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCa1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCa2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCaMG2, & + CpMCF1, & + CpMCF2, & + CpMCFMG1, & + CpMCFMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCd2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCdMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCa2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCaMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCp2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberAxCpMG2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCb1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCb2, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG1, & + InputFileData%Morison%CoefMembersCyl(I)%MemberCbMG2 if ( KCFile1(1:2) == "KC" ) then InputFileData%Morison%CoefMembersCyl(I)%MemberCd1 = 0.0 @@ -927,48 +943,126 @@ SUBROUTINE HydroDyn_ParseInput( InputFileName, OutRootName, FileInfo_In, InputFi END IF DO I = 1,InputFileData%Morison%NCoefMembersRec - - CALL ParseRAryWKywrd( FileInfo_In, CurLine, 'Member-based rectangular member hydrodynamic coefficients table row '//trim( Int2LStr(I)), tmpReArray, size(tmpReArray), & - 'MCF', 1.0_ReKi, (/18,19,20,21/), InputFileData%Morison%CoefMembersRec(I)%MemberMCF, ErrStat2, ErrMsg2, UnEc ) + + Line = FileInfo_In%Lines(CurLine) + READ(Line,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberID, & + KCFile1, & + KCFile2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCdAMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCdAMG2, & + KCFile3, & + KCFile4, & + InputFileData%Morison%CoefMembersRec(I)%MemberCdBMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCdBMG2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaA1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaA2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaAMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaAMG2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaB1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaB2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaBMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCaBMG2, & + CpMCF1, & + CpMCF2, & + CpMCFMG1, & + CpMCFMG2, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCd1, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCd2, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCdMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCdMG2, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCa1, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCa2, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCaMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCaMG2, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCp1, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCp2, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCpMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberAxCpMG2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCb1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCb2, & + InputFileData%Morison%CoefMembersRec(I)%MemberCbMG1, & + InputFileData%Morison%CoefMembersRec(I)%MemberCbMG2 + + if ( KCFile1(1:2) == "KC" ) then + InputFileData%Morison%CoefMembersRec(I)%MemberCdA1 = 0.0 + InputFileData%Morison%CoefMembersRec(I)%MemberCdA2 = 0.0 + InputFileData%Morison%CoefMembersRec(I)%MemberCdB1 = 0.0 + InputFileData%Morison%CoefMembersRec(I)%MemberCdB2 = 0.0 + if ( trim(KCFile1) /= trim(KCFile2) ) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "MemberCdA1 and MemberCdA2 must be the same KC file reference when using KC-based input (e.g., KC1 and KC1)." + if (Failed()) return + end if + READ( KCFile1(3:), '(I10)', iostat=ErrStat2 ) InputFileData%Morison%CoefMembersRec(I)%MemberiKC + if ( KCFile3(1:2) == "KC" ) then + if ( KCFile4(1:2) == "KC" ) then + READ( KCFile3(3:), '(I10)', iostat=ErrStat2 ) InputFileData%Morison%CoefMembersRec(I)%MemberiKCB + else + ErrStat2 = ErrID_Fatal + ErrMsg2 = "If KC-based input is used for CdA it must also be used for CdB for the same member." + if (Failed()) return + end if + end if + else + READ(KCFile1,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCdA1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCdA1 input needs to either be a real number or a string starting with 'KC' followed by an integer between 1 and NKCCd" + end if + READ(KCFile2,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCdA2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCdA2 input needs to either be a real number or a string starting with 'KC' followed by an integer between 1 and NKCCd" + end if + READ(KCFile3,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCdB1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCdB1 input needs to either be a real number or a string starting with 'KC' followed by an integer between 1 and NKCCd" + end if + READ(KCFile4,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCdB2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCdB2 input needs to either be a real number or a string starting with 'KC' followed by an integer between 1 and NKCCd" + end if + InputFileData%Morison%CoefMembersRec(I)%MemberiKC = 0 + InputFileData%Morison%CoefMembersRec(I)%MemberiKCB = 0 + if (Failed()) return + end if + + ! MCF keyword must appear in all four of MemberCp1, MemberCp2, MemberCpMG1, MemberCpMG2, or none of them + IF ( trim(CpMCF1) == 'MCF' .OR. trim(CpMCF2) == 'MCF' .OR. trim(CpMCFMG1) == 'MCF' .OR. trim(CpMCFMG2) == 'MCF' ) THEN + IF ( trim(CpMCF1) /= 'MCF' .OR. trim(CpMCF2) /= 'MCF' .OR. trim(CpMCFMG1) /= 'MCF' .OR. trim(CpMCFMG2) /= 'MCF' ) THEN + ErrStat2 = ErrID_Fatal + ErrMsg2 = "When parsing member-based rectangular member hydrodynamic coefficients table, MCF is used at some but not all of MemberCp1, MemberCp2, MemberCpMG1, MemberCpMG2." + if (Failed()) return + END IF + InputFileData%Morison%CoefMembersRec(I)%MemberMCF = .true. + InputFileData%Morison%CoefMembersRec(I)%MemberCp1 = 1.0_ReKi + InputFileData%Morison%CoefMembersRec(I)%MemberCp2 = 1.0_ReKi + InputFileData%Morison%CoefMembersRec(I)%MemberCpMG1 = 1.0_ReKi + InputFileData%Morison%CoefMembersRec(I)%MemberCpMG2 = 1.0_ReKi + ELSE + InputFileData%Morison%CoefMembersRec(I)%MemberMCF = .false. + READ(CpMCF1, *,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCp1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCp1 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + READ(CpMCF2, *,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCp2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCp2 input needs to either be a real number or 'MCF'" + end if if (Failed()) return + READ(CpMCFMG1,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCpMG1 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCpMG1 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + READ(CpMCFMG2,*,IOSTAT=ErrStat2) InputFileData%Morison%CoefMembersRec(I)%MemberCpMG2 + if (ErrStat2 /= 0) then + ErrMsg2 = "MemberCpMG2 input needs to either be a real number or 'MCF'" + end if + if (Failed()) return + END IF + + CurLine = CurLine + 1 - InputFileData%Morison%CoefMembersRec(I)%MemberID = NINT(tmpReArray( 1)) - InputFileData%Morison%CoefMembersRec(I)%MemberCdA1 = tmpReArray( 2) - InputFileData%Morison%CoefMembersRec(I)%MemberCdA2 = tmpReArray( 3) - InputFileData%Morison%CoefMembersRec(I)%MemberCdAMG1 = tmpReArray( 4) - InputFileData%Morison%CoefMembersRec(I)%MemberCdAMG2 = tmpReArray( 5) - InputFileData%Morison%CoefMembersRec(I)%MemberCdB1 = tmpReArray( 6) - InputFileData%Morison%CoefMembersRec(I)%MemberCdB2 = tmpReArray( 7) - InputFileData%Morison%CoefMembersRec(I)%MemberCdBMG1 = tmpReArray( 8) - InputFileData%Morison%CoefMembersRec(I)%MemberCdBMG2 = tmpReArray( 9) - InputFileData%Morison%CoefMembersRec(I)%MemberCaA1 = tmpReArray(10) - InputFileData%Morison%CoefMembersRec(I)%MemberCaA2 = tmpReArray(11) - InputFileData%Morison%CoefMembersRec(I)%MemberCaAMG1 = tmpReArray(12) - InputFileData%Morison%CoefMembersRec(I)%MemberCaAMG2 = tmpReArray(13) - InputFileData%Morison%CoefMembersRec(I)%MemberCaB1 = tmpReArray(14) - InputFileData%Morison%CoefMembersRec(I)%MemberCaB2 = tmpReArray(15) - InputFileData%Morison%CoefMembersRec(I)%MemberCaBMG1 = tmpReArray(16) - InputFileData%Morison%CoefMembersRec(I)%MemberCaBMG2 = tmpReArray(17) - InputFileData%Morison%CoefMembersRec(I)%MemberCp1 = tmpReArray(18) - InputFileData%Morison%CoefMembersRec(I)%MemberCp2 = tmpReArray(19) - InputFileData%Morison%CoefMembersRec(I)%MemberCpMG1 = tmpReArray(20) - InputFileData%Morison%CoefMembersRec(I)%MemberCpMG2 = tmpReArray(21) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCd1 = tmpReArray(22) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCd2 = tmpReArray(23) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCdMG1 = tmpReArray(24) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCdMG2 = tmpReArray(25) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCa1 = tmpReArray(26) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCa2 = tmpReArray(27) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCaMG1 = tmpReArray(28) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCaMG2 = tmpReArray(29) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCp1 = tmpReArray(30) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCp2 = tmpReArray(31) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCpMG1 = tmpReArray(32) - InputFileData%Morison%CoefMembersRec(I)%MemberAxCpMG2 = tmpReArray(33) - InputFileData%Morison%CoefMembersRec(I)%MemberCb1 = tmpReArray(34) - InputFileData%Morison%CoefMembersRec(I)%MemberCb2 = tmpReArray(35) - InputFileData%Morison%CoefMembersRec(I)%MemberCbMG1 = tmpReArray(36) - InputFileData%Morison%CoefMembersRec(I)%MemberCbMG2 = tmpReArray(37) END DO if (allocated(tmpReArray)) deallocate(tmpReArray) diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index a63e4ca460..7c15b8eb99 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -1679,6 +1679,8 @@ SUBROUTINE SetExternalHydroCoefs_Rec( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG2*s member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG2*s member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG2*s + member%iKC (i) = CoefMembers(MmbrCoefIDIndx)%MemberiKC + member%iKCB (i) = CoefMembers(MmbrCoefIDIndx)%MemberiKCB else member%CdA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdA1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdA2 *s member%CdB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdB1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdB2 *s @@ -1689,6 +1691,8 @@ SUBROUTINE SetExternalHydroCoefs_Rec( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCd1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCd2 *s member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCa1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCa2 *s member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCp1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCp2 *s + member%iKC (i) = CoefMembers(MmbrCoefIDIndx)%MemberiKC + member%iKCB (i) = CoefMembers(MmbrCoefIDIndx)%MemberiKCB end if end do member%propMCF = CoefMembers(MmbrCoefIDIndx)%MemberMCF @@ -1842,6 +1846,8 @@ subroutine AllocateMemberDataArrays( member, memberLoads, errStat, errMsg ) call AllocAry(member%CdB , member%NElements+1, 'member%CdB ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry(member%CaA , member%NElements+1, 'member%CaA ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) call AllocAry(member%CaB , member%NElements+1, 'member%CaB ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) + call AllocAry(member%iKC , member%NElements+1, 'member%iKC ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) + call AllocAry(member%iKCB , member%NElements+1, 'member%iKCB ', errStat2, errMsg2); call SetErrStat(errStat2, errMsg2, errStat, errMsg, routineName) end if if (ErrStat >= AbortErrLev) return @@ -1925,6 +1931,8 @@ subroutine AllocateMemberDataArrays( member, memberLoads, errStat, errMsg ) member%CdB = 0.0_ReKi member%CaA = 0.0_ReKi member%CaB = 0.0_ReKi + member%iKC = 0_IntKi + member%iKCB = 0_IntKi end if end subroutine AllocateMemberDataArrays @@ -2987,6 +2995,14 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In RETURN END IF xd%AKC = 0.0_ReKi + + ALLOCATE ( xd%AKCB(p%NNodes), STAT = ErrStat ) + IF ( ErrStat /= ErrID_None ) THEN + ErrMsg = ' Error allocating space for AKCB array.' + ErrStat = ErrID_Fatal + RETURN + END IF + xd%AKCB = 0.0_ReKi ALLOCATE ( xd%vrel_rad_prev(3,p%NNodes), STAT = ErrStat ) IF ( ErrStat /= ErrID_None ) THEN @@ -2996,6 +3012,14 @@ SUBROUTINE Morison_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, In END IF xd%vrel_rad_prev = 0.0_ReKi + ALLOCATE ( xd%vrel_rad_prevB(3,p%NNodes), STAT = ErrStat ) + IF ( ErrStat /= ErrID_None ) THEN + ErrMsg = ' Error allocating space for vrel_rad_prevB array.' + ErrStat = ErrID_Fatal + RETURN + END IF + xd%vrel_rad_prevB = 0.0_ReKi + ALLOCATE ( xd%vrel_ax_prev(3,p%NNodes), STAT = ErrStat ) IF ( ErrStat /= ErrID_None ) THEN ErrMsg = ' Error allocating space for vrel_ax_prev array.' @@ -6325,7 +6349,9 @@ SUBROUTINE GetDistDrag_Rec(p, m, u, xd, Time, mem, i, dSadl_p, dSbdl_p, f_hydro, Real(ReKi) :: FiltStat(4) ! High-pass filter states for the four faces Real(ReKi) :: SaMG, SbMG ! Section side lengths at the current node/free-surface intersection Real(ReKi) :: CdA, CdB, AxCd ! Drag coefficients at the current node/free-surface intersection + Real(ReKi) :: Cd_KC ! Drag coefficient for KC dependent Cd Real(ReKi) :: vrel(3) ! Relative flow velocity at the current node/free-surface intersection + INTEGER(IntKi) :: ILo ! Dummy starting index needed for interpbin Integer(IntKi) :: ErrStat2 Character(ErrMsgLen) :: ErrMsg2 @@ -6368,10 +6394,25 @@ SUBROUTINE GetDistDrag_Rec(p, m, u, xd, Time, mem, i, dSadl_p, dSbdl_p, f_hydro, ! Transverse drag IF (mem%FDMod == 0_IntKi) THEN ! Centerline-based formulation - vec = matmul( mem%Ak,vrel ) - f_hydro = f_hydro + & - 0.5*CdB*p%WaveField%WtrDens*SbMG*TwoNorm(vec)*Dot_Product(vec,mem%x_hat)*mem%x_hat + & ! local x-direction - 0.5*CdA*p%WaveField%WtrDens*SaMG*TwoNorm(vec)*Dot_Product(vec,mem%y_hat)*mem%y_hat ! local y-direction + IF ( mem%iKC(i) > 0_IntKi ) THEN + Ilo = 1_IntKi + Cd_KC = InterpBin( xd%AKCB(mem%NodeIndx(i))*2*Pi/(mem%RMG(i)*2), & + p%KCCd(p%iKCstart(mem%iKCB(i),1):p%iKCstart(mem%iKCB(i),2),1), & + p%KCCd(p%iKCstart(mem%iKCB(i),1):p%iKCstart(mem%iKCB(i),2),2), Ilo, & + (p%iKCstart(mem%iKCB(i),2)-p%iKCstart(mem%iKCB(i),1)+1) ) + f_hydro = f_hydro + 0.5*Cd_KC*p%WaveField%WtrDens*SbMG*TwoNorm(vec)*Dot_Product(vec,mem%x_hat)*mem%x_hat ! local x-direction + Ilo = 1_IntKi + Cd_KC = InterpBin( xd%AKC(mem%NodeIndx(i))*2*Pi/(mem%RMG(i)*2), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),1), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),2), Ilo, & + (p%iKCstart(mem%iKC(i),2)-p%iKCstart(mem%iKC(i),1)+1) ) + f_hydro = f_hydro + 0.5*Cd_KC*p%WaveField%WtrDens*SaMG*TwoNorm(vec)*Dot_Product(vec,mem%y_hat)*mem%y_hat ! local y-direction + ELSE + vec = matmul( mem%Ak,vrel ) + f_hydro = f_hydro + & + 0.5*CdB*p%WaveField%WtrDens*SbMG*TwoNorm(vec)*Dot_Product(vec,mem%x_hat)*mem%x_hat + & ! local x-direction + 0.5*CdA*p%WaveField%WtrDens*SaMG*TwoNorm(vec)*Dot_Product(vec,mem%y_hat)*mem%y_hat ! local y-direction + END IF ELSE ! Face-based formulation @@ -6415,10 +6456,27 @@ SUBROUTINE GetDistDrag_Rec(p, m, u, xd, Time, mem, i, dSadl_p, dSbdl_p, f_hydro, DragLoFSc(4) = mem%DragLoFScA ! Dimensional drag coefficient for each face - Cd(1) = CdB*p%WaveField%WtrDens*SbMG - Cd(2) = Cd(1) - Cd(3) = CdA*p%WaveField%WtrDens*SaMG - Cd(4) = Cd(3) + IF ( mem%iKC(i) > 0_IntKi ) THEN + Ilo = 1_IntKi + Cd_KC = InterpBin( xd%AKCB(mem%NodeIndx(i))*2*Pi/(mem%RMG(i)*2), & + p%KCCd(p%iKCstart(mem%iKCB(i),1):p%iKCstart(mem%iKCB(i),2),1), & + p%KCCd(p%iKCstart(mem%iKCB(i),1):p%iKCstart(mem%iKCB(i),2),2), Ilo, & + (p%iKCstart(mem%iKCB(i),2)-p%iKCstart(mem%iKCB(i),1)+1) ) + Cd(1) = Cd_KC*p%WaveField%WtrDens*SbMG + Cd(2) = Cd(1) + Ilo = 1_IntKi + Cd_KC = InterpBin( xd%AKC(mem%NodeIndx(i))*2*Pi/(mem%RMG(i)*2), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),1), & + p%KCCd(p%iKCstart(mem%iKC(i),1):p%iKCstart(mem%iKC(i),2),2), Ilo, & + (p%iKCstart(mem%iKC(i),2)-p%iKCstart(mem%iKC(i),1)+1) ) + Cd(3) = Cd_KC*p%WaveField%WtrDens*SaMG + Cd(4) = Cd(3) + ELSE + Cd(1) = CdB*p%WaveField%WtrDens*SbMG + Cd(2) = Cd(1) + Cd(3) = CdA*p%WaveField%WtrDens*SaMG + Cd(4) = Cd(3) + END IF ! Compute and sum the drag force on all four faces DO fNo = 1,4 @@ -6612,9 +6670,25 @@ SUBROUTINE Morison_UpdateDiscState( Time, u, p, x, xd, z, OtherState, m, errStat xd%AKC(nodeIndx) = xd%AKC(nodeIndx) + TwoNorm(vrel_rad)*p%DT END IF END IF + ELSE + IF ( mem%iKC(i) > 0_IntKi ) THEN + IF (nodeInWater == 0_IntKi) THEN + xd%AKC(nodeIndx) = 0.0_ReKi + xd%AKCB(nodeIndx) = 0.0_ReKi + ELSE + IF ( dot_product(dot_product(vrel, mem%y_hat) * mem%y_hat, xd%vrel_rad_prev(:,nodeIndx)) <= 0.0_ReKi ) THEN + xd%AKC(nodeIndx) = 0.0_ReKi + END IF + IF ( dot_product(dot_product(vrel, mem%x_hat) * mem%x_hat, xd%vrel_rad_prevB(:,nodeIndx)) <= 0.0_ReKi ) THEN + xd%AKCB(nodeIndx) = 0.0_ReKi + END IF + xd%AKC(nodeIndx) = xd%AKC(nodeIndx) + dot_product(vrel, mem%y_hat)*p%DT + xd%AKCB(nodeIndx) = xd%AKCB(nodeIndx) + dot_product(vrel, mem%x_hat)*p%DT + END IF + END IF END IF - - xd%vrel_rad_prev(:,nodeIndx) = vrel_rad + xd%vrel_rad_prev(:,nodeIndx) = dot_product(vrel, mem%y_hat) * mem%y_hat + xd%vrel_rad_prevB(:,nodeIndx) = dot_product(vrel, mem%x_hat) * mem%x_hat END DO ! i = 1,N+1 ! loop through member nodes END DO ! im diff --git a/modules/hydrodyn/src/Morison.txt b/modules/hydrodyn/src/Morison.txt index b43f4c5c42..5f7680be50 100644 --- a/modules/hydrodyn/src/Morison.txt +++ b/modules/hydrodyn/src/Morison.txt @@ -180,6 +180,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi alpha_fb_star {:} - - "load distribution factor for each element after adjusting alpha_fb for node reference depths" - typedef ^ ^ ReKi Cd {:} - - "Member Cd at each node" - typedef ^ ^ INTEGER iKC {:} - - "Member KC-Cd function index at each node" - +typedef ^ ^ INTEGER iKCB {:} - - "Member KC-Cd function index at each node for rectangular members Side B" - typedef ^ ^ ReKi Ca {:} - - "Member Ca at each node" - typedef ^ ^ ReKi CdA {:} - - "Member Cd normal to side A at each node" - typedef ^ ^ ReKi CaA {:} - - "Member Ca normal to side A at each node" - @@ -288,6 +289,8 @@ typedef ^ ^ LOGICAL typedef ^ Morison_CoefMembersRec INTEGER MemberID - - - "User-specified integer id for the Member-based coefs" - typedef ^ ^ ReKi MemberCdA1 - - - "Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - typedef ^ ^ ReKi MemberCdA2 - - - "Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - +typedef ^ ^ INTEGER MemberiKC - - - "Rectangular member-based coefs, same iKC for both ends - +typedef ^ ^ INTEGER MemberiKCB - - - "Rectangular member-based coefs, same iKC for both ends - typedef ^ ^ ReKi MemberCdAMG1 - - - "Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - typedef ^ ^ ReKi MemberCdAMG2 - - - "Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - typedef ^ ^ ReKi MemberCdB1 - - - "Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end)" - @@ -437,8 +440,10 @@ typedef ^ ContinuousStateType SiKi # typedef ^ DiscreteStateType ReKi V_rel_n_FiltStat {:} - - "State of the high-pass filter for the joint relative normal velocity" m/s typedef ^ DiscreteStateType ReKi AKC {:} - - "State of the KC number amplitude for instantaneous KC dependent drag" +typedef ^ DiscreteStateType ReKi AKCB {:} - - "State of the KC number amplitude for instantaneous KC dependent drag for rectangular member B side" typedef ^ DiscreteStateType ReKi vrel_ax_prev {:}{:} - - "Relative axial velocity vector from previous time step" typedef ^ DiscreteStateType ReKi vrel_rad_prev {:}{:} - - "Relative radial velocity vector from previous time step" +typedef ^ DiscreteStateType ReKi vrel_rad_prevB {:}{:} - - "Relative radial velocity vector from previous time step for rectangular member B side" typedef ^ DiscreteStateType ReKi MV_rel_n_FiltStat {:}{:} - - "State of the high-pass filter for the rectangular member relative normal velocity" m/s # # diff --git a/modules/hydrodyn/src/Morison_Types.f90 b/modules/hydrodyn/src/Morison_Types.f90 index 9e5007f97f..e4a6113806 100644 --- a/modules/hydrodyn/src/Morison_Types.f90 +++ b/modules/hydrodyn/src/Morison_Types.f90 @@ -233,6 +233,7 @@ MODULE Morison_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: alpha_fb_star !< load distribution factor for each element after adjusting alpha_fb for node reference depths [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Cd !< Member Cd at each node [-] INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: iKC !< Member KC-Cd function index at each node [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: iKCB !< Member KC-Cd function index at each node for rectangular members Side B [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Ca !< Member Ca at each node [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: CdA !< Member Cd normal to side A at each node [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: CaA !< Member Ca normal to side A at each node [-] @@ -344,6 +345,8 @@ MODULE Morison_Types INTEGER(IntKi) :: MemberID = 0_IntKi !< User-specified integer id for the Member-based coefs [-] REAL(ReKi) :: MemberCdA1 = 0.0_ReKi !< Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] REAL(ReKi) :: MemberCdA2 = 0.0_ReKi !< Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] + INTEGER(IntKi) :: MemberiKC = 0_IntKi + INTEGER(IntKi) :: MemberiKCB = 0_IntKi REAL(ReKi) :: MemberCdAMG1 = 0.0_ReKi !< Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] REAL(ReKi) :: MemberCdAMG2 = 0.0_ReKi !< Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] REAL(ReKi) :: MemberCdB1 = 0.0_ReKi !< Rectangular member-based coefs, see above descriptions for meanings (1 = start, 2=end) [-] @@ -504,8 +507,10 @@ MODULE Morison_Types TYPE, PUBLIC :: Morison_DiscreteStateType REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: V_rel_n_FiltStat !< State of the high-pass filter for the joint relative normal velocity [m/s] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AKC !< State of the KC number amplitude for instantaneous KC dependent drag [-] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AKCB !< State of the KC number amplitude for instantaneous KC dependent drag for rectangular member B side [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: vrel_ax_prev !< Relative axial velocity vector from previous time step [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: vrel_rad_prev !< Relative radial velocity vector from previous time step [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: vrel_rad_prevB !< Relative radial velocity vector from previous time step for rectangular member B side [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MV_rel_n_FiltStat !< State of the high-pass filter for the rectangular member relative normal velocity [m/s] END TYPE Morison_DiscreteStateType ! ======================= @@ -1676,6 +1681,18 @@ subroutine Morison_CopyMemberType(SrcMemberTypeData, DstMemberTypeData, CtrlCode end if DstMemberTypeData%iKC = SrcMemberTypeData%iKC end if + if (allocated(SrcMemberTypeData%iKCB)) then + LB(1:1) = lbound(SrcMemberTypeData%iKCB) + UB(1:1) = ubound(SrcMemberTypeData%iKCB) + if (.not. allocated(DstMemberTypeData%iKCB)) then + allocate(DstMemberTypeData%iKCB(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMemberTypeData%iKCB.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMemberTypeData%iKCB = SrcMemberTypeData%iKCB + end if if (allocated(SrcMemberTypeData%Ca)) then LB(1:1) = lbound(SrcMemberTypeData%Ca) UB(1:1) = ubound(SrcMemberTypeData%Ca) @@ -2201,6 +2218,9 @@ subroutine Morison_DestroyMemberType(MemberTypeData, ErrStat, ErrMsg) if (allocated(MemberTypeData%iKC)) then deallocate(MemberTypeData%iKC) end if + if (allocated(MemberTypeData%iKCB)) then + deallocate(MemberTypeData%iKCB) + end if if (allocated(MemberTypeData%Ca)) then deallocate(MemberTypeData%Ca) end if @@ -2362,6 +2382,7 @@ subroutine Morison_PackMemberType(RF, Indata) call RegPackAlloc(RF, InData%alpha_fb_star) call RegPackAlloc(RF, InData%Cd) call RegPackAlloc(RF, InData%iKC) + call RegPackAlloc(RF, InData%iKCB) call RegPackAlloc(RF, InData%Ca) call RegPackAlloc(RF, InData%CdA) call RegPackAlloc(RF, InData%CaA) @@ -2477,6 +2498,7 @@ subroutine Morison_UnPackMemberType(RF, OutData) call RegUnpackAlloc(RF, OutData%alpha_fb_star); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%Cd); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%iKC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%iKCB); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%Ca); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%CdA); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%CaA); if (RegCheckErr(RF, RoutineName)) return @@ -2918,6 +2940,8 @@ subroutine Morison_CopyCoefMembersRec(SrcCoefMembersRecData, DstCoefMembersRecDa DstCoefMembersRecData%MemberID = SrcCoefMembersRecData%MemberID DstCoefMembersRecData%MemberCdA1 = SrcCoefMembersRecData%MemberCdA1 DstCoefMembersRecData%MemberCdA2 = SrcCoefMembersRecData%MemberCdA2 + DstCoefMembersRecData%MemberiKC = SrcCoefMembersRecData%MemberiKC + DstCoefMembersRecData%MemberiKCB = SrcCoefMembersRecData%MemberiKCB DstCoefMembersRecData%MemberCdAMG1 = SrcCoefMembersRecData%MemberCdAMG1 DstCoefMembersRecData%MemberCdAMG2 = SrcCoefMembersRecData%MemberCdAMG2 DstCoefMembersRecData%MemberCdB1 = SrcCoefMembersRecData%MemberCdB1 @@ -2972,6 +2996,8 @@ subroutine Morison_PackCoefMembersRec(RF, Indata) call RegPack(RF, InData%MemberID) call RegPack(RF, InData%MemberCdA1) call RegPack(RF, InData%MemberCdA2) + call RegPack(RF, InData%MemberiKC) + call RegPack(RF, InData%MemberiKCB) call RegPack(RF, InData%MemberCdAMG1) call RegPack(RF, InData%MemberCdAMG2) call RegPack(RF, InData%MemberCdB1) @@ -3018,6 +3044,8 @@ subroutine Morison_UnPackCoefMembersRec(RF, OutData) call RegUnpack(RF, OutData%MemberID); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdA1); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdA2); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MemberiKC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MemberiKCB); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdAMG1); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdAMG2); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MemberCdB1); if (RegCheckErr(RF, RoutineName)) return @@ -4420,6 +4448,18 @@ subroutine Morison_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, E end if DstDiscStateData%AKC = SrcDiscStateData%AKC end if + if (allocated(SrcDiscStateData%AKCB)) then + LB(1:1) = lbound(SrcDiscStateData%AKCB) + UB(1:1) = ubound(SrcDiscStateData%AKCB) + if (.not. allocated(DstDiscStateData%AKCB)) then + allocate(DstDiscStateData%AKCB(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%AKCB.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstDiscStateData%AKCB = SrcDiscStateData%AKCB + end if if (allocated(SrcDiscStateData%vrel_ax_prev)) then LB(1:2) = lbound(SrcDiscStateData%vrel_ax_prev) UB(1:2) = ubound(SrcDiscStateData%vrel_ax_prev) @@ -4444,6 +4484,18 @@ subroutine Morison_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, E end if DstDiscStateData%vrel_rad_prev = SrcDiscStateData%vrel_rad_prev end if + if (allocated(SrcDiscStateData%vrel_rad_prevB)) then + LB(1:2) = lbound(SrcDiscStateData%vrel_rad_prevB) + UB(1:2) = ubound(SrcDiscStateData%vrel_rad_prevB) + if (.not. allocated(DstDiscStateData%vrel_rad_prevB)) then + allocate(DstDiscStateData%vrel_rad_prevB(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%vrel_rad_prevB.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstDiscStateData%vrel_rad_prevB = SrcDiscStateData%vrel_rad_prevB + end if if (allocated(SrcDiscStateData%MV_rel_n_FiltStat)) then LB(1:2) = lbound(SrcDiscStateData%MV_rel_n_FiltStat) UB(1:2) = ubound(SrcDiscStateData%MV_rel_n_FiltStat) @@ -4471,12 +4523,18 @@ subroutine Morison_DestroyDiscState(DiscStateData, ErrStat, ErrMsg) if (allocated(DiscStateData%AKC)) then deallocate(DiscStateData%AKC) end if + if (allocated(DiscStateData%AKCB)) then + deallocate(DiscStateData%AKCB) + end if if (allocated(DiscStateData%vrel_ax_prev)) then deallocate(DiscStateData%vrel_ax_prev) end if if (allocated(DiscStateData%vrel_rad_prev)) then deallocate(DiscStateData%vrel_rad_prev) end if + if (allocated(DiscStateData%vrel_rad_prevB)) then + deallocate(DiscStateData%vrel_rad_prevB) + end if if (allocated(DiscStateData%MV_rel_n_FiltStat)) then deallocate(DiscStateData%MV_rel_n_FiltStat) end if @@ -4489,8 +4547,10 @@ subroutine Morison_PackDiscState(RF, Indata) if (RF%ErrStat >= AbortErrLev) return call RegPackAlloc(RF, InData%V_rel_n_FiltStat) call RegPackAlloc(RF, InData%AKC) + call RegPackAlloc(RF, InData%AKCB) call RegPackAlloc(RF, InData%vrel_ax_prev) call RegPackAlloc(RF, InData%vrel_rad_prev) + call RegPackAlloc(RF, InData%vrel_rad_prevB) call RegPackAlloc(RF, InData%MV_rel_n_FiltStat) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -4505,8 +4565,10 @@ subroutine Morison_UnPackDiscState(RF, OutData) if (RF%ErrStat /= ErrID_None) return call RegUnpackAlloc(RF, OutData%V_rel_n_FiltStat); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%AKC); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%AKCB); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%vrel_ax_prev); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%vrel_rad_prev); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%vrel_rad_prevB); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%MV_rel_n_FiltStat); if (RegCheckErr(RF, RoutineName)) return end subroutine From 93e9d0471afcb27fa34b1b8b6261ab3db2749bc4 Mon Sep 17 00:00:00 2001 From: willwiley Date: Tue, 6 Oct 2026 17:15:17 -0600 Subject: [PATCH 8/8] Update KC-Cd documentation --- docs/source/user/hydrodyn/input_files.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/source/user/hydrodyn/input_files.rst b/docs/source/user/hydrodyn/input_files.rst index 36e2e6825f..457e2873db 100644 --- a/docs/source/user/hydrodyn/input_files.rst +++ b/docs/source/user/hydrodyn/input_files.rst @@ -912,6 +912,11 @@ computing the wave load even if second-order wave kinematics are enabled in SeaS However, the MacCamy-Fuchs diffraction model can be used in conjunction with any of the available wave-stretching models. +Keulegan-Carpenter number dependent drag coefficient model +++++++++++++++++++++++++++++++++++++ +The Keulegan-Carpenter number dependent drag coefficient model can be enabled for circular or rectangular strip-theory +members using member-based coefficients. To enable the model, **MemberCd1** and **MemberCd2** for circular members, or **MemberCdA1**, **MemberCdA2**, **MemberCdB1**, and **MemberCdB2** for rectangular members should be set to the keyword **KC#**, where **#** is an integer indicating which KC-Cd function is used. KC-Cd functions are set in an optional section preceding the Member Output List. For a given member, the same KC-Cd function must be used for both ends of the member (e.g. **MemberCdA1** = **MemberCdA2**). + .. _hd-members: Members @@ -984,6 +989,12 @@ to zero. The hydrodynamic coefficient tables contain coefficients with and without marine growth. If **MGThck** = 0 for a particular node, the coefficients not associated with marine growth are used. +.. _hd-keulegan-carpenter-drag-coefficient-functions: + +Keulegan-Carpenter Drag Coefficient Functions +------------- +KC-Cd functions are set in an optional section preceding the Member Output List. **NKCCd** defines the number of KC-Cd functions. It is followed by a list of path names for KC-Cd function files. KC-Cd function files are a two column file, where the first column contains increasing Keulegan-Carpenter numbers and the second column contains corresponding drag coefficients. Each of these KC-Cd function files should begin with **NKC**, which sets the number of Keulegan-Carpenter numbers in the function. Note that the functions are linearly interpolated between provided Keulegan-Carpenter numbers, and are constantly extrapolated below the lowest and above the highest Keulegan-Carpenter numbers. + .. _hd-member-output-list: Member Output List