FV3 Bundle
type_minim.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: type_minim
3 ! Purpose: minimization data derived type
4 ! Author: Benjamin Menetrier
5 ! Licensing: this code is distributed under the CeCILL-C license
6 ! Copyright © 2015-... UCAR, CERFACS, METEO-FRANCE and IRIT
7 !----------------------------------------------------------------------
8 module type_minim
9 
10 use tools_fit, only: ver_smooth
12 use tools_kinds, only: kind_real
13 use tools_missing, only: isnotmsr
14 use tools_repro, only: rth,eq,inf,infeq,sup
15 use type_mpl, only: mpl_type
16 
17 implicit none
18 
19 real(kind_real),parameter :: rho = 0.5_kind_real ! Convergence parameter for the Hooke algorithm
20 real(kind_real),parameter :: tol = 1.0e-6_kind_real ! Tolerance for the Hooke algorithm
21 integer,parameter :: itermax = 10 ! Maximum number of iteration for the Hooke algorithm
22 
23 ! Minimization data derived type
25  ! Generic data
26  integer :: nx ! Control vector size
27  integer :: ny ! Function output size
28  real(kind_real),allocatable :: x(:) ! Control vector
29  real(kind_real),allocatable :: guess(:) ! Control vector guess
30  real(kind_real),allocatable :: binf(:) ! Control vector lower bound
31  real(kind_real),allocatable :: bsup(:) ! Control vector upper bound
32  real(kind_real),allocatable :: obs(:) ! Observation
33  character(len=1024) :: cost_function ! Cost function
34  real(kind_real) :: f_guess ! Guess cost
35  real(kind_real) :: f_min ! Minimum cost
36  character(len=1024) :: algo ! Minimization algorithm
37 
38  ! Common data
39  integer :: nl0 ! Number of levels
40  integer :: nc3 ! Number of classes
41 
42  ! Specific data (fit)
43  integer :: nl0r ! Reduced number of levels
44  logical :: lhomh ! Vertically homogenous horizontal support radius key
45  logical :: lhomv ! Vertically homogenous vertical support radius key
46  integer,allocatable :: l0rl0_to_l0(:,:) ! Reduced level to level
47  real(kind_real),allocatable :: disth(:) ! Horizontal distance
48  real(kind_real),allocatable :: distv(:,:) ! Vertical distance
49 
50  ! Specific data (LCT)
51  integer :: nscales ! Number of LCT scales
52  real(kind_real),allocatable :: dx(:,:) ! Zonal separation
53  real(kind_real),allocatable :: dy(:,:) ! Meridian separation
54  real(kind_real),allocatable :: dz(:,:) ! Vertical separation
55  logical,allocatable :: dmask(:,:) ! Mask
56 contains
57  procedure :: compute => minim_compute
58  procedure :: cost => minim_cost
59  procedure :: cost_fit_diag => minim_cost_fit_diag
60  procedure :: cost_fit_diag_dble => minim_cost_fit_diag_dble
61  procedure :: cost_fit_lct => minim_cost_fit_lct
62  procedure :: hooke => minim_hooke
63  procedure :: best_nearby => minim_best_nearby
64  procedure :: vt_dir => minim_vt_dir
65  procedure :: vt_inv => minim_vt_inv
66 end type minim_type
67 
68 private
69 public :: minim_type
70 
71 contains
72 
73 !----------------------------------------------------------------------
74 ! subroutine: minim_compute
75 ! Purpose: minimize ensuring bounds constraints
76 !----------------------------------------------------------------------
77 subroutine minim_compute(minim,mpl,lprt)
78 
79 implicit none
80 
81 ! Passed variables
82 class(minim_type),intent(inout) :: minim ! Minimization data
83 type(mpl_type),intent(in) :: mpl ! MPI data
84 logical,intent(in) :: lprt ! Print key
85 
86 ! Local variables
87 real(kind_real) :: guess(minim%nx)
88 
89 ! Check
90 if (minim%nx<=0) call mpl%abort('nx should be positive to minimize')
91 if (minim%ny<=0) call mpl%abort('nx should be positive to minimize')
92 
93 ! Initialization
94 guess = minim%guess
95 call minim%vt_inv(mpl,guess)
96 
97 ! Initial cost
98 call minim%cost(mpl,guess,minim%f_guess)
99 
100 select case (trim(minim%algo))
101 case ('hooke')
102  ! Hooke algorithm
103  call minim%hooke(mpl,guess)
104 end select
105 
106 ! Final cost
107 call minim%cost(mpl,minim%x,minim%f_min)
108 
109 ! Test
110 if (minim%f_min<minim%f_guess) then
111  call minim%vt_dir(minim%x)
112  if (lprt) then
113  write(mpl%info,'(a13,a,f6.1,a,e9.2,a,e9.2,a)') '','Minimizer '//trim(minim%algo)//', cost function decrease:', &
114  & abs(minim%f_min-minim%f_guess)/minim%f_guess*100.0,'% (',minim%f_guess,' to ',minim%f_min,')'
115  call flush(mpl%info)
116  end if
117 else
118  minim%x = minim%guess
119  if (lprt) call mpl%warning('Minimizer '//trim(minim%algo)//' failed')
120 end if
121 
122 end subroutine minim_compute
123 
124 !----------------------------------------------------------------------
125 ! Subroutine: minim_cost
126 ! Purpose: compute cost function
127 !----------------------------------------------------------------------
128 subroutine minim_cost(minim,mpl,x,f)
130 implicit none
131 
132 ! Passed variables
133 class(minim_type),intent(in) :: minim ! Minimization data
134 type(mpl_type),intent(in) :: mpl ! MPI data
135 real(kind_real),intent(in) :: x(minim%nx) ! Control vector
136 real(kind_real),intent(out) :: f ! Cost function value
137 
138 select case (minim%cost_function)
139 case ('fit_diag')
140  call minim%cost_fit_diag(mpl,x,f)
141 case ('fit_diag_dble')
142  call minim%cost_fit_diag_dble(mpl,x,f)
143 case ('fit_lct')
144  call minim%cost_fit_lct(mpl,x,f)
145 end select
146 
147 end subroutine minim_cost
148 
149 !----------------------------------------------------------------------
150 ! Subroutine: minim_cost_fit_diag
151 ! Purpose: diagnosic fit function cost
152 !----------------------------------------------------------------------
153 subroutine minim_cost_fit_diag(minim,mpl,x,f)
155 implicit none
156 
157 ! Passed variables
158 class(minim_type),intent(in) :: minim ! Minimization data
159 type(mpl_type),intent(in) :: mpl ! MPI data
160 real(kind_real),intent(in) :: x(minim%nx) ! Control vector
161 real(kind_real),intent(out) :: f ! Cost function value
162 
163 ! Local variables
164 integer :: offset,il0
165 real(kind_real) :: fo,fh,fv,norm,fit_rh_avg,fit_rv_avg
166 real(kind_real) :: fit_rh(minim%nl0),fit_rv(minim%nl0)
167 real(kind_real) :: fit(minim%nc3,minim%nl0r,minim%nl0)
168 real(kind_real) :: xtmp(minim%nx),fit_pack(minim%ny)
169 
170 ! Check control vector size
171 offset = 0
172 if (minim%lhomh) then
173  offset = offset+1
174 else
175  offset = offset+minim%nl0
176 end if
177 if (minim%lhomv) then
178  offset = offset+1
179 else
180  offset = offset+minim%nl0
181 end if
182 
183 ! Renormalize
184 xtmp = x
185 call minim%vt_dir(xtmp)
186 
187 ! Get data
188 offset = 0
189 if (minim%lhomh) then
190  fit_rh = xtmp(offset+1)
191  offset = offset+1
192 else
193  fit_rh = xtmp(offset+1:offset+minim%nl0)
194  offset = offset+minim%nl0
195 end if
196 if (minim%lhomv) then
197  fit_rv = xtmp(offset+1)
198  offset = offset+1
199 else
200  fit_rv = xtmp(offset+1:offset+minim%nl0)
201  offset = offset+minim%nl0
202 end if
203 
204 ! Compute function
205 call fit_diag(mpl,minim%nc3,minim%nl0r,minim%nl0,minim%l0rl0_to_l0,minim%disth,minim%distv,fit_rh,fit_rv,fit)
206 
207 ! Pack
208 fit_pack = pack(fit,mask=.true.)
209 
210 ! Observations penalty
211 fo = sum((fit_pack-minim%obs)**2,mask=isnotmsr(minim%obs).and.isnotmsr(fit_pack))
212 norm = sum(minim%obs**2,mask=isnotmsr(minim%obs).and.isnotmsr(fit_pack))
213 if (norm>0.0) fo = fo/norm
214 
215 ! Smoothing penalty
216 fh = 0.0
217 fv = 0.0
218 do il0=2,minim%nl0-1
219  fit_rh_avg = 0.5*(fit_rh(il0-1)+fit_rh(il0+1))
220  norm = fit_rh_avg**2
221  if (norm>0.0) fh = fh+(fit_rh(il0)-fit_rh_avg)**2/norm
222  fit_rv_avg = 0.5*(fit_rv(il0-1)+fit_rv(il0+1))
223  norm = fit_rv_avg**2
224  if (norm>0.0) fv = fv+(fit_rv(il0)-fit_rv_avg)**2/norm
225 end do
226 
227 ! Full penalty function
228 f = fo+fh+fv
229 
230 end subroutine minim_cost_fit_diag
231 
232 !----------------------------------------------------------------------
233 ! Subroutine: minim_cost_fit_diag_dble
234 ! Purpose: diagnosic fit function cost, double fit
235 !----------------------------------------------------------------------
236 subroutine minim_cost_fit_diag_dble(minim,mpl,x,f)
238 implicit none
239 
240 ! Passed variables
241 class(minim_type),intent(in) :: minim ! Minimization data
242 type(mpl_type),intent(in) :: mpl ! MPI data
243 real(kind_real),intent(in) :: x(minim%nx) ! Control vector
244 real(kind_real),intent(out) :: f ! Cost function value
245 
246 ! Local variables
247 integer :: offset,il0
248 real(kind_real) :: fo,fh,fv,fr,fc,norm,fit_rh_avg,fit_rv_avg,fit_rv_rfac_avg,fit_rv_coef_avg
249 real(kind_real) :: fit_rh(minim%nl0),fit_rv(minim%nl0),fit_rv_rfac(minim%nl0),fit_rv_coef(minim%nl0)
250 real(kind_real) :: fit(minim%nc3,minim%nl0r,minim%nl0)
251 real(kind_real) :: xtmp(minim%nx),fit_pack(minim%ny)
252 
253 ! Renormalize
254 xtmp = x
255 call minim%vt_dir(xtmp)
256 
257 ! Get data
258 offset = 0
259 if (minim%lhomh) then
260  fit_rh = xtmp(offset+1)
261  offset = offset+1
262 else
263  fit_rh = xtmp(offset+1:offset+minim%nl0)
264  offset = offset+minim%nl0
265 end if
266 if (minim%lhomv) then
267  fit_rv = xtmp(offset+1)
268  offset = offset+1
269  fit_rv_rfac = xtmp(offset+1)
270  offset = offset+1
271  fit_rv_coef = xtmp(offset+1)
272  offset = offset+1
273 else
274  fit_rv = xtmp(offset+1:offset+minim%nl0)
275  offset = offset+minim%nl0
276  fit_rv_rfac = xtmp(offset+1:offset+minim%nl0)
277  offset = offset+minim%nl0
278  fit_rv_coef = xtmp(offset+1:offset+minim%nl0)
279  offset = offset+minim%nl0
280 end if
281 
282 ! Compute function
283 call fit_diag_dble(mpl,minim%nc3,minim%nl0r,minim%nl0,minim%l0rl0_to_l0,minim%disth,minim%distv,fit_rh,fit_rv, &
284  & fit_rv_rfac,fit_rv_coef,fit)
285 
286 ! Pack
287 fit_pack = pack(fit,mask=.true.)
288 
289 ! Observations penalty
290 fo = sum((fit_pack-minim%obs)**2,mask=isnotmsr(minim%obs).and.isnotmsr(fit_pack))
291 norm = sum(minim%obs**2,mask=isnotmsr(minim%obs).and.isnotmsr(fit_pack))
292 if (norm>0.0) fo = fo/norm
293 
294 ! Smoothing penalty
295 fh = 0.0
296 fv = 0.0
297 fr = 0.0
298 fc = 0.0
299 do il0=2,minim%nl0-1
300  fit_rh_avg = 0.5*(fit_rh(il0-1)+fit_rh(il0+1))
301  norm = fit_rh_avg**2
302  if (norm>0.0) fh = fh+(fit_rh(il0)-fit_rh_avg)**2/norm
303  fit_rv_avg = 0.5*(fit_rv(il0-1)+fit_rv(il0+1))
304  norm = fit_rv_avg**2
305  if (norm>0.0) fv = fv+(fit_rv(il0)-fit_rv_avg)**2/norm
306  fit_rv_rfac_avg = 0.5*(fit_rv_rfac(il0-1)+fit_rv_rfac(il0+1))
307  norm = fit_rv_rfac_avg**2
308  if (norm>0.0) fr = fr+(fit_rv_rfac(il0)-fit_rv_rfac_avg)**2/norm
309  fit_rv_coef_avg = 0.5*(fit_rv_coef(il0-1)+fit_rv_coef(il0+1))
310  norm = fit_rv_coef_avg**2
311  if (norm>0.0) fc = fc+(fit_rv_coef(il0)-fit_rv_coef_avg)**2/norm
312 end do
313 
314 ! Full penalty function
315 f = fo+fh+fv+fr+fc
316 
317 end subroutine minim_cost_fit_diag_dble
318 
319 !----------------------------------------------------------------------
320 ! Function: minim_cost_fit_lct
321 ! Purpose: LCT fit function cost
322 !----------------------------------------------------------------------
323 subroutine minim_cost_fit_lct(minim,mpl,x,f)
325 implicit none
326 
327 ! Passed variables
328 class(minim_type),intent(in) :: minim ! Minimization data
329 type(mpl_type),intent(in) :: mpl ! MPI data
330 real(kind_real),intent(in) :: x(minim%nx) ! Control vector
331 real(kind_real),intent(out) :: f ! Cost function value
332 
333 ! Local variables
334 integer :: iscales,icomp
335 real(kind_real) :: norm
336 real(kind_real) :: fit(minim%nc3,minim%nl0),D(4,minim%nscales)
337 real(kind_real) :: xtmp(minim%nx),fit_pack(minim%ny),coef(minim%nscales)
338 
339 ! Renormalize
340 xtmp = x
341 call minim%vt_dir(xtmp)
342 
343 ! Compute function
344 do iscales=1,minim%nscales
345  do icomp=1,4
346  d(icomp,iscales) = xtmp((iscales-1)*4+icomp)
347  end do
348 end do
349 if (minim%nscales>1) then
350  coef(1:minim%nscales-1) = xtmp(minim%nscales*4+1:minim%nscales*4+minim%nscales-1)
351  coef(minim%nscales) = 1.0-sum(coef(1:minim%nscales-1))
352 else
353  coef(1) = 1.0
354 end if
355 call fit_lct(mpl,minim%nc3,minim%nl0,minim%dx,minim%dy,minim%dz,minim%dmask,minim%nscales, &
356  & xtmp(1:minim%nscales*4),coef,fit)
357 
358 ! Pack
359 fit_pack = pack(fit,mask=.true.)
360 
361 ! Observations penalty
362 f = sum((fit_pack-minim%obs)**2,mask=isnotmsr(minim%obs).and.isnotmsr(fit_pack))
363 norm = sum(minim%obs**2,mask=isnotmsr(minim%obs).and.isnotmsr(fit_pack))
364 if (norm>0.0) f = f/norm
365 
366 end subroutine minim_cost_fit_lct
367 
368 !----------------------------------------------------------------------
369 ! Subroutine: minim_hooke
370 ! Purpose: seeks a minimizer of a scalar function of several variables
371 ! Author: ALGOL original by Arthur Kaupe, C version by Mark Johnson, FORTRAN90 version by John Burkardt
372 !----------------------------------------------------------------------
373 subroutine minim_hooke(minim,mpl,guess)
375 implicit none
376 
377 ! Passed variables
378 class(minim_type),intent(inout) :: minim ! Minimization data
379 type(mpl_type),intent(in) :: mpl ! MPI data
380 real(kind_real),intent(in) :: guess(minim%nx) ! Guess
381 
382 ! Local variables
383 integer :: funevals,i,iters,keep
384 real(kind_real) :: fbefore,newf,steplength,tmp
385 real(kind_real) :: delta(minim%nx),newx(minim%nx)
386 
387 ! Initialization
388 newx = guess
389 minim%x = guess
390 do i=1,minim%nx
391  if (sup(abs(guess(i)),rth)) then
392  delta(i) = rho*abs(guess(i))
393  else
394  delta(i) = rho
395  end if
396 end do
397 funevals = 0
398 steplength = rho
399 iters = 0
400 call minim%cost(mpl,newx,fbefore)
401 funevals = funevals + 1
402 newf = fbefore
403 
404 ! Iterative search
405 do while ((iters<itermax).and.inf(tol,steplength))
406  ! Update iteration
407  iters = iters + 1
408 
409  ! Find best new point, one coordinate at a time
410  newx = minim%x
411  call minim%best_nearby(mpl,delta,newx,fbefore,funevals,newf)
412 
413  ! If we made some improvements, pursue that direction
414  keep = 1
415 
416  do while (inf(newf,fbefore).and.(keep==1))
417  do i=1,minim%nx
418  ! Arrange the sign of delta
419  if (sup(newx(i),minim%x(i))) then
420  delta(i) = abs(delta(i))
421  else
422  delta(i) = -abs(delta(i))
423  end if
424 
425  ! Now, move further in this direction.
426  tmp = minim%x(i)
427  minim%x(i) = newx(i)
428  newx(i) = newx(i)+newx(i)-tmp
429  end do
430 
431  ! Update
432  fbefore = newf
433  call minim%best_nearby(mpl,delta,newx,fbefore,funevals,newf)
434 
435  ! If the further (optimistic) move was bad...
436  if (inf(fbefore,newf)) exit
437 
438  ! Make sure that the differences between the new and the old points
439  ! are due to actual displacements; beware of roundoff errors that
440  ! might cause NEWF < FBEFORE.
441  keep = 0
442 
443  do i=1,minim%nx
444  if (inf(0.5*abs(delta(i)),abs(newx(i)-minim%x(i)))) then
445  keep = 1
446  exit
447  end if
448  end do
449  end do
450 
451  if (infeq(tol,steplength).and.infeq(fbefore,newf)) then
452  steplength = steplength*rho
453  delta = delta*rho
454  end if
455 end do
456 
457 end subroutine minim_hooke
458 
459 !----------------------------------------------------------------------
460 ! Subroutine: minim_best_nearby
461 ! Purpose: looks for a better nearby point, one coordinate at a time
462 ! Author: ALGOL original by Arthur Kaupe, C version by Mark Johnson, FORTRAN90 version by John Burkardt
463 !----------------------------------------------------------------------
464 subroutine minim_best_nearby(minim,mpl,delta,point,prevbest,funevals,minf)
466 implicit none
467 
468 ! Passed variables
469 class(minim_type),intent(inout) :: minim ! Minimization data
470 type(mpl_type),intent(in) :: mpl ! MPI data
471 real(kind_real),intent(inout) :: delta(minim%nx) ! Step
472 real(kind_real),intent(inout) :: point(minim%nx) ! Point
473 real(kind_real),intent(in) :: prevbest ! Best existing cost
474 integer,intent(inout) :: funevals ! Number of evaluations
475 real(kind_real),intent(out) :: minf ! Minimum cost
476 
477 ! Local variables
478 integer :: i
479 real(kind_real) :: ftmp
480 real(kind_real) :: z(minim%nx)
481 
482 ! Initialization
483 minf = prevbest
484 z = point
485 
486 do i=1,minim%nx
487  z(i) = point(i)+delta(i)
488  call minim%cost(mpl,z,ftmp)
489  funevals = funevals+1
490  if (inf(ftmp,minf)) then
491  minf = ftmp
492  else
493  delta(i) = -delta(i)
494  z(i) = point(i)+delta(i)
495  call minim%cost(mpl,z,ftmp)
496  funevals = funevals+1
497  if (inf(ftmp,minf)) then
498  minf = ftmp
499  else
500  z(i) = point(i)
501  end if
502  end if
503 end do
504 
505 ! Update
506 point = z
507 
508 end subroutine minim_best_nearby
509 
510 !----------------------------------------------------------------------
511 ! Subroutine: vt_dir
512 ! Purpose: direct variable transform
513 !----------------------------------------------------------------------
514 subroutine minim_vt_dir(minim,x)
516 implicit none
517 
518 ! Passed variables
519 class(minim_type),intent(in) :: minim ! Minimization data
520 real(kind_real),intent(inout) :: x(minim%nx) ! Vector
521 
522 ! Linear expansion of the hyperbolic tangent of the variable
523 x = minim%binf+0.5*(1.0+tanh(x))*(minim%bsup-minim%binf)
524 
525 end subroutine minim_vt_dir
526 
527 !----------------------------------------------------------------------
528 ! Subroutine: vt_inv
529 ! Purpose: inverse variable transform
530 !----------------------------------------------------------------------
531 subroutine minim_vt_inv(minim,mpl,x)
533 implicit none
534 
535 ! Passed variables
536 class(minim_type),intent(in) :: minim ! Minimization data
537 type(mpl_type),intent(in) :: mpl ! MPI data
538 real(kind_real),intent(inout) :: x(minim%nx) ! Vector
539 
540 ! Local variables
541 integer :: ix
542 
543 ! Inverse hyperbolic tangent of the linearly bounded variable
544 if (any((x<minim%binf).or.(x>minim%bsup))) call mpl%abort('variable out of bounds in vt_inv')
545 do ix=1,minim%nx
546  if (sup(minim%bsup(ix),minim%binf(ix))) then
547  x(ix) = atanh(2.0*(x(ix)-minim%binf(ix))/(minim%bsup(ix)-minim%binf(ix))-1.0)
548  else
549  x(ix) = 0.0
550  end if
551 end do
552 
553 end subroutine minim_vt_inv
554 
555 end module type_minim
logical function, public sup(x, y)
Definition: tools_repro.F90:85
subroutine minim_vt_dir(minim, x)
Definition: type_minim.F90:515
subroutine minim_cost(minim, mpl, x, f)
Definition: type_minim.F90:129
subroutine minim_cost_fit_diag_dble(minim, mpl, x, f)
Definition: type_minim.F90:237
subroutine minim_best_nearby(minim, mpl, delta, point, prevbest, funevals, minf)
Definition: type_minim.F90:465
real(kind_real), parameter rho
Definition: type_minim.F90:19
real(kind_real), parameter tol
Definition: type_minim.F90:20
subroutine minim_hooke(minim, mpl, guess)
Definition: type_minim.F90:374
subroutine, public fit_diag_dble(mpl, nc3, nl0r, nl0, l0rl0_to_l0, disth, distv, rh, rv, rv_rfac, rv_coef, fit)
Definition: tools_func.F90:369
real(kind_real), parameter, public rth
Definition: tools_repro.F90:15
subroutine minim_compute(minim, mpl, lprt)
Definition: type_minim.F90:78
subroutine minim_cost_fit_diag(minim, mpl, x, f)
Definition: type_minim.F90:154
logical function, public infeq(x, y)
Definition: tools_repro.F90:66
subroutine, public ver_smooth(mpl, n, x, rv, profile)
Definition: tools_fit.F90:182
subroutine minim_vt_inv(minim, mpl, x)
Definition: type_minim.F90:532
subroutine minim_cost_fit_lct(minim, mpl, x, f)
Definition: type_minim.F90:324
integer, parameter, public kind_real
logical function, public inf(x, y)
Definition: tools_repro.F90:47
integer, parameter itermax
Definition: type_minim.F90:21
logical function, public eq(x, y)
Definition: tools_repro.F90:28
subroutine, public fit_diag(mpl, nc3, nl0r, nl0, l0rl0_to_l0, disth, distv, rh, rv, fit)
Definition: tools_func.F90:235
subroutine, public fit_lct(mpl, nc, nl0, dx, dy, dz, dmask, nscales, D, coef, fit)
Definition: tools_func.F90:548