@@ -173,6 +173,7 @@ end subroutine HA_register
173
173
174
174
! > This subroutine accumulates the temporal basis functions in FtF.
175
175
! ! The tidal constituents are those used in MOM_tidal_forcing, plus the mean (of zero frequency).
176
+ ! ! Only the main diagonal and entries below it are calculated, which are needed for Cholesky decomposition.
176
177
subroutine HA_accum_FtF (Time , CS )
177
178
type (time_type), intent (in ) :: Time ! < The current model time
178
179
type (harmonic_analysis_CS), intent (inout ) :: CS ! < Control structure of the MOM_harmonic_analysis module
@@ -191,27 +192,31 @@ subroutine HA_accum_FtF(Time, CS)
191
192
nc = CS% nc
192
193
now = CS% US% s_to_T * time_type_to_real(Time - CS% time_ref)
193
194
194
- ! Accumulate FtF
195
- CS% FtF(1 ,1 ) = CS% FtF(1 ,1 ) + 1.0 ! < For the zero frequency
195
+ ! < First entry, corresponding to the zero frequency constituent (mean)
196
+ CS% FtF(1 ,1 ) = CS% FtF(1 ,1 ) + 1.0
197
+
196
198
do c= 1 ,nc
197
199
icos = 2 * c
198
200
isin = 2 * c+1
199
201
cosomegat = cos (CS% freq(c) * now + CS% phase0(c))
200
202
sinomegat = sin (CS% freq(c) * now + CS% phase0(c))
203
+
204
+ ! First column, corresponding to the zero frequency constituent (mean)
201
205
CS% FtF(icos,1 ) = CS% FtF(icos,1 ) + cosomegat
202
206
CS% FtF(isin,1 ) = CS% FtF(isin,1 ) + sinomegat
203
- CS% FtF(1 ,icos) = CS% FtF(icos,1 )
204
- CS% FtF(1 ,isin) = CS% FtF(isin,1 )
205
- do cc= c,nc
207
+
208
+ do cc= 1 ,c
206
209
iccos = 2 * cc
207
210
issin = 2 * cc+1
208
211
ccosomegat = cos (CS% freq(cc) * now + CS% phase0(cc))
209
212
ssinomegat = sin (CS% freq(cc) * now + CS% phase0(cc))
213
+
214
+ ! Interior of the matrix, corresponding to the products of cosine and sine terms
210
215
CS% FtF(icos,iccos) = CS% FtF(icos,iccos) + cosomegat * ccosomegat
211
216
CS% FtF(icos,issin) = CS% FtF(icos,issin) + cosomegat * ssinomegat
212
217
CS% FtF(isin,iccos) = CS% FtF(isin,iccos) + sinomegat * ccosomegat
213
218
CS% FtF(isin,issin) = CS% FtF(isin,issin) + sinomegat * ssinomegat
214
- enddo ! cc=c,nc
219
+ enddo ! cc=1,c
215
220
enddo ! c=1,nc
216
221
217
222
end subroutine HA_accum_FtF
@@ -276,14 +281,18 @@ subroutine HA_accum_FtSSH(key, data, Time, G, CS)
276
281
277
282
is = ha1% is ; ie = ha1% ie ; js = ha1% js ; je = ha1% je
278
283
279
- ! Accumulate FtF and FtSSH
284
+ ! < First entry, corresponding to the zero frequency constituent (mean)
285
+ do j= js,je ; do i= is,ie
286
+ ha1% FtSSH(i,j,1 ) = ha1% FtSSH(i,j,1 ) + (data (i,j) - ha1% ref(i,j))
287
+ enddo ; enddo
288
+
289
+ ! < The remaining entries
280
290
do c= 1 ,nc
281
291
icos = 2 * c
282
292
isin = 2 * c+1
283
293
cosomegat = cos (CS% freq(c) * now + CS% phase0(c))
284
294
sinomegat = sin (CS% freq(c) * now + CS% phase0(c))
285
295
do j= js,je ; do i= is,ie
286
- ha1% FtSSH(i,j,1 ) = ha1% FtSSH(i,j,1 ) + (data (i,j) - ha1% ref(i,j))
287
296
ha1% FtSSH(i,j,icos) = ha1% FtSSH(i,j,icos) + (data (i,j) - ha1% ref(i,j)) * cosomegat
288
297
ha1% FtSSH(i,j,isin) = ha1% FtSSH(i,j,isin) + (data (i,j) - ha1% ref(i,j)) * sinomegat
289
298
enddo ; enddo
@@ -315,7 +324,7 @@ subroutine HA_write(ha1, Time, G, CS)
315
324
! Local variables
316
325
real , dimension (:,:,:), allocatable :: FtSSHw ! < An array containing the harmonic constants [A]
317
326
integer :: year, month, day, hour, minute, second
318
- integer :: nc, k, is, ie, js, je
327
+ integer :: nc, i, j, k, is, ie, js, je
319
328
320
329
character (len= 255 ) :: filename ! < Output file name
321
330
type (MOM_infra_file) :: cdf ! < The file handle for output harmonic constants
@@ -348,6 +357,11 @@ subroutine HA_write(ha1, Time, G, CS)
348
357
call create_MOM_file(cdf, trim (filename), cdf_vars, &
349
358
2 * nc+1 , cdf_fields, SINGLE_FILE, 86400.0 , G= G)
350
359
360
+ ! Add the initial field back to the mean state
361
+ do j= js,je ; do i= is,ie
362
+ FtSSHw(i,j,1 ) = FtSSHw(i,j,1 ) + ha1% ref(i,j)
363
+ enddo ; enddo
364
+
351
365
! Write data
352
366
call MOM_write_field(cdf, cdf_fields(1 ), G% domain, FtSSHw(:,:,1 ), 0.0 )
353
367
do k= 1 ,nc
@@ -362,75 +376,68 @@ subroutine HA_write(ha1, Time, G, CS)
362
376
363
377
end subroutine HA_write
364
378
365
- ! > This subroutine computes the harmonic constants (stored in FtSSHw ) using the dot products of the temporal
379
+ ! > This subroutine computes the harmonic constants (stored in x ) using the dot products of the temporal
366
380
! ! basis functions accumulated in FtF, and the dot products of the SSH (or other fields) with the temporal basis
367
381
! ! functions accumulated in FtSSH. The system is solved by Cholesky decomposition,
368
382
! !
369
- ! ! FtF * FtSSHw = FtSSH, => FtFw * (FtFw ' * FtSSHw) = FtSSH,
383
+ ! ! FtF * x = FtSSH, => L * (L ' * x) = FtSSH, => L * y = FtSSH,
370
384
! !
371
- ! ! where FtFw is a lower triangular matrix, and the prime denotes matrix transpose .
385
+ ! ! where L is the lower triangular matrix, y = L' * x, and x is the solution vector .
372
386
! !
373
- subroutine HA_solver (ha1 , nc , FtF , FtSSHw )
387
+ subroutine HA_solver (ha1 , nc , FtF , x )
374
388
type (HA_type), pointer , intent (in ) :: ha1 ! < Control structure for the current field
375
389
integer , intent (in ) :: nc ! < Number of harmonic constituents
376
390
real , dimension (:,:), intent (in ) :: FtF ! < Accumulator of (F' * F) for all fields [nondim]
377
- real , dimension (:,:,:), allocatable , intent (out ) :: FtSSHw ! < Work array for Cholesky decomposition [A]
391
+ real , dimension (ha1% is:ha1% ie,ha1% js:ha1% je,2 * nc+1 ), &
392
+ intent (out ) :: x ! < Solution vector of harmonic constants [A]
378
393
379
394
! Local variables
380
- real :: tmp0 ! < Temporary variable for Cholesky decomposition [nondim]
381
- real , dimension (:), allocatable :: tmp1 ! < Temporary variable for Cholesky decomposition [nondim]
382
- real , dimension (:,:), allocatable :: tmp2 ! < Temporary variable for Cholesky decomposition [A]
383
- real , dimension (:,:), allocatable :: FtFw ! < Lower triangular matrix for Cholesky decomposition [nondim]
384
- integer :: k, m, n, is, ie, js, je
385
-
386
- is = ha1% is ; ie = ha1% ie ; js = ha1% js ; je = ha1% je
387
-
388
- allocate (tmp1(1 :2 * nc+1 ), source= 0.0 )
389
- allocate (tmp2(is:ie,js:je), source= 0.0 )
390
- allocate (FtFw(1 :2 * nc+1 ,1 :2 * nc+1 ), source= 0.0 )
391
- allocate (FtSSHw(is:ie,js:je,2 * nc+1 ), source= 0.0 )
392
-
393
- ! Construct FtFw
394
- FtFw(:,:) = 0.0
395
+ real :: tmp0 ! < Temporary variable for Cholesky decomposition [nondim]
396
+ real , dimension (2 * nc+1 ,2 * nc+1 ) :: L ! < Lower triangular matrix of Cholesky decomposition [nondim]
397
+ real , dimension (2 * nc+1 ) :: tmp1 ! < Inverse of the diagonal entries of L [nondim]
398
+ real , dimension (ha1% is:ha1% ie,ha1% js:ha1% je) :: tmp2 ! < 2D temporary array involving FtSSH [A]
399
+ real , dimension (ha1% is:ha1% ie,ha1% js:ha1% je,2 * nc+1 ) :: y ! < 3D temporary array, i.e., L' * x [A]
400
+ integer :: k, m, n
401
+
402
+ ! Cholesky decomposition
395
403
do m= 1 ,2 * nc+1
404
+
405
+ ! First, calculate the diagonal entries
396
406
tmp0 = 0.0
397
- do k= 1 ,m-1
398
- tmp0 = tmp0 + FtFw (m,k) * FtFw (m,k)
407
+ do k= 1 ,m-1 ! This loop operates along the m-th row
408
+ tmp0 = tmp0 + L (m,k) * L (m,k)
399
409
enddo
400
- FtFw(m,m) = sqrt (FtF(m,m) - tmp0)
401
- tmp1(m) = 1 / FtFw(m,m)
402
- do k= m+1 ,2 * nc+1
410
+ L(m,m) = sqrt (FtF(m,m) - tmp0) ! This is the m-th diagonal entry
411
+
412
+ ! Now calculate the off-diagonal entries
413
+ tmp1(m) = 1 / L(m,m)
414
+ do k= m+1 ,2 * nc+1 ! This loop operates along the column below the m-th diagonal entry
403
415
tmp0 = 0.0
404
416
do n= 1 ,m-1
405
- tmp0 = tmp0 + FtFw (k,n) * FtFw (m,n)
417
+ tmp0 = tmp0 + L (k,n) * L (m,n)
406
418
enddo
407
- FtFw (k,m) = (FtF(k,m) - tmp0) * tmp1(m)
419
+ L (k,m) = (FtF(k,m) - tmp0) * tmp1(m) ! This is the k-th off-diagonal entry below the m-th diagonal entry
408
420
enddo
409
421
enddo
410
422
411
- ! Solve for (FtFw' * FtSSHw)
412
- FtSSHw(:,:,:) = ha1% FtSSH(:,:,:)
423
+ ! Solve for y from L * y = FtSSH
413
424
do k= 1 ,2 * nc+1
414
425
tmp2(:,:) = 0.0
415
426
do m= 1 ,k-1
416
- tmp2(:,:) = tmp2(:,:) + FtFw (k,m) * FtSSHw (:,:,m)
427
+ tmp2(:,:) = tmp2(:,:) + L (k,m) * y (:,:,m)
417
428
enddo
418
- FtSSHw (:,:,k) = (FtSSHw (:,:,k) - tmp2(:,:)) * tmp1(k)
429
+ y (:,:,k) = (ha1 % FtSSH (:,:,k) - tmp2(:,:)) * tmp1(k)
419
430
enddo
420
431
421
- ! Solve for FtSSHw
432
+ ! Solve for x from L' * x = y
422
433
do k= 2 * nc+1 ,1 ,- 1
423
434
tmp2(:,:) = 0.0
424
435
do m= k+1 ,2 * nc+1
425
- tmp2(:,:) = tmp2(:,:) + FtSSHw(:,:,m ) * FtFw(m,k )
436
+ tmp2(:,:) = tmp2(:,:) + L(m,k ) * x(:,:,m )
426
437
enddo
427
- FtSSHw (:,:,k) = (FtSSHw (:,:,k) - tmp2(:,:)) * tmp1(k)
438
+ x (:,:,k) = (y (:,:,k) - tmp2(:,:)) * tmp1(k)
428
439
enddo
429
440
430
- deallocate (tmp1)
431
- deallocate (tmp2)
432
- deallocate (FtFw)
433
-
434
441
end subroutine HA_solver
435
442
436
443
! > \namespace harmonic_analysis
@@ -441,7 +448,7 @@ end subroutine HA_solver
441
448
! ! step, and x is a 2*nc-by-1 vector containing the constant coefficients of the sine/cosine for each constituent
442
449
! ! (i.e., the harmonic constants). At each grid point, the harmonic constants are computed using least squares,
443
450
! !
444
- ! ! (F' * F) * x = F' * SSH_in,
451
+ ! ! (F' * F) * x = F' * SSH_in, => FtF * x = FtSSH,
445
452
! !
446
453
! ! where the prime denotes matrix transpose, and SSH_in is the sea surface height (or other fields) determined by
447
454
! ! the model. The dot products (F' * F) and (F' * SSH_in) are computed by accumulating the sums as the model is
0 commit comments