! Copyright 2024 - The Minton Group at Purdue University ! This file is part of Swiftest. ! Swiftest is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License ! as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ! Swiftest is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty ! of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ! You should have received a copy of the GNU General Public License along with Swiftest. ! If not, see: https://www.gnu.org/licenses. submodule (symba) s_symba_encounter_check use swiftest contains module function symba_encounter_check_pl(self, param, nbody_system, dt, irec) result(lany_encounter) !! author: David A. Minton !! !! Check for an encounter between massive bodies. !! implicit none ! Arguments class(symba_pl), intent(inout) :: self !! SyMBA test particle object class(swiftest_parameters), intent(inout) :: param !! Current Swiftest run configuration parameters class(symba_nbody_system), intent(inout) :: nbody_system !! SyMBA nbody system object real(DP), intent(in) :: dt !! step size integer(I4B), intent(in) :: irec !! Current recursion level ! Result logical :: lany_encounter !! Returns true if there is at least one close encounter ! Internals integer(I8B) :: k, nenc integer(I4B) :: i, j, npl, nplm, nplt logical, dimension(:), allocatable :: lvdotr integer(I4B), dimension(:), allocatable :: index1, index2 lany_encounter = .false. if (self%nbody == 0) return associate(pl => self, plpl_encounter => nbody_system%plpl_encounter, cb => nbody_system%cb) npl = pl%nbody nplm = pl%nplm nplt = npl - nplm call pl%set_renc(irec) if (nplt == 0) then call encounter_check_all_plpl(param, npl, pl%rh, pl%vb, pl%renc, dt, nenc, index1, index2, lvdotr) else call encounter_check_all_plplm(param, nplm, nplt, pl%rh(:,1:nplm), pl%vb(:,1:nplm), pl%rh(:,nplm+1:npl), & pl%vb(:,nplm+1:npl), pl%renc(1:nplm), pl%renc(nplm+1:npl), dt, nenc, index1, index2, lvdotr) end if lany_encounter = nenc > 0_I8B if (lany_encounter) then call plpl_encounter%resize(nenc) call move_alloc(lvdotr, plpl_encounter%lvdotr) call move_alloc(index1, plpl_encounter%index1) call move_alloc(index2, plpl_encounter%index2) end if if (lany_encounter) then do k = 1_I8B, nenc plpl_encounter%t = nbody_system%t i = plpl_encounter%index1(k) j = plpl_encounter%index2(k) plpl_encounter%id1(k) = pl%id(i) plpl_encounter%id2(k) = pl%id(j) plpl_encounter%status(k) = ACTIVE plpl_encounter%level(k) = irec plpl_encounter%r1(:,k) = pl%rh(:,i) plpl_encounter%r2(:,k) = pl%rh(:,j) plpl_encounter%v1(:,k) = pl%vb(:,i) - cb%vb(:) plpl_encounter%v2(:,k) = pl%vb(:,j) - cb%vb(:) pl%lencounter(i) = .true. pl%lencounter(j) = .true. pl%levelg(i) = irec pl%levelm(i) = irec pl%levelg(j) = irec pl%levelm(j) = irec pl%nplenc(i) = pl%nplenc(i) + 1 pl%nplenc(j) = pl%nplenc(j) + 1 end do end if end associate return end function symba_encounter_check_pl module function symba_encounter_check_list_plpl(self, param, nbody_system, dt, irec) result(lany_encounter) implicit none class(symba_list_plpl), intent(inout) :: self !! SyMBA pl-pl encounter list object class(swiftest_parameters), intent(inout) :: param !! Current Swiftest run configuration parameters class(symba_nbody_system), intent(inout) :: nbody_system !! SyMBA nbody system object real(DP), intent(in) :: dt !! step size integer(I4B), intent(in) :: irec !! Current recursion level logical :: lany_encounter !! Returns true if there is at least one close encounter ! Internals integer(I4B) :: i, j, nenc_enc integer(I8B) :: k, lidx real(DP), dimension(NDIM) :: xr, vr real(DP) :: rlim2, rji2, rcrit12 logical, dimension(:), allocatable :: lencmask, lencounter integer(I8B), dimension(:), allocatable :: eidx lany_encounter = .false. if (self%nenc == 0) return select type(pl => nbody_system%pl) class is (symba_pl) allocate(lencmask(self%nenc)) lencmask(:) = (self%status(1:self%nenc) == ACTIVE) .and. (self%level(1:self%nenc) == irec - 1) nenc_enc = count(lencmask(:)) if (nenc_enc == 0) return call pl%set_renc(irec) allocate(eidx(nenc_enc)) allocate(lencounter(nenc_enc)) eidx(:) = pack([(k, k = 1_I8B, self%nenc)], lencmask(:)) lencounter(:) = .false. #ifdef DOCONLOC do concurrent(lidx = 1_I8B:nenc_enc) shared(self,pl,eidx,lencounter,dt) local(i,j,k,xr,vr,rcrit12,rlim2,rji2) #else do concurrent(lidx = 1_I8B:nenc_enc) #endif k = eidx(lidx) i = self%index1(k) j = self%index2(k) xr(:) = pl%rh(:,j) - pl%rh(:,i) vr(:) = pl%vb(:,j) - pl%vb(:,i) rcrit12 = pl%renc(i) + pl%renc(j) call encounter_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), rcrit12, dt, lencounter(lidx), self%lvdotr(k)) if (lencounter(lidx)) then rlim2 = (pl%radius(i) + pl%radius(j))**2 rji2 = dot_product(xr(:), xr(:)) ! Check to see if these are physically overlapping bodies first, ! which we should ignore lencounter(lidx) = rji2 > rlim2 end if end do lany_encounter = any(lencounter(:)) if (lany_encounter) then nenc_enc = count(lencounter(:)) eidx(1_I8B:nenc_enc) = pack(eidx(:), lencounter(:)) do lidx = 1_I8B, nenc_enc k = eidx(lidx) i = self%index1(k) j = self%index2(k) pl%levelg(i) = irec pl%levelm(i) = MAX(irec, pl%levelm(i)) pl%levelg(j) = irec pl%levelm(j) = MAX(irec, pl%levelm(j)) self%level(k) = irec end do end if end select return end function symba_encounter_check_list_plpl module function symba_encounter_check_list_pltp(self, param, nbody_system, dt, irec) result(lany_encounter) implicit none class(symba_list_pltp), intent(inout) :: self !! SyMBA pl-tp encounter list object class(swiftest_parameters), intent(inout) :: param !! Current Swiftest run configuration parameters class(symba_nbody_system), intent(inout) :: nbody_system !! SyMBA nbody system object real(DP), intent(in) :: dt !! step size integer(I4B), intent(in) :: irec !! Current recursion level logical :: lany_encounter !! Returns true if there is at least one close encounter ! Internals integer(I4B) :: i, j, nenc_enc integer(I8B) :: k, lidx real(DP), dimension(NDIM) :: xr, vr real(DP) :: rlim2, rji2 logical, dimension(:), allocatable :: lencmask, lencounter integer(I8B), dimension(:), allocatable :: eidx lany_encounter = .false. if (self%nenc == 0) return select type(pl => nbody_system%pl) class is (symba_pl) select type(tp => nbody_system%tp) class is (symba_tp) allocate(lencmask(self%nenc)) lencmask(:) = (self%status(1:self%nenc) == ACTIVE) .and. (self%level(1:self%nenc) == irec - 1) nenc_enc = count(lencmask(:)) if (nenc_enc == 0) return call pl%set_renc(irec) allocate(eidx(nenc_enc)) allocate(lencounter(nenc_enc)) eidx(:) = pack([(k, k = 1_I8B, self%nenc)], lencmask(:)) lencounter(:) = .false. #ifdef DOCONLOC do concurrent(lidx = 1_I8B:nenc_enc) shared(self,pl,tp,eidx,lencounter,dt) local(i,j,k,xr,vr,rlim2,rji2) #else do concurrent(lidx = 1_I8B:nenc_enc) #endif k = eidx(lidx) i = self%index1(k) j = self%index2(k) xr(:) = tp%rh(:,j) - pl%rh(:,i) vr(:) = tp%vb(:,j) - pl%vb(:,i) call encounter_check_one(xr(1), xr(2), xr(3), vr(1), vr(2), vr(3), pl%renc(i), dt, & lencounter(lidx), self%lvdotr(k)) if (lencounter(lidx)) then rlim2 = (pl%radius(i))**2 rji2 = dot_product(xr(:), xr(:)) ! Check to see if these are physically overlapping bodies first, ! which we should ignore lencounter(lidx) = rji2 > rlim2 end if end do lany_encounter = any(lencounter(:)) if (lany_encounter) then nenc_enc = count(lencounter(:)) eidx(1_I8B:nenc_enc) = pack(eidx(:), lencounter(:)) do lidx = 1_I8B, nenc_enc k = eidx(lidx) i = self%index1(k) j = self%index2(k) pl%levelg(i) = irec pl%levelm(i) = MAX(irec, pl%levelm(i)) tp%levelg(j) = irec tp%levelm(j) = MAX(irec, tp%levelm(j)) self%level(k) = irec end do end if end select end select return end function symba_encounter_check_list_pltp module function symba_encounter_check_tp(self, param, nbody_system, dt, irec) result(lany_encounter) !! author: David A. Minton !! !! Check for an encounter between test particles and massive bodies. !! implicit none ! Arguments class(symba_tp), intent(inout) :: self !! SyMBA test particle object class(swiftest_parameters), intent(inout) :: param !! Current Swiftest run configuration parameters class(symba_nbody_system), intent(inout) :: nbody_system !! SyMBA nbody system object real(DP), intent(in) :: dt !! step size integer(I4B), intent(in) :: irec !! Current recursion level ! Result logical :: lany_encounter !! Returns true if there is at least one close encounter ! Internals integer(I4B) :: plind, tpind integer(I8B) :: k, nenc logical, dimension(:), allocatable :: lvdotr integer(I4B), dimension(:), allocatable :: index1, index2 lany_encounter = .false. if (self%nbody == 0 .or. nbody_system%pl%nbody == 0) return associate(tp => self, ntp => self%nbody, pl => nbody_system%pl, npl => nbody_system%pl%nbody, cb => nbody_system%cb) call pl%set_renc(irec) call encounter_check_all_pltp(param, npl, ntp, pl%rh, pl%vb, tp%rh, tp%vb, pl%renc, dt, nenc, index1, index2, lvdotr) lany_encounter = nenc > 0 if (lany_encounter) then associate(pltp_encounter => nbody_system%pltp_encounter) call pltp_encounter%resize(nenc) pltp_encounter%status(1:nenc) = ACTIVE pltp_encounter%level(1:nenc) = irec call move_alloc(index1, pltp_encounter%index1) call move_alloc(index2, pltp_encounter%index2) call move_alloc(lvdotr, pltp_encounter%lvdotr) pltp_encounter%id1(1:nenc) = pl%id(pltp_encounter%index1(1:nenc)) pltp_encounter%id2(1:nenc) = tp%id(pltp_encounter%index2(1:nenc)) select type(pl) class is (symba_pl) pl%lencounter(1:npl) = .false. do k = 1_I8B, nenc plind = pltp_encounter%index1(k) tpind = pltp_encounter%index2(k) pl%lencounter(plind) = .true. pl%levelg(plind) = irec pl%levelm(plind) = irec tp%levelg(tpind) = irec tp%levelm(tpind) = irec pl%ntpenc(plind) = pl%ntpenc(plind) + 1 tp%nplenc(tpind) = tp%nplenc(tpind) + 1 end do end select end associate end if end associate return end function symba_encounter_check_tp end submodule s_symba_encounter_check