@@ -66,6 +66,198 @@ SUBROUTINE UserCurrent ( zi, EffWtrDpth, DirRoot, CurrVxi, CurrVyi )
6666 RETURN
6767
6868END 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
156354SUBROUTINE 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
0 commit comments