Skip to content

Commit 7b86459

Browse files
authored
Merge pull request #3390 from luwang00/SeaSt_UsrCrntPrfl
Add SeaState current profile input through a user-provided ASCII file (CurrMod=3)
2 parents f5a0166 + 2f1a2e1 commit 7b86459

10 files changed

Lines changed: 302 additions & 9 deletions

File tree

‎docs/source/user/seastate/appendix.rst‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@ structure::
5454
0 CrestXi - X-position of the crest (m) [unused when ConstWaveMod=0]
5555
0 CrestYi - Y-position of the crest (m) [unused when ConstWaveMod=0]
5656
---------------------- CURRENT ------------------------------------------------- [unused with WaveMod=6]
57-
0 CurrMod - Current profile model {0: none=no current, 1: standard, 2: user-defined from routine UserCurrent} (switch)
57+
0 CurrMod - Current profile model {0: none=no current, 1: standard, 2: user-defined from routine UserCurrent, 3: user-defined from ASCII profile file} (switch)
5858
0 CurrSSV0 - Sub-surface current velocity at still water level (m/s) [used only when CurrMod=1]
5959
"DEFAULT" CurrSSDir - Sub-surface current heading direction (degrees) or DEFAULT (string) [used only when CurrMod=1]
6060
20 CurrNSRef - Near-surface current reference depth (meters) [used only when CurrMod=1]
6161
0 CurrNSV0 - Near-surface current velocity at still water level (m/s) [used only when CurrMod=1]
6262
0 CurrNSDir - Near-surface current heading direction (degrees) [used only when CurrMod=1]
6363
0 CurrDIV - Depth-independent current velocity (m/s) [used only when CurrMod=1]
6464
0 CurrDIDir - Depth-independent current heading direction (degrees) [used only when CurrMod=1]
65+
"unused" CurrFile - User-defined current profile file (ASCII depth below SWL (<=0), x-velocity, y-velocity table) [used only when CurrMod=3]
6566
---------------------- MacCamy-Fuchs diffraction model -------------------------
6667
0 MCFD - MacCamy-Fuchs member radius (ignored if radius <= 0) [must be 0 when WaveMod 0 or 6]
6768
---------------------- OUTPUT --------------------------------------------------

‎docs/source/user/seastate/input_files.rst‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ You can include water velocity due to a current model by setting
504504
not include current. **CurrMod** = 2 requires that the *UserCurrent()*
505505
subroutine of the *Current.f90* source file be implemented by the user,
506506
and will require recompiling either the standalone SeaState program or
507-
OpenFAST. Current induces steady hydrodynamic loads through the viscous-drag
507+
OpenFAST. **CurrMod** = 3 reads an ASCII current profile file specified by
508+
**CurrFile**. Current induces steady hydrodynamic loads through the viscous-drag
508509
terms (both distributed and lumped) of strip-theory members in HydroDyn. Current is
509510
not used in the potential-flow solution or when **WaveMod** = 6.
510511

@@ -556,6 +557,13 @@ The depth-independent current velocity everywhere equals **CurrDIV**.
556557
This current has a heading direction **CurrDIDir**, following the same
557558
convention as **WaveDir**.
558559

560+
For **CurrMod** = 3, SeaState reads an ASCII file with three columns:
561+
depth in meters negative downward from SWL, x-velocity in m/s, and
562+
y-velocity in m/s. Depth values less than or equal to zero must be strictly
563+
decreasing. SeaState linearly interpolates the x- and y-velocity components
564+
separately by depth, and uses the nearest endpoint value when the query
565+
depth lies outside the range covered by the table.
566+
559567
MacCamy-Fuchs diffraction model
560568
-------------------------------
561569
HydroDyn now supports the MacCamy-Fuchs wave-diffraction solution for strip-theory members.

‎modules/seastate/src/Current.f90‎

Lines changed: 205 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,198 @@ SUBROUTINE UserCurrent ( zi, EffWtrDpth, DirRoot, CurrVxi, CurrVyi )
6666
RETURN
6767

