1module minimization_mod
2 ! MODULE minimization_mod (prefix='min' category='1. High-level functionality')
3 !
4 !:Purpose: Minimization for variational assimilation, including the
5 ! subroutine that evaluates the cost function and its gradient.
6 !
7 use codePrecision_mod
8 use timeCoord_mod
9 use obsTimeInterp_mod
10 use verticalCoord_mod
11 use columnData_mod
12 use obsSpaceData_mod
13 use controlVector_mod
14 use midasMpi_mod
15 use horizontalCoord_mod
16 use gridStateVector_mod
17 use gridStateVectorFileIO_mod
18 use bmatrix_mod
19 use bMatrix1DVar_mod
20 use stateToColumn_mod
21 use varqc_mod
22 use rmatrix_mod
23 use costFunction_mod
24 use residual_mod
25 use obsOperators_mod
26 use quasinewton_mod
27 use utilities_mod
28 use biasCorrectionSat_mod
29 use columnVariableTransforms_mod
30 implicit none
31 save
32 private
33
34 ! public variables
35 public :: min_niter, min_nsim
36
37 ! public procedures
38 public :: min_Setup, min_minimize, min_writeHessian
39
40 type(struct_obs) , pointer :: obsSpaceData_ptr => null()
41 type(struct_columnData), pointer :: columnAnlInc_ptr => null()
42 type(struct_columnData), pointer :: columnTrlOnAnlIncLev_ptr => null()
43 type(struct_hco) , pointer :: hco_anl => null()
44
45 logical :: initialized = .false.
46
47 integer :: nmtra,nwork,min_nsim
48 integer :: nvadim_mpilocal ! for mpi
49 integer :: min_niter
50 integer,external :: get_max_rss
51 logical :: preconFileExists
52 character(len=20) :: preconFileName = './preconin'
53 character(len=20) :: preconFileNameOut = './pm1q'
54 character(len=20) :: preconFileNameOut_pert = './pm1q_pert'
55 integer :: n1gc = 3
56
57 ! variables stored for later call to min_writeHessian
58 real(8), allocatable :: vatra(:)
59 real(8), pointer :: controlVectorIncrSum_ptr(:)
60 real(8), allocatable :: controlVectorIncrSumZero(:)
61 real(8) :: zeps0, zdf1
62 integer :: itertot, isimtot, iztrl(5), imode
63 integer :: outerLoopIndex
64 integer :: numIterMaxInnerLoopUsed
65 logical :: llvazx
66 logical :: initializeForOuterLoop
67 logical :: deallocHessian
68 logical :: isMinimizationFinalCall
69 logical :: oneDVarMode
70
71 ! namelist variables
72 real(8) :: REPSG ! relative gradient amplitude used as stopping criteria
73 real(8) :: rdf1fac ! factor applied to initial cost function value to approximate final value
74 integer :: NVAMAJ ! number of vector pairs to store in memory for Hessian approximation
75 integer :: NITERMAX ! maximum number of minimization iterations
76 integer :: NSIMMAX ! maximum number of cost function evaluations during minimization
77 integer :: nwoqcv ! number of iterations to initially perform without varQC
78 logical :: lxbar ! generally not used any longer
79 logical :: lwrthess ! choose to write the Hessian approximation
80 logical :: lgrtest ! choose to perform the 'gradient test" before and after minimization
81 logical :: lvazx ! generally not used any longer
82 logical :: lvarqc ! choose to activate varQC
83
84 NAMELIST /NAMMIN/ NVAMAJ, NITERMAX, NSIMMAX
85 NAMELIST /NAMMIN/ LGRTEST
86 NAMELIST /NAMMIN/ lxbar, lwrthess, lvazx
87 NAMELIST /NAMMIN/ REPSG, rdf1fac
88 NAMELIST /NAMMIN/ LVARQC, NWOQCV
89
90CONTAINS
91
92 subroutine min_setup( nvadim_mpilocal_in, hco_anl_in, oneDVarMode_opt, &
93 varqc_opt, nwoqcv_opt )
94 !
95 !:Purpose: Reading NAMMIN namelist to setup variables for minimization.
96 !
97 implicit none
98
99 ! Arguments:
100 integer, intent(in) :: nvadim_mpilocal_in
101 type(struct_hco), pointer, intent(in) :: hco_anl_in
102 logical, optional, intent(in) :: oneDVarMode_opt
103 logical, optional, intent(out) :: varqc_opt
104 integer, optional, intent(out) :: nwoqcv_opt
105
106 ! Locals:
107 integer :: ierr,nulnam
108 integer,external :: fnom,fclos
109
110 call utl_tmg_start(90,'--Minimization')
111
112 if ( nvadim_mpilocal_in /= cvm_nvadim ) then
113 write(*,*) 'nvadim_mpilocal_in,cvm_nvadim=',nvadim_mpilocal_in,cvm_nvadim
114 call utl_abort('min_setup: control vector dimension not consistent')
115 endif
116
117 nvadim_mpilocal = nvadim_mpilocal_in
118
119 if ( present(oneDVarMode_opt) ) then
120 oneDVarMode = oneDVarMode_opt
121 else
122 oneDVarMode = .false.
123 end if
124
125 hco_anl => hco_anl_in
126
127 ! set default values for namelist variables
128 nvamaj = 6
129 nitermax = 0
130 rdf1fac = 0.25d0
131 nsimmax = 500
132 lgrtest = .false.
133 lwrthess = .true.
134 lxbar = .false.
135 lvazx = .false.
136 repsg = 1.0d-5
137 lvarqc = .false.
138 nwoqcv = 5
139
140 ! read in the namelist NAMMIN
141 nulnam=0
142 ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
143 read(nulnam,nml=nammin,iostat=ierr)
144 if(ierr.ne.0) call utl_abort('min_setup: Error reading namelist')
145 write(*,nml=nammin)
146 ierr=fclos(nulnam)
147
148 IF(N1GC == 3)THEN
149 NMTRA = (4 + 2*NVAMAJ)*nvadim_mpilocal
150 ELSE
151 call utl_abort('min_setup: only N1GC=3 currently supported!')
152 END IF
153 WRITE(*,9401)N1GC,NVAMAJ,NMTRA
154 9401 FORMAT(4X,'N1GC = ',I2,4X,'NVAMAJ = ',I3,/5X,"NMTRA =",1X,I14)
155
156 if ( present(varqc_opt) ) varqc_opt = lvarqc
157 if ( present(nwoqcv_opt) ) nwoqcv_opt = nwoqcv
158
159 if(LVARQC .and. mmpi_myid == 0) write(*,*) 'VARIATIONAL QUALITY CONTROL ACTIVATED.'
160
161 initialized=.true.
162
163 call utl_tmg_stop(90)
164
165 end subroutine min_setup
166
167
168 subroutine min_minimize( outerLoopIndex_in, columnTrlOnAnlIncLev, obsSpaceData, controlVectorIncrSum, &
169 vazx, numIterMaxInnerLoop, deallocHessian_opt, &
170 isMinimizationFinalCall_opt, numIterMaxInnerLoopUsed_opt )
171 !
172 !:Purpose: Minimizing cost function to get the increments.
173 ! The maximum number of inner-loop iterations is set to nitermax if the
174 ! namelist variable nitermax is provided. Otherwise, it is set to the
175 ! numIterMaxInnerLoop supplied by the calling subroutine/program.
176 ! numIterMaxInnerLoopUsed_opt is passing the maximum number of inner-loop
177 ! iterations to the calling subroutine/program.
178 !
179 implicit none
180
181 ! Arguments:
182 integer, intent(in) :: outerLoopIndex_in
183 type(struct_columnData), intent(inout) :: columnTrlOnAnlIncLev
184 type(struct_obs), intent(inout) :: obsSpaceData
185 real(8), target, intent(inout) :: controlVectorIncrSum(:)
186 real(8), intent(inout) :: vazx(:)
187 integer, intent(in) :: numIterMaxInnerLoop
188 integer, optional, intent(out) :: numIterMaxInnerLoopUsed_opt
189 logical, optional, intent(in) :: deallocHessian_opt
190 logical, optional, intent(in) :: isMinimizationFinalCall_opt
191
192 ! Locals:
193 type(struct_columnData) :: columnAnlInc
194 integer :: get_max_rss
195
196 call utl_tmg_start(90,'--Minimization')
197
198 write(*,*) '--------------------------------'
199 write(*,*) '--Starting subroutine minimize--'
200 write(*,*) '--------------------------------'
201 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
202
203 initializeForOuterLoop = .true.
204 outerLoopIndex = outerLoopIndex_in
205
206 if ( present(deallocHessian_opt) ) then
207 deallocHessian = deallocHessian_opt
208 else
209 deallocHessian = .true.
210 end if
211 if ( present(isMinimizationFinalCall_opt) ) then
212 isMinimizationFinalCall = isMinimizationFinalCall_opt
213 else
214 isMinimizationFinalCall = .true.
215 end if
216
217 if ( (nitermax > 0 .and. numIterMaxInnerLoop > 0) .or. &
218 (nitermax == 0 .and. numIterMaxInnerLoop == 0) ) then
219 call utl_abort('min_minimize: one of nitermax or numIterMaxInnerLoop should be zero and the other positive')
220 end if
221
222 if ( nitermax > 0 ) then
223 numIterMaxInnerLoopUsed = nitermax
224 else if ( numIterMaxInnerLoop > 0 ) then
225 numIterMaxInnerLoopUsed = numIterMaxInnerLoop
226 else
227 call utl_abort('min_minimize: one of the variables nIterMax and numIterMaxInnerLoop must be positive')
228 end if
229 if ( present(numIterMaxInnerLoopUsed_opt) ) numIterMaxInnerLoopUsed_opt = numIterMaxInnerLoopUsed
230
231 controlVectorIncrSum_ptr => controlVectorIncrSum
232
233 ! zero array for writing to hessian
234 allocate(controlVectorIncrSumZero(cvm_nvadim))
235 controlVectorIncrSumZero(:) = 0.0d0
236
237 call col_setVco(columnAnlInc,col_getVco(columnTrlOnAnlIncLev))
238 call col_allocate(columnAnlInc,col_getNumCol(columnTrlOnAnlIncLev),mpiLocal_opt=.true.)
239
240 write(*,*) 'oti_timeBinning: For 4D increment'
241 call oti_timeBinning(obsSpaceData,tim_nstepobsinc)
242
243 call quasiNewtonMinimization( columnAnlInc, columnTrlOnAnlIncLev, obsSpaceData, vazx )
244
245 call col_deallocate(columnAnlInc)
246
247 deallocate(controlVectorIncrSumZero)
248
249 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
250 write(*,*) '--Done subroutine minimize--'
251
252 call utl_tmg_stop(90)
253
254 end subroutine min_minimize
255
256
257 subroutine quasiNewtonMinimization( columnAnlInc, columnTrlOnAnlIncLev, obsSpaceData, vazx )
258 !
259 !:Purpose: 3D/En-VAR minimization
260 !
261 implicit none
262
263 ! Arguments:
264 type(struct_columnData), target, intent(in) :: columnAnlInc
265 type(struct_columnData), target, intent(in) :: columnTrlOnAnlIncLev
266 type(struct_obs), target, intent(inout) :: obsSpaceData
267 real(8) , intent(out) :: vazx(:)
268
269 ! Locals:
270 integer :: nulout = 6
271 integer :: impres
272 INTEGER :: NGRANGE = 10 ! range of powers of 10 used for gradient test
273 real :: zzsunused(1)
274 integer :: intUnused(1)
275 real(8),allocatable :: vazg(:)
276 real(8) :: dlds(1)
277 logical :: llvarqc, lrdvatra, llxbar
278 integer :: itermax, iterdone, itermaxtodo, isimmax, indic, iitnovqc
279 integer :: ierr, isimdone, jdata, isimnovqc
280 integer :: ibrpstamp, isim3d
281 real(8) :: zjsp, zxmin, zeps1
282 real(8) :: dlgnorm, dlxnorm
283 real(8) :: zeps0_000,zdf1_000
284 integer :: iterdone_000,isimdone_000
285
286 if (lvarqc .and. outerLoopIndex==1) call vqc_setup(obsSpaceData)
287
288 min_nsim=0
289
290 if(mmpi_myid == 0) then
291 impres=5
292 else
293 impres=0
294 endif
295
296 ! Check for preconditioning file
297 inquire(file=preconFileName,exist=preconFileExists)
298 if(preconFileExists) then
299 if(mmpi_myid == 0) write(*,*) 'PRECONDITIONING FILE FOUND:',preconFileName
300 else
301 if(mmpi_myid == 0) write(*,*) 'NO PRECONDITIONING FILE FOUND:',preconFileName
302 endif
303
304 ! Initialize Hessian only at first outerLoop (mpilocal)
305 if ( outerLoopIndex == 1 ) then
306 allocate(vatra(nmtra))
307 vatra(:) = 0.0d0
308 end if
309
310 allocate(vazg(nvadim_mpilocal),stat=ierr)
311 if(ierr.ne.0) then
312 write(*,*) 'minimization: Problem allocating memory! id=2',ierr
313 call utl_abort('min quasiNewtonMinimization')
314 endif
315
316 ! set module variable pointers for obsspacedata and the two column objects
317 obsSpaceData_ptr => obsSpaceData
318 columnAnlInc_ptr => columnAnlInc
319 columnTrlOnAnlIncLev_ptr => columnTrlOnAnlIncLev
320
321 ! Set-up the minimization
322
323 ! initialize iteration/simulation counters to zero
324 ITERTOT = 0
325 isimtot = 0
326
327 ! initialize control vector related arrays to zero
328 vazx(:) = 0.0d0
329 vazg(:) = 0.0d0
330
331
332 ! If minimization start without qcvar : turn off varqc to compute
333 ! innovations and test the gradients
334
335 if (outerLoopIndex == 1) zeps0 = repsg
336
337 if (preconFileExists) then
338 if ( mmpi_myid == 0 ) write(*,*) 'quasiNewtonMinimization : Preconditioning mode'
339 lrdvatra = .true.
340 imode = 2
341 llvazx = lvazx ! from namelist (default is .false.)
342 llxbar = lxbar ! from namelist (default is .false.)
343 else
344 lrdvatra = .false.
345 imode = 0
346 endif
347
348 ! read the hessian from preconin file at first outer-loop iteration
349 if ( lrdvatra .and. outerLoopIndex == 1 ) then
350 ibrpstamp = tim_getDatestamp() ! ibrpstamp is a I/O argument of hessianIO
351
352 call hessianIO (preconFileName,0, &
353 isim3d,ibrpstamp,zeps0_000,zdf1_000,iterdone_000, &
354 isimdone_000,iztrl,vatra,controlVectorIncrSumZero, &
355 vazx,llxbar,llvazx,n1gc,imode)
356 endif
357
358 iterdone = 0
359 isimdone = 0
360 itermax = numIterMaxInnerLoopUsed
361 itermaxtodo = itermax
362 isimmax = nsimmax
363
364 ! do the gradient test for the starting point of minimization
365 if ( lgrtest .and. outerLoopIndex == 1 ) then
366 ! save user-requested varqc switch
367 llvarqc = lvarqc
368
369 if ( nwoqcv > 0 ) lvarqc = .false.
370 call utl_tmg_start(91,'----QuasiNewton')
371 call grtest2(simvar,nvadim_mpilocal,vazx,ngrange)
372 call utl_tmg_stop(91)
373
374 lvarqc = llvarqc
375 endif
376
377 itertot = iterdone
378 isimtot = isimdone
379
380 llvarqc = lvarqc
381 if ( nwoqcv > 0 .and. outerLoopIndex == 1 ) lvarqc = .false.
382 INDIC =2
383 call utl_tmg_start(91,'----QuasiNewton')
384 call simvar(indic,nvadim_mpilocal,vazx,zjsp,vazg)
385 call utl_tmg_stop(91)
386 lvarqc = llvarqc
387
388 if ( outerLoopIndex == 1 ) zdf1 = rdf1fac * ABS(zjsp)
389
390 CALL PRSCAL(nvadim_mpilocal,VAZG,VAZG,DLGNORM)
391 DLGNORM = DSQRT(DLGNORM)
392 CALL PRSCAL(nvadim_mpilocal,VAZX,VAZX,DLXNORM)
393 DLXNORM = DSQRT(DLXNORM)
394 WRITE(*,*)' |X| = ', DLXNORM
395 WRITE(*,FMT=9220) ZJSP, DLGNORM
396 9220 FORMAT(/4X,'J(X) = ',G23.16,4X,'|Grad J(X)| = ',G23.16)
397
398 ! Iterations of the minimization algorithm
399
400 ZXMIN = epsilon(ZXMIN)
401 WRITE(*,FMT=9320)ZXMIN,ZDF1,ZEPS0,IMPRES,ITERMAX,NSIMMAX
402
403 9320 FORMAT(//,10X,' Minimization QNA_N1QN3 starts ...',/ &
404 10x,'DXMIN =',G23.16,2X,'DF1 =',G23.16,2X,'EPSG =',G23.16 &
405 /,10X,'IMPRES =',I3,2X,'NITER = ',I3,2X,'NSIM = ',I3)
406
407 ! Begin the minimization
408 if ( numIterMaxInnerLoopUsed > 0 ) then
409
410 ! First do iterations without var-QC only at the beginning of first outer-loop.
411 if (lvarqc .and. nwoqcv > 0 .and. iterdone < nwoqcv .and. outerLoopIndex == 1 ) then
412 iitnovqc = min(nwoqcv - iterdone,itermax)
413 isimnovqc = isimmax
414 lvarqc = .false.
415 call utl_tmg_start(91,'----QuasiNewton')
416
417 zeps1 = zeps0
418
419 call qna_n1qn3(simvar, dscalqn, dcanonb, dcanab, nvadim_mpilocal, vazx, &
420 zjsp,vazg, zxmin, zdf1, zeps1, impres, nulout, imode, &
421 iitnovqc, isimnovqc ,iztrl, vatra, nmtra, intUnused, &
422 zzsunused, dlds)
423 call utl_tmg_stop(91)
424 call fool_optimizer(obsSpaceData)
425
426 isimnovqc = isimnovqc - 1
427 itermaxtodo = itermaxtodo - iitnovqc + 1
428 isimmax = isimmax - isimnovqc + 1
429
430 itertot = itertot + iitnovqc
431 isimtot = isimtot + isimnovqc
432
433 zeps0 = zeps0/zeps1
434 lvarqc = .true.
435
436 if ((imode == 4 .or. imode == 1) .and. itertot < itermax) then
437 imode = 2
438 INDIC = 2
439 call utl_tmg_start(91,'----QuasiNewton')
440 call simvar(indic,nvadim_mpilocal,vazx,zjsp,vazg)
441 call utl_tmg_stop(91)
442 else
443 write(*,*) 'minimization_mod: qna_n1qn3 imode = ', imode
444 call utl_abort('minimization_mod: qna_n1qn3 mode not equal to 1 or 4')
445 endif
446 endif
447
448 ! Now do main minimization with var-QC
449 call utl_tmg_start(91,'----QuasiNewton')
450
451 if ( outerLoopIndex > 1 ) imode = 2
452
453 zeps1 = zeps0
454
455 call qna_n1qn3(simvar, dscalqn, dcanonb, dcanab, nvadim_mpilocal, vazx, &
456 zjsp,vazg, zxmin, zdf1, zeps1, impres, nulout, imode, &
457 itermaxtodo,isimmax, iztrl, vatra, nmtra, intUnused, zzsunused, &
458 dlds)
459 call utl_tmg_stop(91)
460 call fool_optimizer(obsSpaceData)
461
462 itertot = itertot + itermaxtodo
463 isimtot = isimtot + isimmax
464
465 zeps0 = zeps0/zeps1
466
467
468 WRITE(*,FMT=9500) imode,iterdone,itertot-iterdone,itertot,isimdone,isimtot-isimdone,isimtot
469 9500 FORMAT(//,20X,20('*'),2X &
470 ,/,20X,' Minimization ended with MODE:',I4 &
471 ,/,20X,' Number of iterations done in previous job:',I4 &
472 ,/,20X,' Number of iterations in this job:',I4 &
473 ,/,20X,' Total number of iterations:',I4 &
474 ,/,20X,'Number of simulations done in previous job:',I4 &
475 ,/,20X,' Number of simulations in this job:',I4 &
476 ,/,20X,' Total number of simulations:',I4)
477
478 min_niter = itertot
479
480 ! Test the gradient at the final point
481 if ( lgrtest .and. isMinimizationFinalCall ) then
482 WRITE(*,FMT=9400)
483 9400 FORMAT(//,12X,40('**'),/,12X,'TESTING THE GRADIENT AT THE FINAL POINT',/,40('**'))
484 call utl_tmg_start(91,'----QuasiNewton')
485 call grtest2(simvar,nvadim_mpilocal,vazx,ngrange)
486 call utl_tmg_stop(91)
487 end if
488
489 ! Print some contents of obsSpaceData to the listing
490 if(mmpi_myid == 0) then
491 do jdata = 1, min(1,obs_numHeader(obsSpaceData))
492 call obs_prnthdr(obsSpaceData,jdata)
493 call obs_prntbdy(obsSpaceData,jdata)
494 end do
495 endif
496
497 else
498
499 ! no analysis performed for ensemble mean, ensure mean increment is zero
500 vazx(:) = 0.0d0
501 min_niter = 0
502
503 endif ! if numIterMaxInnerLoopUsed > 0
504
505 ! deallocate the gradient
506 deallocate(vazg)
507 if ( deallocHessian .and. .not. lwrthess ) deallocate(vatra)
508
509 end subroutine quasiNewtonMinimization
510
511
512 subroutine min_writeHessian(vazx)
513 implicit none
514
515 ! Arguments:
516 real(8), intent(inout) :: vazx(:)
517
518 ! Locals:
519 integer :: dateStamp
520
521 call utl_tmg_start(90,'--Minimization')
522
523 if ( lwrthess ) then
524 ! Write out the Hessian to file
525 if ( mmpi_myid == 0 ) write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
526
527 ! zero array for writing to hessian
528 if ( .not. allocated(controlVectorIncrSumZero) ) then
529 allocate(controlVectorIncrSumZero(cvm_nvadim))
530 end if
531 controlVectorIncrSumZero(:) = 0.0d0
532
533 dateStamp = tim_getDatestamp()
534 call hessianIO (preconFileNameOut,1, &
535 min_nsim,datestamp,zeps0,zdf1,itertot,isimtot, &
536 iztrl,vatra,controlVectorIncrSumZero,vazx,.true.,llvazx,n1gc,imode)
537
538 deallocate(controlVectorIncrSumZero)
539
540 if ( mmpi_myid == 0 ) write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
541 endif
542
543 if ( lwrthess ) then
544 deallocate(vatra)
545 end if
546
547 call utl_tmg_stop(90)
548
549 end subroutine min_writeHessian
550
551
552 subroutine simvar(na_indic,na_dim,da_v,da_J,da_gradJ)
553 !
554 !:Purpose: To implement the Variational solver as described in
555 ! Courtier, 1997, Dual Formulation of Four-Dimentional Variational
556 ! Assimilation, Q.J.R., pp2449-2461.
557 !
558 !:Arguments:
559 ! :na_indic:
560 ! =1 No action taken
561 !
562 ! =2 Same as 4 (compute J and gradJ) but do not interrupt
563 ! timer of the minimizer.
564 !
565 ! =3 Compute Jo and gradJo only.
566 !
567 ! =4 Both J(u) and its gradient are computed.
568 !
569 ! .. Note:: 1 and 4 are reserved values for call back from
570 ! m1qn3. For direct calls, use a value other than 1
571 ! and 4.
572 implicit none
573
574 ! Arguments:
575 integer, intent(in) :: na_indic
576 integer, intent(in) :: na_dim ! Control-vector dimension, forecast-error covariance space
577 real(8), intent(in) :: da_v(na_dim) ! Control variable, forecast-error covariance space
578 real(8), intent(out) :: da_J ! Cost function of the Variational algorithm
579 real(8), intent(out) :: da_gradJ(na_dim) ! Gradient of the Variational Cost funtion
580
581 ! Locals:
582 real*8, dimension(na_dim) :: dl_v
583 real*8 :: dl_Jb, dl_Jo
584 type(struct_gsv), save :: statevector
585 type(struct_vco), pointer :: vco_anl
586
587 call utl_tmg_stop(91)
588 call utl_tmg_stop(90)
589
590 if (na_indic .ne. 1) then ! No action taken if na_indic == 1
591 min_nsim = min_nsim + 1
592
593 if(mmpi_myid == 0) then
594 write(*,*) 'Entering simvar for simulation ',min_nsim
595 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
596 endif
597
598 ! note: controlVectorIncrSum_ptr is sum of previous outer-loops
599 dl_v(1:nvadim_mpilocal) = da_v(1:nvadim_mpilocal) + controlVectorIncrSum_ptr(1:nvadim_mpilocal)
600
601 ! Computation of background term of cost function:
602 dl_Jb = dot_product(dl_v(1:nvadim_mpilocal),dl_v(1:nvadim_mpilocal))/2.d0
603 call mmpi_allreduce_sumreal8scalar(dl_Jb,"GRID")
604
605 if (oneDVarMode) then
606 call bmat1D_sqrtB(da_v, nvadim_mpilocal, columnAnlInc_ptr, obsSpaceData_ptr)
607 call cvt_transform(columnAnlInc_ptr, 'ZandP_tl', columnTrlOnAnlIncLev_ptr)
608 else
609 if (.not.gsv_isAllocated(statevector)) then
610 write(*,*) 'min-simvar: allocating increment stateVector'
611 vco_anl => col_getVco(columnTrlOnAnlIncLev_ptr)
612 call gsv_allocate(statevector, tim_nstepobsinc, hco_anl, vco_anl, &
613 dataKind_opt=pre_incrReal, mpi_local_opt=.true.)
614 call gio_readMaskFromFile(statevector,'./analysisgrid')
615 end if
616
617 call bmat_sqrtB(da_v,nvadim_mpilocal,statevector)
618
619 ! put in columnAnlInc H_horiz dx
620 call s2c_tl(statevector,columnAnlInc_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr)
621 end if
622
623 ! Save as OBS_WORK: H_vert H_horiz dx = Hdx
624 call utl_tmg_start(10,'--Observations')
625 call utl_tmg_start(18,'----ObsOper_TL')
626 call oop_Htl(columnAnlInc_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr,min_nsim, &
627 initializeLinearization_opt=initializeForOuterLoop)
628 call utl_tmg_stop(18)
629 call utl_tmg_stop(10)
630
631 ! Calculate OBS_OMA from OBS_WORK : d-Hdx
632 call res_compute(obsSpaceData_ptr)
633
634 call bcs_calcbias_tl(da_v,OBS_OMA,obsSpaceData_ptr,columnTrlOnAnlIncLev_ptr)
635
636 ! Save as OBS_WORK : R**-1/2 (d-Hdx)
637 call utl_tmg_start(10,'--Observations')
638 call rmat_RsqrtInverseAllObs(obsSpaceData_ptr,OBS_WORK,OBS_OMA)
639 call utl_tmg_stop(10)
640
641 ! Store J-obs in OBS_JOBS : 1/2 * R**-1 (d-Hdx)**2
642 call cfn_calcJo(obsSpaceData_ptr)
643
644 ! Store modified J_obs in OBS_JOBS : -ln((gamma-exp(J))/(gamma+1))
645 IF ( LVARQC ) THEN
646 call vqc_NlTl(obsSpaceData_ptr)
647 endif
648
649 dl_Jo = 0.d0
650 call utl_tmg_start(90,'--Minimization')
651 call utl_tmg_start(92,'----SumCostFunction')
652 call cfn_sumJo(obsSpaceData_ptr,dl_Jo)
653 da_J = dl_Jb + dl_Jo
654 if (na_indic == 3) then
655 da_J = dl_Jo
656 IF(mmpi_myid == 0) write(*,FMT='(6X,"SIMVAR: JO = ",G23.16,6X)') dl_Jo
657 else
658 da_J = dl_Jb + dl_Jo
659 IF(mmpi_myid == 0) write(*,FMT='(6X,"SIMVAR: Jb = ",G23.16,6X,"JO = ",G23.16,6X,"Jt = ",G23.16)') dl_Jb,dl_Jo,da_J
660 endif
661 call utl_tmg_stop(92)
662 call utl_tmg_stop(90)
663
664 ! Modify OBS_WORK : R**-1 (d-Hdx)
665 call utl_tmg_start(10,'--Observations')
666 call rmat_RsqrtInverseAllObs(obsSpaceData_ptr,OBS_WORK,OBS_WORK)
667 call utl_tmg_stop(10)
668
669 IF ( LVARQC ) THEN
670 call vqc_ad(obsSpaceData_ptr)
671 endif
672
673 ! Calculate adjoint of d-Hdx (mult OBS_WORK by -1)
674 call res_computeAd(obsSpaceData_ptr)
675
676 call col_zero(columnAnlInc_ptr)
677
678 ! Put in column : -H_vert**T R**-1 (d-Hdx)
679 call utl_tmg_start(10,'--Observations')
680 call utl_tmg_start(19,'----ObsOper_AD')
681 call oop_Had(columnAnlInc_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr, &
682 initializeLinearization_opt=initializeForOuterLoop)
683 call utl_tmg_stop(19)
684 call utl_tmg_stop(10)
685
686 ! Put in statevector -H_horiz**T H_vert**T R**-1 (d-Hdx)
687 if (oneDVarMode) then
688 ! no interpolation needed for 1Dvar case
689 else
690 call s2c_ad(statevector,columnAnlInc_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr)
691 end if
692
693 da_gradJ(:) = 0.d0
694 call bcs_calcbias_ad(da_gradJ,OBS_WORK,obsSpaceData_ptr)
695
696 if (oneDVarMOde) then
697 call cvt_transform( columnAnlInc_ptr, 'ZandP_ad', columnTrlOnAnlIncLev_ptr) ! IN
698 call bmat1D_sqrtBT(da_gradJ, nvadim_mpilocal, columnAnlInc_ptr, obsSpaceData_ptr)
699 else
700 call bmat_sqrtBT(da_gradJ,nvadim_mpilocal,statevector)
701 end if
702
703 if (na_indic .ne. 3) then
704 da_gradJ(1:nvadim_mpilocal) = dl_v(1:nvadim_mpilocal) + da_gradJ(1:nvadim_mpilocal)
705 endif
706 endif
707
708 call utl_tmg_start(90,'--Minimization')
709 call utl_tmg_start(91,'----QuasiNewton')
710
711 initializeForOuterLoop = .false.
712
713 if(mmpi_myid == 0) write(*,*) 'end of simvar'
714
715 end subroutine simvar
716
717
718 subroutine dscalqn(kdim,px,py,ddsc)
719 !:Purpose: Interface for the inner product to be used by the minimization
720 ! subroutines QNA_N1QN3.
721 !
722 !:Arguments:
723 ! i : kdim
724 ! i : px,py
725 ! o : ddsc
726 ! i : kzs
727 ! i : pzs
728 ! i : ddzs
729 implicit none
730
731 ! Arguments:
732 integer, intent(in) :: kdim ! dimension of the vectors
733 real(8), intent(in) :: px(kdim) ! vector for which <PX,PY> is being calculated
734 real(8), intent(in) :: py(kdim) ! vector for which <PX,PY> is being calculated
735 real(8), intent(out) :: ddsc ! result of the inner product
736
737 CALL PRSCAL(KDIM,PX,PY,DDSC)
738
739 end subroutine dscalqn
740
741
742 subroutine prscal(kdim,px,py,ddsc)
743 !
744 !:Purpose: To evaluate the inner product used in the minimization
745 !
746 implicit none
747
748 ! Arguments:
749 integer, intent(in) :: kdim ! dimension of the vectors
750 real(8), intent(in) :: px(kdim) ! vector for which <PX,PY> is being calculated
751 real(8), intent(in) :: py(kdim) ! vector for which <PX,PY> is being calculated
752 real(8), intent(out) :: ddsc ! result of the inner product
753
754 ! Locals:
755 integer :: j
756
757 ddsc = 0.D0
758
759 do j=1,nvadim_mpilocal
760 ddsc = ddsc + px(j)*py(j)
761 end do
762
763 call mmpi_allreduce_sumreal8scalar(ddsc,"GRID")
764
765 end subroutine prscal
766
767
768 subroutine dcanab(kdim,py,px)
769 !
770 !:Purpose: Change of variable associated with the canonical inner product:
771 !
772 ! * PX = L^-1 * Py with L related to the inner product
773 ! * <PX,PY> = PX^t L^t L PY
774 ! * (see the modulopt documentation aboutn DTCAB)
775 ! * NOTE: L is assumed to be the identity!
776 !
777 implicit none
778
779 ! Arguments:
780 integer, intent(in) :: kdim
781 real(8), intent(out) :: px(kdim)
782 real(8), intent(in) :: py(kdim)
783
784 ! Locals:
785 integer :: jdim
786
787 do jdim = 1, kdim
788 px(jdim) = py(jdim)
789 end do
790
791 end subroutine DCANAB
792
793
794 subroutine dcanonb(kdim,px,py)
795 !
796 !:Purpose: Change of variable associated with the canonical inner product:
797 !
798 ! * PY = L * PX with L related to the inner product
799 ! * <PX,PY> = PX^t L^t L PY
800 ! * (see the modulopt documentation about DTONB)
801 !
802 implicit none
803
804 ! Arguments:
805 integer, intent(in) :: kdim
806 real(8), intent(in) :: px(kdim)
807 real(8), intent(out) :: py(kdim)
808
809 ! Locals:
810 INTEGER :: jdim
811
812 do jdim = 1, kdim
813 py(jdim) = px(jdim)
814 end do
815
816 end subroutine DCANONB
817
818
819 subroutine hessianIO (cfname,status, &
820 nsim,kbrpstamp,zeps1,zdf1,itertot,isimtot, &
821 iztrl,vatra,vazxbar,vazx,llxbar,llvazx,k1gc,imode)
822 !
823 !:Purpose: Read-Write Hessian and increment (possibly for outer loop) on a
824 ! file
825 !
826 !:Arguments:
827 ! * i cfname
828 ! * i status
829 ! * i nsim
830 ! * io kbrpstamp
831 ! * i zeps1
832 ! * i zdf1
833 ! * i itertot
834 ! * i isimtot
835 ! * i iztrl
836 ! * i vatra
837 ! * i vazxbar
838 ! * i vazx
839 ! * i llxbar
840 ! * i llvazx
841 ! * i k1gc
842 ! * o imode
843 !
844 implicit none
845
846 ! Arguments:
847 character(len=*), intent(in) :: cfname ! precon file
848 integer , intent(in) :: status ! = 0 if READ, = 1 if WRITE
849 integer , intent(inout) :: nsim ! Number of simulations in QNA_N1QN3
850 integer , intent(inout) :: kbrpstamp ! Date
851 real(8) , intent(inout) :: zeps1 ! Parameter in QNA_N1QN3
852 real(8) , intent(inout) :: zdf1 ! Parameter in QNA_N1QN3
853 integer , intent(inout) :: itertot ! Parameter in QNA_N1QN3
854 integer , intent(inout) :: isimtot ! Parameter in QNA_N1QN3
855 integer, target , intent(inout) :: iztrl(5) ! Localisation parameters for Hessian
856 real(8), target , intent(inout) :: vatra(nmtra) ! Hessian
857 real(8), target , intent(inout) :: vazxbar(nvadim_mpilocal) ! Vazx of previous loop
858 real(8), target , intent(inout) :: vazx(nvadim_mpilocal) ! Current state of the minimization
859 logical , intent(in) :: llxbar ! read in vaxzbar if dates are compatible
860 logical , intent(in) :: llvazx ! Logical to read vazx
861 integer , intent(in) :: k1gc ! Minimizer ID (2: m1qn2, 3: m1qn3)
862 integer , intent(out) :: imode ! If status=0, set imode=0 (no prec) or 2 (prec)
863
864 ! Locals:
865 real(4), allocatable :: vatravec_r4_mpiglobal(:)
866 real(4), allocatable :: vatra_r4(:)
867 real(8), allocatable :: vazxbar_mpiglobal(:),vazx_mpiglobal(:)
868 integer :: ibrpstamp,ireslun, ierr, fnom, fclos
869 integer :: nvadim_mpiglobal,nmtra_mpiglobal
870 integer :: ivadim, itrunc
871 integer :: ivamaj
872 integer :: jvec, i1gc,ictrlvec,ii
873 integer, dimension(10), target, save :: iztrl_io
874 character(len=3) :: cl_version
875
876 if (status == 0) then
877 if (mmpi_myid == 0) write(*,*) 'Read Hessian in min_hessianIO from file ', cfname
878 call utl_tmg_start(93,'----ReadHess')
879 elseif (status == 1) then
880 if (mmpi_myid == 0) write(*,*) 'Write Hessian in min_hessianIO to file ', cfname
881 call utl_tmg_start(94,'----WriteHess')
882 else
883 call utl_abort('min_hessianIO: status not valid ')
884 endif
885
886 call rpn_comm_allreduce(nvadim_mpilocal,nvadim_mpiglobal,1,"mpi_integer","mpi_sum","GRID",ierr)
887 call rpn_comm_allreduce(nmtra ,nmtra_mpiglobal, 1,"mpi_integer","mpi_sum","GRID",ierr)
888
889 ireslun=0
890
891 if (status == 0) then
892 !
893 !- 1. Read Hessian on processor 0 and distribute the data to the other processors
894 !
895
896 if (mmpi_myid == 0) then
897 !- Open the Hessian matrix file
898 ierr = fnom(ireslun,cfname,'FTN+SEQ+UNF+OLD+R/O',0)
899
900 !- Checking version number
901 read (ireslun) cl_version, i1gc
902 if (trim(cl_version) == 'V5') then
903 write(*,*) 'min_hessianIO: using single precision V5 Hessian'
904 else if (trim(cl_version) == 'V4') then
905 write(*,*) 'min_hessianIO: using DEPRECIATED single precision V5 Hessian'
906 else
907 write(*,*)
908 write(*,*) 'min_hessianIO : Only V5 Hessian are supported, found ', trim(cl_version)
909 call utl_abort('min_hessianIO')
910 endif
911
912 if (i1gc == 3 .and. i1gc == k1gc) then
913 write(*,*) trim(cl_version),' M1QN3'
914 else
915 write(*,*) 'Version, n1gc =',trim(cl_version),i1gc
916 call utl_abort('min_hessianIO: Inconsistant input hessian')
917 endif
918
919 rewind (ireslun)
920
921 read(ireslun) cl_version,i1gc,nsim,ibrpstamp,zeps1,zdf1,itertot,isimtot,ivadim,itrunc
922 read(ireslun) ivamaj,iztrl_io
923 if ((ivamaj.ne.nvamaj).or.(nvadim_mpiglobal.ne.ivadim)) then
924 write(*,*) nvamaj,ivamaj,nvadim_mpiglobal,ivadim
925 call utl_abort('min_hessianIO : ERROR, size of V5 Hessian not consistent')
926 endif
927
928 end if
929
930 ! ibrpstamp and iztrl_io must be broadcasted
931 call rpn_comm_bcast(ibrpstamp, 1, "MPI_INTEGER", 0, "GRID", ierr)
932 call rpn_comm_bcast(iztrl_io , 10, "MPI_INTEGER", 0, "GRID", ierr)
933
934 !- Read the Hessian
935 if(mmpi_myid == 0) then
936 write(*,*) 'min_hessianIO : reading Hessian'
937 allocate(vatravec_r4_mpiglobal(nvadim_mpiglobal))
938 else
939 allocate(vatravec_r4_mpiglobal(1))
940 end if
941 allocate(vatra_r4(nvadim_mpilocal))
942
943 if (k1gc == 3) ictrlvec = 2*nvamaj+1
944 do jvec = 1, ictrlvec
945
946 if (mmpi_myid == 0) then
947 read(ireslun) vatravec_r4_mpiglobal
948 end if
949
950 call bmat_reduceToMPILocal_r4( vatra_r4, & ! OUT
951 vatravec_r4_mpiglobal) ! IN (contains data only on proc 0)
952 !$OMP PARALLEL DO PRIVATE(ii)
953 do ii = 1, nvadim_mpilocal
954 vatra((jvec-1)*nvadim_mpilocal+ii) = real(vatra_r4(ii),8)
955 enddo
956 !$OMP END PARALLEL DO
957
958 end do
959
960 deallocate(vatravec_r4_mpiglobal)
961 deallocate(vatra_r4)
962
963 imode = 2
964 iztrl(:) = iztrl_io(1:5)
965 if(k1gc == 3) then
966 iztrl(1) = nvadim_mpilocal
967 iztrl(2) = 0
968 iztrl(3) = nvamaj
969 iztrl(4) = iztrl_io(4)
970 iztrl(5) = iztrl_io(5)
971 end if
972
973 !- Read VAZXBAR
974 if (ibrpstamp == kbrpstamp .and. llxbar) then
975 if (mmpi_myid == 0) then
976 write(*,*) 'min_hessianIO : reading vazxbar'
977 allocate(vazxbar_mpiglobal(nvadim_mpiglobal))
978 read(ireslun) vazxbar_mpiglobal
979 else
980 allocate(vazxbar_mpiglobal(1))
981 end if
982 call bmat_reduceToMPILocal( vazxbar, & ! OUT
983 vazxbar_mpiglobal ) ! IN (contains data only on proc 0)
984 deallocate(vazxbar_mpiglobal)
985 end if
986
987 !- Read VAZX
988 if (ibrpstamp == kbrpstamp .and. llvazx) then
989 if (mmpi_myid == 0) then
990 write(*,*) 'min_hessianIO : reading vazx'
991 allocate(vazx_mpiglobal(nvadim_mpiglobal))
992 read(ireslun) vazx_mpiglobal
993 else
994 allocate(vazx_mpiglobal(1))
995 end if
996 call bmat_reduceToMPILocal( vazx, & ! OUT
997 vazx_mpiglobal ) ! IN (contains data only on proc 0)
998 deallocate(vazx_mpiglobal)
999 end if
1000
1001 if (ibrpstamp /= kbrpstamp) then
1002 kbrpstamp = ibrpstamp
1003 end if
1004
1005 if (mmpi_myid == 0) ierr = fclos(ireslun)
1006
1007 elseif (status == 1) then
1008 !
1009 !- 2. Write Hessian
1010 !
1011
1012 !- Open the Hessian matrix file on processor 0 and write some metadata
1013 if (mmpi_myid == 0) ierr = fnom(ireslun,cfname, 'FTN+SEQ+UNF' , 0)
1014 cl_version = 'V5'
1015 itrunc=0
1016 iztrl_io(1: 5) = iztrl(:)
1017 iztrl_io(6:10) = 0 ! dummy values for hessian size legacy purposes
1018 if(mmpi_myid == 0) write(ireslun) cl_version,k1gc,nsim,kbrpstamp,zeps1,zdf1,itertot,isimtot,nvadim_mpiglobal,itrunc
1019 if(mmpi_myid == 0) write(ireslun) nvamaj,iztrl_io
1020
1021 !- Write the Hessian
1022 if (mmpi_myid == 0) then
1023 allocate(vatravec_r4_mpiglobal(nvadim_mpiglobal))
1024 else
1025 allocate(vatravec_r4_mpiglobal(1))
1026 endif
1027 allocate(vatra_r4(nvadim_mpilocal))
1028
1029 if (k1gc == 3) ictrlvec = 2*nvamaj+1
1030 do jvec = 1, ictrlvec
1031 !$OMP PARALLEL DO PRIVATE(ii)
1032 do ii = 1, nvadim_mpilocal
1033 vatra_r4(ii) = vatra((jvec-1)*nvadim_mpilocal+ii)
1034 enddo
1035 !$OMP END PARALLEL DO
1036 call bmat_expandToMPIGlobal_r4( vatra_r4, & ! IN
1037 vatravec_r4_mpiglobal ) ! OUT
1038 if (mmpi_myid == 0) write(ireslun) vatravec_r4_mpiglobal
1039 enddo
1040 deallocate(vatravec_r4_mpiglobal)
1041 deallocate(vatra_r4)
1042
1043 !- Write VAZXBAR
1044 if (mmpi_myid == 0) then
1045 allocate(vazxbar_mpiglobal(nvadim_mpiglobal))
1046 else
1047 allocate(vazxbar_mpiglobal(1)) ! dummy
1048 end if
1049 call bmat_expandToMPIGlobal( vazxbar, & ! IN
1050 vazxbar_mpiglobal ) ! OUT
1051 if (mmpi_myid == 0) write(ireslun) vazxbar_mpiglobal(1:nvadim_mpiglobal)
1052 deallocate(vazxbar_mpiglobal)
1053
1054 !- Write VAZX
1055 if (mmpi_myid == 0) then
1056 allocate(vazx_mpiglobal(nvadim_mpiglobal))
1057 else
1058 allocate(vazx_mpiglobal(1))
1059 end if
1060 call bmat_expandToMPIGlobal( vazx, & ! IN
1061 vazx_mpiglobal ) ! OUT
1062 if (mmpi_myid == 0) write(ireslun) vazx_mpiglobal(1:nvadim_mpiglobal)
1063 deallocate(vazx_mpiglobal)
1064
1065 !- Close the Hessian matrix file
1066 if (mmpi_myid == 0) ierr = fclos(ireslun)
1067 else
1068 call utl_abort('min_hessianIO: status not valid')
1069 endif
1070
1071 if (status == 0) then
1072 call utl_tmg_stop(93)
1073 elseif (status == 1) then
1074 call utl_tmg_stop(94)
1075 endif
1076
1077 end subroutine hessianIO
1078
1079
1080 subroutine grtest2(simul,na_dim,da_x0,na_range)
1081 !
1082 !:Purpose: To compare the variation of the functional against what the
1083 ! gradient gives for small changes in the control variable. This test
1084 ! should be accurate for values as small as DLALPHA = SQRT(machine
1085 ! precision). (see Courtier, 1987)
1086 !
1087 !:Arguments:
1088 ! :na_range: the test will be carried over values of ALPHA ranging between
1089 ! 10**(-NA_RANGE) < ALPHA < 0.1
1090 !
1091 implicit none
1092
1093 ! Arguments:
1094 external :: simul ! simulator: return cost function estimate and its gradient
1095 integer, intent(in) :: na_dim ! Size of the control vector
1096 real(8), intent(in) :: da_x0(na_dim) ! Control vector
1097 integer, intent(in) :: na_range
1098
1099 ! Locals:
1100 integer :: nl_indic, nl_j
1101 real(8) :: dl_wrk(na_dim),dl_gradj0(na_dim), dl_x(na_dim)
1102 real(8) :: dl_J0, dl_J, dl_test, dl_start,dl_end
1103 real(8) :: dl_alpha, dl_gnorm0
1104
1105 ! 1. Initialize dl_gradj0 at da_x0
1106 ! ------------------------------------
1107
1108 nl_indic = 2
1109 call simul(nl_indic,na_dim,da_x0,dl_j0,dl_gradj0)
1110 dl_gnorm0 = dot_product(dl_gradj0,dl_gradj0)
1111 call mmpi_allreduce_sumreal8scalar(dl_gnorm0,"GRID")
1112 dl_start = 1.d0
1113 dl_end = 10.0d0**(-na_range)
1114 write(*,FMT=9100) dl_start,dl_end, dl_j0, dl_gnorm0
1115 ! 2. Perform the test
1116 ! ----------------
1117
1118 if(dl_gnorm0 == 0.d0)then
1119 write(*,FMT=9101)
1120 return
1121 end if
1122 write(*,FMT=9200)
1123 do nl_j = 1, na_range
1124 dl_alpha = 10.0d0**(- nl_j)
1125 dl_x(:) = da_x0(:) - dl_alpha*dl_gradJ0(:)
1126 call simul(nl_indic,na_dim,dl_x,dl_j,dl_wrk)
1127 dl_test = (dl_j-dl_j0)/(-dl_alpha * dl_gnorm0)
1128 write(*,FMT=9201)nl_j, dl_alpha, dl_j, dl_test
1129 end do
1130
11319100 format(//,4X,&
1132 'GRTEST- The gradient is being tested for',&
1133 G23.16,' <= ALPHA <= ',G23.16,/,12X,&
1134 'Initial value of J(X):',1x,G23.16,4x,&
1135 'Norm of GRAD J(X)**2: ',G23.16)
11369101 format(/,4X,'-In GRTEST: gradient vanishes exactly',&
1137 '. Gradient test cannot be performed at this point')
11389200 format(/,4X,'J',8X,'ALPHA',11X,'J(X)',12X,'TEST')
1139
11409201 format(2X,'GRTEST: step',2X,I3,4X,G23.16,4X,G23.16,4X,&
1141 G23.16)
1142
1143 end subroutine grtest2
1144
1145end module minimization_mod