LCOV - code coverage report
Current view: top level - src - mo_anneal.f90 (source / functions) Hit Total Coverage
Test: forces coverage Lines: 322 488 66.0 %
Date: 2024-03-13 19:03:28 Functions: 4 6 66.7 %

          Line data    Source code
       1             : !> \file mo_anneal.f90
       2             : !> \brief \copybrief mo_anneal
       3             : !> \details \copydetails mo_anneal
       4             : 
       5             : !> \brief Anneal optimization of cost function.
       6             : !> \details Minimization of cost function and temperature finding of minima.
       7             : !> \changelog
       8             : !! - Juliane Mai, Mar 2012
       9             : !!   - module implementation
      10             : !! - Juliane Mai, May 2012
      11             : !!   - anneal: sp version
      12             : !! - Juliane Mai, May 2012
      13             : !!   - anneal: documentation
      14             : !! - Juliane Mai, May 2012
      15             : !!   - GetTemperature: sp and dp version
      16             : !! - Juliane Mai, Jun 2012
      17             : !!   - weighted parameter selection
      18             : !! - Juliane Mai, Aug 2012
      19             : !!   - function anneal instead of subroutine
      20             : !!   - using new module get_timeseed as default for seeding
      21             : !!   - new optional for minimization or maximization
      22             : !!   - fixed parameter ranges possible instead of interface range
      23             : !! - Juliane Mai, Nov 2012
      24             : !!   - history of achieved objective function values as optional out only in anneal but not anneal_valid
      25             : !! - Juliane Mai, Jan 2013
      26             : !!   - including DDS features in anneal, i.e. reflection at parameter boundaries,
      27             : !!     different parameter selection modes (one, all, neighborhood), and
      28             : !!     different parameter pertubation modes (flexible r=dR (anneal version) or
      29             : !!     constant r=0.2 (dds version))
      30             : !!   - remove sp versions
      31             : !!   - fixed and flexible parameter ranges are now in one function
      32             : !!     using optional arguments
      33             : !!   - undef_funcval instead of anneal_valid function
      34             : !! - Juliane Mai, Feb 2013
      35             : !!   - xor4096 optionals combined in save_state
      36             : !! - Arya Prasetya, Dec 2021
      37             : !!   - doxygen documentation anneal and get_temperature
      38             : !> \author Juliane Mai
      39             : !> \date Mar 2012
      40             : !> \copyright Copyright 2005-\today, the CHS Developers, Sabine Attinger: All rights reserved.
      41             : !! FORCES is released under the LGPLv3+ license \license_note
      42             : MODULE mo_anneal
      43             : 
      44             :   USE mo_kind, ONLY : i4, i8, dp
      45             :   USE mo_utils, ONLY : le, ge
      46             :   USE mo_xor4096, ONLY : get_timeseed, xor4096, xor4096g, n_save_state
      47             :   USE mo_optimization_utils, ONLY : eval_interface, objective_interface
      48             : 
      49             :   IMPLICIT NONE
      50             : 
      51             :   PUBLIC :: anneal                  ! Minimization of a cost function via Simaulated Annealing
      52             :   PUBLIC :: GetTemperature          ! Function which returns the optimal initial temperature for
      53             :   !                                 ! a given acceptance ratio and initial parameter set
      54             : 
      55             :   ! ------------------------------------------------------------------
      56             : 
      57             :   !>        \brief Optimize cost function with simulated annealing.
      58             : 
      59             :   !>        \details
      60             :   !!        Optimizes a user provided cost function using the Simulated Annealing strategy.
      61             :   !!
      62             :   !!        \b Example
      63             :   !!
      64             :   !!        User defined function `cost_dp` which calculates the cost function value for a
      65             :   !!        parameter set (the interface given below has to be used for this function!).
      66             :   !!
      67             :   !!        \code{.f90}
      68             :   !!        para = (/ 1.0_dp , 2.0_dp /)
      69             :   !!        prange(1,:) = (/ 0.0_dp, 10.0_dp /)
      70             :   !!        prange(2,:) = (/ 0.0_dp, 50.0_dp /)
      71             :   !!
      72             :   !!        parabest = anneal(cost_dp, para, prange)
      73             :   !!        \endcode
      74             :   !!
      75             :   !!        See also test folder for a detailed example, "test/test_mo_anneal".
      76             :   !!
      77             :   !!        \b Literature
      78             :   !!        1. S. Kirkpatrick, C. D. Gelatt, and M. P. Vecchi.
      79             :   !!           _Optimization by simulated annealing_.
      80             :   !!           Science, 220:671-680, 1983.
      81             :   !!        2. N. Metropolis, A. W. Rosenbluth, M. N. Rosenbluth, A. H. Teller, and E. Teller.
      82             :   !!           _Equation of state calculations by fast computing machines_.
      83             :   !!           J. Chem. Phys., 21:1087-1092, June 1953.
      84             :   !!        3. B. A. Tolson, and C. A. Shoemaker.
      85             :   !!           _Dynamically dimensioned search algorithm for computationally efficient watershed model calibration_.
      86             :   !!           WRR, 43(1), W01413, 2007.
      87             :   !!
      88             :   !>        \param[in] "INTERFACE                   :: eval"   Interface calculating the eval function at a given point.
      89             :   !>        \param[in] "INTERFACE                   :: cost"   Interface calculating the cost function at a given point.
      90             :   !>        \param[in]  "real(dp),    dimension(:)       :: para"            Initial parameter set.
      91             :   !>        \param[in]  "real(dp), dimension(size(para),2), optional :: prange"
      92             :   !!                                                                         Lower and upper bound per parameter.
      93             :   !>        \param[in]  "interface, optional             :: prange_func"     Interface calculating the
      94             :   !!                                                                         feasible range for a parameter
      95             :   !!                                                                         at a certain point, if ranges are variable.
      96             :   !>        \param[in]  "real(dp), optional              :: temp"            Initial temperature. \n
      97             :   !!                                                                         DEFAULT: Get_Temperature.
      98             :   !>        \param[in]  "real(dp), optional              :: DT"              Geometrical decreement of temperature. \n
      99             :   !!                                                                         0.7<DT<0.999. \n
     100             :   !!                                                                         DEFAULT: 0.9_dp.
     101             :   !>        \param[in]  "integer(i4), optional           :: nITERmax"        Maximal number of iterations. \n
     102             :   !!                                                                         Will be increased by 10% if stopping criteria of
     103             :   !!                                                                         acceptance ratio or epsilon decreement of cost
     104             :   !!                                                                         function is not fullfilled. \n
     105             :   !!                                                                         DEFAULT: 1000_i4.
     106             :   !>        \param[in]  "integer(i4), optional           :: LEN"             Length of Markov Chain. \n
     107             :   !!                                                                         DEFAULT: MAX(250_i4, size(para,1)).
     108             :   !>        \param[in]  "integer(i4), optional           :: nST"             Number of consecutive LEN steps. \n
     109             :   !!                                                                         DEFAULT: 5_i4.
     110             :   !>        \param[in]  "real(dp), optional              :: eps"             Stopping criteria of epsilon decreement of
     111             :   !!                                                                         cost function \n
     112             :   !!                                                                         DEFAULT: 0.0001_dp
     113             :   !>        \param[in]  "real(dp), optional              :: acc"             Stopping criteria for Acceptance Ratio \n
     114             :   !!                                                                         acc    <= 0.1_dp \n
     115             :   !!                                                                         DEFAULT: 0.1_dp
     116             :   !>        \param[in]  "integer(i4/i8), dimension(3), optional      :: seeds"
     117             :   !!                                                                         Seeds of random numbers used for random parameter
     118             :   !!                                                                         set generation \n
     119             :   !!                                                                         DEFAULT: dependent on current time
     120             :   !>        \param[in]  "logical, optional                           :: printflag"
     121             :   !!                                                                         If .true. detailed command line output is written \n
     122             :   !!                                                                         DEFAULT: .false.
     123             :   !>        \param[in]  "logical, dimension(size(para)), optional  :: maskpara"
     124             :   !!                                                                         maskpara(i) = .true.  --> parameter is optimized \n
     125             :   !!                                                                         maskpara(i) = .false. --> parameter is discarded
     126             :   !!                                                                                                   from optimiztaion \n
     127             :   !!                                                                         DEFAULT: .true.
     128             :   !>        \param[in]  "real(dp), dimension(size(para)), optional :: weight"
     129             :   !!                                                                         vector of weights per parameter: \n
     130             :   !!                                                                         gives the frequency of parameter to be chosen for
     131             :   !!                                                                         optimization (will be scaled to a CDF internally) \n
     132             :   !!                                                                         eg. [1,2,1] --> parameter 2 is chosen twice as
     133             :   !!                                                                         often as parameter 1 and 2 \n
     134             :   !!                                                                         DEFAULT: weight = 1.0_dp
     135             :   !>        \param[in]  "integer(i4), optional           :: changeParaMode"  which and how many param.are changed in one step \n
     136             :   !!                                                                         1 = one parameter \n
     137             :   !!                                                                         2 = all parameter \n
     138             :   !!                                                                         3 = neighborhood parameter \n
     139             :   !!                                                                         DEFAULT: 1_i4
     140             :   !>        \param[in]  "logical, optional               :: reflectionFlag"  if new parameter values are Gaussian
     141             :   !!                                                                         distributed and reflected (.true.) or
     142             :   !!                                                                         uniform in range (.false.) \n
     143             :   !!                                                                         DEFAULT: .false.
     144             :   !>        \param[in]  "logical, optional               :: pertubFlexFlag"  if pertubation of Gaussian distributed
     145             :   !!                                                                         parameter values is constant
     146             :   !!                                                                         at 0.2 (.false.) or
     147             :   !!                                                                         depends on dR (.true.) \n
     148             :   !!                                                                         DEFAULT: .true.
     149             :   !>        \param[in]  "logical, optional               :: maxit"           maximizing (.true.) or minimizing (.false.)
     150             :   !!                                                                         a function \n
     151             :   !!                                                                         DEFAULT: .false. (minimization)
     152             :   !>        \param[in]  "real(dp), optional              :: undef_funcval"   objective function value defining invalid
     153             :   !!                                                                         model output, e.g. -999.0_dp \n
     154             :   !>        \param[in]  "character(len=*) , optional     :: tmp_file"        file with temporal output
     155             :   !>        \param[out]  "real(dp), optional             :: funcbest"        minimized value of cost function
     156             :   !>        \param[out]  "real(dp), dimension(:,:), allocatable, optional   :: history"
     157             :   !!                                                                         returns a vector of achieved objective
     158             :   !!                                                                         after ith model evaluation
     159             :   !>        \retval "real(dp) :: parabest(size(para))"                       Parameter set minimizing the cost function.
     160             : 
     161             :   !>     \note
     162             :   !!           - Either fixed parameter range (`prange`) OR flexible parameter range (function interface `prange_func`)
     163             :   !!             has to be given in calling sequence.
     164             :   !!           - Only double precision version available.
     165             :   !!           - If single precision is needed not only dp has to be replaced by sp
     166             :   !!             but also i8 of `save_state` (random number variables) has to be replaced by i4.
     167             :   !!           - ParaChangeMode > 1 is not applied in GetTemperature.
     168             :   !!           - For Temperature estimation always only one single parameter is changed (ParaChangeMode=1)
     169             :   !!             which should give theoretically always the best estimate.
     170             :   !!           - `cost_func` and `prange_func` are user defined functions. See interface definition.
     171             : 
     172             :   !>    \author Luis Samaniego
     173             :   !>    \date Jan 2000
     174             :   !>    \date Mar 2003
     175             :   !!          - Re-heating
     176             : 
     177             :   !>    \author Juliane Mai
     178             :   !>    \date Mar 2012
     179             :   !!          - Modular version
     180             :   !>    \date May 2012
     181             :   !!          - sp version
     182             :   !!          - documentation
     183             : 
     184             :   ! ------------------------------------------------------------------
     185             : 
     186             :   INTERFACE anneal
     187             :     MODULE PROCEDURE anneal_dp
     188             :   END INTERFACE anneal
     189             : 
     190             :   ! ------------------------------------------------------------------
     191             : 
     192             :   !>        \brief Find initial temperature for simulated annealing.
     193             : 
     194             :   !>        \details Determines an initial temperature for Simulated Annealing achieving
     195             :   !!         certain acceptance ratio.
     196             :   !!
     197             :   !!        \b Example
     198             :   !!
     199             :   !!        User defined function 'cost_dp' which calculates the cost function value for a
     200             :   !!        parameter set (the interface given below has to be used for this function!).
     201             :   !!
     202             :   !!        \code{.f90}
     203             :   !!        para = (/ 1.0_dp , 2.0_dp /)
     204             :   !!        acc_goal   = 0.95_dp
     205             :   !!        prange(1,:) = (/ 0.0_dp, 10.0_dp /)
     206             :   !!        prange(2,:) = (/ 0.0_dp, 50.0_dp /)
     207             :   !!
     208             :   !!        temp = GetTemperature(para, cost_dp, acc_goal, prange)
     209             :   !!        \endcode
     210             :   !!
     211             :   !!        See also test folder for a detailed example, "pf_tests/test_mo_anneal".
     212             :   !!
     213             :   !!        \b Literature
     214             :   !!        1. Walid Ben-Ameur.
     215             :   !!           _Compututing the Initial Temperature of Simulated Annealing_.
     216             :   !!           Comput. Opt. and App. (2004).
     217             :   !!
     218             :   !>        \param[in] "real(dp),    dimension(:)   :: paraset"   Initial (valid) parameter set.
     219             :   !>        \param[in] "INTERFACE                   :: eval"   Interface calculating the eval function at a given point.
     220             :   !>        \param[in] "INTERFACE                   :: cost"   Interface calculating the cost function at a given point.
     221             :   !>        \param[in] "INTERFACE                   :: prange_func"   Interface for functional ranges.
     222             :   !>        \param[in] "real(dp)                    :: acc_goal"  Acceptance Ratio which has to be achieved.
     223             :   !>        \param[in] "real(dp), dimension(size(para),2), optional :: prange"
     224             :   !!                                                                         Lower and upper bound per parameter.
     225             :   !>        \param[in] "INTERFACE, optional              :: prange_func"     Interface calculating the
     226             :   !!                                                                         feasible range for a parameter
     227             :   !!                                                                         at a certain point, if ranges are variable.
     228             :   !>        \param[in] "integer(i4), optional            :: samplesize"      Number of iterations the estimation of temperature
     229             :   !!                                                                         is based on. \n
     230             :   !!                                                                         DEFAULT: Max(20_i4*n,250_i4)
     231             :   !>        \param[in] "logical, dimension(size(para)), optional    :: maskpara"
     232             :   !!                                                                         maskpara(i) = .true.  --> parameter is optimized. \n
     233             :   !!                                                                         maskpara(i) = .false. --> parameter is discarded
     234             :   !!                                                                                                   from optimiztaion. \n
     235             :   !!                                                                         DEFAULT: .true. .
     236             :   !>        \param[in] "INTEGER(I4/I8), dimension(2), optional      :: seeds"
     237             :   !!                                                                         Seeds of random numbers used for random parameter
     238             :   !!                                                                         set generation. \n
     239             :   !!                                                                         DEFAULT: dependent on current time.
     240             :   !>        \param[in] "logical, optional                           :: printflag"
     241             :   !!                                                                         If .true. detailed command line output is written.
     242             :   !!                                                                         DEFAULT: .false. .
     243             :   !>        \param[in] "real(dp), dimension(size(para,1)), optional :: weight"
     244             :   !!                                                                         Vector of weights per parameter. \n
     245             :   !!                                                                         Gives the frequency of parameter to be chosen for
     246             :   !!                                                                         optimization (will be scaled to a CDF internally). \n
     247             :   !!                                                                         eg. [1,2,1] --> parameter 2 is chosen twice as
     248             :   !!                                                                                         often as parameter 1 and 2. \n
     249             :   !!                                                                         DEFAULT: weight = 1.0_dp.
     250             :   !>        \param[in] "logical, optional                :: maxit"            Minimizing (.false.) or maximizing (.true.)
     251             :   !!                                                                          a function. \n
     252             :   !!                                                                          DEFAULT: .false. (minimization).
     253             :   !>        \param[in] "real(dp), optional               :: undef_funcval"    Objective function value defining invalid
     254             :   !!                                                                          model output, e.g. -999.0_dp.
     255             : 
     256             :   !>        \retval "real(dp) :: temperature"                                 Temperature achieving a certain
     257             :   !!                                                                          acceptance ratio in Simulated Annealing.
     258             : 
     259             :   !>        \note
     260             :   !!              - Either fixed parameter range (`prange`) OR flexible parameter range (function interface `prange_func`)
     261             :   !!                has to be given in calling sequence.
     262             :   !!              - Only double precision version available.
     263             :   !!              - If single precision is needed not only dp has to be replaced by sp
     264             :   !!                but also i8 of `save_state` (random number variables) has to be replaced by i4.
     265             :   !!              - ParaChangeMode > 1 is not applied in GetTemperature.
     266             :   !!              - For Temperature estimation always only one single parameter is changed (ParaChangeMode=1)
     267             :   !!                which should give theoretically always the best estimate.
     268             :   !!              - `cost_dp` and `prange_func` are user defined functions. See interface definition.
     269             : 
     270             :   !>        \author  Juliane Mai
     271             :   !>        \date May 2012
     272             : 
     273             :   ! ------------------------------------------------------------------
     274             : 
     275             :   INTERFACE GetTemperature
     276             :     MODULE PROCEDURE GetTemperature_dp
     277             :   END INTERFACE GetTemperature
     278             : 
     279             :   PRIVATE
     280             : 
     281             :   INTERFACE Generate_Neighborhood_weight
     282             :     MODULE PROCEDURE Generate_Neighborhood_weight_dp
     283             :   END INTERFACE Generate_Neighborhood_weight
     284             : 
     285             :   ! ------------------------------------------------------------------
     286             : 
     287             : CONTAINS
     288             : 
     289           4 :   FUNCTION anneal_dp(eval, cost, para, &   ! obligatory
     290           0 :           prange, prange_func, &   ! optional IN: exactly one of both
     291             :           temp, Dt, nITERmax, Len, nST, eps, acc, &   ! optional IN
     292           8 :           seeds, printflag, maskpara, weight, &   ! optional IN
     293             :           changeParaMode, reflectionFlag, &   ! optional IN
     294             :           pertubFlexFlag, maxit, undef_funcval, &   ! optional IN
     295             :           tmp_file, &   ! optional IN
     296             :           funcbest, history                          &   ! optional OUT
     297             :           ) &
     298          20 :           result(parabest)
     299             : 
     300             :     IMPLICIT NONE
     301             : 
     302             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
     303             :     procedure(objective_interface), intent(in), pointer :: cost
     304             : 
     305             :     INTERFACE
     306             :       SUBROUTINE prange_func(paraset, iPar, rangePar)
     307             :         ! gives the range (min,max) of the parameter iPar at a certain parameter set paraset
     308             :         use mo_kind
     309             :         real(dp), dimension(:), INTENT(IN) :: paraset
     310             :         integer(i4), INTENT(IN) :: iPar
     311             :         real(dp), dimension(2), INTENT(OUT) :: rangePar
     312             :       END SUBROUTINE prange_func
     313             :     END INTERFACE
     314             :     optional :: prange_func
     315             : 
     316             :     real(dp), dimension(:), intent(in) :: para                         !< initial parameter
     317             : 
     318             :     real(dp), optional, dimension(size(para, 1), 2), intent(in) :: prange !< lower and upper limit per parameter
     319             :     real(dp), optional, intent(in) :: temp                                !< starting temperature (DEFAULT: Get_Temperature)
     320             :     real(dp), optional, intent(in) :: Dt                                  !< geometrical decreement, 0.7<DT<0.999 (DEFAULT: 0.9)
     321             :     integer(i4), optional, intent(in) :: nITERmax                         !< maximal number of iterations (DEFAULT: 1000)
     322             :     integer(i4), optional, intent(in) :: Len                              !< Length of Markov Chain,
     323             :                                                                           !! DEFAULT: max(250, size(para,1))
     324             :     integer(i4), optional, intent(in) :: nST                              !< Number of consecutive LEN steps! (DEFAULT: 5)
     325             :     real(dp), optional, intent(in) :: eps                                 !< epsilon decreement of cost function (DEFAULT: 0.01)
     326             :     real(dp), optional, intent(in) :: acc                                 !< Acceptance Ratio, <0.1 stopping criteria
     327             :                                                                           !! (DEFAULT: 0.1)
     328             :     INTEGER(I8), optional, intent(in) :: seeds(3)                         !< Seeds of random numbers (DEFAULT: Get_timeseed)
     329             :     logical, optional, intent(in) :: printflag                            !< If command line output is written (.true.)
     330             :                                                                           !!  (DEFAULT: .false.)
     331             :     logical, optional, dimension(size(para, 1)), intent(in) :: maskpara   !< true if parameter will be optimized
     332             :                                                                           !! false if parameter is discarded in optimization
     333             :                                                                           !! (DEFAULT: .true.)
     334             :     real(dp), optional, dimension(size(para, 1)), intent(in) :: weight    !< vector of weights per parameter
     335             :                                                                           !! gives the frequency of parameter to be
     336             :                                                                           !!  chosen for optimization (DEFAULT: uniform)
     337             :     integer(i4), optional, intent(in) :: changeParaMode                   !< which and how many param. are changed in a step
     338             :                                                                           !!  1 = one parameter 2 = all parameter
     339             :                                                                           !!  3 = neighborhood parameter (DEFAULT: 1_i4)
     340             :     logical, optional, intent(in) :: reflectionFlag                       !< if new parameter values are selected normal
     341             :                                                                           !!  distributed and reflected (.true.) or
     342             :                                                                           !!  uniform in range (.false.) (DEFAULT: .false.)
     343             :     logical, optional, intent(in) :: pertubFlexFlag                       !< if pertubation of normal distributed parameter
     344             :                                                                           !!  values is constant 0.2 (.false.) or
     345             :                                                                           !!  depends on dR (.true.) (DEFAULT: .true.)
     346             :     logical, optional, intent(in) :: maxit                                !< Maximization or minimization of function
     347             :                                                                           !! maximization = .true., minimization = .false.
     348             :                                                                           !! (DEFAULT: .false.)
     349             :     real(dp), optional, intent(in) :: undef_funcval                       !< objective function value occuring if
     350             :                                                                           !!  parameter set leads to  invalid model results,
     351             :                                                                           !! e.g. -9999.0_dp! (DEFAULT: not present)
     352             :     CHARACTER(LEN = *), optional, intent(in) :: tmp_file                  !< file for temporal output
     353             :     real(dp), optional, intent(out) :: funcbest                           !< minimized value of cost function
     354             :                                                                           !! (DEFAULT: not present)
     355             :     real(dp), optional, dimension(:, :), allocatable, intent(out) :: history !< returns a vector of achieved objective!
     356             :                                                                           !! after ith model evaluation (DEFAULT: not present)
     357             : 
     358             :     real(dp), dimension(size(para, 1)) :: parabest                     !< parameter set minimizing objective
     359             : 
     360             :     integer(i4) :: n              ! Number of parameters
     361             :     integer(i4) :: iPar, par      ! counter for parameter
     362          12 :     real(dp), dimension(2) :: iParRange      ! parameter's range
     363           4 :     real(dp) :: T_in           ! Temperature
     364           4 :     real(dp) :: DT_IN          ! Temperature decreement
     365             :     integer(i4) :: nITERmax_in    ! maximal number of iterations
     366             :     integer(i4) :: LEN_IN         ! Length of Markov Chain
     367             :     integer(i4) :: nST_in         ! Number of consecutive LEN steps
     368           4 :     real(dp) :: eps_in         ! epsilon decreement of cost function
     369           4 :     real(dp) :: acc_in         ! Acceptance Ratio, <0.1 stopping criteria
     370             :     INTEGER(I8) :: seeds_in(3)    ! Seeds of random numbers
     371             :     logical :: printflag_in   ! If command line output is written
     372             :     logical :: coststatus     ! Checks status of cost function value,
     373             :     !                                                          ! i.e. is parameter set is feasible
     374           0 :     logical, dimension(size(para, 1)) :: maskpara_in    ! true if parameter will be optimized
     375           4 :     integer(i4), dimension(:), allocatable :: truepara       ! indexes of parameters to be optimized
     376          24 :     real(dp), dimension(size(para, 1)) :: weight_in      ! CDF of parameter chosen for optimization
     377          20 :     real(dp), dimension(size(para, 1)) :: weightUni      ! uniform CDF of parameter chosen for optimization
     378          24 :     real(dp), dimension(size(para, 1)) :: weightGrad     ! linear combination of weight and weightUni
     379             :     integer(i4) :: changeParaMode_inin  ! 1=one, 2=all, 3=neighborhood
     380             :     logical :: reflectionFlag_inin  ! true=gaussian distributed and reflected,
     381             :     !                                                                ! false=uniform distributed in range
     382             :     logical :: pertubFlexFlag_inin  ! variance of normal distributed parameter values
     383             :     !                                                                ! .true. = depends on dR, .false. = constant 0.2
     384           4 :     real(dp) :: maxit_in             ! maximization = -1._dp, minimization = 1._dp
     385           4 :     real(dp) :: costbest             ! minimized value of cost function
     386           4 :     real(dp), dimension(:, :), allocatable :: history_out          ! best cost function value after k iterations
     387           4 :     real(dp), dimension(:, :), allocatable :: history_out_tmp      ! helper vector
     388             : 
     389             :     type paramLim
     390             :       real(dp) :: min         ! minimum value
     391             :       real(dp) :: max         ! maximum value
     392             :       real(dp) :: new         ! new state value
     393             :       real(dp) :: old         ! old state value
     394             :       real(dp) :: best        ! best value found
     395             :       real(dp) :: dMult       ! sensitivity multiplier for parameter search
     396             :     end type paramLim
     397           4 :     type (paramLim), dimension (size(para, 1)), target :: gamma ! Parameter
     398             : 
     399             :     ! for random numbers
     400           4 :     real(dp) :: RN1, RN2, RN3     ! Random numbers
     401             :     integer(I8), dimension(n_save_state) :: save_state_1      ! optional arguments for restarting RN stream 1
     402             :     integer(I8), dimension(n_save_state) :: save_state_2      ! optional arguments for restarting RN stream 2
     403             :     integer(I8), dimension(n_save_state) :: save_state_3      ! optional arguments for restarting RN stream 3
     404             :     ! for dds parameter selection
     405           4 :     logical, dimension(size(para, 1)) :: neighborhood      ! selected parameter in neighborhood
     406           4 :     real(dp) :: pertubationR      ! neighborhood pertubation size parameter
     407             : 
     408             :     ! for SA
     409             :     integer(i4) :: idummy, i
     410           4 :     real(dp) :: NormPhi
     411           8 :     real(dp) :: ac_ratio, pa
     412           4 :     integer(i4), dimension(:, :), allocatable :: iPos_iNeg_history      ! stores iPos and iNeg of nST last Markov Chains
     413           4 :     real(dp) :: fo, fn, df, fBest
     414           8 :     real(dp) :: rho, fInc, fbb, dr
     415           8 :     real(dp) :: T0, DT0
     416             :     real(dp), parameter :: small = -700._DP
     417             :     integer(i4) :: j, iter, kk
     418             :     integer(i4) :: Ipos, Ineg
     419             :     integer(i4) :: iConL, iConR, iConF
     420             :     integer(i4) :: iTotalCounter          ! includes reheating for final conditions
     421             :     integer(i4) :: iTotalCounterR         ! counter of interations in one reheating
     422             :     logical :: iStop
     423             :     logical :: ldummy
     424             : 
     425             :     ! CHECKING OPTIONALS
     426             : 
     427           4 :     n = size(para, 1)
     428             : 
     429             :     ! either range or rangfunc has to be given
     430           4 :     if (present(prange) .eqv. present(prange_func)) then
     431           0 :       stop 'anneal: Either range or prange_func has to be given'
     432             :     end if
     433             : 
     434           4 :     if (present(Dt)) then
     435           4 :       if ((Dt .lt. 0.7_dp) .or. (Dt .gt. 0.999_dp)) then
     436           0 :         stop 'Input argument DT must lie between 0.7 and 0.999'
     437             :       else
     438             :         DT_IN = Dt
     439             :       end if
     440             :     else
     441             :       DT_IN = 0.9_dp
     442             :     end if
     443             : 
     444           4 :     if (present(nITERmax)) then
     445           4 :       if (nITERmax .lt. 1_I4) then
     446           0 :         stop 'Input argument nITERmax must be greater 0'
     447             :       else
     448           4 :         nITERmax_in = nITERmax
     449             :       end if
     450             :     else
     451           0 :       nITERmax_in = 1000_i4
     452             :     end if
     453             : 
     454           4 :     if (present(Len)) then
     455           4 :       if (Len .lt. Max(20_i4 * n, 250_i4)) then
     456           0 :         print*, 'WARNING: Input argument LEN should be greater than Max(250,20*N), N=number of parameters'
     457           0 :         LEN_IN = Len
     458             :       else
     459             :         LEN_IN = Len
     460             :       end if
     461             :     else
     462           0 :       LEN_IN = Max(20_i4 * n, 250_i4)
     463             :     end if
     464             : 
     465           4 :     idummy = nITERmax_in / LEN_IN + 1_i4
     466          12 :     allocate(history_out(idummy, 2))
     467             : 
     468           4 :     if (present(nST)) then
     469           4 :       if (nST .lt. 0_i4) then
     470           0 :         stop 'Input argument nST must be greater than 0'
     471             :       else
     472             :         nST_in = nST
     473             :       end if
     474             :     else
     475             :       nST_in = 5_i4
     476             :     end if
     477             : 
     478          12 :     allocate(iPos_iNeg_history(nST_in, 2))
     479          52 :     iPos_iNeg_history = 0_i4
     480             : 
     481           4 :     if (present(eps)) then
     482           4 :       if (le(eps, 0.0_dp)) then
     483           0 :         stop 'Input argument eps must be greater than 0'
     484             :       else
     485           4 :         eps_in = eps
     486             :       end if
     487             :     else
     488             :       eps_in = 0.0001_dp
     489             :     end if
     490             : 
     491           4 :     if (present(acc)) then
     492           4 :       if (le(acc, 0.0_dp)  .or. ge(acc, 1.0_dp)) then
     493           0 :         stop 'Input argument acc must lie between 0.0 and 1.0'
     494             :       else
     495           4 :         acc_in = acc
     496             :       end if
     497             :     else
     498             :       acc_in = 0.1_dp
     499             :     end if
     500             : 
     501           4 :     if (present(seeds)) then
     502           4 :       seeds_in = seeds
     503             :     else
     504             :       ! Seeds depend on actual time
     505           0 :       call get_timeseed(seeds_in)
     506             :     end if
     507             : 
     508           4 :     if (present(printflag)) then
     509           4 :       printflag_in = printflag
     510             :     else
     511           0 :       printflag_in = .false.
     512             :     end if
     513             : 
     514           4 :     if (present(maskpara)) then
     515          20 :       if (count(maskpara) .eq. 0_i4) then
     516           0 :         stop 'Input argument maskpara: At least one element has to be true'
     517             :       else
     518          20 :         maskpara_in = maskpara
     519             :       end if
     520             :     else
     521           0 :       maskpara_in = .true.
     522             :     end if
     523             : 
     524           4 :     if (present(maxit)) then
     525           4 :       ldummy = maxit
     526           4 :       if (maxit) then
     527             :         maxit_in = -1._dp
     528             :       else
     529           4 :         maxit_in = 1._dp
     530             :       end if
     531             :     else
     532           0 :       ldummy = .false.
     533           0 :       maxit_in = 1._dp
     534             :     end if
     535             : 
     536           4 :     if (present(temp)) then
     537           4 :       if ((temp .lt. 0.0_dp)) then
     538           0 :         stop 'Input argument temp must be greater then zero'
     539             :       else
     540           4 :         T_in = temp
     541             :       end if
     542             :     else
     543           0 :       if (present(prange_func)) then
     544           0 :         if (present(undef_funcval)) then
     545             :           T_in = GetTemperature(eval, cost, para, 0.95_dp, prange_func = prange_func, &
     546             :                   maskpara = maskpara_in, samplesize = 2_i4 * LEN_IN, &
     547             :                   seeds = seeds_in(1 : 2), printflag = printflag_in, &
     548           0 :                   maxit = ldummy, undef_funcval = undef_funcval)
     549             :         else
     550             :           T_in = GetTemperature(eval, cost, para, 0.95_dp, prange_func = prange_func, &
     551             :                   maskpara = maskpara_in, samplesize = 2_i4 * LEN_IN, &
     552             :                   seeds = seeds_in(1 : 2), printflag = printflag_in, &
     553           0 :                   maxit = ldummy)
     554             :         end if
     555             :       else
     556           0 :         if (present(undef_funcval)) then
     557             :           T_in = GetTemperature(eval, cost, para, 0.95_dp, prange = prange, &
     558             :                   maskpara = maskpara_in, samplesize = 2_i4 * LEN_IN, &
     559             :                   seeds = seeds_in(1 : 2), printflag = printflag_in, &
     560           0 :                   maxit = ldummy, undef_funcval = undef_funcval)
     561             :         else
     562             :           T_in = GetTemperature(eval, cost, para, 0.95_dp, prange = prange, &
     563             :                   maskpara = maskpara_in, samplesize = 2_i4 * LEN_IN, &
     564             :                   seeds = seeds_in(1 : 2), printflag = printflag_in, &
     565           0 :                   maxit = ldummy)
     566             :         end if
     567             :       end if
     568             :     end if
     569             : 
     570           4 :     if (present(changeParaMode)) then
     571           4 :       changeParaMode_inin = changeParaMode
     572             :     else
     573             :       changeParaMode_inin = 1_i4
     574             :     end if
     575             : 
     576           4 :     if (present(reflectionFlag)) then
     577           4 :       reflectionFlag_inin = reflectionFlag
     578             :     else
     579             :       reflectionFlag_inin = .false.
     580             :     end if
     581             : 
     582           4 :     if (present(pertubFlexFlag)) then
     583           4 :       pertubFlexFlag_inin = pertubFlexFlag
     584             :     else
     585             :       pertubFlexFlag_inin = .true.
     586             :     end if
     587             : 
     588             :     ! Temporal file writing
     589           4 :     if(present(tmp_file)) then
     590           0 :       open(unit = 999, file = trim(adjustl(tmp_file)), action = 'write', status = 'unknown')
     591           0 :       write(999, *) '# settings :: general'
     592           0 :       write(999, *) '# nIterations    iseed'
     593           0 :       write(999, *) nITERmax_in, seeds_in
     594           0 :       write(999, *) '# settings :: anneal specific'
     595           0 :       write(999, *) '# sa_tmp'
     596           0 :       write(999, *) T_in
     597           0 :       write(999, *) '# iter   bestf   (bestx(j),j=1,nopt)'
     598           0 :       close(999)
     599             :     end if
     600             : 
     601             :     ! INITIALIZATION
     602             : 
     603          28 :     allocate (truepara(count(maskpara_in)))
     604           4 :     idummy = 0_i4
     605          20 :     do i = 1, N
     606          20 :       if (maskpara_in(i)) then
     607          16 :         idummy = idummy + 1_i4
     608          16 :         truepara(idummy) = i
     609             :       end if
     610             :     end do
     611             : 
     612           4 :     if (printflag_in) then
     613           0 :       print*, 'Following parameters will be optimized: ', truepara
     614             :     end if
     615             : 
     616          20 :     weight_in = 0.0_dp
     617          20 :     weightUni = 0.0_dp
     618           4 :     if (present(weight)) then
     619          56 :       where (maskpara_in(:))
     620           4 :         weight_in(:) = weight(:)
     621             :         weightUni(:) = 1.0_dp
     622             :       end where
     623             :     else
     624           0 :       where (maskpara_in(:))
     625             :         weight_in(:) = 1.0_dp
     626             :         weightUni(:) = 1.0_dp
     627             :       end where
     628             :     end if
     629             :     ! scaling the weights
     630          36 :     weight_in = weight_in / sum(weight_in)
     631          36 :     weightUni = weightUni / sum(weightUni)
     632             :     ! cummulating the weights
     633          16 :     do i = 2, n
     634          12 :       weight_in(i) = weight_in(i) + weight_in(i - 1)
     635          16 :       weightUni(i) = weightUni(i) + weightUni(i - 1)
     636             :     end do
     637             : 
     638           4 :     call xor4096 (seeds_in(1), RN1, save_state = save_state_1)
     639           4 :     call xor4096 (seeds_in(2), RN2, save_state = save_state_2)
     640           4 :     call xor4096g(seeds_in(3), RN3, save_state = save_state_3)
     641           4 :     seeds_in = 0_i8
     642             : 
     643             :     ! Start Simulated Annealing routine
     644          20 :     gamma(:)%dmult = 1.0_dp
     645          20 :     gamma(:)%new = para(:)
     646          20 :     gamma(:)%old = para(:)
     647          20 :     gamma(:)%best = para(:)
     648           4 :     NormPhi = -9999.9_dp
     649           4 :     T0 = T_in
     650           4 :     DT0 = DT_IN
     651             : 
     652             :     ! Generate and evaluate the initial solution state
     653          20 :     fo = cost(gamma(:)%old, eval) * maxit_in
     654           4 :     if (abs(fo) .lt. tiny(0.0_dp)) fo = 0.0000001_dp * maxit_in
     655             : 
     656           4 :     file_write : if (present(tmp_file)) then
     657           0 :       open(unit = 999, file = trim(adjustl(tmp_file)), action = 'write', position = 'append', recl = (n + 2) * 30)
     658           0 :       if (.not. ldummy) then
     659           0 :         write(999, *) '0', fo, gamma(:)%old
     660             :       else
     661           0 :         write(999, *) '0', -fo, gamma(:)%old
     662             :       end if
     663           0 :       close(999)
     664             :     end if file_write
     665             : 
     666             :     ! initialize counters /var for new SA
     667           4 :     iConL = 0_i4
     668           4 :     iConR = 1_i4
     669           4 :     iConF = 0_i4
     670           4 :     iTotalCounter = 0_i4
     671           4 :     iTotalCounterR = 0_i4
     672           4 :     fn = 0.0_DP
     673           4 :     ac_ratio = 1.0_DP
     674           4 :     iStop = .TRUE.
     675           4 :     iter = 0_i4
     676             :     ! restoring initial T param.
     677           4 :     DT_IN = DT0
     678             :     ! Storing the best solution so far
     679           4 :     NormPhi = fo * maxit_in
     680           4 :     fo = fo / NormPhi
     681           4 :     fBest = 1.0_dp * maxit_in
     682             : 
     683           4 :     if (printflag_in) then
     684           0 :       print '(A15,E15.7,A4,E15.7,A4)', ' start NSe   = ', fBest * maxit_in, '  ( ', fBest * normPhi * maxit_in, ' )  '
     685           0 :       print '(I8, 2I5, 4E15.7)', 1_i4, 0_i4, 0_i4, 1._dp, T_in, fo * normPhi * maxit_in, fBest * normPhi * maxit_in
     686             :     end if
     687             : 
     688             :     ! ****************** Stop Criterium  *******************
     689             :     ! Repeat until the % reduction of nST consecutive Markov
     690             :     ! chains (LEN) of the objective function (f) <= epsilon
     691             :     ! ******************************************************
     692        5152 :     loopTest : do while (iStop)
     693        5148 :       iter = iter + 1_i4
     694        5148 :       Ipos = 0_i4
     695        5148 :       Ineg = 0_i4
     696        5148 :       fbb = fBest
     697             :       !LEN_IN = int( real(iter,dp)*sqrt(real(iter,dp)) / nITER + 1.5*real(N,dp),i4)
     698             :       ! Repeat LEN times with feasible solution
     699        5148 :       j = 1
     700     1292148 :       loopLEN : do while (j .le. LEN_IN)
     701             : 
     702     1287000 :         iTotalCounterR = iTotalCounterR + 1_i4
     703     1287000 :         iTotalCounter = iTotalCounter + 1_i4
     704             : 
     705             :         ! Generate a random subsequent state and evaluate its objective function
     706             :         ! (1) Generate new parameter set
     707     1287000 :         dR = (1.0_DP - real(iTotalCounterR, dp) / real(nITERmax_in, dp))**2.0_DP
     708     1287000 :         if (.not. (ge(dR, 0.05_DP)) .and. &
     709             :                 (iTotalCounterR <= int(real(nIterMax_in, dp) / 3._dp * 4_dp, i4))) then
     710      821168 :           dR = 0.05_DP
     711             :         end if
     712             : 
     713     1287000 :         if (pertubFlexFlag_inin) then
     714     1287000 :           pertubationR = max(dR / 5.0_dp, 0.05_dp)
     715             :         else
     716           0 :           pertubationR = 0.2_dp
     717             :         end if
     718             : 
     719             :         ! gradual weights:
     720             :         !        acc_ratio close to 1 --> weight
     721             :         !        acc_ratio close to 0 --> weight uniform
     722             :         ! gradual weighting is linear combination of these two weights
     723     6435000 :         weightGrad(:) = weightUni(:) + (ac_ratio) * (weight_in(:) - weightUni(:))
     724             : 
     725     1287000 :         select case(changeParaMode_inin)
     726             :         case(1_i4)  ! only one parameter is changed
     727             :           ! (1a) Change one parameter traditionally
     728     1287000 :           call xor4096(seeds_in(2), RN2, save_state = save_state_2)
     729     1287000 :           iPar = 1_i4
     730             :           !
     731     3216686 :           do while (weightGrad(iPar) .lt. RN2)
     732     1929686 :             iPar = iPar + 1_i4
     733             :           end do
     734     1287000 :           if (present(prange_func)) then
     735     6435000 :             call prange_func(gamma(:)%old, iPar, iParRange)
     736             :           else
     737           0 :             iParRange(1) = prange(iPar, 1)
     738           0 :             iParRange(2) = prange(iPar, 2)
     739             :           end if
     740     1287000 :           gamma(iPar)%min = iParRange(1)
     741     1287000 :           gamma(iPar)%max = iParRange(2)
     742     1287000 :           if (reflectionFlag_inin) then
     743           0 :             call xor4096g(seeds_in(3), RN3, save_state = save_state_3)
     744           0 :             gamma(iPar)%new = parGen_dds_dp(gamma(iPar)%old, pertubationR, &
     745           0 :                     gamma(iPar)%min, gamma(iPar)%max, RN3)
     746             :           else
     747     1287000 :             call xor4096(seeds_in(2), RN2, save_state = save_state_2)
     748     1287000 :             gamma(iPar)%new = parGen_anneal_dp(gamma(iPar)%old, dR, &
     749     2574000 :                     gamma(iPar)%min, gamma(iPar)%max, RN2)
     750             :           end if
     751             :         case(2_i4)  ! all parameter are changed
     752           0 :           do par = 1, size(truepara)
     753           0 :             iPar = truepara(par)
     754           0 :             if (present(prange_func)) then
     755           0 :               call prange_func(gamma(:)%old, iPar, iParRange)
     756             :             else
     757           0 :               iParRange(1) = prange(iPar, 1)
     758           0 :               iParRange(2) = prange(iPar, 2)
     759             :             end if
     760           0 :             gamma(iPar)%min = iParRange(1)
     761           0 :             gamma(iPar)%max = iParRange(2)
     762           0 :             if (reflectionFlag_inin) then
     763           0 :               call xor4096g(seeds_in(3), RN3, save_state = save_state_3)
     764           0 :               gamma(iPar)%new = parGen_dds_dp(gamma(iPar)%old, pertubationR, &
     765           0 :                       gamma(iPar)%min, gamma(iPar)%max, RN3)
     766             :             else
     767           0 :               call xor4096(seeds_in(2), RN2, save_state = save_state_2)
     768           0 :               gamma(iPar)%new = parGen_anneal_dp(gamma(iPar)%old, dR, &
     769           0 :                       gamma(iPar)%min, gamma(iPar)%max, RN2)
     770             :             end if
     771             :           end do
     772             :         case(3_i4)  ! parameter in neighborhood are changed
     773             :           ! Generate new neighborhood
     774             :           call generate_neighborhood_weight_dp(truepara, weightGrad, save_state_1, &
     775           0 :                   iTotalCounter, nIterMax_in, neighborhood)
     776             :           !
     777             :           ! change parameter in neighborhood
     778     1287000 :           do iPar = 1, n
     779           0 :             if (neighborhood(iPar)) then
     780             :               ! find range of parameter
     781           0 :               if (present(prange_func)) then
     782           0 :                 call prange_func(gamma(:)%old, iPar, iParRange)
     783             :               else
     784           0 :                 iParRange(1) = prange(iPar, 1)
     785           0 :                 iParRange(2) = prange(iPar, 2)
     786             :               end if
     787           0 :               gamma(iPar)%min = iParRange(1)
     788           0 :               gamma(iPar)%max = iParRange(2)
     789             :               !
     790           0 :               if (reflectionFlag_inin) then
     791             :                 ! generate gaussian distributed new parameter value which is reflected if out of bound
     792           0 :                 call xor4096g(seeds_in(3), RN3, save_state = save_state_3)
     793           0 :                 gamma(iPar)%new = parGen_dds_dp(gamma(iPar)%old, pertubationR, &
     794           0 :                         gamma(iPar)%min, gamma(iPar)%max, RN3)
     795             :               else
     796             :                 ! generate new parameter value uniform distributed in range (no reflection)
     797           0 :                 call xor4096(seeds_in(2), RN2, save_state = save_state_2)
     798           0 :                 gamma(iPar)%new = parGen_anneal_dp(gamma(iPar)%old, dR, &
     799           0 :                         gamma(iPar)%min, gamma(iPar)%max, RN2)
     800             :               end if
     801             :             end if
     802             :           end do
     803             :         end select
     804             : 
     805             :         ! (2) Calculate new objective function value
     806     6435000 :         fn = cost(gamma(:)%new, eval) * maxit_in
     807     1287000 :         coststatus = .true.
     808     1287000 :         if (present(undef_funcval)) then
     809     1287000 :           if (abs(fn * maxit_in - undef_funcval) .lt. tiny(1.0_dp)) then
     810             :             coststatus = .false.
     811             :           end if
     812             :         end if
     813             : 
     814        5148 :         feasible : if (coststatus) then   ! feasible parameter set
     815     1287000 :           fn = fn / normPhi
     816             : 
     817             :           ! Change in cost function value
     818     1287000 :           df = fn - fo
     819             : 
     820             :           ! analyze change in the objective function: df
     821     1287000 :           if (df < 0.0_DP) then
     822             : 
     823             :             ! accept the new state
     824       86921 :             Ipos = Ipos + 1_i4
     825       86921 :             fo = fn
     826      434605 :             gamma(:)%old = gamma(:)%new
     827             : 
     828             :             ! keep best solution
     829       86921 :             if (fo < fBest) then
     830         400 :               fBest = fo
     831         400 :               gamma(:)%best = gamma(:)%new
     832             :             end if
     833             :           else
     834     1200079 :             if (df >  eps_in) then
     835     1197051 :               rho = -df / T_in
     836     1197051 :               if (rho < small) then
     837             :                 pa = 0.0_DP
     838             :               else
     839     1197048 :                 pa = EXP(rho)
     840             :               end if
     841             :               !
     842     1197051 :               call xor4096(seeds_in(1), RN1, save_state = save_state_1)
     843             :               !
     844     1197051 :               if (pa > RN1) then
     845             :                 ! accept new state with certain probability
     846       85784 :                 Ineg = Ineg + 1_i4
     847       85784 :                 fo = fn
     848             :                 ! save old state
     849      428920 :                 gamma(:)%old = gamma(:)%new
     850             :               end if
     851             :             end if
     852             :           end if
     853     1287000 :           j = j + 1
     854             :         else
     855             :           ! function value was not valid
     856           0 :           iTotalCounterR = iTotalCounterR - 1_i4
     857           0 :           iTotalCounter = iTotalCounter - 1_i4
     858             :         end if feasible !valid parameter set
     859             :       end do loopLEN
     860             : 
     861             :       ! estimate acceptance ratio
     862        5148 :       ac_ratio = (real(Ipos, dp) + real(Ineg, dp)) / real(LEN_IN, dp)
     863        5148 :       if (modulo(iTotalCounter, LEN_IN * nST_in) / LEN_IN .gt. 0_i4) then
     864        4120 :         idummy = modulo(iTotalCounter, LEN_IN * nST_in) / LEN_IN
     865             :       else
     866             :         idummy = nST_in
     867             :       end if
     868       15444 :       iPos_iNeg_history(idummy, :) = (/ iPos, iNeg /)
     869             : 
     870             :       ! store current best in history vector
     871        5148 :       history_out(iTotalCounter / LEN_IN, 1) = real(iTotalCounter, dp)
     872        5148 :       history_out(iTotalCounter / LEN_IN, 2) = maxit_in * fBest * normPhi
     873             : 
     874        5148 :       file_write2 : if (present(tmp_file)) then
     875           0 :         open(unit = 999, file = trim(adjustl(tmp_file)), action = 'write', position = 'append', recl = (n + 2) * 30)
     876           0 :         write(999, *) iTotalCounter, maxit_in * fBest * normPhi, gamma(:)%best
     877           0 :         close(999)
     878             :       end if file_write2
     879             : 
     880        5148 :       if (printflag_in) then
     881           0 :         print '(I8, 2I5, E15.7, 3E15.7)', ITotalCounter, Ipos, Ineg, ac_ratio, T_in, &
     882           0 :                 fo * NormPhi * maxit_in, fBest * NormPhi * maxit_in
     883             :       end if
     884             : 
     885             :       ! Cooling schedule
     886        5148 :       if (fbb .gt. tiny(1.0_dp)) then
     887        5148 :         fInc = (fbb - fBest) / fbb
     888             :       else
     889             :         fInc = 1.0_dp
     890             :       end if
     891        5148 :       if (fInc < 0.00000001_dp) then
     892        5086 :         iConF = iConF + 1_i4
     893             :       else
     894             :         iConF = 0_i4
     895             :       end if
     896             : 
     897        5148 :       if ((ac_ratio < 0.15_dp) .and. (iConF > 5_i4) .and. (iConR <= -3_i4)) then     ! - iConR  no reheating
     898             :         ! Re-heating
     899           0 :         if (printflag_in) then
     900           0 :           print *, 'Re-heating: ', iConR
     901             :         end if
     902           0 :         iConR = iConR + 1_i4
     903           0 :         T_in = T0 / 2._DP
     904           0 :         iter = 0_i4       ! for LEN
     905           0 :         iTotalCounterR = 0_i4       ! for dR
     906             :         ! start from current best
     907           0 :         gamma(:)%old = gamma(:)%best
     908             :       else
     909             :         ! Update Temperature (geometrical decrement)
     910        5148 :         if (ac_ratio < 0.4_dp)  then
     911             :           DT_IN = 0.995_dp
     912             :         else
     913         202 :           DT_IN = DT0
     914             :         end if
     915        5148 :         T_in = T_in * DT_IN
     916             :       end if
     917             : 
     918             :       ! Stop Criteria: consecutive MC with marginal decrements and acceptance ratio
     919        5148 :       if (fInc < eps_in) then
     920        5086 :         iConL = iConL + 1_i4
     921             :       else
     922             :         iConL = 0_i4
     923             :       end if
     924       66924 :       if ((iConL > nST_in) .and. (ac_ratio < acc_in) .and. (iConR > 2_i4) .and. &
     925             :               (sum(iPos_iNeg_history(:, :)) .lt. 1_i4)) then
     926           0 :         iStop = .FALSE.
     927             :       end if
     928             :       ! a way out maximum
     929       66924 :       if ((iTotalCounter > nITERmax_in) .and. & !(ac_ratio > acc_in) .and. &
     930             :               (sum(iPos_iNeg_history(:, :)) .ge. 1_i4)) then
     931             : 
     932             :         ! store achieved history so far
     933         128 :         allocate(history_out_tmp(size(history_out, 1), size(history_out, 2)))
     934       55064 :         history_out_tmp = history_out
     935          32 :         deallocate(history_out)
     936             : 
     937          32 :         nITERmax_in = max(nITERmax_in + LEN_IN, int(real(nITERmax_in, dp) * 1.10_dp, i4))
     938          32 :         if (printflag_in) then
     939           0 :           print *, 'nITERmax changed to =', nITERmax_in
     940             :         end if
     941             : 
     942             :         ! increase lenght of history vecor
     943          32 :         idummy = nITERmax_in / LEN_IN + 1_i4
     944          96 :         allocate(history_out(idummy, 2))
     945             : 
     946       27500 :         do j = 1, size(history_out_tmp, 1)
     947       82436 :           history_out(j, :) = history_out_tmp(j, :)
     948             :         end do
     949             : 
     950          32 :         deallocate(history_out_tmp)
     951             : 
     952             :       end if
     953             :       ! STOP condition
     954        5152 :       if (iTotalCounter > nITERmax_in) then
     955           4 :         iStop = .FALSE.
     956             :       end if
     957             : 
     958             :     end do loopTest
     959             : 
     960             :     ! calculate cost function again (only for check and return values)
     961          20 :     parabest = gamma(:)%best
     962           4 :     costbest = cost(parabest, eval) * maxit_in
     963           4 :     if (present (funcbest)) then
     964           4 :       funcbest = costbest * maxit_in
     965             :     end if
     966           4 :     fo = costbest / NormPhi
     967             : 
     968           4 :     if (printflag_in) then
     969           0 :       print *, '   '
     970           0 :       print '(A15,E15.7)', ' end cost    = ', maxit_in * fBest * normPhi
     971           0 :       print *, 'end parameter: '
     972           0 :       do kk = 1, N
     973           0 :         print '(A10,I3,A3, E15.7)', '    para #', kk, ' = ', gamma(kk)%best
     974             :       end do
     975             : 
     976           0 :       print *, 'Final check:    ', (fo - fBest)
     977             :     end if
     978             : 
     979           4 :     if (present(history)) then
     980          16 :       allocate(history(size(history_out, 1) - 1, size(history_out, 2)))
     981       10300 :       history(:, :) = history_out(1 : size(history_out, 1) - 1, :)
     982             :     end if
     983             : 
     984           4 :     deallocate(truepara)
     985           4 :     deallocate(history_out)
     986             : 
     987           4 :     return
     988             : 
     989          20 :   END FUNCTION anneal_dp
     990             : 
     991           1 :   real(dp) function GetTemperature_dp(eval, cost, paraset, acc_goal, &    ! obligatory
     992           0 :           prange, prange_func, &    ! optional IN: exactly one of both
     993           0 :           samplesize, maskpara, seeds, printflag, &    ! optional IN
     994           0 :           weight, maxit, undef_funcval                             &    ! optional IN
     995             :           )
     996           4 :     use mo_kind, only : dp, i4, i8
     997             :     implicit none
     998             : 
     999             :     procedure(eval_interface), INTENT(IN), POINTER :: eval
    1000             :     procedure(objective_interface), intent(in), pointer :: cost
    1001             :     real(dp), dimension(:), intent(in) :: paraset                               !< a valid parameter set of the model
    1002             :     real(dp), intent(in) :: acc_goal                                            !< acceptance ratio to achieve
    1003             :     real(dp), optional, dimension(size(paraset, 1), 2), intent(in) :: prange    !< lower and upper limit per parameter
    1004             :     integer(i4), optional, intent(in) :: samplesize                             !< size of random set the acc_estimate is based on.
    1005             :                                                                                 !! DEFAULT: Max(250, 20*Number paras)
    1006             :     logical, optional, dimension(size(paraset, 1)), intent(in) :: maskpara      !< true if parameter will be optimized.
    1007             :                                                                                 !! false if parameter is discarded in optimization.
    1008             :                                                                                 !! DEFAULT: .true.
    1009             :     integer(i8), optional, dimension(2), intent(in) :: seeds                    !< Seeds of random numbers. DEFAULT: time dependent.
    1010             :     logical, optional, intent(in) :: printflag                                  !< .true. if detailed temperature estimation is
    1011             :                                                                                 !! printed. DEFAULT: .false.
    1012             :     real(dp), OPTIONAL, dimension(size(paraset, 1)), INTENT(IN) :: weight       !< vector of weights per parameter gives the
    1013             :                                                                                 !! frequency of parameter to be chosen for
    1014             :                                                                                 !! optimization. DEFAULT: equal weighting
    1015             :     logical, OPTIONAL, INTENT(IN) :: maxit                                      !< Maxim. or minim. of function
    1016             :                                                                                 !! maximization = .true., minimization = .false. .
    1017             :                                                                                 !! DEFAULT: .false.
    1018             :     real(dp), OPTIONAL, INTENT(IN) :: undef_funcval                             !< objective function value occuring if
    1019             :                                                                                 !! parameter set leads to invalid model results,
    1020             :                                                                                 !! e.g. -999.0_dp. DEFAULT: not present
    1021             : 
    1022             :     INTERFACE
    1023             :       SUBROUTINE prange_func(paraset, iPar, rangePar)
    1024             :         ! gives the range (min,max) of the parameter iPar at a certain parameter set paraset
    1025             :         use mo_kind
    1026             :         real(dp), dimension(:), INTENT(IN) :: paraset
    1027             :         integer(i4), INTENT(IN) :: iPar
    1028             :         real(dp), dimension(2), INTENT(OUT) :: rangePar
    1029             :       END SUBROUTINE prange_func
    1030             :     END INTERFACE
    1031             :     OPTIONAL :: prange_func
    1032             :     !
    1033             :     ! Local variables
    1034             : 
    1035             :     integer(i4) :: n
    1036             :     integer(i4) :: samplesize_in
    1037             :     integer(i4) :: idummy, i, j
    1038             :     integer(i4) :: iPar
    1039           3 :     real(dp), dimension(2) :: iParRange
    1040           1 :     real(dp) :: NormPhi
    1041           1 :     real(dp) :: fo, fn, dr
    1042           1 :     real(dp) :: T
    1043             : 
    1044             :     logical :: coststatus     ! true if model output is valid
    1045           0 :     logical, dimension(size(paraset, 1)) :: maskpara_in    ! true if parameter will be optimized
    1046           1 :     integer(i4), dimension(:), ALLOCATABLE :: truepara       ! indexes of parameters to be optimized
    1047             :     logical :: printflag_in   ! if detailed estimation of temperature is printed
    1048           5 :     real(dp), dimension(size(paraset, 1)) :: weight_in      ! CDF of parameter to chose for optimization
    1049           1 :     real(dp) :: maxit_in       ! Maximization or minimization of function:
    1050             :     !                                                           ! -1 = maxim, 1 = minim
    1051             : 
    1052             :     type paramLim
    1053             :       real(dp) :: min                 ! minimum value
    1054             :       real(dp) :: max                 ! maximum value
    1055             :       real(dp) :: new                 ! new state value
    1056             :       real(dp) :: old                 ! old state value
    1057             :       real(dp) :: best                ! best value found
    1058             :       real(dp) :: dMult               ! sensitivity multiplier
    1059             :       !                                                               ! for parameter search
    1060             :     end type paramLim
    1061           1 :     type (paramLim), dimension (size(paraset, 1)), target :: gamma    ! Parameter
    1062             : 
    1063             :     ! for random numbers
    1064             :     INTEGER(I8) :: seeds_in(2)
    1065           1 :     real(dp) :: RN1, RN2     ! Random numbers
    1066             :     integer(I8), dimension(n_save_state) :: save_state_1      ! optional arguments for restarting RN stream 1
    1067             :     integer(I8), dimension(n_save_state) :: save_state_2      ! optional arguments for restarting RN stream 2
    1068             : 
    1069             :     ! for initial temperature estimate
    1070           1 :     real(dp) :: acc_estim  ! estimate of acceptance probability
    1071             :     ! depends on temperature
    1072             :     ! goal: find a temperature such that acc_estim ~ 0.9
    1073           1 :     real(dp), dimension(:, :), allocatable :: Energy     ! dim(LEN,2) cost function values before (:,1) and
    1074             :     !                                                   ! after (:,2) transition
    1075             : 
    1076           1 :     n = size(paraset, 1)
    1077             : 
    1078             :     ! either range or rangfunc has to be given
    1079           1 :     if (present(prange) .eqv. present(prange_func)) then
    1080           0 :       stop 'anneal: Either range or prange_func has to be given'
    1081             :     end if
    1082             : 
    1083           1 :     if (present(samplesize)) then
    1084           1 :       if (samplesize .lt. Max(20_i4 * n, 250_i4)) then
    1085             :         !stop 'Input argument LEN must be greater than Max(250,20*N), N=number of parameters'
    1086           0 :         print*, 'WARNING (GetTemperature): '
    1087           0 :         print*, 'Input argument samplesize should be greater than Max(250,20*N), N=number of parameters'
    1088           0 :         samplesize_in = samplesize
    1089             :       else
    1090             :         samplesize_in = samplesize
    1091             :       end if
    1092             :     else
    1093           0 :       samplesize_in = Max(20_i4 * n, 250_i4)
    1094             :     end if
    1095             : 
    1096           1 :     if (present(maskpara)) then
    1097           0 :       if (count(maskpara) .eq. 0_i4) then
    1098           0 :         stop 'Input argument maskpara: At least one element has to be true'
    1099             :       else
    1100           0 :         maskpara_in = maskpara
    1101             :       end if
    1102             :     else
    1103           5 :       maskpara_in = .true.
    1104             :     end if
    1105             : 
    1106           1 :     if (present(seeds)) then
    1107           1 :       seeds_in = seeds
    1108             :     else
    1109             :       ! Seeds depend on actual time
    1110           0 :       call get_timeseed(seeds_in)
    1111           0 :       print*, 'temp: seeds(1)=', seeds_in(1)
    1112             :     end if
    1113             : 
    1114           1 :     if (present(printflag)) then
    1115           1 :       printflag_in = printflag
    1116             :     else
    1117             :       printflag_in = .false.
    1118             :     end if
    1119             : 
    1120           1 :     if (present(maxit)) then
    1121           0 :       if (maxit) then
    1122             :         maxit_in = -1._dp
    1123             :       else
    1124           0 :         maxit_in = 1._dp
    1125             :       end if
    1126             :     else
    1127             :       maxit_in = 1._dp
    1128             :     end if
    1129             : 
    1130           3 :     allocate(Energy(samplesize_in, 2))
    1131             : 
    1132           7 :     allocate (truepara(count(maskpara_in)))
    1133           1 :     idummy = 0_i4
    1134           5 :     do i = 1, N
    1135           5 :       if (maskpara_in(i)) then
    1136           4 :         idummy = idummy + 1_i4
    1137           4 :         truepara(idummy) = i
    1138             :       end if
    1139             :     end do
    1140             : 
    1141           5 :     weight_in = 0.0_dp
    1142           1 :     if (present(weight)) then
    1143           0 :       where (maskpara_in(:))
    1144           0 :         weight_in(:) = weight(:)
    1145             :       end where
    1146             :     else
    1147           5 :       where (maskpara_in(:))
    1148             :         weight_in(:) = 1.0_dp
    1149             :       end where
    1150             :     end if
    1151             :     ! scaling the weights
    1152           9 :     weight_in = weight_in / sum(weight_in)
    1153             :     ! cummulating the weights
    1154           4 :     do i = 2, n
    1155           4 :       weight_in(i) = weight_in(i) + weight_in(i - 1)
    1156             :     end do
    1157             : 
    1158             :     ! Setting up the RNG
    1159             :     ! (1) Seeds depend on actual time or on input seeds
    1160             :     ! (2) Initialize the streams
    1161           1 :     call xor4096(seeds_in(1), RN1, save_state = save_state_1)
    1162           1 :     call xor4096(seeds_in(2), RN2, save_state = save_state_2)
    1163           1 :     seeds_in = 0_i8
    1164             :     ! (3) Now ready for calling
    1165             : 
    1166           5 :     gamma(:)%dmult = 1.0_dp
    1167           5 :     gamma(:)%new = paraset(:)
    1168           5 :     gamma(:)%old = paraset(:)
    1169           5 :     gamma(:)%best = paraset(:)
    1170           1 :     NormPhi = -9999.9_dp
    1171             : 
    1172           1 :     fo = cost(paraset, eval) * maxit_in
    1173           1 :     if (abs(fo) .lt. tiny(0.0_dp)) fo = 0.0000001_dp * maxit_in
    1174           1 :     NormPhi = fo
    1175           1 :     fo = fo / NormPhi * maxit_in
    1176             : 
    1177           1 :     j = 1
    1178         501 :     loopSamplesize : do while (j .le. samplesize_in)
    1179             :       ! Generate a random subsequent state and evaluate its objective function
    1180             :       ! (1)  Generate new parameter set
    1181         500 :       dR = 1.0_DP
    1182             : 
    1183             :       ! (1a) Select parameter to be changed
    1184         500 :       call xor4096(seeds_in(1), RN1, save_state = save_state_1)
    1185         500 :       iPar = 1_i4
    1186        1250 :       do while (weight_in(iPar) .lt. RN1)
    1187         750 :         iPar = iPar + 1_i4
    1188             :       end do
    1189             : 
    1190             :       ! (1b) Generate new value of selected parameter
    1191         500 :       if (present(prange_func)) then
    1192        2500 :         call prange_func(gamma(:)%old, iPar, iParRange)
    1193             :       else
    1194           0 :         iParRange(1) = prange(iPar, 1)
    1195           0 :         iParRange(2) = prange(iPar, 2)
    1196             :       end if
    1197         500 :       gamma(iPar)%min = iParRange(1)
    1198         500 :       gamma(iPar)%max = iParRange(2)
    1199         500 :       call xor4096(seeds_in(2), RN2, save_state = save_state_2)
    1200         500 :       gamma(iPar)%new = parGen_anneal_dp(gamma(iPar)%old, gamma(iPar)%dMult * dR, &
    1201        1000 :               gamma(iPar)%min, gamma(iPar)%max, RN2)
    1202             :       !
    1203             :       ! (2)  Calculate new objective function value and normalize it
    1204        2500 :       fn = cost(gamma(:)%new, eval) * maxit_in
    1205         500 :       coststatus = .true.
    1206         500 :       if (present(undef_funcval)) then
    1207           0 :         if (abs(fn * maxit_in - undef_funcval) .lt. tiny(1.0_dp)) then
    1208             :           coststatus = .false.
    1209             :         end if
    1210             :       end if
    1211             : 
    1212           1 :       feasible : if (coststatus) then   ! feasible parameter set
    1213         500 :         fn = fn / normPhi
    1214             :         ! Save Energy states of feasible transitions
    1215             :         ! for adaption of optimal initial temperature
    1216             :         ! Walid Ben-Ameur: "Comput. the Initial Temperature of Sim. Annealing"
    1217             :         ! Comput. Opt. and App. 2004
    1218         500 :         Energy(j, 2) = fn     ! E_max_t
    1219         500 :         Energy(j, 1) = fo     ! E_min_t
    1220         500 :         j = j + 1
    1221             :       end if feasible !valid parameter set
    1222             :     end do loopSamplesize
    1223             : 
    1224             :     ! estimation of the acceptance probability based on the random set ||<Samplesize>||
    1225             :     ! only if actual temperature (T) equals initial temperature (temp)
    1226        1003 :     T = maxval(Energy)
    1227             : 
    1228        1001 :     acc_estim = sum(exp(-(Energy(:, 2) / T))) / sum(exp(-(Energy(:, 1) / T)))
    1229           1 :     if (printflag_in) then
    1230           1 :       print*, "acc_estimate = ", acc_estim, "    ( T = ", T, " )"
    1231             :     end if
    1232          12 :     Do While ((acc_estim .lt. 1.0_dp) .and. (abs(acc_estim - acc_goal) .gt. 0.0001_dp))
    1233          11 :       T = T * (Log(acc_estim) / Log(acc_goal))**(0.5_dp) ! **(1.0/p)  with p=1.0
    1234       11023 :       if (all(T .gt. Energy(:, 1) / 709._dp) .and. all(T .gt. Energy(:, 2) / 709._dp)) then
    1235       11011 :         acc_estim = sum(exp(-(Energy(:, 2) / T))) / sum(exp(-(Energy(:, 1) / T)))
    1236          11 :         if (printflag_in) then
    1237          11 :           print*, "acc_estimate = ", acc_estim, "    ( T = ", T, " )"
    1238             :         end if
    1239             :       else
    1240           0 :         T = T / (Log(acc_estim) / Log(acc_goal))**(0.5_dp)
    1241           0 :         exit
    1242             :       end if
    1243             :     end do
    1244           1 :     GetTemperature_dp = T
    1245             : 
    1246           3 :   end function GetTemperature_dp
    1247             : 
    1248             :   !***************************************************
    1249             :   !*               PRIVATE FUNCTIONS                 *
    1250             :   !***************************************************
    1251             : 
    1252     1287500 :   real(dp) function parGen_anneal_dp(old, dMax, oMin, oMax, RN)
    1253           1 :     use mo_kind, only : dp, i4
    1254             :     implicit none
    1255             : 
    1256             :     real(dp), intent(IN) :: old, dMax, oMin, oMax
    1257             :     real(dp), intent(IN) :: RN
    1258             : 
    1259             :     ! local variables
    1260     1287500 :     real(dp) :: oi, ox, delta, dMaxScal
    1261             :     integer(i4) :: iDigit, iszero             ! were intent IN before
    1262             :     !
    1263     1287500 :     iszero = 1_i4
    1264     1287500 :     iDigit = 8_i4
    1265             : 
    1266     1287500 :     oi = oMin
    1267     1287500 :     ox = oMax
    1268             :     ! scaling (new)
    1269     1287500 :     dMaxScal = dMax * ABS(oMax - oMin)
    1270             : 
    1271     1287500 :     if(ge(oi, ox)) then
    1272           0 :       parGen_anneal_dp = (oi + ox) / 2.0_DP
    1273             :     else
    1274     1287500 :       if ((old - dMaxScal) > oi)  oi = old - dMaxScal
    1275     1287500 :       if ((old + dMaxScal) < ox)  ox = old + dMaxScal
    1276     1287500 :       delta = (oi + RN * (ox - oi)) - old
    1277     1287500 :       if (delta > dMaxScal) delta = dMaxScal
    1278     1287500 :       if (delta <-dMaxScal) delta = -dMaxScal
    1279     1287500 :       parGen_anneal_dp = old + dChange_dp(delta, iDigit, isZero)
    1280             :     end if
    1281             : 
    1282             :     ! Parameter is bounded between Max and Min.
    1283             :     ! Correction from Kumar and Luis
    1284             : 
    1285     1287500 :     if (parGen_anneal_dp < oMin) then
    1286             :       parGen_anneal_dp = oMin
    1287     1287480 :     elseif(parGen_anneal_dp > oMax)then
    1288         116 :       parGen_anneal_dp = oMax
    1289             :     end if
    1290     1287500 :   end function parGen_anneal_dp
    1291             : 
    1292           0 :   real(dp) function parGen_dds_dp(old, perturb, oMin, oMax, RN)
    1293             :     ! PURPOSE:
    1294             :     !    Perturb variable using a standard normal random number RN
    1295             :     !    and reflecting at variable bounds if necessary
    1296             :     !    Tolson et al. (2007): STEP 4
    1297             :     !
    1298             :     implicit none
    1299             : 
    1300             :     real(dp), intent(IN) :: old, perturb, oMin, oMax
    1301             :     real(dp), intent(IN) :: RN
    1302             : 
    1303             :     ! generating new value
    1304           0 :     parGen_dds_dp = old + perturb * (oMax - oMin) * RN
    1305             : 
    1306             :     ! reflect one time and set to boundary value
    1307           0 :     if (parGen_dds_dp .lt. oMin) then
    1308           0 :       parGen_dds_dp = oMin + (oMin - parGen_dds_dp)
    1309           0 :       if (parGen_dds_dp .gt. oMax) parGen_dds_dp = oMin
    1310             :     end if
    1311             : 
    1312           0 :     if (parGen_dds_dp .gt. oMax) then
    1313           0 :       parGen_dds_dp = oMax - (parGen_dds_dp - oMax)
    1314           0 :       if (parGen_dds_dp .lt. oMin) parGen_dds_dp = oMax
    1315             :     end if
    1316             : 
    1317     1287500 :   end function parGen_dds_dp
    1318             : 
    1319     1287500 :   real(dp) function  dChange_dp(delta, iDigit, isZero)
    1320           0 :     use mo_kind, only : i4, i8, dp
    1321             :     implicit none
    1322             : 
    1323             :     integer(i4), intent(IN) :: iDigit, isZero
    1324             :     real(dp), intent(IN) :: delta
    1325             : 
    1326             :     ! local variables
    1327             :     integer(i4) :: ioszt
    1328             :     integer(I8) :: iDelta
    1329             : 
    1330     1287500 :     ioszt = 10**iDigit
    1331     1287500 :     iDelta = int(delta * real(ioszt, dp), i8)
    1332     1287500 :     if(isZero == 1_i4) then
    1333     1287500 :       if(iDelta == 0_i8) then
    1334           0 :         if(delta < 0_i4) then
    1335             :           iDelta = -1_i8
    1336             :         else
    1337           0 :           iDelta = 1_i8
    1338             :         end if
    1339             :       end if
    1340             :     end if
    1341     1287500 :     dChange_dp = real(iDelta, dp) / real(ioszt, dp)
    1342     1287500 :   end function  dChange_dp
    1343             : 
    1344           0 :   subroutine generate_neighborhood_weight_dp(truepara, cum_weight, save_state_xor, iTotalCounter, &
    1345           0 :           nITERmax, neighborhood)
    1346             :     ! PURPOSE:
    1347             :     !    generates a new neighborhood
    1348             :     !    Tolson et al. (2007): STEP 3
    1349             :     !
    1350             :     integer(i4), dimension(:), intent(in) :: truepara
    1351             :     real(dp), dimension(:), intent(in) :: cum_weight
    1352             :     integer(i8), dimension(n_save_state), intent(inout) :: save_state_xor
    1353             :     integer(i4), intent(in) :: iTotalCounter
    1354             :     integer(i4), intent(in) :: nITERmax
    1355             :     logical, dimension(size(cum_weight)), intent(out) :: neighborhood
    1356             : 
    1357             :     ! local variables
    1358             :     integer(i4) :: ipar, npar
    1359             :     integer(i4) :: iSize, size_neighbor
    1360           0 :     real(dp) :: prob
    1361           0 :     real(dp) :: rn
    1362           0 :     real(dp) :: weight, norm
    1363           0 :     real(dp), dimension(size(cum_weight)) :: local_cum_weight
    1364             : 
    1365           0 :     prob = 1.0_dp - Log(real(iTotalCounter, dp)) / Log(real(nITERmax, dp))
    1366           0 :     neighborhood = .false.
    1367             : 
    1368           0 :     npar = size(cum_weight)
    1369           0 :     local_cum_weight = cum_weight
    1370             : 
    1371             :     ! How many parameters will be selected for neighborhood?
    1372           0 :     size_neighbor = 0_i4
    1373           0 :     do ipar = 1, size(truepara)
    1374           0 :       call xor4096(0_i8, rn, save_state = save_state_xor)
    1375           0 :       if (rn < prob) then
    1376           0 :         size_neighbor = size_neighbor + 1_i4
    1377             :       end if
    1378             :     end do
    1379             :     ! at least one...
    1380           0 :     size_neighbor = max(1_i4, size_neighbor)
    1381             : 
    1382             :     ! Which parameter will be used for neighborhood?
    1383           0 :     do iSize = 1, size_neighbor
    1384             :       ! (1) generate RN
    1385           0 :       call xor4096(0_i8, rn, save_state = save_state_xor)
    1386             :       !
    1387             :       ! (2) find location <iPar> in cummulative distribution function
    1388           0 :       iPar = 1_i4
    1389           0 :       do while (local_cum_weight(iPar) .lt. rn)
    1390           0 :         iPar = iPar + 1_i4
    1391             :       end do
    1392             :       !
    1393             :       ! (3) add parameter to neighborhood
    1394           0 :       neighborhood(iPar) = .true.
    1395             :       !
    1396             :       ! (4) recalculate cummulative distribution function
    1397             :       ! (4.1) Which weight had iPar?
    1398           0 :       if (iPar .gt. 1_i4) then
    1399           0 :         weight = local_cum_weight(iPar) - local_cum_weight(iPar - 1)
    1400             :       else
    1401           0 :         weight = local_cum_weight(1)
    1402             :       end if
    1403             :       ! (4.2) Substract this weight from cummulative array starting at iPar
    1404           0 :       local_cum_weight(iPar : nPar) = local_cum_weight(iPar : nPar) - weight
    1405             :       ! (4.3) Renormalize cummulative weight to one
    1406           0 :       if (count(neighborhood) .lt. size(truepara)) then
    1407           0 :         norm = 1.0_dp / local_cum_weight(nPar)
    1408             :       else
    1409             :         norm = 1.0_dp
    1410             :       end if
    1411           0 :       local_cum_weight(:) = local_cum_weight(:) * norm
    1412             :       !
    1413             :     end do
    1414             : 
    1415     1287500 :   end subroutine generate_neighborhood_weight_dp
    1416             : 
    1417             : END MODULE mo_anneal

Generated by: LCOV version 1.16