6868
END SUBROUTINE UserCurrent
69+
70+
71+
!----------------------------------------------------------------------------------------------------------------------------------
72+
!> Read and validate user-defined current profile data from an ASCII file.
73+
SUBROUTINE ReadUsrCurrentProfile( CurrFile, CurrentData, ErrStat, ErrMsg )
74+
75+
IMPLICIT NONE
76+
77+
CHARACTER(*), INTENT(IN ) :: CurrFile
78+
TYPE(Current_InitInputType), INTENT(INOUT) :: CurrentData
79+
INTEGER(IntKi), INTENT( OUT) :: ErrStat
80+
CHARACTER(*), INTENT( OUT) :: ErrMsg
81+
INTEGER(IntKi) :: UnIn
82+
INTEGER(IntKi) :: IOS
83+
INTEGER(IntKi) :: NRows
84+
INTEGER(IntKi) :: RowIdx
85+
INTEGER(IntKi) :: LineNum
86+
CHARACTER(1024) :: Line
87+
CHARACTER(1024) :: CleanLine
88+
REAL(SiKi) :: TmpRow(3)
89+
REAL(SiKi) :: Depth
90+
REAL(SiKi) :: PrevDepth
91+
LOGICAL :: DataStarted
92+
INTEGER(IntKi) :: ErrStat2
93+
CHARACTER(ErrMsgLen) :: ErrMsg2
94+
CHARACTER(*), PARAMETER :: RoutineName = 'ReadUsrCurrentProfile'
95+
96+
ErrStat = ErrID_None
97+
ErrMsg = ''
98+
99+
if (allocated(CurrentData%UsrCurrProfileDepth)) deallocate(CurrentData%UsrCurrProfileDepth)
100+
if (allocated(CurrentData%UsrCurrProfileVxi )) deallocate(CurrentData%UsrCurrProfileVxi)
101+
if (allocated(CurrentData%UsrCurrProfileVyi )) deallocate(CurrentData%UsrCurrProfileVyi)
102+
103+
call GetNewUnit( UnIn )
104+
call OpenFInpFile( UnIn, TRIM(CurrFile), ErrStat2, ErrMsg2 ); if (Failed()) return
105+
106+
NRows = 0
107+
LineNum = 0
108+
DataStarted = .false.
109+
do
110+
read( UnIn, '(A)', IOSTAT=IOS ) Line
111+
LineNum = LineNum + 1
112+
if ( IOS < 0 ) exit
113+
if ( IOS /= 0 ) then
114+
call SetErrStat( ErrID_Fatal, 'Error reading current profile file '//TRIM(CurrFile)//' at line '//TRIM(Num2LStr(LineNum))//'.', ErrStat, ErrMsg, RoutineName )
115+
call CleanUp()
116+
return
117+
end if
118+
119+
CleanLine = ADJUSTL( Line )
120+
ErrStat2 = ErrID_None
121+
ErrMsg2 = ''
122+
call ReadAry( CleanLine, TmpRow, 3, 'CurrentProfileLine', 'depth, x-velocity, y-velocity', ErrStat2, ErrMsg2 )
123+
if ( ErrStat2 >= AbortErrLev .or. LEN_TRIM(ErrMsg2) > 0 ) then
124+
if ( .not. DataStarted ) cycle
125+
call SetErrStat( ErrID_Fatal, 'Invalid current profile data at line '//TRIM(Num2LStr(LineNum))//' in file '//TRIM(CurrFile)//'. '//TRIM(ErrMsg2), ErrStat, ErrMsg, RoutineName )
126+
call CleanUp()
127+
return
128+
end if
129+
130+
DataStarted = .true.
131+
NRows = NRows + 1
132+
end do
133+
134+
if ( NRows <= 0 ) then
135+
call SetErrStat( ErrID_Fatal, 'Current profile file '//TRIM(CurrFile)//' contains no data rows.', ErrStat, ErrMsg, RoutineName )
136+
call CleanUp()
137+
return
138+
end if
139+
140+
rewind( UnIn, IOSTAT=IOS )
141+
if ( IOS /= 0 ) then
142+
call SetErrStat( ErrID_Fatal, 'Error rewinding current profile file '//TRIM(CurrFile)//'.', ErrStat, ErrMsg, RoutineName )
143+
call CleanUp()
144+
return
145+
end if
146+
147+
call AllocAry( CurrentData%UsrCurrProfileDepth, NRows, 'UsrCurrProfileDepth', ErrStat2, ErrMsg2 ); if (Failed()) return
148+
call AllocAry( CurrentData%UsrCurrProfileVxi, NRows, 'UsrCurrProfileVxi', ErrStat2, ErrMsg2 ); if (Failed()) return
149+
call AllocAry( CurrentData%UsrCurrProfileVyi, NRows, 'UsrCurrProfileVyi', ErrStat2, ErrMsg2 ); if (Failed()) return
150+
151+
RowIdx = 0
152+
LineNum = 0
153+
DataStarted = .false.
154+
PrevDepth = HUGE(PrevDepth)
155+
do
156+
read( UnIn, '(A)', IOSTAT=IOS ) Line
157+
LineNum = LineNum + 1
158+
if ( IOS < 0 ) exit
159+
if ( IOS /= 0 ) then
160+
call SetErrStat( ErrID_Fatal, 'Error reading current profile file '//TRIM(CurrFile)//' at line '//TRIM(Num2LStr(LineNum))//'.', ErrStat, ErrMsg, RoutineName )
161+
call CleanUp()
162+
return
163+
end if
164+
165+
CleanLine = ADJUSTL( Line )
166+
ErrStat2 = ErrID_None
167+
ErrMsg2 = ''
168+
call ReadAry( CleanLine, TmpRow, 3, 'CurrentProfileLine', 'depth, x-velocity, y-velocity', ErrStat2, ErrMsg2 )
169+
if ( ErrStat2 >= AbortErrLev .or. LEN_TRIM(ErrMsg2) > 0 ) then
170+
if ( .not. DataStarted ) cycle
171+
call SetErrStat( ErrID_Fatal, 'Invalid current profile data at line '//TRIM(Num2LStr(LineNum))//' in file '//TRIM(CurrFile)//'. '//TRIM(ErrMsg2), ErrStat, ErrMsg, RoutineName )
172+
call CleanUp()
173+
return
174+
end if
175+
176+
DataStarted = .true.
177+
RowIdx = RowIdx + 1
178+
Depth = TmpRow(1)
179+
180+
if ( Depth > 0.0_SiKi ) then
181+
call SetErrStat( ErrID_Fatal, 'Current profile depth must be non-positive at line '//TRIM(Num2LStr(LineNum))//' in file '//TRIM(CurrFile)//'.', ErrStat, ErrMsg, RoutineName )
182+
call CleanUp()
183+
return
184+
end if
185+
186+
if ( RowIdx > 1 ) then
187+
if ( Depth >= PrevDepth ) then
188+
call SetErrStat( ErrID_Fatal, 'Current profile depths must be strictly decreasing at line '//TRIM(Num2LStr(LineNum))//' in file '//TRIM(CurrFile)//'.', ErrStat, ErrMsg, RoutineName )
189+
call CleanUp()
190+
return
191+
end if
192+
end if
193+
194+
CurrentData%UsrCurrProfileDepth(RowIdx) = Depth
195+
CurrentData%UsrCurrProfileVxi(RowIdx) = TmpRow(2)
196+
CurrentData%UsrCurrProfileVyi(RowIdx) = TmpRow(3)
197+
PrevDepth = Depth
198+
end do
199+
200+
close( UnIn )
201+
202+
contains
203+
204+
logical function Failed()
205+
call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )
206+
Failed = ErrStat >= AbortErrLev
207+
if (Failed) call CleanUp()
208+
end function
209+
210+
subroutine CleanUp
211+
close( UnIn )
212+
if (allocated(CurrentData%UsrCurrProfileDepth)) deallocate(CurrentData%UsrCurrProfileDepth)
213+
if (allocated(CurrentData%UsrCurrProfileVxi )) deallocate(CurrentData%UsrCurrProfileVxi)
214+
if (allocated(CurrentData%UsrCurrProfileVyi )) deallocate(CurrentData%UsrCurrProfileVyi)
215+
end subroutine CleanUp
216+
217+
END SUBROUTINE ReadUsrCurrentProfile
218+
219+
220+
!----------------------------------------------------------------------------------------------------------------------------------
221+
!> Interpolate the user-defined current profile at a given depth.
222+
SUBROUTINE CurrentProfileAtDepth( QueryDepth, UsrCurrProfileDepth, UsrCurrProfileVxi, UsrCurrProfileVyi, CurrVxi, CurrVyi )
223+
224+
IMPLICIT NONE
225+
226+
REAL(SiKi), INTENT(IN) :: QueryDepth
227+
REAL(SiKi), INTENT(IN), DIMENSION(:) :: UsrCurrProfileDepth
228+
REAL(SiKi), INTENT(IN), DIMENSION(:) :: UsrCurrProfileVxi
229+
REAL(SiKi), INTENT(IN), DIMENSION(:) :: UsrCurrProfileVyi
230+
REAL(SiKi), INTENT(OUT) :: CurrVxi
231+
REAL(SiKi), INTENT(OUT) :: CurrVyi
232+
233+
INTEGER(IntKi) :: I
234+
INTEGER(IntKi) :: NProfileRows
235+
REAL(SiKi) :: Frac
236+
237+
NProfileRows = SIZE( UsrCurrProfileDepth )
238+
239+
if ( QueryDepth >= UsrCurrProfileDepth(1) ) then
240+
CurrVxi = UsrCurrProfileVxi(1)
241+
CurrVyi = UsrCurrProfileVyi(1)
242+
return
243+
end if
244+
245+
if ( QueryDepth <= UsrCurrProfileDepth(NProfileRows) ) then
246+
CurrVxi = UsrCurrProfileVxi(NProfileRows)
247+
CurrVyi = UsrCurrProfileVyi(NProfileRows)
248+
return
249+
end if
250+
251+
do I = 1, NProfileRows - 1
252+
if ( QueryDepth >= UsrCurrProfileDepth(I+1) ) then
253+
Frac = ( QueryDepth - UsrCurrProfileDepth(I) ) / ( UsrCurrProfileDepth(I+1) - UsrCurrProfileDepth(I) )
254+
CurrVxi = UsrCurrProfileVxi(I) + Frac * ( UsrCurrProfileVxi(I+1) - UsrCurrProfileVxi(I) )
255+
CurrVyi = UsrCurrProfileVyi(I) + Frac * ( UsrCurrProfileVyi(I+1) - UsrCurrProfileVyi(I) )
256+
return
257+
end if
258+
end do
259+
260+
END SUBROUTINE CurrentProfileAtDepth
69261

70262

71263
!----------------------------------------------------------------------------------------------------------------------------------
@@ -140,6 +332,12 @@ SUBROUTINE Calc_Current( InitInp, z, h , DirRoot, CurrVxi, CurrVyi )
140332

141333
CALL UserCurrent ( z, h, DirRoot, CurrVxi, CurrVyi )
142334

335+
336+
CASE ( 3 ) ! User-defined current profile from ASCII file.
337+
338+
CALL CurrentProfileAtDepth( z, InitInp%UsrCurrProfileDepth, InitInp%UsrCurrProfileVxi, InitInp%UsrCurrProfileVyi, &
339+
CurrVxi, CurrVyi )
340+
143341
ENDSELECT
144342

145343
END IF
@@ -156,7 +354,7 @@ END SUBROUTINE Calc_Current
156354
SUBROUTINE Current_Init( InitInp, InitOut, ErrStat, ErrMsg )
157355
!..................................................................................................................................
158356

159-
TYPE(Current_InitInputType), INTENT(IN ) :: InitInp !< Input data for initialization routine
357+
TYPE(Current_InitInputType), INTENT(INOUT) :: InitInp !< Input data for initialization routine
160358
TYPE(Current_InitOutputType), INTENT( OUT) :: InitOut !< Output for initialization routine
161359
INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation
162360
CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None
@@ -182,8 +380,12 @@ SUBROUTINE Current_Init( InitInp, InitOut, ErrStat, ErrMsg )
182380

183381
ErrStat = ErrID_None
184382
ErrMsg = ""
185-
186-
383+
384+
IF ( InitInp%CurrMod == 3 ) THEN
385+
CALL ReadUsrCurrentProfile( TRIM(InitInp%CurrFile), InitInp, ErrStat, ErrMsg )
386+
IF ( ErrStat >= AbortErrLev ) RETURN
387+
END IF
388+
187389
! IF there are Morison elements, then compute the current components at each morison node elevation
188390

189391
IF ( InitInp%NGridPts > 0 ) THEN

‎modules/seastate/src/Current.txt‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ typedef ^ ^ SiKi Cu
2626
typedef ^ ^ SiKi CurrNSDir - - - "" -
2727
typedef ^ ^ SiKi CurrDIV - - - "" -
2828
typedef ^ ^ SiKi CurrDIDir - - - "" -
29+
typedef ^ ^ CHARACTER(1024) CurrFile - - - "" -
30+
typedef ^ ^ SiKi UsrCurrProfileDepth {:} - - "" -
31+
typedef ^ ^ SiKi UsrCurrProfileVxi {:} - - "" -
32+
typedef ^ ^ SiKi UsrCurrProfileVyi {:} - - "" -
2933
typedef ^ ^ INTEGER CurrMod - - - "" -
3034
typedef ^ ^ SiKi EffWtrDpth - - - "" -
3135
typedef ^ ^ SiKi WaveKinGridzi {:} - - "" -

0 commit comments

Comments
 (0)