diff --git a/docs/source/user/subdyn/input_files.rst b/docs/source/user/subdyn/input_files.rst index 86573afb26..b6a91ef491 100644 --- a/docs/source/user/subdyn/input_files.rst +++ b/docs/source/user/subdyn/input_files.rst @@ -277,11 +277,21 @@ formulation: 1) Euler-Bernoulli; 3) Timoshenko. Tapered formulations (2 and 4) have yet to be implemented and will be available in a future release. -**NDiv** specifies the number of elements per member. Analysis nodes -are located at the ends of elements and the number of analysis nodes per -member equals **NDiv** + 1. **NDiv** is applied uniformly to all -members regardless of the member’s length, hence it could result in -small elements in some members and long elements in other members. +**NDiv** specifies the default number of elements per member. Analysis +nodes are located at the ends of elements and the default number of +analysis nodes per member equals **NDiv** + 1. By default, **NDiv** is +applied uniformly to all beam members. + +An optional per-member input **MDivSize** can be provided in the +MEMBERS table (as an extra last column) for beam members. When +**MDivSize** is provided for a beam member, it overrides **NDiv** for +that member and the number of elements is computed as +:math:`\lceil L/\mathrm{MDivSize} \rceil`, where :math:`L` is the +member length. Members without **MDivSize** continue to use **NDiv**. +For cable, rigid-link, and spring members, **MDivSize** is ignored. + +This mixed approach allows users to keep legacy **NDiv** behavior while +selectively controlling maximum element length on individual members. Increasing the number of elements per member may increase accuracy, with the trade-off of increased memory usage and computation time. We recommend using **NDiv** > 1 when modeling tapered members. @@ -528,6 +538,22 @@ An example of member table is given below 10 101 102 2 2 1c 0 11 102 103 2 2 1c 0 + The MEMBERS table also supports an optional final column + **MDivSize** for beam members. This provides per-member maximum element + length (in meters). Example: + + .. code:: + + 2 NMembers - Number of frame members + MemberID MJointID1 MJointID2 MPropSetID1 MPropSetID2 MType MSpin/COSMID MDivSize + (-) (-) (-) (-) (-) (-) (deg/-) (m) + 10 101 102 2 2 1c 0 + 11 102 103 2 2 1c 0 1.5 + + In this example, member 10 uses the global **NDiv**, while member 11 + uses **MDivSize** and is discretized with + :math:`\lceil L_{11}/1.5\rceil` elements. + @@ -780,9 +806,11 @@ ID specified in the MEMBERS table, and **NOutCnt** specifies how many nodes along the member will generate output. **NodeCnt** specifies those node numbers (a separate entry on the same line for each node) for output as an integer index from the start-joint (node 1) to the -end-joint (node **NDiv** + 1) of the member. The outputs specified in -the SDOutList section determines which quantities are actually output at -these locations. +end-joint (node count on that member). The maximum node index is +therefore member-specific and depends on whether that member uses +global **NDiv** or optional **MDivSize** in the MEMBERS table. The +outputs specified in the SDOutList section determines which quantities +are actually output at these locations. Output Channels- SDOutList Section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/modules/subdyn/src/SD_FEM.f90 b/modules/subdyn/src/SD_FEM.f90 index 2b56d40019..9fea82ff16 100644 --- a/modules/subdyn/src/SD_FEM.f90 +++ b/modules/subdyn/src/SD_FEM.f90 @@ -482,8 +482,10 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) LOGICAL :: CreateNewProp INTEGER(IntKi) :: nMemberCable, nMemberRigid, nMemberSpring, nMemberBeamCirc, nMemberBeamRect, nMemberBeamArb !< Number of members per type INTEGER(IntKi) :: eType !< Element Type + INTEGER(IntKi) :: memberDiv, maxMemberDiv, iJoint1, iJoint2 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 + REAL(ReKi) :: memberLength ErrStat = ErrID_None ErrMsg = "" @@ -494,6 +496,28 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) CALL Fatal('FEMMod '//TRIM(Num2LStr(Init%FEMMod))//' not implemented.'); return ENDIF + ! --- Resolve the number of finite elements per member before sizing arrays + do I = 1, size(Init%Members,1) + eType = Init%Members(I, iMType) + if (eType==idMemberBeamCirc .or. eType==idMemberBeamRect .or. eType==idMemberBeamArb) then + Init%MemberNDiv(I) = max(1_IntKi, Init%MemberNDiv(I)) + if (Init%MemberDivSize(I) > 0.0_ReKi) then + iJoint1 = FINDLOCI(Init%Joints(:,1), Init%Members(I,2)) + iJoint2 = FINDLOCI(Init%Joints(:,1), Init%Members(I,3)) + if (iJoint1 <= 0 .or. iJoint2 <= 0) then + CALL Fatal(' Failed to find one or both joints for member at position '//trim(num2lstr(I))//' in member list while resolving MDivSize.') + return + endif + memberLength = sqrt( (Init%Joints(iJoint2,2)-Init%Joints(iJoint1,2))**2 + & + (Init%Joints(iJoint2,3)-Init%Joints(iJoint1,3))**2 + & + (Init%Joints(iJoint2,4)-Init%Joints(iJoint1,4))**2 ) + Init%MemberNDiv(I) = max(1_IntKi, int(ceiling(memberLength/Init%MemberDivSize(I)), IntKi)) + endif + else + Init%MemberNDiv(I) = 1_IntKi + endif + enddo + ! --- Total number of element nMemberBeamCirc = count(Init%Members(:,iMType) == idMemberBeamCirc) nMemberBeamRect = count(Init%Members(:,iMType) == idMemberBeamRect) @@ -501,21 +525,31 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) nMemberRigid = count(Init%Members(:,iMType) == idMemberRigid) nMemberBeamArb = count(Init%Members(:,iMType) == idMemberBeamArb) nMemberSpring = count(Init%Members(:,iMType) == idMemberSpring) - Init%NElem = (nMemberBeamCirc + nMemberBeamRect + nMemberBeamArb)*Init%NDiv + nMemberCable + nMemberRigid + nMemberSpring ! NOTE: only Beams are divided + Init%NElem = sum(Init%MemberNDiv) + CALL AllocAry(Init%MemberElemStart, p%NMembers, 'MemberElemStart', ErrStat2, ErrMsg2); if(Failed()) return + if (p%NMembers > 0) then + Init%MemberElemStart(1) = 1_IntKi + do I = 2, p%NMembers + Init%MemberElemStart(I) = Init%MemberElemStart(I-1) + Init%MemberNDiv(I-1) + enddo + end if IF ( (nMemberBeamCirc+nMemberBeamRect+nMemberRigid+nMemberCable+nMemberBeamArb+nMemberSpring) /= size(Init%Members,1)) then CALL Fatal(' Member list contains an element which is not a beam, a cable, a rigid link or a spring'); return ENDIF ! Total number of nodes - Depends on division and number of nodes per element - p%nNodes = Init%NJoints + ( Init%NDiv - 1 )*(nMemberBeamCirc+nMemberBeamRect+nMemberBeamArb) + p%nNodes = Init%NJoints + sum(max(Init%MemberNDiv - 1_IntKi, 0_IntKi), mask = Init%Members(:,iMType) == idMemberBeamCirc .or. & + Init%Members(:,iMType) == idMemberBeamRect .or. & + Init%Members(:,iMType) == idMemberBeamArb) ! check the number of interior modes IF ( p%nDOFM > 6*(p%nNodes - p%nNodes_I - p%nNodes_C) ) THEN CALL Fatal(' NModes must be less than or equal to '//TRIM(Num2LStr( 6*(p%nNodes - p%nNodes_I - p%nNodes_C) ))); return ENDIF - ! TODO replace this with an integer list! - CALL AllocAry(Init%MemberNodes,p%NMembers,Init%NDiv+1,'Init%MemberNodes',ErrStat2, ErrMsg2); if(Failed()) return ! for two-node element only, otherwise the number of nodes in one element is different + ! Store only member endpoints here; interior member nodes are reconstructed from p%Elems and MemberNDiv when needed. + maxMemberDiv = maxval(Init%MemberNDiv) + CALL AllocAry(Init%MemberNodes,p%NMembers,2,'Init%MemberNodes',ErrStat2, ErrMsg2); if(Failed()) return ! --- Reindexing JointsID and MembersID into Nodes and Elems arrays ! NOTE: need NNode and NElem @@ -563,16 +597,16 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) endif ! is beam enddo - Init%MemberNodes = 0 + Init%MemberNodes = 0 ! --- Setting up MemberNodes (And Elems, Props, Nodes if divisions) - if (Init%NDiv==1) then + if (maxMemberDiv==1) then ! NDiv = 1 - Init%MemberNodes(1:p%NMembers, 1:2) = p%Elems(1:Init%NElem, 2:3) + Init%MemberNodes(1:p%NMembers, 1:2) = p%Elems(1:Init%NElem, 2:3) Init%NPropBC = Init%NPropSetsBC Init%NPropBR = Init%NPropSetsBR Init%NPropX = Init%NPropSetsX - else if (Init%NDiv > 1) then + else if (maxMemberDiv > 1) then ! Discretize structure according to NDiv ! - Elems is fully reinitialized, connectivity needs to be done again using SetNewElem @@ -612,6 +646,7 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) eType = TempMembers(I, iMType ) iDirCos = TempMembers(I, iMDirCosID) spin = TempMemSpin(I) + memberDiv = Init%MemberNDiv(I) if (eType==idMemberRigid .OR. eType==idMemberCable .OR. eType==idMemberSpring) then ! --- Cables, rigid links and springs are not subdivided and have same prop at nodes @@ -623,9 +658,17 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) cycle endif + if (memberDiv == 1) then + Init%MemberNodes(I, 1) = Node1 + Init%MemberNodes(I, 2) = Node2 + kelem = kelem + 1 + CALL SetNewElem(kelem, Node1, Node2, eType, Prop1, Prop2, p, iDirCos, spin) + cycle + endif + ! --- Subdivision of beams - Init%MemberNodes(I, 1) = Node1 - Init%MemberNodes(I, Init%NDiv+1) = Node2 + Init%MemberNodes(I, 1) = Node1 + Init%MemberNodes(I, 2) = Node2 x1 = Init%Nodes(Node1, 2) y1 = Init%Nodes(Node1, 3) @@ -635,9 +678,9 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) y2 = Init%Nodes(Node2, 3) z2 = Init%Nodes(Node2, 4) - dx = ( x2 - x1 )/Init%NDiv - dy = ( y2 - y1 )/Init%NDiv - dz = ( z2 - z1 )/Init%NDiv + dx = ( x2 - x1 )/memberDiv + dy = ( y2 - y1 )/memberDiv + dz = ( z2 - z1 )/memberDiv if (eType == idMemberBeamCirc) then @@ -647,8 +690,8 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) d2 = TempPropsBC(Prop2, 5) t2 = TempPropsBC(Prop2, 6) - dd = ( d2 - d1 )/Init%NDiv - dt = ( t2 - t1 )/Init%NDiv + dd = ( d2 - d1 )/memberDiv + dt = ( t2 - t1 )/memberDiv ! If both dd and dt are 0, no interpolation is needed, and we can use the same property set for new nodes/elements. otherwise we'll have to create new properties for each new node @@ -664,9 +707,9 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) Sb2 = TempPropsBR(Prop2, 6) t2 = TempPropsBR(Prop2, 7) - dSa = ( Sa2 - Sa1 )/Init%NDiv - dSb = ( Sb2 - Sb1 )/Init%NDiv - dt = ( t2 - t1 )/Init%NDiv + dSa = ( Sa2 - Sa1 )/memberDiv + dSb = ( Sb2 - Sb1 )/memberDiv + dt = ( t2 - t1 )/memberDiv ! If dSa, dSb, and dt are all 0, no interpolation is needed, and we can use the same property set for new nodes/elements. otherwise we'll have to create new properties for each new node @@ -681,7 +724,6 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) ! node connect to Node1 knode = knode + 1 - Init%MemberNodes(I, 2) = knode CALL SetNewNode(knode, x1+dx, y1+dy, z1+dz, Init); if (ErrStat>ErrID_None) return; IF ( CreateNewProp ) THEN IF ( eType == idMemberBeamCirc ) THEN @@ -704,9 +746,8 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) CALL SetNewElem(kelem, Node1, knode, eType, Prop1, nprop, p, iDirCos, spin); if (ErrStat>ErrID_None) return; ! interior nodes - DO J = 2, (Init%NDiv-1) + DO J = 2, (memberDiv-1) knode = knode + 1 - Init%MemberNodes(I, J+1) = knode CALL SetNewNode(knode, x1 + J*dx, y1 + J*dy, z1 + J*dz, Init) ! Set Init%Nodes(knode,:) IF ( CreateNewProp ) THEN IF ( eType == idMemberBeamCirc ) THEN @@ -746,16 +787,16 @@ SUBROUTINE SD_Discrt(Init,p, ErrStat, ErrMsg) call Fatal('Implementation error. Number of elements wrongly estimated.');return endif - ENDIF ! if NDiv is greater than 1 + ENDIF ! if at least one member has more than one element ! set the props in Init CALL AllocAry(Init%PropsBC, Init%NPropBC, PropSetsBCCol, 'Init%PropsBC', ErrStat2, ErrMsg2); if(Failed()) return CALL AllocAry(Init%PropsBR, Init%NPropBR, PropSetsBRCol, 'Init%PropsBR', ErrStat2, ErrMsg2); if(Failed()) return - if (Init%NDiv==1) then + if (maxMemberDiv==1) then Init%PropsBC(1:Init%NPropBC, 1:PropSetsBCCol) = Init%PropSetsBC(1:Init%NPropBC, 1:PropSetsBCCol) Init%PropsBR(1:Init%NPropBR, 1:PropSetsBRCol) = Init%PropSetsBR(1:Init%NPropBR, 1:PropSetsBRCol) - else if (Init%NDiv>1) then + else if (maxMemberDiv>1) then Init%PropsBC(1:Init%NPropBC, 1:PropSetsBCCol) = TempPropsBC(1:Init%NPropBC, 1:PropSetsBCCol) Init%PropsBR(1:Init%NPropBR, 1:PropSetsBRCol) = TempPropsBR(1:Init%NPropBR, 1:PropSetsBRCol) endif diff --git a/modules/subdyn/src/SubDyn.f90 b/modules/subdyn/src/SubDyn.f90 index f7615d29a5..0c9472de5b 100644 --- a/modules/subdyn/src/SubDyn.f90 +++ b/modules/subdyn/src/SubDyn.f90 @@ -26,7 +26,7 @@ Module SubDyn USE NWTC_Library USE SubDyn_Types USE SubDyn_Output - USE SubDyn_Tests + USE SubDyn_Tests, only: SD_Tests USE SD_FEM USE FEM, only: FINDLOCI @@ -229,10 +229,10 @@ SUBROUTINE SD_Init( InitInput, u, p, x, xd, z, OtherState, y, m, Interval, InitO CALL DispNVD( SD_ProgDesc ) InitOut%Ver = SD_ProgDesc - ! --- Test TODO remove me in the future - if (DEV_VERSION) then - CALL SD_Tests(ErrStat2, ErrMsg2); if(Failed()) return - endif + ! --- Development tests + if (DEV_VERSION) then + CALL SD_Tests(ErrStat2, ErrMsg2); if(Failed()) return + endif ! transfer glue-code information to data structure for SubDyn initialization: Init%g = InitInput%g @@ -1057,13 +1057,14 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None ! local variable for input and output CHARACTER(1024) :: PriPath ! The path to the primary input file -CHARACTER(1024) :: Line, Dummy_Str ! String to temporarially hold value of read line +CHARACTER(1024) :: Line, Dummy_Str, MemberLine ! String to temporarially hold value of read line CHARACTER(64), ALLOCATABLE :: StrArray(:) ! Array of strings, for better control of table inputs LOGICAL :: Echo LOGICAL :: LegacyFormat LOGICAL :: bNumeric, bInteger, bCableHasPretension INTEGER(IntKi) :: UnIn INTEGER(IntKi) :: nColumns, nColValid, nColNumeric +INTEGER(IntKi) :: nMemberCols INTEGER(IntKi) :: IOS INTEGER(IntKi) :: UnEc !Echo file ID REAL(ReKi),PARAMETER :: WrongNo=-9999. ! Placeholder value for bad(old) values in JDampings @@ -1072,6 +1073,8 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) INTEGER(IntKi) :: Dummy_IntAry(SDMaxInpCols) LOGICAL :: Dummy_Bool INTEGER(IntKi) :: Dummy_Int +INTEGER(IntKi) :: MemberNodeMax, MemberDivCount +REAL(ReKi) :: MemberLen REAL(R8Ki) :: tmpMat(3,3) INTEGER(IntKi), ALLOCATABLE :: TPIdxInput(:) INTEGER(IntKi), ALLOCATABLE :: tmpIntAry(:) @@ -1376,18 +1379,30 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) CALL ReadCom ( UnIn, SDInputFile, 'Members Units ' ,ErrStat2, ErrMsg2, UnEc ); if(Failed()) return CALL AllocAry(Init%Members, p%NMembers, MembersCol, 'Members', ErrStat2, ErrMsg2) CALL AllocAry(Init%MemberSpin, p%NMembers, 'MemberSpin', ErrStat2, ErrMsg2) +CALL AllocAry(Init%MemberDivSize, p%NMembers, 'MemberDivSize', ErrStat2, ErrMsg2) +CALL AllocAry(Init%MemberNDiv, p%NMembers, 'MemberNDiv', ErrStat2, ErrMsg2) Init%Members(:,:) = 0.0_IntKi Init%MemberSpin(:) = 0.0_ReKi +Init%MemberDivSize(:) = 0.0_ReKi +Init%MemberNDiv(:) = 0_IntKi if (p%NMembers == 0) then CALL Fatal(' Error in file "'//TRIM(SDInputFile)//'": There should be at least one SubDyn member: "'//trim(Line)//'"') return endif -CALL AllocAry(StrArray, MembersCol, 'StrArray',ErrStat2,ErrMsg2); if (Failed()) return +CALL AllocAry(StrArray, MembersCol+1, 'StrArray',ErrStat2,ErrMsg2); if (Failed()) return DO J = 1, p%NMembers READ(UnIn, FMT='(A)', IOSTAT=ErrStat2) Line; ErrMsg2='Error reading SubDyn members table'; if (Failed()) return - CALL ReadCAryFromStr ( Line, StrArray, MembersCol, 'Members', 'SubDyn members table should have 7 entries on each line', ErrStat2, ErrMsg2 ); if (Failed()) return + MemberLine = Line + I = index(MemberLine, '!') + if (I > 0) MemberLine = MemberLine(:I-1) + K = index(MemberLine, '#') + if (K > 0) MemberLine = MemberLine(:K-1) + StrArray(:) = '' + CALL ReadCAryFromStr ( MemberLine, StrArray, MembersCol+1, 'Members', 'SubDyn members table should have 7 required entries and one optional MDivSize entry', ErrStat2, ErrMsg2 ) + nMemberCols = count(len_trim(StrArray) > 0) + IF (Check( (nMemberCols /= MembersCol) .and. (nMemberCols /= MembersCol+1), 'SubDyn members table should have 7 entries, or 8 entries when optional MDivSize is provided. Problematic line: "'//trim(Line)//'"')) return ! Extract fields from first line DO I = 1, MembersCol - 1 bInteger = is_integer(StrArray(I), Init%Members(J,I)) ! Convert from string to integer @@ -1409,9 +1424,22 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) end if Init%MemberSpin(J) = Init%MemberSpin(J) * D2R Init%Members(J,I) = -1 + Init%MemberNDiv(J) = Init%NDiv + if (nMemberCols == MembersCol+1) then + bNumeric = is_numeric( StrArray(MembersCol+1), Init%MemberDivSize(J) ) + if (.not.bNumeric) then + CALL Fatal(' Error in file "'//TRIM(SDInputFile)//'": Optional MDivSize entry for a beam member must be a positive numeric value. Problematic line: "'//trim(Line)//'"') + return + end if + if (Init%MemberDivSize(J) <= 0.0_ReKi) then + CALL Fatal(' Error in file "'//TRIM(SDInputFile)//'": Optional MDivSize entry for a beam member must be greater than zero. Problematic line: "'//trim(Line)//'"') + return + end if + end if else if ( Init%Members(J,6)==idMemberCable .or. Init%Members(J,6)==idMemberRigid ) then Init%MemberSpin(J) = 0 Init%Members(J,I) = -1 + Init%MemberNDiv(J) = 1 else Init%MemberSpin(J) = 0 bInteger = is_integer(StrArray(I), Init%Members(J,I)) ! Convert from string to integer @@ -1419,6 +1447,7 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) CALL Fatal(' Error in file "'//TRIM(SDInputFile)//'": Non integer character found for COSMID for a spring member. Problematic line: "'//trim(Line)//'"') return end if + Init%MemberNDiv(J) = 1 end if ENDDO @@ -1668,8 +1697,8 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) READ(UnIn,'(A)',IOSTAT=ErrStat2) Line !read into a line IF (ErrStat2 == 0) THEN READ(Line,*,IOSTAT=ErrStat2) p%MOutLst(I)%MemberID, p%MOutLst(I)%NOutCnt - IF ( ErrStat2 /= 0 .OR. p%MOutLst(I)%NOutCnt < 1 .OR. p%MOutLst(I)%NOutCnt > 9 .OR. p%MOutLst(I)%NOutCnt > Init%Ndiv+1) THEN - CALL Fatal(' Error in file "'//TRIM(SDInputFile)//'": NOutCnt must be >= 1 and <= minimim(Ndiv+1,9)') + IF ( ErrStat2 /= 0 .OR. p%MOutLst(I)%NOutCnt < 1 .OR. p%MOutLst(I)%NOutCnt > 9 ) THEN + CALL Fatal(' Error in file "'//TRIM(SDInputFile)//'": NOutCnt must be >= 1 and <= 9') RETURN END IF CALL AllocAry( p%MOutLst(I)%NodeCnt, p%MOutLst(I)%NOutCnt, 'NodeCnt', ErrStat2, ErrMsg2); if(Failed()) return @@ -1681,12 +1710,22 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) flg = 0 DO J = 1, p%NMembers IF(p%MOutLst(I)%MemberID .EQ. Init%Members(j, 1)) THEN + MemberDivCount = Init%MemberNDiv(J) + if (Init%MemberDivSize(J) > 0.0_ReKi .and. (Init%Members(J,iMType)==idMemberBeamCirc .or. Init%Members(J,iMType)==idMemberBeamRect .or. Init%Members(J,iMType)==idMemberBeamArb)) then + MemberLen = MemberLength(p%MOutLst(I)%MemberID,Init,ErrStat2,ErrMsg2) + if (Failed()) return + MemberDivCount = max(1_IntKi, int(ceiling(MemberLen/Init%MemberDivSize(J)), IntKi)) + endif + MemberNodeMax = MemberDivCount + 1 flg = flg + 1 ! flg could be greater than 1, when there are more than 9 internal nodes of a member. + IF( p%MOutLst(I)%NOutCnt > min(MemberNodeMax, 9_IntKi) ) THEN + CALL Fatal(' NOutCnt should be less than or equal to min(number of nodes on the requested member, 9). ') + RETURN + ENDIF IF( (p%MOutLst(I)%NOutCnt < 10) .and. ((p%MOutLst(I)%NOutCnt > 0)) ) THEN DO K = 1,p%MOutLst(I)%NOutCnt - ! node number should be less than NDiv + 1 - IF( (p%MOutLst(I)%NodeCnt(k) > (Init%NDiv+1)) .or. (p%MOutLst(I)%NodeCnt(k) < 1) ) THEN - CALL Fatal(' NodeCnt should be less than NDIV+1 and greater than 0. ') + IF( (p%MOutLst(I)%NodeCnt(k) > MemberNodeMax) .or. (p%MOutLst(I)%NodeCnt(k) < 1) ) THEN + CALL Fatal(' NodeCnt should be less than or equal to the number of nodes on the requested member and greater than 0. ') RETURN ENDIF ENDDO @@ -4282,6 +4321,7 @@ SUBROUTINE OutSummary(Init, p, m, InitInput, CBparams, Modes, Omega, Omega_Gy, E INTEGER(IntKi) :: i, j, k, propIDs(2), Iprop(2) !counter and temporary holders INTEGER(IntKi) :: iNode1, iNode2 ! Node indices INTEGER(IntKi) :: mType ! Member Type + INTEGER(IntKi) :: iElemStart INTEGER :: iDirCos REAL(ReKi) :: mMass, mLength ! Member mass and length REAL(ReKi) :: M_O(6,6) ! Equivalent mass matrix at origin @@ -4297,6 +4337,7 @@ SUBROUTINE OutSummary(Init, p, m, InitInput, CBparams, Modes, Omega, Omega_Gy, E real(ReKi), dimension(:,:), allocatable :: TI2 ! For Equivalent mass matrix real(FEKi) :: Ke(12,12), Me(12, 12), FCe(12), FGe(12) ! element stiffness and mass matrices gravity force vector real(ReKi), dimension(:,:), allocatable :: DummyArray ! + integer(IntKi), allocatable :: MemberNodeIDs(:) ! Variables for Eigenvalue analysis real(R8Ki), dimension(:,:), allocatable :: AA, BB, CC, DD ! Linearization matrices character(len=*),parameter :: ReFmt='ES15.6E2' @@ -4508,9 +4549,16 @@ SUBROUTINE OutSummary(Init, p, m, InitInput, CBparams, Modes, Omega, Omega_Gy, E WRITE(UnSum, '()') WRITE(UnSum, '(A,I6)') '#Number of members',p%NMembers - WRITE(UnSum, '(A,I6)') '#Number of nodes per member:', Init%Ndiv+1 + WRITE(UnSum, '(A,I6)') '#Maximum number of nodes per member:', maxval(Init%MemberNDiv)+1 WRITE(UnSum, '(A9,A10,A10,A10,A10,A15,A15,A16)') '#Member ID', 'Joint1_ID', 'Joint2_ID','Prop_I','Prop_J', 'Mass','Length', 'Node IDs...' DO i=1,p%NMembers + iElemStart = Init%MemberElemStart(i) + call AllocAry(MemberNodeIDs, Init%MemberNDiv(i)+1_IntKi, 'MemberNodeIDs', ErrStat2, ErrMsg2); if(Failed()) return + MemberNodeIDs(1) = p%Elems(iElemStart, 2) + do j=2, Init%MemberNDiv(i)+1_IntKi + MemberNodeIDs(j) = p%Elems(iElemStart + j - 2_IntKi, 3) + end do + !Calculate member mass here; this should really be done somewhere else, yet it is not used anywhere else !IT WILL HAVE TO BE MODIFIED FOR OTHER THAN CIRCULAR PIPE ELEMENTS propIDs=Init%Members(i,iMProp:iMProp+1) @@ -4525,45 +4573,47 @@ SUBROUTINE OutSummary(Init, p, m, InitInput, CBparams, Modes, Omega, Omega_Gy, E mMass = BeamMassC(Init%PropSetsBC(iProp(1),4),Init%PropSetsBC(iProp(1),5),Init%PropSetsBC(iProp(1),6), & Init%PropSetsBC(iProp(2),4),Init%PropSetsBC(iProp(2),5),Init%PropSetsBC(iProp(2),6), mLength, method=-1) - WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,'//Num2LStr(Init%NDiv + 1 )//'(I6))') Init%Members(i,1:3),propIDs(1),propIDs(2),& - mMass,mLength,' ',(Init%MemberNodes(i, j), j = 1, Init%NDiv+1) + WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,'//Num2LStr(Init%MemberNDiv(i) + 1 )//'(I6))') Init%Members(i,1:3),propIDs(1),propIDs(2),& + mMass,mLength,' ',(MemberNodeIDs(j), j = 1, Init%MemberNDiv(i)+1) else if (mType==idMemberBeamRect) then iProp(1) = FINDLOCI(Init%PropSetsBR(:,1), propIDs(1)) iProp(2) = FINDLOCI(Init%PropSetsBR(:,1), propIDs(2)) mMass = BeamMassR(Init%PropSetsBR(iProp(1),4),Init%PropSetsBR(iProp(1),5),Init%PropSetsBR(iProp(1),6),Init%PropSetsBR(iProp(1),7), & Init%PropSetsBR(iProp(2),4),Init%PropSetsBR(iProp(2),5),Init%PropSetsBR(iProp(2),6),Init%PropSetsBR(iProp(2),7), mLength, method=-1) - WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,'//Num2LStr(Init%NDiv + 1 )//'(I6))') Init%Members(i,1:3),propIDs(1),propIDs(2),& - mMass,mLength,' ',(Init%MemberNodes(i, j), j = 1, Init%NDiv+1) + WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,'//Num2LStr(Init%MemberNDiv(i) + 1 )//'(I6))') Init%Members(i,1:3),propIDs(1),propIDs(2),& + mMass,mLength,' ',(MemberNodeIDs(j), j = 1, Init%MemberNDiv(i)+1) else if (mType==idMemberCable) then iProp(1) = FINDLOCI(Init%PropSetsC(:,1), propIDs(1)) mMass= Init%PropSetsC(iProp(1),3) * mLength ! rho [kg/m] * L WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,2(I6),A)') Init%Members(i,1:3),propIDs(1),propIDs(2),& - mMass,mLength,' ',(Init%MemberNodes(i, j), j = 1, 2), ' # Cable' + mMass,mLength,' ',(MemberNodeIDs(j), j = 1, 2), ' # Cable' else if (mType==idMemberRigid) then iProp(1) = FINDLOCI(Init%PropSetsR(:,1), propIDs(1)) mMass= Init%PropSetsR(iProp(1),2) * mLength ! rho [kg/m] * L WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,2(I6),A)') Init%Members(i,1:3),propIDs(1),propIDs(2),& - mMass,mLength,' ',(Init%MemberNodes(i, j), j = 1, 2), ' # Rigid link' + mMass,mLength,' ',(MemberNodeIDs(j), j = 1, 2), ' # Rigid link' else if (mType==idMemberSpring) then iProp(1) = FINDLOCI(Init%PropSetsS(:,1), propIDs(1)) mMass= 0.0 ! Spring element has no mass mLength = 0.0 ! Spring element has no length. Both JointIDs must be coincident. WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,2(I6),A)') Init%Members(i,1:3),propIDs(1),propIDs(2),& - mMass,mLength,' ',(Init%MemberNodes(i, j), j = 1, 2), ' # Spring element' + mMass,mLength,' ',(MemberNodeIDs(j), j = 1, 2), ' # Spring element' else if (mType==idMemberBeamArb) then iProp(1) = FINDLOCI(Init%PropSetsX(:,1), propIDs(1)) iProp(2) = FINDLOCI(Init%PropSetsX(:,1), propIDs(2)) mMass = Init%PropSetsX(iProp(1),4) * Init%PropSetsX(iProp(1),5) * mLength ! Simplified calculation because arbitrary beams only support uniform section properties - WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,'//Num2LStr(Init%NDiv + 1 )//'(I6))') Init%Members(i,1:3),propIDs(1),propIDs(2),& - mMass, mLength,' ',(Init%MemberNodes(i, j), j = 1, Init%NDiv+1) + WRITE(UnSum, '("#",I9,I10,I10,I10,I10,ES15.6E2,ES15.6E2, A3,'//Num2LStr(Init%MemberNDiv(i) + 1 )//'(I6))') Init%Members(i,1:3),propIDs(1),propIDs(2),& + mMass, mLength,' ',(MemberNodeIDs(j), j = 1, Init%MemberNDiv(i)+1) else WRITE(UnSum, '(A)') '#TODO, member unknown' endif - ELSE + ELSE + if (allocated(MemberNodeIDs)) deallocate(MemberNodeIDs) RETURN ENDIF - ENDDO + if (allocated(MemberNodeIDs)) deallocate(MemberNodeIDs) + ENDDO !------------------------------------------------------------------------------------------------------------- ! write Cosine matrix for all members to a txt file !------------------------------------------------------------------------------------------------------------- diff --git a/modules/subdyn/src/SubDyn_Output.f90 b/modules/subdyn/src/SubDyn_Output.f90 index 41f0df6cf2..a276cbeccc 100644 --- a/modules/subdyn/src/SubDyn_Output.f90 +++ b/modules/subdyn/src/SubDyn_Output.f90 @@ -103,10 +103,11 @@ SUBROUTINE SDOut_Init( Init, y, p, misc, InitOut, WtrDpth, ErrStat, ErrMsg ) CALL AllocAry(pLst%Fg, 12, pLst%NoutCnt, 2, 'MOutLst(I)%Fg' , ErrStat2, ErrMsg2); if(Failed()) return CALL AllocAry(pLst%extrap, pLst%NoutCnt , 'MOutLst(I)%extrap' , ErrStat2, ErrMsg2); if(Failed()) return - ! NOTE: len(MemberNodes) >2 if nDiv>1 iMember = FINDLOCI(Init%Members(:,1), pLst%MemberID) ! Reindexing from MemberID to 1:nMembers - nNodesPerMember = count(Init%MemberNodes(iMember,:)>0_IntKi) - pLst%NodeIDs(1:pLst%NoutCnt)=Init%MemberNodes(iMember, pLst%NodeCnt) ! We are storing the actual node numbers corresponding to what the user ordinal number is requesting + nNodesPerMember = Init%MemberNDiv(iMember) + 1_IntKi + do J=1,pLst%NoutCnt + pLst%NodeIDs(J) = MemberNodeID(iMember, pLst%NodeCnt(J)) ! Store actual node IDs corresponding to requested ordinals + enddo pLst%ElmIDs=0 !Initialize to 0 pLst%ElmNds=0 !Initialize to 0 pLst%extrap=.false. @@ -126,10 +127,10 @@ SUBROUTINE SDOut_Init( Init, y, p, misc, InitOut, WtrDpth, ErrStat, ErrMsg ) ENDDO ! iiElem, nElemPerNode if ( (K2==2_IntKi).or.(nNodesPerMember==2_IntKi) ) cycle ! No need to proceed further if we have an interior node or only one element ! Save neighboring element info for force extrapolation to an end node if more than 1 element per member - if (iNode == Init%MemberNodes(iMember,1)) then ! First node of the member - iNode = Init%MemberNodes(iMember,2) ! Index of the second node - else if (iNode == Init%MemberNodes(iMember,nNodesPerMember)) then ! Last node of the member - iNode = Init%MemberNodes(iMember,nNodesPerMember-1_IntKi) ! Index of the second to last node + if (iNode == MemberNodeID(iMember,1_IntKi)) then ! First node of the member + iNode = MemberNodeID(iMember,2_IntKi) ! Index of the second node + else if (iNode == MemberNodeID(iMember,nNodesPerMember)) then ! Last node of the member + iNode = MemberNodeID(iMember,nNodesPerMember-1_IntKi) ! Index of the second to last node end if do iiElem = 1, 2 ! Should have exactly two elements connecting to an interior node iElem = Init%NodesConnE(iNode, iiElem+1) ! iiElem-th element Number; no need to call ThisElementIsAlongMember since interior node but kept for safety for now @@ -156,9 +157,9 @@ SUBROUTINE SDOut_Init( Init, y, p, misc, InitOut, WtrDpth, ErrStat, ErrMsg ) CALL AllocAry(pLst%Fg, 12, 2, 2, 'MOutLst(I)%Fg' , ErrStat2, ErrMsg2); if(Failed()) return CALL AllocAry(pLst%extrap, 2 , 'MOutLst(I)%extrap' , ErrStat2, ErrMsg2); if(Failed()) return pLst%MemberID = Init%Members(iMember,1) - nNodesPerMember = count(Init%MemberNodes(iMember,:)>0_IntKi) - pLst%NodeIDs(1) = Init%MemberNodes(iMember,1) ! First node of the member - pLst%NodeIDs(2) = Init%MemberNodes(iMember,nNodesPerMember) ! Last node of the member + nNodesPerMember = Init%MemberNDiv(iMember) + 1_IntKi + pLst%NodeIDs(1) = MemberNodeID(iMember,1_IntKi) ! First node of the member + pLst%NodeIDs(2) = MemberNodeID(iMember,nNodesPerMember) ! Last node of the member pLst%ElmIDs=0 !Initialize to 0 pLst%ElmNds=0 !Initialize to 0 pLst%extrap=.false. @@ -175,10 +176,10 @@ SUBROUTINE SDOut_Init( Init, y, p, misc, InitOut, WtrDpth, ErrStat, ErrMsg ) ENDDO ! iiElem, nElemPerNode if ( nNodesPerMember==2_IntKi ) cycle ! No need to proceed further if we only have one element ! Save neighboring element info for force extrapolation to an end node if more than 1 element per member - if (iNode == Init%MemberNodes(iMember,1)) then ! First node of the member - iNode = Init%MemberNodes(iMember,2) ! Index of the second node - else if (iNode == Init%MemberNodes(iMember,nNodesPerMember)) then ! Last node of the member - iNode = Init%MemberNodes(iMember,nNodesPerMember-1_IntKi) ! Index of the second to last node + if (iNode == MemberNodeID(iMember,1_IntKi)) then ! First node of the member + iNode = MemberNodeID(iMember,2_IntKi) ! Index of the second node + else if (iNode == MemberNodeID(iMember,nNodesPerMember)) then ! Last node of the member + iNode = MemberNodeID(iMember,nNodesPerMember-1_IntKi) ! Index of the second to last node end if do iiElem = 1,2 ! Should have exactly two elements connecting to an interior node iElem = Init%NodesConnE(iNode, iiElem+1) ! iiElem-th element Number; no need to call ThisElementIsAlongMember since interior node but kept for safety for now @@ -236,22 +237,41 @@ LOGICAL FUNCTION ThisElementIsAlongMember(iElem, iNode, iMember) integer(IntKi), intent(in) :: iElem !< Element index integer(IntKi), intent(in) :: iNode !< Node index integer(IntKi), intent(in) :: iMember !< Member index - integer(IntKi), dimension(2) :: ElemNodes ! Node IDs for element under consideration (may not be consecutive numbers) - integer(IntKi) :: iOtherNode ! Other node than iNode for element iElem + integer(IntKi), dimension(2) :: ElemNodes ! Node IDs for element under consideration (may not be consecutive numbers) + integer(IntKi) :: iElemStart ! First element index for member iMember + integer(IntKi) :: iElemEnd ! Last element index for member iMember ElemNodes = p%Elems(iElem,2:3) ! 1st and 2nd node of the element - ! Check that the other node belongs to the member - IF (ElemNodes(1) == iNode) then - iOtherNode=ElemNodes(2) - else if (ElemNodes(2) == iNode) then - iOtherNode=ElemNodes(1) - else + if (ElemNodes(1) /= iNode .and. ElemNodes(2) /= iNode) then ThisElementIsAlongMember=.false. ! Not along member since nodes don't match return - endif - ! Being along the member means the second node of the element is in the node list of the member - ThisElementIsAlongMember= ANY(Init%MemberNodes(iMember,:) == iOtherNode) + end if + + iElemStart = MemberElemStart(iMember) + iElemEnd = iElemStart + Init%MemberNDiv(iMember) - 1_IntKi + ThisElementIsAlongMember = (iElem >= iElemStart) .and. (iElem <= iElemEnd) END FUNCTION + INTEGER(IntKi) FUNCTION MemberElemStart(iMember) + integer(IntKi), intent(in) :: iMember + MemberElemStart = Init%MemberElemStart(iMember) + END FUNCTION MemberElemStart + + INTEGER(IntKi) FUNCTION MemberNodeID(iMember, iOrdinal) + integer(IntKi), intent(in) :: iMember + integer(IntKi), intent(in) :: iOrdinal + integer(IntKi) :: iElemStart + + MemberNodeID = 0_IntKi + if (iOrdinal < 1_IntKi .or. iOrdinal > Init%MemberNDiv(iMember)+1_IntKi) return + + iElemStart = MemberElemStart(iMember) + if (iOrdinal == 1_IntKi) then + MemberNodeID = p%Elems(iElemStart, 2) + else + MemberNodeID = p%Elems(iElemStart + iOrdinal - 2_IntKi, 3) + end if + END FUNCTION MemberNodeID + !> Set different "data" for a given output node, and possibly store more than one "data" per node: !! The "data" is: !! - Mass, stiffness matrices and constant element force vector Fg: diff --git a/modules/subdyn/src/SubDyn_Registry.txt b/modules/subdyn/src/SubDyn_Registry.txt index 4d44290c29..8026066398 100644 --- a/modules/subdyn/src/SubDyn_Registry.txt +++ b/modules/subdyn/src/SubDyn_Registry.txt @@ -127,6 +127,9 @@ typedef ^ SD_InitType IntKi GuyanDampSize - - - "Size of G typedef ^ SD_InitType ReKi GuyanDampMat {:}{:} - - "Guyan Damping Matrix, see also CBB" typedef ^ SD_InitType INTEGER Members {:}{:} - - "Member joints connection " typedef ^ SD_InitType ReKi MemberSpin {:} - - "Member spin angle about its axis - for rectangular members " rad +typedef ^ SD_InitType ReKi MemberDivSize {:} - - "Optional maximum element length for each member" m +typedef ^ SD_InitType IntKi MemberNDiv {:} - - "Resolved number of finite elements per member" - +typedef ^ SD_InitType IntKi MemberElemStart {:} - - "First element index for each member in p%Elems" - typedef ^ SD_InitType CHARACTER(ChanLen) SSOutList {:} - - "List of Output Channels " typedef ^ SD_InitType LOGICAL OutCOSM - - - "Output Cos-matrices Flag " typedef ^ SD_InitType LOGICAL TabDelim - - - "Generate a tab-delimited output file in OutJckF-Flag " @@ -152,7 +155,7 @@ typedef ^ SD_InitType ReKi PropsS {:}{:} - - "Property typedef ^ SD_InitType R8Ki K {:}{:} - - "System stiffness matrix " typedef ^ SD_InitType R8Ki M {:}{:} - - "System mass matrix " typedef ^ SD_InitType ReKi ElemProps {:}{:} - - "Element properties(A, L, Ixx, Iyy, Jzz, Shear, Kappa, E, G, Rho, DirCos(1,1), DirCos(2, 1), ....., DirCos(3, 3) )" -typedef ^ SD_InitType INTEGER MemberNodes {:}{:} - - "Member number and list of nodes making up a member (>2 if subdivided)" +typedef ^ SD_InitType INTEGER MemberNodes {:}{:} - - "Member number and endpoint node IDs (interior member nodes reconstructed from connectivity)" typedef ^ SD_InitType INTEGER NodesConnN {:}{:} - - "Nodes that connect to a common node " typedef ^ SD_InitType INTEGER NodesConnE {:}{:} - - "Elements that connect to a common node" typedef ^ SD_InitType LOGICAL SSSum - - - "SubDyn Summary File Flag " diff --git a/modules/subdyn/src/SubDyn_Types.f90 b/modules/subdyn/src/SubDyn_Types.f90 index 0a8678de13..9fee52b5f2 100644 --- a/modules/subdyn/src/SubDyn_Types.f90 +++ b/modules/subdyn/src/SubDyn_Types.f90 @@ -168,6 +168,9 @@ MODULE SubDyn_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: GuyanDampMat !< Guyan Damping Matrix, see also CBB [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: Members !< Member joints connection [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: MemberSpin !< Member spin angle about its axis - for rectangular members [rad] + REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: MemberDivSize !< Optional maximum element length for each member [m] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: MemberNDiv !< Resolved number of finite elements per member [-] + INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: MemberElemStart !< First element index for each member in p%Elems [-] CHARACTER(ChanLen) , DIMENSION(:), ALLOCATABLE :: SSOutList !< List of Output Channels [-] LOGICAL :: OutCOSM = .false. !< Output Cos-matrices Flag [-] LOGICAL :: TabDelim = .false. !< Generate a tab-delimited output file in OutJckF-Flag [-] @@ -1369,6 +1372,42 @@ subroutine SD_CopyInitType(SrcInitTypeData, DstInitTypeData, CtrlCode, ErrStat, end if DstInitTypeData%MemberSpin = SrcInitTypeData%MemberSpin end if + if (allocated(SrcInitTypeData%MemberDivSize)) then + LB(1:1) = lbound(SrcInitTypeData%MemberDivSize) + UB(1:1) = ubound(SrcInitTypeData%MemberDivSize) + if (.not. allocated(DstInitTypeData%MemberDivSize)) then + allocate(DstInitTypeData%MemberDivSize(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitTypeData%MemberDivSize.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitTypeData%MemberDivSize = SrcInitTypeData%MemberDivSize + end if + if (allocated(SrcInitTypeData%MemberNDiv)) then + LB(1:1) = lbound(SrcInitTypeData%MemberNDiv) + UB(1:1) = ubound(SrcInitTypeData%MemberNDiv) + if (.not. allocated(DstInitTypeData%MemberNDiv)) then + allocate(DstInitTypeData%MemberNDiv(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitTypeData%MemberNDiv.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitTypeData%MemberNDiv = SrcInitTypeData%MemberNDiv + end if + if (allocated(SrcInitTypeData%MemberElemStart)) then + LB(1:1) = lbound(SrcInitTypeData%MemberElemStart) + UB(1:1) = ubound(SrcInitTypeData%MemberElemStart) + if (.not. allocated(DstInitTypeData%MemberElemStart)) then + allocate(DstInitTypeData%MemberElemStart(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInitTypeData%MemberElemStart.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInitTypeData%MemberElemStart = SrcInitTypeData%MemberElemStart + end if if (allocated(SrcInitTypeData%SSOutList)) then LB(1:1) = lbound(SrcInitTypeData%SSOutList) UB(1:1) = ubound(SrcInitTypeData%SSOutList) @@ -1659,6 +1698,15 @@ subroutine SD_DestroyInitType(InitTypeData, ErrStat, ErrMsg) if (allocated(InitTypeData%MemberSpin)) then deallocate(InitTypeData%MemberSpin) end if + if (allocated(InitTypeData%MemberDivSize)) then + deallocate(InitTypeData%MemberDivSize) + end if + if (allocated(InitTypeData%MemberNDiv)) then + deallocate(InitTypeData%MemberNDiv) + end if + if (allocated(InitTypeData%MemberElemStart)) then + deallocate(InitTypeData%MemberElemStart) + end if if (allocated(InitTypeData%SSOutList)) then deallocate(InitTypeData%SSOutList) end if @@ -1758,6 +1806,9 @@ subroutine SD_PackInitType(RF, Indata) call RegPackAlloc(RF, InData%GuyanDampMat) call RegPackAlloc(RF, InData%Members) call RegPackAlloc(RF, InData%MemberSpin) + call RegPackAlloc(RF, InData%MemberDivSize) + call RegPackAlloc(RF, InData%MemberNDiv) + call RegPackAlloc(RF, InData%MemberElemStart) call RegPackAlloc(RF, InData%SSOutList) call RegPack(RF, InData%OutCOSM) call RegPack(RF, InData%TabDelim) @@ -1834,6 +1885,9 @@ subroutine SD_UnPackInitType(RF, OutData) call RegUnpackAlloc(RF, OutData%GuyanDampMat); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%Members); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%MemberSpin); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%MemberDivSize); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%MemberNDiv); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%MemberElemStart); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%SSOutList); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%OutCOSM); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%TabDelim); if (RegCheckErr(RF, RoutineName)) return diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index 44517ee591..c49fca12f4 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -523,6 +523,7 @@ sd_regression("SD_Spring_Case3" "subdyn;offshore") sd_regression("SD_Revolute_Joint" "subdyn;offshore") sd_regression("SD_2Beam_Spring" "subdyn;offshore") sd_regression("SD_2Beam_Cantilever" "subdyn;offshore") +sd_regression("SD_2Beam_MixedDiscretization" "subdyn;offshore") sd_regression("SD_CantileverBeam_Rectangular" "subdyn;offshore") sd_regression("SD_SelfWeight_FloatingSystem" "subdyn;offshore") # TODO test below are bugs, should be added when fixed diff --git a/reg_tests/r-test b/reg_tests/r-test index 5629749eda..2e2d39424a 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 5629749edaa217cff27ebfee02ea20c5b01272a7 +Subproject commit 2e2d39424a8afc06e1aea2c6d6426b8fbea9c738