module m_ia_vdw !! **Vdw potentials** !! !! * Style _none_: None !! * Style _lj_: Lennard-Jones (with cut-off) !! * Style _hs_: Hard sphere !! * Style _sqw_: Square-well potential !! * Style _gauss_: Gaussian potential !! * Style _tab_: Tabulated potential use m_precision use m_constants_math use m_ia_types, only: ia_specs_t use m_spline implicit none contains !****************************************************************************** subroutine ia_vdw_setup(specs) !! Sets up parameters for vdw potential. type(ia_specs_t), intent(in out) :: specs select case(specs%style) case('none') continue case('lj') call vdw_lj_set(specs) case('hs') continue case('sqw') continue case('gauss') call vdw_gaussian_set(specs) case('tab') call vdw_tab_set(specs) case default continue end select end subroutine !****************************************************************************** subroutine ia_get_vdw_energy(ri, rj, specs, enrg, ierr) !! Calculates the energy due to a single interacting pair of atoms. real(rp), dimension(3), intent(in) :: ri !!Coordinate of atom *i* real(rp), dimension(3), intent(in) :: rj !!Coordinate of atom *j* type(ia_specs_t), intent(in) :: specs real(rp), intent(out) :: enrg integer, intent(out) :: ierr real(rp), dimension(3) :: rij real(rp) :: rij_mag integer :: styl ierr = 0; enrg = 0.0_rp rij = rj - ri rij_mag = norm2(rij) select case(specs%style) case('none') enrg = 0.0_rp case('lj') call vdw_lj(rij_mag, specs, enrg, ierr) case('hs') call vdw_hs(rij_mag, specs, enrg, ierr) case('sqw') call vdw_sqwell(rij_mag, specs, enrg, ierr) case('gauss') call vdw_gaussian(rij_mag, specs, enrg, ierr) case('tab') call vdw_tab(rij_mag, specs, enrg, ierr) end select end subroutine !****************************************************************************** subroutine vdw_lj_set(specs) !! Setter for LJ interaction. !! params(1) = eps, params(2) = sigma, params(3) = rcut !! cache(1) = sigma**2, cache(2) = U(rcut) type(ia_specs_t), intent(in out) :: specs real(rp) :: eps real(rp) :: sigma real(rp) :: rcut real(rp) :: sigmasq real(rp) :: pot_rcut real(rp) :: sir2, sir6, sir12 eps = specs%params(1); sigma = specs%params(2); rcut = specs%params(3) sigmasq = sigma*sigma sir2 = sigmasq/(rcut*rcut); sir6 = sir2*sir2*sir2; sir12 = sir6*sir6 pot_rcut = 4*eps*(sir12 - sir6) allocate( specs%cache(2) ) specs%cache(1) = sigmasq specs%cache(2) = pot_rcut end subroutine !****************************************************************************** pure subroutine vdw_lj(r, specs, enrg, ierr) !! Calculates energy for LJ interaction. !! params(1) = eps, params(2) = sigma, params(3) = rcut !! cache(1) = sigma**2, cache(2) = U(rcut) real(rp), intent(in) :: r type(ia_specs_t), intent(in) :: specs real(rp), intent(out) :: enrg integer, intent(out) :: ierr real(rp) :: eps, sigma real(rp) :: rcut, sigmasq real(rp) :: pot_rcut real(rp) :: sir2, sir6, sir12 ierr = 0; enrg = 0.0_rp eps = specs%params(1); sigma = specs%params(2); rcut = specs%params(3) sigmasq = specs%cache(1) pot_rcut = specs%cache(2) if ( r >= rcut ) then enrg = 0.0_rp else sir2 = sigmasq/(r*r); sir6 = sir2*sir2*sir2; sir12 = sir6*sir6 enrg = 4*eps*(sir12 - sir6) - pot_rcut end if end subroutine !****************************************************************************** pure subroutine vdw_hs(r, specs, enrg, ierr) !! Calculates energy for hard sphere interaction. Overlap will be indicated !! with `ierr = 1`. !! params(1) = r1, params(2) = r2, params(3) = tol real(rp), intent(in) :: r type(ia_specs_t), intent(in) :: specs real(rp), intent(out) :: enrg integer, intent(out) :: ierr ierr = 0; enrg = 0.0_rp if ( r < (specs%params(1)+specs%params(2)-specs%params(3)) ) ierr = 1 end subroutine !****************************************************************************** pure subroutine vdw_sqwell(r, specs, enrg, ierr) !! Calculates energy for square well interaction. Overlap will be indicated !! with `ierr = 1`. !! params(1) = eps, params(2) = sigma, params(3) = width real(rp), intent(in) :: r type(ia_specs_t), intent(in) :: specs real(rp), intent(out) :: enrg integer, intent(out) :: ierr if ( r <= specs%params(2) ) then ierr = 1; enrg = 0.0_rp else if ( r >= (specs%params(2)+specs%params(3)) ) then ierr = 0; enrg = 0.0_rp else ierr = 0; enrg = -specs%params(1) end if end subroutine !****************************************************************************** subroutine vdw_gaussian_set(specs) !! Setter for Gaussian interaction. !! params(1) = A, params(2) = B, params(3) = rcut !! cache(1) = V(rcut) type(ia_specs_t), intent(in out) :: specs real(rp) :: A, B, rcut real(rp) :: pot_rcut A = specs%params(1); B = specs%params(2); rcut = specs%params(3) pot_rcut = A*exp(-B*rcut**2) allocate( specs%cache(1) ) specs%cache(1) = pot_rcut end subroutine !****************************************************************************** pure subroutine vdw_gaussian(r, specs, enrg, ierr) !! Calculates energy for Gaussian interaction. !! params(1) = A, params(2) = B, params(3) = rcut !! cache(1) = V(rcut) real(rp), intent(in) :: r type(ia_specs_t), intent(in) :: specs real(rp), intent(out) :: enrg integer, intent(out) :: ierr real(rp) :: A, B, rcut real(rp) :: pot_rcut real(rp) :: exrs ierr = 0; enrg = 0.0_rp A = specs%params(1); B = specs%params(2); rcut = specs%params(3) pot_rcut = specs%cache(1) if ( r < rcut ) then exrs = exp(-B*r*r) enrg = A*exrs - pot_rcut end if end subroutine !****************************************************************************** subroutine vdw_tab_set(specs) !! Setter for tabulated interaction. !! params(1) = vtltmin, params(2) = vtgtmax, params(3) = intrp_meth, !! params(4) = flag_bc !! cache(1) = tmin, cache(2) = tmax type(ia_specs_t), intent(in out) :: specs integer :: intrp_meth, flag_bc integer :: n intrp_meth = int(specs%params(3)) n = specs%tab_size allocate( specs%cache(2) ) specs%cache(1) = minval(specs%tab_t) specs%cache(2) = maxval(specs%tab_t) if (intrp_meth == 1) then allocate( specs%tab_vpp(n) ) flag_bc = int(specs%params(4)) if (flag_bc == 0) then !Natural BC: second derivative equals zero at the two ends call spline_cubic_set(n, specs%tab_t, specs%tab_v, & 2, 0.0_rp, 2, 0.0_rp, specs%tab_vpp) else if (flag_bc == 1) then !Spline is quadratic over the first and last interval call spline_cubic_set(n, specs%tab_t, specs%tab_v, & 0, 0.0_rp, 0, 0.0_rp, specs%tab_vpp) else if (flag_bc == 2) then !Not-a-knot condition call spline_cubic_set(n, specs%tab_t, specs%tab_v, & 3, 0.0_rp, 3, 0.0_rp, specs%tab_vpp) end if end if end subroutine !****************************************************************************** subroutine vdw_tab(r, specs, enrg, ierr) !! Calculates energy for tabulated interaction. ! params(1) = vtltmin, params(2) = vtgtmax, params(3) = intrp_meth, ! params(4) = flag_bc ! cache(1) = tmin, cache(2) = tmax real(rp), intent(in) :: r type(ia_specs_t), intent(in) :: specs real(rp), intent(out) :: enrg integer, intent(out) :: ierr real(rp) :: tmin, tmax, vtltmin, vtgtmax real(rp) :: vp, vpp integer :: intrp_meth, n ierr = 0; enrg = 0.0_rp n = specs%tab_size vtltmin = specs%params(1); vtgtmax = specs%params(2) tmin = specs%cache(1); tmax = specs%cache(2) intrp_meth = int(specs%params(3)) if ( r < tmin ) then enrg = vtltmin else if ( r > tmax ) then enrg = vtgtmax else if (intrp_meth == 0) then call spline_linear_val(n, specs%tab_t, specs%tab_v, r, enrg, vp) else if (intrp_meth == 1) then call spline_cubic_val(n, specs%tab_t, specs%tab_v, specs%tab_vpp, & r, enrg, vp, vpp) end if end if end subroutine !****************************************************************************** end module m_ia_vdw