#include "Includes.h" module WallDistance #if defined(NAVIERSTOKES) use SMConstants use NodalStorageClass use Utilities, only: almostEqual private public ComputeWallDistance ! ! ************ ! Return codes ! ************ ! integer, parameter :: SUCCESS_RC = 0 integer, parameter :: EXCEED_ITERS_RC = -1 ! ! *********************** ! Quasi-Newton parameters ! *********************** ! integer, parameter :: N_MAX_ITER_NEWTON = 50 real(kind=RP), parameter :: MIN_GRAD_TOL = 1.0e-11_RP real(kind=RP), parameter :: ALPHA_LIMITER = 1.0_RP ! ! ***************************************** ! Global quantities to store previous steps ! ***************************************** ! real(kind=RP) :: x0(2) real(kind=RP) :: phi0 real(kind=RP) :: dphi0(2) real(kind=RP) :: B0(2,2) ! ! ************************* ! Line search HZ parameters ! ************************* ! integer, parameter :: MAX_ITERS_LS_HZ = 100 integer, parameter :: MAX_ITERS_LS_HZ_bracket = 50 integer, parameter :: MAX_ITERS_LS_HZ_update = 100 real(kind=RP), parameter :: GAMMA_LS_HZ = 0.66_RP real(kind=RP), parameter :: PSI0_LS_HZ = 0.01_RP real(kind=RP), parameter :: PSI1_LS_HZ = 0.1_RP real(kind=RP), parameter :: PSI2_LS_HZ = 2._RP real(kind=RP), parameter :: TOLA_LS_HZ_init = 1e-5_RP real(kind=RP), parameter :: RHO_LS_HZ = 5._RP real(kind=RP), parameter :: EPSILON_LS_HZ = 1e-6_RP real(kind=RP), parameter :: THETA_LS_HZ = 0.5_RP real(kind=RP), parameter :: ONEMTHETA_LS_HZ = 1._RP-THETA_LS_HZ real(kind=RP), parameter :: DELTA_LS_HZ = 0.1_RP real(kind=RP), parameter :: DELTAAP_LS_HZ = 2._RP*DELTA_LS_HZ -1._RP real(kind=RP), parameter :: SIGMA_LS_HZ = 0.9_RP ! ! **************************** ! Hessian Estimation functions ! **************************** ! abstract interface subroutine HE_F(x, dphi, B) use SMConstants implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(in) :: dphi(2) real(kind=RP), intent(out) :: B(2,2) end subroutine HE_F end interface contains subroutine ComputeWallDistance(xP, Nf, xf, spAf, xSeed, d, xi_Wall, RC) ! ! ***************************************************************** ! ! This method performs an optimization of the distance to ! the wall given by its nodal coordinates "xf". ! The algorithm consists in a Quasi-Newton optimization core ! with a gradient projection method to consider the domain ! end points. ! ! ***************************************************************** ! implicit none real(kind=RP), intent(in) :: xP(NDIM) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) real(kind=RP), intent(in) :: xSeed(2) real(kind=RP), intent(out) :: d real(kind=RP), intent(out) :: xi_Wall(2) integer, intent(out) :: RC ! ! --------------- ! Local variables ! --------------- ! integer :: iter real(kind=RP) :: x(2), phi, dphi(2), dir_corr(2) ! ! Initialize fitness function and gradient ! ---------------------------------------- x0 = xSeed call FitnessFunctionAndGradient(x0, xP, Nf, xf, spAf, phi0, dphi0) ! ! Initialize the inverse hessian matrix ! ------------------------------------- B0 = 0.0_RP B0(1,1) = 1.0_RP B0(2,2) = 1.0_RP x = x0 do iter = 0, N_MAX_ITER_NEWTON ! ! Get new fitness function values ! ------------------------------- call FitnessFunctionAndGradient(x, xP, Nf, xf, spAf, phi, dphi) ! ! Check the exit criteria ! ----------------------- if ( exitCriteria(x, dphi) ) then xi_Wall = x d = sqrt(phi) RC = SUCCESS_RC return end if call OptimizationStep(xP, Nf, xf, spAf, phi, dphi, x) end do xi_Wall = x d = sqrt(phi) RC = EXCEED_ITERS_RC end subroutine ComputeWallDistance logical function exitCriteria(x, dphi) ! ! **************************************** ! The only exit criteria considered is the ! size of the projected gradient ! **************************************** ! implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(in) :: dphi(2) ! ! --------------- ! Local variables ! --------------- ! real(kind=RP) :: dir(2) dir = -dphi call CorrectDirection(x,dir) if ( norm2(dir) .le. MIN_GRAD_TOL ) then exitCriteria = .true. else exitCriteria = .false. end if end function exitCriteria subroutine OptimizationStep(xP, Nf, xf, spAf, phi, dphi, x) ! ! ************************************************************* ! ! An optimization step consists in: ! 1/ Estimate the Hessian inverse ! 2/ Compute the Newton step direction ! 3/ Correct the direction according to the domain limits ! 4/ Compute the maximum Quasi-Newton alpha coefficient ! 5/ Perform a Line Search along the direction ! ! ************************************************************* ! implicit none real(kind=RP), intent(in) :: xP(NDIM) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) real(kind=RP), intent(in) :: phi real(kind=RP), intent(in) :: dphi(2) real(kind=RP), intent(inout) :: x(2) ! ! --------------- ! Local variables ! --------------- ! real(kind=RP) :: B(2,2), dir(2), alphaMax, alpha integer :: RC real(kind=RP) :: y(2), f_change real(kind=RP) :: phiOut, dPhiOut(2) ! ! Update Hessian matrix inverse ! ----------------------------- call HessianEstimation_DFP(x,dphi,B) ! ! Update values for the next iteration ! ------------------------------------ x0 = x phi0 = phi dphi0 = dphi B0 = B ! ! Compute the Newton method direction ! ----------------------------------- dir = -matmul(B,dphi) ! ! Correct the direction to account for domain limits ! -------------------------------------------------- call correctDirection(x, dir) ! ! Get an estimation of the maximum alpha coefficient ! -------------------------------------------------- call getAlphaMax(x, dir, alphaMax) ! ! Perform a line search along the direction ! ----------------------------------------- phiOut = phi dphiOut = dphi alpha = alphaMax RC = LS_HZ(xP, Nf, xf, spAf, alpha, x, dir, phiOut, dphiOut, y, f_change) ! ! Get a new alpha estimation with the LineSearch resulting direction ! ------------------------------------------------------------------ call getAlphaMax(x0, dir, alphaMax) ! ! Perform a limiting if alpha has been exceeded ! --------------------------------------------- if ( alpha .gt. alphaMax ) then x = x0 + alphaMax * dir end if end subroutine OptimizationStep subroutine HessianEstimation_DFP_DFP(x, dphi, B) ! ! ****************************************************** ! This (inverse) Hessian estimation is performed ! using the Davidon-Fletcher-Powell formula ! ****************************************************** ! implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(in) :: dphi(2) real(kind=RP), intent(out) :: B(2,2) ! ! --------------- ! Local variables ! --------------- ! integer :: i, j real(kind=RP) :: u(2), g(2) real(kind=RP) :: uu(2,2), gg(2,2) u = x-x0 g = dphi - dphi0 do j = 1, 2 ; do i = 1, 2 uu(i,j) = u(i) * u(j) gg(i,j) = g(i) * g(j) end do ; end do if ( almostEqual(dot_product(u,g), 0.0_RP) ) then B = B0 else B = B0 + uu / dot_product(u,g) - matmul(B0,matmul(gg,B0))/ (dot_product(g,matmul(B0,g))) end if end subroutine HessianEstimation_DFP_DFP subroutine correctDirection(x, dir) ! ! **************************************************************** ! ! Correct the search direction so that it remains ! parallel to the domain bounds if the point lays in those ! ! **************************************************************** ! implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(inout) :: dir(2) if ( almostEqual(x(1), -1.0_RP) .and. (dir(1) .lt. 0.0_RP) ) then dir(1) = 0.0_RP elseif ( almostEqual(x(1), 1.0_RP) .and. (dir(1) .gt. 0.0_RP) ) then dir(1) = 0.0_RP end if if ( almostEqual(x(2), -1.0_RP) .and. (dir(2) .lt. 0.0_RP) ) then dir(2) = 0.0_RP elseif ( almostEqual(x(2), 1.0_RP) .and. (dir(2) .gt. 0.0_RP) ) then dir(2) = 0.0_RP end if end subroutine correctDirection subroutine getAlphaMax(x, dir, alphaMax) ! ! ******************************************************* ! ! The maximum alpha is estimated by computing the ! minimum distance to the domain limits following ! the search direction ! ! ******************************************************* ! implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(in) :: dir(2) real(kind=RP), intent(out) :: alphaMax ! ! --------------- ! Local variables ! --------------- ! real(kind=RP) :: alpha(4) if ( (.not. almostEqual(dir(1), 0.0_RP)) .and. (.not. almostEqual(x(1), -1.0_RP)) ) then alpha(1) = (-1.0_RP - x(1)) / dir(1) else alpha(1) = -1.0_RP end if if ( (.not. almostEqual(dir(1), 0.0_RP)) .and. (.not. almostEqual(x(1), 1.0_RP)) ) then alpha(2) = (1.0_RP - x(1)) / dir(1) else alpha(2) = -1.0_RP end if if ( (.not. almostEqual(dir(2), 0.0_RP)) .and. (.not. almostEqual(x(2), -1.0_RP)) ) then alpha(3) = (-1.0_RP - x(2)) / dir(2) else alpha(3) = -1.0_RP end if if ( (.not. almostEqual(dir(2), 0.0_RP)) .and. (.not. almostEqual(x(2), 1.0_RP)) ) then alpha(4) = (1.0_RP - x(2)) / dir(2) else alpha(4) = -1.0_RP end if alphaMax = minval(alpha,(alpha .gt. 0.0_RP)) alphaMax = min(abs(alphaMax), ALPHA_LIMITER) end subroutine getAlphaMax subroutine FitnessFunction(x, xP, Nf, xf, spAf, phi) ! ! ************************************************************ ! This subroutine evaluates the fitness function in ! the point x. The fitness function is the squared ! distance to the wall ! ************************************************************ ! implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(in) :: xP(NDIM) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) real(kind=RP), intent(out) :: phi ! ! --------------- ! Local variables ! --------------- ! integer :: i, j real(kind=RP) :: xWall(NDIM) real(kind=RP) :: lxi(0:Nf(1)), leta(0:Nf(2)) ! ! Get the interpolating polynomials ! --------------------------------- lxi = spAf(1) % lj(x(1)) leta = spAf(2) % lj(x(2)) ! ! Get the surface point ! --------------------- xWall = 0.0_RP do j = 0, Nf(2) ; do i = 0, Nf(1) xWall = xWall + xf(:,i,j) * lxi(i) * leta(j) end do ; end do ! ! Compute the fitness function ! ---------------------------- phi = sum( POW2(xWall-xP) ) end subroutine FitnessFunction subroutine FitnessFunctionAndGradient(x, xP, Nf, xf, spAf, phi, dphi) ! ! ************************************************************ ! This subroutine evaluates the fitness function and ! its gradient in the point x. ! ************************************************************ ! implicit none real(kind=RP), intent(in) :: x(2) real(kind=RP), intent(in) :: xP(NDIM) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) real(kind=RP), intent(out) :: phi real(kind=RP), intent(out) :: dphi(2) ! ! --------------- ! Local variables ! --------------- ! integer :: i, j real(kind=RP) :: xWall(NDIM), xWall_xi(NDIM), xWall_eta(NDIM) real(kind=RP) :: lxi(0:Nf(1)) , leta(0:Nf(2)) real(kind=RP) :: dlxi(0:Nf(1)) , dleta(0:Nf(2)) ! ! Get the interpolating polynomials and derivatives ! ------------------------------------------------- lxi = spAf(1) % lj(x(1)) leta = spAf(2) % lj(x(2)) dlxi = spAf(1) % dlj(x(1)) dleta = spAf(2) % dlj(x(2)) ! ! Get the surface point and derivatives ! ------------------------------------- xWall = 0.0_RP xWall_xi = 0.0_RP xWall_eta = 0.0_RP do j = 0, Nf(2) ; do i = 0, Nf(1) xWall = xWall + xf(:,i,j) * lxi(i) * leta(j) xWall_xi = xWall_xi + xf(:,i,j) * dlxi(i) * leta(j) xWall_eta = xWall_eta + xf(:,i,j) * lxi(i) * dleta(j) end do ; end do ! ! Compute the fitness function ! ---------------------------- phi = sum( POW2(xWall-xP) ) ! ! Compute the fitness function gradient ! ------------------------------------- dphi(1) = 2.0_RP * sum((xWall - xP)* xWall_xi ) dphi(2) = 2.0_RP * sum((xWall - xP)* xWall_eta) end subroutine FitnessFunctionAndGradient ! !/////////////////////////////////////////////////////////////////////////////// ! ! Line Search algorithms ! ---------------------- ! !/////////////////////////////////////////////////////////////////////////////// ! function LS_HZ(xP, Nf, xf, spAf, alpha, x, dir, fval, g, y, f_change) result(exit_flag) ! ! *************************************************************************** ! This is an implementation of the Hager-Zhang line search algorithm ! *************************************************************************** implicit none real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) real(kind=RP), intent(inout) :: alpha real(kind=RP), dimension(2), intent(inout) :: x real(kind=RP), dimension(2), intent(inout) :: dir real(kind=RP), intent(inout) :: fval real(kind=RP), dimension(2), intent(inout) :: g real(kind=RP), dimension(2), intent(out) :: y real(kind=RP), intent(out) :: f_change integer :: exit_flag ! ! --------------- ! Local variables ! --------------- ! real (kind=RP) :: a, b, w, wohi, wolo, awohi, fpert integer :: i real (kind=RP) :: phi, dphi real (kind=RP) :: phia, dphia real (kind=RP), dimension (2) :: g1, ga real (kind=RP), dimension (2) :: d1, da real (kind=RP), dimension (2) :: x1, xa ! ! Compute Wolfe condition bounds ! ------------------------------ awohi = dot_product(g, dir) wohi = DELTA_LS_HZ * awohi ! Wolfe upper bound wolo = SIGMA_LS_HZ * awohi ! Wolfe lower bound awohi = DELTAAP_LS_HZ * awohi ! Approximated Wolfe upper bound fpert = fval + EPSILON_LS_HZ ! Perturbation for the AP Wolfe: phi(alpha) = phi(0) + eps_k if (LS_HZ_init(alpha, x, dir, fval, g, wohi, wolo, awohi, fpert, y, f_change, xP, Nf, xf, spAf)) then exit_flag = 0 return elseif (LS_HZ_bracket(a, b, alpha, x, dir, fval, g, y , f_change , fpert, wohi, wolo, awohi, exit_flag, xP, Nf, xf, spAf).or. & (exit_flag.ne.0)) then return end if w=b-a da = a*dir xa = x+ da call FitnessFunctionAndGradient(xa, xP, Nf, xf, spAf, phia, ga) dphia = dot_product(ga, dir) d1 = b*dir x1 = x+ d1 call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1, dir) do i=1, MAX_ITERS_LS_HZ if ( LS_HZ_secant2(a,b,alpha, x, dir, g, y, fval, f_change, phi, dphi, phia, dphia, g1, d1, x1, & ga, da, xa, wohi, wolo, awohi,fpert, exit_flag, xP, Nf, xf, spAf).or.& (exit_flag.ne.0)) return if((b-a)>GAMMA_LS_HZ*w) then alpha=(a+b)*0.5_RP d1 = alpha* dir x1 = x+ d1 call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1, dir) if (LS_HZ_check(alpha, fval, phi, dphi, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then exit_flag = 0 f_change = phi-fval fval = phi y = g1-g g = g1 dir = d1 x = x + alpha * dir return end if exit_flag= LS_HZ_update(a,b,alpha,x, dir, phi, dphi, fpert, g1, x1, d1,ga, xa, da, phia, dphia, xP, Nf, xf, spAf) if(exit_flag.ne.0) return end if w=b-a end do exit_flag=-2 end function function LSinit_HZ(alpha, x, dir, fval, g, y, f_change, xnorm, gnorm, xP, Nf, xf, spAf) result(exit_flag) implicit none real(kind=RP), intent(inout) :: alpha real(kind=RP), dimension(2), intent(inout) :: x real(kind=RP), dimension(2), intent(inout) :: dir real(kind=RP), intent(inout) :: fval real(kind=RP), dimension(2), intent(inout) :: g real(kind=RP), dimension(2), intent(out) :: y real(kind=RP), intent(out) :: f_change real(kind=RP), intent(in) :: xnorm real(kind=RP), intent(in) :: gnorm real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) integer :: exit_flag ! ! --------------- ! Local variables ! --------------- ! real (kind=RP) :: a, b, w,wohi, wolo, awohi, fpert integer :: i real (kind=RP) :: phi, dphi real (kind=RP) :: phia, dphia real (kind=RP), dimension (2) :: g1, ga real (kind=RP), dimension (2) :: d1, da real (kind=RP), dimension (2) :: x1, xa awohi = dot_product(g, dir) wohi = DELTA_LS_HZ * awohi wolo = SIGMA_LS_HZ * awohi awohi = DELTAAP_LS_HZ * awohi fpert = fval + EPSILON_LS_HZ if (LS_HZ_init1(alpha, x, dir, fval, g, wohi, wolo, awohi, fpert, y, f_change, xnorm, gnorm, xP, Nf, xf, spAf)) then exit_flag =0 return elseif (LS_HZ_bracket(a, b, alpha, x, dir, fval, g, y , f_change , fpert, wohi, wolo, awohi, exit_flag, xP, Nf, xf, spAf).or. & (exit_flag.ne.0)) then return end if w=b-a da = a*dir xa = x+ da ! call Compute_func_gradient (phia, ga, xa) call FitnessFunctionAndGradient(xa, xP, Nf, xf, spAf, phia, ga) dphia = dot_product(ga, dir) d1 = b*dir x1 = x+ d1 ! call Compute_func_gradient (phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1, dir) do i=1, MAX_ITERS_LS_HZ if ( LS_HZ_secant2(a,b,alpha, x, dir, g, y, fval, f_change, phi, dphi, phia, dphia, g1, d1, x1, ga, & da, xa, wohi, wolo, awohi,fpert, exit_flag, xP, Nf, xf, spAf).or.& (exit_flag.ne.0)) return if((b-a)>GAMMA_LS_HZ*w) then alpha=(a+b)*0.5_RP d1 = alpha* dir x1 = x+ d1 ! call compute_func_gradient (phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1, dir) if (LS_HZ_check(alpha, fval, phi, dphi, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then exit_flag = 0 f_change = phi-fval fval = phi y = g1-g g = g1 dir = d1 x = x1 return end if exit_flag= LS_HZ_update(a,b,alpha,x, dir, phi, dphi, fpert, g1, x1, d1,ga, xa, da, phia, dphia, xP, Nf, xf, spAf) if(exit_flag.ne.0) return end if w=b-a end do exit_flag=-2 end function function LS_HZ_secant2(a,b,alpha, x, dir, g, y, fval, f_change, phi, dphi, phia, dphia, & g1, d1, x1, ga, da, xa, wohi, wolo, awohi,fpert, flag, xP, Nf, xf, spAf) result (found) implicit none real(kind=RP), intent(inout) :: a real(kind=RP), intent(inout) :: b real(kind=RP), intent(inout) :: alpha real(kind=RP), dimension(2), intent(inout) :: x real(kind=RP), dimension(2), intent(inout) :: dir real(kind=RP), dimension(2), intent(inout) :: g real(kind=RP), dimension(2), intent(inout) :: y real(kind=RP), intent(inout) :: fval real(kind=RP), intent(inout) :: f_change real(kind=RP), intent(inout) :: phi, dphi real(kind=RP), intent(inout) :: phia, dphia real(kind=RP), dimension(2), intent(inout) :: g1, ga real(kind=RP), dimension(2), intent(inout) :: d1, da real(kind=RP), dimension(2), intent(inout) :: x1, xa real(kind=RP), intent(in) :: wohi real(kind=RP), intent(in) :: wolo real(kind=RP), intent(in) :: awohi real(kind=RP), intent(in) :: fpert integer, intent(out) :: flag real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) logical :: found ! ! --------------- ! Local variables ! --------------- ! real (kind=RP) :: c, aa, bb, dphiaa, dphibb c = (a*dphi-b*dphia)/(dphi-dphia) aa=a bb=b flag=LS_HZ_update(aa,bb,c,x, dir, phi, dphibb, fpert, g1, x1, d1, & ga, xa, da, phia, dphiaa, xP, Nf, xf, spAf) if(flag.ne.0) then found = .false. return elseif (c.eq.bb) then c = (b*dphibb-bb*dphi)/(dphibb-dphi) goto 100 elseif (c.eq.aa) then c = (a*dphiaa-aa*dphia)/(dphiaa-dphia) goto 100 else dphia=dphiaa dphi=dphibb goto 200 end if 100 dphia=dphiaa;dphi=dphibb flag=LS_HZ_update(aa,bb,c,x, dir, phi, dphi, fpert, g1, x1, d1, & ga, xa, da, phia, dphia, xP, Nf, xf, spAf) if(flag.ne.0) then found = .false. return end if 200 a= aa; b=bb flag = 0 if (LS_HZ_check(a, fval, phia, dphia, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then alpha = a f_change = phia-fval fval = phia y = ga-g g = ga dir = da x = xa found = .true. elseif (LS_HZ_check(b, fval, phi, dphi, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then alpha = b f_change = phi-fval fval = phi y = g1-g g = g1 dir = d1 x = x1 found = .true. else found = .false. end if return end function function LS_HZ_update(a,b,alpha,x, dir, phi, dphi, fpert, g1, x1, d1, & ga, xa, da, phia, dphia, xP, Nf, xf, spAf) result(flag) implicit none real(kind=RP), intent(inout) :: a real(kind=RP), intent(inout) :: b real(kind=RP), intent(in) :: alpha real(kind=RP), dimension(2), intent(in) :: x real(kind=RP), dimension(2), intent(in) :: dir real(kind=RP), intent(inout) :: phi real(kind=RP), intent(inout) :: dphi real(kind=RP), intent(out) :: phia real(kind=RP), intent(out) :: dphia real(kind=RP), intent(in) :: fpert real(kind=RP), dimension(2), intent(out) :: g1, ga real(kind=RP), dimension(2), intent(out) :: x1, xa real(kind=RP), dimension(2), intent(out) :: d1, da real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) integer :: flag ! ! --------------- ! Local variables ! --------------- ! real(kind=RP) :: dphi_alpha, phi_alpha real(kind=RP), dimension(2) :: d_alpha, x_alpha, g_alpha integer :: i if ((alpha<a).or.(alpha>b)) then flag = 0 return end if d_alpha = alpha*dir x_alpha = x+ d_alpha ! call Compute_func_gradient (phi_alpha, g_alpha, x_alpha) call FitnessFunctionAndGradient(x_alpha, xP, Nf, xf, spAf, phi_alpha, g_alpha) dphi_alpha = dot_product(g_alpha, dir) if (dphi_alpha>=0._RP) then b=alpha phi=phi_alpha dphi=dphi_alpha g1=g_alpha d1=d_alpha x1=x_alpha flag = 0 return elseif (phi_alpha<=fpert) then a= alpha phia=phi_alpha dphia=dphi_alpha ga=g_alpha da=d_alpha xa=x_alpha flag = 0 return end if b = alpha do i = 1, MAX_ITERS_LS_HZ_update phi_alpha = ONEMTHETA_LS_HZ*a+THETA_LS_HZ*b d1 = phi_alpha * dir x1 = x + d1 ! call Compute_func_gradient ( phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1, dir) if (dphi>=0._RP) then b = phi_alpha flag = 0 return elseif (phi<=fpert) then a = phi_alpha ga = g1 xa = x1 da = d1 phia = phi dphia = dphi cycle end if b = phi_alpha end do flag = -5 end function !DEC$ ATTRIBUTES FORCEINLINE :: LS_HZ_check function LS_HZ_check(alpha, fval, phi, dphi, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf) result(ok) implicit none real(kind=RP), intent(inout) :: alpha real(kind=RP), intent(in) :: fval real(kind=RP), intent(in) :: phi real(kind=RP), intent(in) :: dphi real(kind=RP), intent(in) :: wohi real(kind=RP), intent(in) :: wolo real(kind=RP), intent(in) :: awohi real(kind=RP), intent(in) :: fpert real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) logical :: ok ok = ((dphi>=wolo).and.(((phi-fval)<=(alpha*wohi)).or.((phi<=fpert).and.(dphi<=awohi)))) end function function LS_HZ_init(alpha, x, dir, f, g, wohi, wolo, awohi, fpert, y, f_change, xP, Nf, xf, spAf) result(found) implicit none real(kind=RP), intent(inout) :: alpha real(kind=RP), dimension(2), intent(inout) :: x real(kind=RP), dimension(2), intent(inout) :: dir real(kind=RP), intent(inout) :: f real(kind=RP), dimension(2), intent(inout) :: g real(kind=RP), intent(in) :: wohi real(kind=RP), intent(in) :: wolo real(kind=RP), intent(in) :: awohi real(kind=RP), intent(in) :: fpert real(kind=RP), dimension(2), intent(out) :: y real(kind=RP), intent(out) :: f_change real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) logical :: found ! ! --------------- ! Local variables ! --------------- ! real (kind=RP), dimension(2) :: g1, x1, d1 real (kind=RP) :: alpha_q, a, phi, dphi alpha_q=PSI1_LS_HZ*alpha d1 = alpha_q*dir x1 = x + d1 ! call Compute_func_gradient(phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) if (phi.le.f) then dphi=alpha_q*alpha_q a=dot_product(g,dir) alpha_q=0.5_RP*dphi*a/(f-phi+alpha_q*a) a=(-f+phi*(1._RP-a))/dphi if ((alpha_q.ge.0._RP).and.(a>TOLA_LS_HZ_init)) then alpha=alpha_q d1 = alpha*dir x1 = x + d1 ! call Compute_func_gradient(phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1, dir) goto 100 end if end if alpha = PSI2_LS_HZ*alpha d1 = alpha*dir x1 = x + d1 ! call Compute_func_gradient(phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1,dir) 100 if (LS_HZ_check(alpha, f, phi, dphi, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then f_change = phi-f f = phi y = g1-g g = g1 dir = d1 x = x1 found = .true. else found = .false. end if end function function LS_HZ_init1(alpha, x, dir, f, g, wohi, wolo, awohi, fpert, y, f_change, xnorm, gnorm, xP, Nf, xf, spAf) result(found) implicit none real(kind=RP), intent(inout) :: alpha real(kind=RP), dimension(2), intent(inout) :: x real(kind=RP), dimension(2), intent(inout) :: dir real(kind=RP), intent(inout) :: f real(kind=RP), dimension(2), intent(inout) :: g real(kind=RP), intent(in) :: wohi real(kind=RP), intent(in) :: wolo real(kind=RP), intent(in) :: awohi real(kind=RP), intent(in) :: fpert real(kind=RP), dimension(2), intent(out) :: y real(kind=RP), intent(out) :: f_change real(kind=RP), intent(in) :: xnorm real(kind=RP), intent(in) :: gnorm real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) logical :: found ! ! --------------- ! Local variables ! --------------- ! real(kind=RP), dimension(2) :: g1, x1, d1 real(kind=RP) :: phi, dphi if (xnorm>0._RP) then alpha=PSI0_LS_HZ*xnorm/gnorm elseif (f.ne.0._RP) then alpha=PSI0_LS_HZ*abs(f)/dot_product(g, g) else alpha = 1._RP end if d1 = alpha*dir x1 = x + d1 ! call Compute_func_gradient(phi, g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi, g1) dphi = dot_product(g1,dir) if (LS_HZ_check(alpha, f, phi, dphi, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then f_change = phi-f f = phi y = g1-g g = g1 dir = d1 x = x1 found = .true. else found = .false. end if end function function LS_HZ_bracket (a, b, alpha, x, dir, fval, g, y , f_change , & fpert, wohi, wolo, awohi, flag, xP, Nf, xf, spAf) result(found) implicit none real(kind=RP), intent(out) :: a real(kind=RP), intent(out) :: b real(kind=RP), intent(inout) :: alpha real(kind=RP), dimension(2), intent(inout) :: x real(kind=RP), dimension(2), intent(inout) :: dir real(kind=RP), intent(inout) :: fval real(kind=RP), dimension(2), intent(inout) :: g real(kind=RP), dimension(2), intent(out) :: y real(kind=RP), intent(out) :: f_change real(kind=RP), intent(in) :: fpert real(kind=RP), intent(in) :: wohi real(kind=RP), intent(in) :: wolo real(kind=RP), intent(in) :: awohi integer, intent(out) :: flag real(kind=RP), intent(in) :: xP(3) integer, intent(in) :: Nf(2) real(kind=RP), intent(in) :: xf(NDIM,0:Nf(1),0:Nf(2)) class(NodalStorage_t), intent(in) :: spAf(2) logical :: found ! ! --------------- ! Local variables ! --------------- ! real(kind=RP) :: cj, a_bar, b_bar, phi_der_cj, phi_cj, phia, dphia real(kind=RP), dimension(2) :: g1, ga, d1, x1, da, xa integer :: i, j cj=alpha a=0._RP do j=1, MAX_ITERS_LS_HZ_bracket d1 = cj*dir x1 = x + d1 ! call compute_func_gradient(phi_cj,g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi_cj, g1) phi_der_cj=dot_product(g1, dir) if (phi_der_cj>=0._RP) then b=cj goto 100!return else if ((phi_der_cj<0._RP).and.(phi_cj>fpert)) then a_bar=0._RP b_bar=cj do i=1, MAX_ITERS_LS_HZ_bracket cj=(THETA_LS_HZ)*b_bar+(ONEMTHETA_LS_HZ)*a_bar d1 = cj*dir x1 = x + d1 ! call compute_func_gradient(phi_cj,g1, x1) call FitnessFunctionAndGradient(x1, xP, Nf, xf, spAf, phi_cj, g1) phi_der_cj=dot_product(g1, dir) if(phi_der_cj>=0._RP) then a=a_bar b=cj goto 100 !return elseif(phi_cj<=fpert) then a_bar=cj phia = phi_cj dphia = phi_der_cj ga = g1 da = d1 xa = x1 cycle end if b_bar=cj end do goto 200 !err else if (phi_cj<=fpert) then a=cj phia = phi_cj dphia = phi_der_cj ga = g1 da = d1 xa = x1 end if cj=RHO_LS_HZ*cj end if end do goto 200 !err 100 flag = 0 if (LS_HZ_check(a, fval, phia, dphia, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then alpha = a f_change = phia-fval fval = phia y = ga-g g = ga dir = da x = xa found = .true. elseif (LS_HZ_check(b, fval, phi_cj, phi_der_cj, wohi, wolo, awohi, fpert, xP, Nf, xf, spAf)) then alpha = b f_change = phi_cj-fval fval = phi_cj y = g1-g g = g1 dir = d1 x = x1 found = .true. else found = .false. end if return 200 flag=-3 end function #endif end module WallDistance