diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2a1dfe449..b4253371f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: - ['test', ''] - ['testkd', ''] - ['testdust', 'dust'] - - ['testgr', 'gr'] + - ['testgr', 'gr ptmass'] - ['testgrav', 'gravity ptmass setstar'] - ['testgrowth', 'dustgrowth'] - ['testnimhd', 'nimhd'] diff --git a/AUTHORS b/AUTHORS index 78279c152..b7745260b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,8 +13,8 @@ David Liptai Lionel Siess Fangyi (Fitz) Hu Yann Bernard -Daniel Mentiplay Megha Sharma +Daniel Mentiplay Arnaud Vericel Mark Hutchison Mats Esseldeurs @@ -60,6 +60,7 @@ Benoit Commercon Christopher Russell Giulia Ballabio Joe Fisher +Kateryna Andrych Maxime Lombart Orsola De Marco Shunquan Huang diff --git a/src/main/checksetup.f90 b/src/main/checksetup.f90 index b7bcc4e3b..a6c7be71b 100644 --- a/src/main/checksetup.f90 +++ b/src/main/checksetup.f90 @@ -410,6 +410,10 @@ subroutine check_setup(nerror,nwarn,restart) ! if (gr) call check_gr(npart,nerror,xyzh,vxyzu) ! +!--check sink GR setup +! + if (gr) call check_gr(nptmass,nerror,xyzmh_ptmass,vxyz_ptmass) +! !--check radiation setup ! if (do_radiation) call check_setup_radiation(npart,nerror,nwarn,radprop,rad) @@ -544,12 +548,6 @@ subroutine check_setup_ptmass(nerror,nwarn,hmin) isoblate = .false. - if (gr .and. nptmass > 0) then - print*,' ERROR: nptmass = ',nptmass, ' should be = 0 for GR' - nwarn = nwarn + 1 - return - endif - if (nptmass < 0) then print*,' ERROR: nptmass = ',nptmass, ' should be >= 0 ' nerror = nerror + 1 diff --git a/src/main/config.F90 b/src/main/config.F90 index 1238e1ea6..7802c2fb9 100644 --- a/src/main/config.F90 +++ b/src/main/config.F90 @@ -262,8 +262,10 @@ module dim integer :: maxgr = 0 #ifdef GR logical, parameter :: gr = .true. + integer, parameter :: maxptmassgr = maxptmass #else logical, parameter :: gr = .false. + integer, parameter :: maxptmassgr = 0 #endif !--------------------- diff --git a/src/main/cons2prim.f90 b/src/main/cons2prim.f90 index 700734b1f..d3e076bf2 100644 --- a/src/main/cons2prim.f90 +++ b/src/main/cons2prim.f90 @@ -20,7 +20,7 @@ module cons2prim ! Liptai & Price (2019), MNRAS 485, 819-842 ! Ballabio et al. (2018), MNRAS 477, 2766-2771 ! -! :Owner: Elisabeth Borchert +! :Owner: Megha Sharma ! ! :Runtime parameters: None ! @@ -29,7 +29,7 @@ module cons2prim ! implicit none - public :: cons2primall,cons2prim_everything + public :: cons2primall,cons2prim_everything,cons2primall_sink public :: prim2consall,prim2consi private @@ -44,17 +44,17 @@ module cons2prim ! (density,velocity,internal energy), for ALL particles !+ !---------------------------------------------------------------------- -subroutine prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens) +subroutine prim2consall(npart,xyzh,metrics,vxyzu,pxyzu,use_dens,dens,use_sink) use part, only:isdead_or_accreted,ien_type,eos_vars,igasP,igamma,itemp use eos, only:gamma,ieos integer, intent(in) :: npart real, intent(in) :: xyzh(:,:),metrics(:,:,:,:),vxyzu(:,:) - real, intent(inout) :: dens(:) real, intent(out) :: pxyzu(:,:) - logical, intent(in), optional :: use_dens + real, intent(inout), optional :: dens(:) + logical, intent(in), optional :: use_dens, use_sink logical :: usedens integer :: i - real :: pri,tempi + real :: pri,tempi,xyzhi(4),vxyzui(4),densi ! By default, use the smoothing length to compute primitive density, and then compute the conserved variables. ! (Alternatively, use the provided primitive density to compute conserved variables. @@ -65,25 +65,37 @@ subroutine prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens) usedens = .false. endif -!$omp parallel do default (none) & -!$omp shared(xyzh,metrics,vxyzu,dens,pxyzu,npart,usedens,ien_type,eos_vars,gamma,ieos) & -!$omp private(i,pri,tempi) + !$omp parallel do default (none) & + !$omp shared(xyzh,metrics,vxyzu,dens,pxyzu,npart,usedens,ien_type,eos_vars,gamma,ieos,use_sink,use_dens) & + !$omp private(i,pri,tempi,xyzhi,vxyzui,densi) do i=1,npart - if (.not.isdead_or_accreted(xyzh(4,i))) then - call prim2consi(xyzh(:,i),metrics(:,:,:,i),vxyzu(:,i),dens(i),pri,tempi,pxyzu(:,i),usedens,ien_type) - ! save eos vars for later use - eos_vars(igasP,i) = pri - eos_vars(itemp,i) = tempi - if (vxyzu(4,i) > 0. .and. ieos == 12) then - eos_vars(igamma,i) = 1. + pri/(dens(i)*vxyzu(4,i)) - else - ! prevent getting NaN or Infinity when u = 0 - eos_vars(igamma,i) = gamma + if (present(use_sink)) then + xyzhi(1:3) = xyzh(1:3,i) ! save positions + xyzhi(4) = xyzh(5,i) ! save smoothing length, h + vxyzui(1:3) = vxyzu(1:3,i) + vxyzui(4) = 0. ! assume energy as 0. for sink + densi = 1. + call prim2consi(xyzhi,metrics(:,:,:,i),vxyzui,pri,tempi,pxyzu(:,i),ien_type,& + use_sink=use_sink,dens_i=densi) ! this returns temperature and pressure as 0. + else + if (.not.isdead_or_accreted(xyzh(4,i))) then + call prim2consi(xyzh(:,i),metrics(:,:,:,i),vxyzu(:,i),pri,tempi,pxyzu(:,i),ien_type,& + use_dens=usedens,dens_i=dens(i)) + + ! save eos vars for later use + eos_vars(igasP,i) = pri + eos_vars(itemp,i) = tempi + if (vxyzu(4,i) > 0. .and. ieos == 12) then + eos_vars(igamma,i) = 1. + pri/(dens(i)*vxyzu(4,i)) + else + ! prevent getting NaN or Infinity when u = 0 + eos_vars(igamma,i) = gamma + endif endif endif enddo -!$omp end parallel do + !$omp end parallel do end subroutine prim2consall @@ -93,19 +105,21 @@ end subroutine prim2consall ! for a single SPH particle !+ !---------------------------------------------------------------------- -subroutine prim2consi(xyzhi,metrici,vxyzui,dens_i,pri,tempi,pxyzui,use_dens,ien_type) +subroutine prim2consi(xyzhi,metrici,vxyzui,pri,tempi,pxyzui,ien_type,use_dens,use_sink,dens_i) use cons2primsolver, only:primitive2conservative use utils_gr, only:h2dens use eos, only:equationofstate,ieos real, dimension(4), intent(in) :: xyzhi, vxyzui real, intent(in) :: metrici(:,:,:) - real, intent(inout) :: dens_i,pri,tempi + real, intent(inout) :: pri,tempi integer, intent(in) :: ien_type real, dimension(4), intent(out) :: pxyzui - logical, intent(in), optional :: use_dens + logical, intent(in), optional :: use_dens,use_sink + real, intent(inout), optional :: dens_i logical :: usedens real :: rhoi,ui,xyzi(1:3),vi(1:3),pondensi,spsoundi,densi + pondensi = 0. ! By default, use the smoothing length to compute primitive density, and then compute the conserved variables. ! (Alternatively, use the provided primitive density to compute conserved variables. ! Depends whether you have prim dens prior or not.) @@ -118,13 +132,20 @@ subroutine prim2consi(xyzhi,metrici,vxyzui,dens_i,pri,tempi,pxyzui,use_dens,ien_ xyzi = xyzhi(1:3) vi = vxyzui(1:3) ui = vxyzui(4) + if (usedens) then densi = dens_i else - call h2dens(densi,xyzhi,metrici,vi) ! Compute dens from h - dens_i = densi ! Feed the newly computed dens back out of the routine + if (present(use_sink)) then + densi = 1. ! using a value of 0. results in NaN values for the pxyzui array. + pondensi = 0. + else + call h2dens(densi,xyzhi,metrici,vi) ! Compute dens from h + dens_i = densi ! Feed the newly computed dens back out of the routine + call equationofstate(ieos,pondensi,spsoundi,densi,xyzi(1),xyzi(2),xyzi(3),tempi,ui) + endif endif - call equationofstate(ieos,pondensi,spsoundi,densi,xyzi(1),xyzi(2),xyzi(3),tempi,ui) + pri = pondensi*densi call primitive2conservative(xyzi,metrici,vi,densi,ui,pri,rhoi,pxyzui(1:3),pxyzui(4),ien_type) @@ -173,7 +194,6 @@ subroutine cons2primall(npart,xyzh,metrics,pxyzu,vxyzu,dens,eos_vars) call conservative2primitive(xyzh(1:3,i),metrics(:,:,:,i),vxyzu(1:3,i),dens(i),vxyzu(4,i), & p_guess,tempi,gammai,rhoi,pxyzu(1:3,i),pxyzu(4,i),ierr,ien_type) - ! store results eos_vars(igasP,i) = p_guess eos_vars(ics,i) = get_spsound(ieos,xyzh(1:3,i),dens(i),vxyzu(:,i),gammai) @@ -191,6 +211,49 @@ subroutine cons2primall(npart,xyzh,metrics,pxyzu,vxyzu,dens,eos_vars) end subroutine cons2primall +!---------------------------------------------------------------------- +!+ +! Conservative to primitive routines (for GR sink particles): +! Solve for primitive variables (density,velocity,internal energy) +! from the evolved/conservative variables (rho*,momentum,entropy) +!+ +!---------------------------------------------------------------------- +subroutine cons2primall_sink(nptmass,xyzmh_ptmass,metrics_ptmass,pxyzu_ptmass,vxyz_ptmass,eos_vars) + use cons2primsolver, only:conservative2primitive + use io, only:fatal + use part, only:ien_type + integer, intent(in) :: nptmass + real, intent(in) :: pxyzu_ptmass(:,:),xyzmh_ptmass(:,:),metrics_ptmass(:,:,:,:) + real, intent(inout) :: vxyz_ptmass(:,:) + real, intent(out), optional :: eos_vars(:,:) + integer :: i, ierr + real :: p_guess,rhoi,tempi,gammai,eni,densi + +!$omp parallel do default (none) & +!$omp shared(xyzmh_ptmass,metrics_ptmass,vxyz_ptmass,pxyzu_ptmass,nptmass,ien_type) & +!$omp private(i,ierr,p_guess,rhoi,tempi,gammai,eni,densi) + do i=1,nptmass + p_guess = 0. + tempi = 0. + gammai = 0. + rhoi = 1. + densi = 1. + ! conservative 2 primitive + call conservative2primitive(xyzmh_ptmass(1:3,i),metrics_ptmass(:,:,:,i),vxyz_ptmass(1:3,i),densi,eni, & + p_guess,tempi,gammai,rhoi,pxyzu_ptmass(1:3,i),pxyzu_ptmass(4,i),ierr,ien_type) + + if (ierr > 0) then + print*,' pmom =',pxyzu_ptmass(1:3,i) + print*,' rho* =',rhoi + print*,' en =',eni + call fatal('cons2prim','could not solve rootfinding',i) + endif + + enddo +!$omp end parallel do + +end subroutine cons2primall_sink + !----------------------------------------------------------------------------- !+ ! Solve for primitive variables (v,u,P,B,dustfrac) from evolved variables diff --git a/src/main/deriv.F90 b/src/main/deriv.F90 index cd7149405..a906cc74e 100644 --- a/src/main/deriv.F90 +++ b/src/main/deriv.F90 @@ -221,6 +221,7 @@ end subroutine derivs ! this should NOT be called during timestepping, it is useful ! for when one requires just a single call to evaluate derivatives ! and store them in the global shared arrays +! does not work for sink GR yet !+ !-------------------------------------- subroutine get_derivs_global(tused,dt_new,dt) @@ -246,7 +247,7 @@ subroutine get_derivs_global(tused,dt_new,dt) ! update conserved quantities in the GR code if (gr) then call init_metric(npart,xyzh,metrics) - call prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens=.false.) + call prim2consall(npart,xyzh,metrics,vxyzu,pxyzu,use_dens=.false.,dens=dens) endif ! evaluate derivatives diff --git a/src/main/energies.F90 b/src/main/energies.F90 index acea5414a..564e085ca 100644 --- a/src/main/energies.F90 +++ b/src/main/energies.F90 @@ -74,7 +74,7 @@ subroutine compute_energies(t) ispinz,mhd,gravity,poten,dustfrac,eos_vars,itemp,igasP,ics,& nden_nimhd,eta_nimhd,iion,ndustsmall,graindens,grainsize,& iamdust,ndusttypes,rad,iradxi,gtgrad,group_info,bin_info,n_group - use part, only:pxyzu,fxyzu,fext,apr_level,aprmassoftype + use part, only:pxyzu,fxyzu,fext,apr_level,aprmassoftype,pxyzu_ptmass use gravwaveutils, only:calculate_strain,calc_gravitwaves use centreofmass, only:get_centreofmass_accel use eos, only:polyk,gamma,eos_is_non_ideal,eos_outputs_gasP @@ -91,7 +91,7 @@ subroutine compute_energies(t) use boundary_dyn, only:dynamic_bdy,find_dynamic_boundaries use kernel, only:radkern use timestep, only:dtmax - use part, only:metrics + use part, only:metrics,metrics_ptmass use metric_tools, only:unpack_metric use utils_gr, only:dot_product_gr,get_geodesic_accel use vectorutils, only:cross_product3D @@ -183,7 +183,7 @@ subroutine compute_energies(t) !$omp shared(iev_B,iev_divB,iev_hdivB,iev_beta,iev_temp,iev_etao,iev_etah) & !$omp shared(iev_etaa,iev_vel,iev_vhall,iev_vion,iev_n) & !$omp shared(iev_dtg,iev_ts,iev_macc,iev_totlum,iev_erot,iev_viscrat) & -!$omp shared(eos_vars,grainsize,graindens,ndustsmall,metrics) & +!$omp shared(eos_vars,grainsize,graindens,ndustsmall,metrics,metrics_ptmass,pxyzu_ptmass) & !$omp private(i,j,xi,yi,zi,hi,rhoi,vxi,vyi,vzi,Bxi,Byi,Bzi,Bi,B2i,epoti,vsigi,v2i) & !$omp private(ponrhoi,spsoundi,gammai,dumx,dumy,dumz,valfven2i,divBi,hdivBonBi,curlBi) & !$omp private(rho1i,shearparam_art,shearparam_phys,ratio_phys_to_av,betai) & @@ -566,49 +566,109 @@ subroutine compute_energies(t) !--add contribution from sink particles ! if (id==master) then - !$omp do - do i=1,nptmass - xi = xyzmh_ptmass(1,i) - yi = xyzmh_ptmass(2,i) - zi = xyzmh_ptmass(3,i) - pmassi = xyzmh_ptmass(4,i) - if (pmassi < 0.) cycle - - vxi = vxyz_ptmass(1,i) - vyi = vxyz_ptmass(2,i) - vzi = vxyz_ptmass(3,i) - - !phii = fxyz_ptmass(4,i) - - xcom = xcom + pmassi*xi - ycom = ycom + pmassi*yi - zcom = zcom + pmassi*zi - mtot = mtot + pmassi - - xmom = xmom + pmassi*vxi - ymom = ymom + pmassi*vyi - zmom = zmom + pmassi*vzi - - angx = angx + pmassi*(yi*vzi - zi*vyi) - angy = angy + pmassi*(zi*vxi - xi*vzi) - angz = angz + pmassi*(xi*vyi - yi*vxi) - - angx = angx + xyzmh_ptmass(ispinx,i) - angy = angy + xyzmh_ptmass(ispiny,i) - angz = angz + xyzmh_ptmass(ispinz,i) - - v2i = vxi*vxi + vyi*vyi + vzi*vzi - ekin = ekin + pmassi*v2i - - ! rotational energy around each axis through the origin - if (calc_erot) then - call get_erot(xi,yi,zi,vxi,vyi,vzi,xyzcom,pmassi,erotxi,erotyi,erotzi) - call ev_data_update(ev_data_thread,iev_erot(1),erotxi) - call ev_data_update(ev_data_thread,iev_erot(2),erotyi) - call ev_data_update(ev_data_thread,iev_erot(3),erotzi) - endif - enddo - !$omp enddo + + if (.not. gr) then + !$omp do + do i=1,nptmass + xi = xyzmh_ptmass(1,i) + yi = xyzmh_ptmass(2,i) + zi = xyzmh_ptmass(3,i) + pmassi = xyzmh_ptmass(4,i) + if (pmassi < 0.) cycle + + vxi = vxyz_ptmass(1,i) + vyi = vxyz_ptmass(2,i) + vzi = vxyz_ptmass(3,i) + + !phii = fxyz_ptmass(4,i) + + xcom = xcom + pmassi*xi + ycom = ycom + pmassi*yi + zcom = zcom + pmassi*zi + mtot = mtot + pmassi + + xmom = xmom + pmassi*vxi + ymom = ymom + pmassi*vyi + zmom = zmom + pmassi*vzi + + angx = angx + pmassi*(yi*vzi - zi*vyi) + angy = angy + pmassi*(zi*vxi - xi*vzi) + angz = angz + pmassi*(xi*vyi - yi*vxi) + + angx = angx + xyzmh_ptmass(ispinx,i) + angy = angy + xyzmh_ptmass(ispiny,i) + angz = angz + xyzmh_ptmass(ispinz,i) + + v2i = vxi*vxi + vyi*vyi + vzi*vzi + ekin = ekin + pmassi*v2i + + + ! rotational energy around each axis through the origin + if (calc_erot) then + call get_erot(xi,yi,zi,vxi,vyi,vzi,xyzcom,pmassi,erotxi,erotyi,erotzi) + call ev_data_update(ev_data_thread,iev_erot(1),erotxi) + call ev_data_update(ev_data_thread,iev_erot(2),erotyi) + call ev_data_update(ev_data_thread,iev_erot(3),erotzi) + endif + enddo + !$omp enddo + else + !$omp do + do i=1,nptmass + ! calculate Kinetic and thermal energy for the GR-sink case. + xi = xyzmh_ptmass(1,i) + yi = xyzmh_ptmass(2,i) + zi = xyzmh_ptmass(3,i) + pmassi = xyzmh_ptmass(4,i) + if (pmassi < 0.) cycle + + vxi = vxyz_ptmass(1,i) + vyi = vxyz_ptmass(2,i) + vzi = vxyz_ptmass(3,i) + + pxi = pxyzu_ptmass(1,i) + pyi = pxyzu_ptmass(2,i) + pzi = pxyzu_ptmass(3,i) + + mtot = mtot + pmassi + + call unpack_metric(metrics_ptmass(:,:,:,i),betaUP=beta_gr_UP,alpha=alpha_gr,gammaijdown=gammaijdown) + bigvi = (vxyz_ptmass(1:3,i)+beta_gr_UP)/alpha_gr + v2i = dot_product_gr(bigvi,bigvi,gammaijdown) + lorentzi = 1./sqrt(1.-v2i) + pdotv = pxi*vxi + pyi*vyi + pzi*vzi + + ! angular momentum + fourvel_space = (lorentzi/alpha_gr)*vxyz_ptmass(1:3,i) + call cross_product3D(xyzmh_ptmass(1:3,i),fourvel_space,angi) ! position cross with four-velocity + + ! kinetic energy + ekini = pmassi*(pdotv + alpha_gr/lorentzi - 1.) ! The 'kinetic term' in total specific energy, minus rest mass + + ! kinetic energy & rms velocity + ekin = ekin + ekini + vrms = vrms + v2i + + ! linear momentum + xmom = xmom + pmassi*pxi + ymom = ymom + pmassi*pyi + zmom = zmom + pmassi*pzi + + ! angular momentum + angx = angx + pmassi*angi(1) + angy = angy + pmassi*angi(2) + angz = angz + pmassi*angi(3) + + ! rotational energy around each axis through the origin + if (calc_erot) then + call get_erot(xi,yi,zi,vxi,vyi,vzi,xyzcom,pmassi,erotxi,erotyi,erotzi) + call ev_data_update(ev_data_thread,iev_erot(1),erotxi) + call ev_data_update(ev_data_thread,iev_erot(2),erotyi) + call ev_data_update(ev_data_thread,iev_erot(3),erotzi) + endif + enddo + !$omp enddo + endif endif !$omp critical(collatedata) diff --git a/src/main/extern_gr.f90 b/src/main/extern_gr.f90 index 7043299e2..cc364c762 100644 --- a/src/main/extern_gr.f90 +++ b/src/main/extern_gr.f90 @@ -17,7 +17,8 @@ module extern_gr ! ! :Runtime parameters: None ! -! :Dependencies: eos, io, metric_tools, part, physcon, timestep, utils_gr +! :Dependencies: eos, io, metric, metric_tools, part, physcon, timestep, +! utils_gr ! implicit none @@ -58,28 +59,45 @@ end subroutine get_grforce ! gradients on all particles !+ !--------------------------------------------------------------- -subroutine get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,dtexternal) +subroutine get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,fext,dtexternal,use_sink,dens) use timestep, only:C_force use eos, only:ieos,get_pressure use part, only:isdead_or_accreted integer, intent(in) :: npart - real, intent(in) :: xyzh(:,:), metrics(:,:,:,:), metricderivs(:,:,:,:), dens(:) + real, intent(in) :: xyzh(:,:), metrics(:,:,:,:), metricderivs(:,:,:,:) real, intent(inout) :: vxyzu(:,:) real, intent(out) :: fext(:,:), dtexternal + real, intent(in), optional :: dens(:) + logical, intent(in), optional :: use_sink ! we pick the data from the xyzh array and assume u=0 for this case integer :: i - real :: dtf,pi + real :: dtf,pi,densi + real :: xyzhi(4),vxyzui(4) dtexternal = huge(dtexternal) !$omp parallel do default(none) & - !$omp shared(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,ieos,C_force) & - !$omp private(i,dtf,pi) & + !$omp shared(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,ieos,C_force,use_sink) & + !$omp private(i,dtf,pi,xyzhi,vxyzui,densi) & !$omp reduction(min:dtexternal) do i=1,npart - if (.not.isdead_or_accreted(xyzh(4,i))) then - pi = get_pressure(ieos,xyzh(:,i),dens(i),vxyzu(:,i)) - call get_grforce(xyzh(:,i),metrics(:,:,:,i),metricderivs(:,:,:,i),vxyzu(1:3,i),dens(i),vxyzu(4,i),pi,fext(1:3,i),dtf) + if (present(use_sink)) then + + xyzhi(1:3) = xyzh(1:3,i) + xyzhi(4) = xyzh(5,i) ! save smoothing length, h + vxyzui(1:3) = vxyzu(1:3,i) + vxyzui(4) = 0. + pi = 0. + densi = 1. + call get_grforce(xyzhi,metrics(:,:,:,i),metricderivs(:,:,:,i),vxyzui(1:3),densi,vxyzui(4),pi,fext(1:3,i),dtf) dtexternal = min(dtexternal,C_force*dtf) + + else + + if (.not.isdead_or_accreted(xyzh(4,i))) then + pi = get_pressure(ieos,xyzh(:,i),dens(i),vxyzu(:,i)) + call get_grforce(xyzh(:,i),metrics(:,:,:,i),metricderivs(:,:,:,i),vxyzu(1:3,i),dens(i),vxyzu(4,i),pi,fext(1:3,i),dtf) + dtexternal = min(dtexternal,C_force*dtf) + endif endif enddo !$omp end parallel do @@ -95,9 +113,10 @@ end subroutine get_grforce_all subroutine dt_grforce(xyzh,fext,dtf) use physcon, only:pi use metric_tools, only:imetric,imet_schwarzschild,imet_kerr + use metric, only:mass1 real, intent(in) :: xyzh(4),fext(3) real, intent(out) :: dtf - real :: r,r2,dtf1,dtf2,f2i + real :: r,r2,dtf1,dtf2,f2i,omega integer, parameter :: steps_per_orbit = 100 f2i = fext(1)*fext(1) + fext(2)*fext(2) + fext(3)*fext(3) @@ -111,7 +130,8 @@ subroutine dt_grforce(xyzh,fext,dtf) case (imet_schwarzschild,imet_kerr) r2 = xyzh(1)*xyzh(1) + xyzh(2)*xyzh(2) + xyzh(3)*xyzh(3) r = sqrt(r2) - dtf2 = (2.*pi*sqrt(r*r2))/steps_per_orbit + omega = sqrt(mass1/(r2*r)) + dtf2 = (2.*pi/(omega + epsilon(omega)))/steps_per_orbit case default dtf2 = huge(dtf2) end select diff --git a/src/main/initial.F90 b/src/main/initial.F90 index 67b480e15..b5689b7d9 100644 --- a/src/main/initial.F90 +++ b/src/main/initial.F90 @@ -23,8 +23,8 @@ module initial ! metric_tools, mf_write, mpibalance, mpidomain, mpimemory, mpitree, ! mpiutils, nicil, nicil_sup, omputils, options, part, partinject, ! porosity, ptmass, radiation_utils, readwrite_dumps, readwrite_infile, -! subgroup, timestep, timestep_ind, timestep_sts, timing, tmunu2grid, -! units, writeheader +! subgroup, substepping, timestep, timestep_ind, timestep_sts, timing, +! tmunu2grid, units, writeheader ! implicit none @@ -124,7 +124,7 @@ subroutine startrun(infile,logfile,evfile,dumpfile,noread) use mpiutils, only:reduceall_mpi,barrier_mpi,reduce_in_place_mpi use dim, only:maxp,maxalpha,maxvxyzu,maxptmass,maxdusttypes,itau_alloc,itauL_alloc,& nalpha,mhd,mhd_nonideal,do_radiation,gravity,use_dust,mpi,do_nucleation,& - use_dustgrowth,ind_timesteps,idumpfile,update_muGamma,use_apr + use_dustgrowth,ind_timesteps,idumpfile,update_muGamma,use_apr,gr use deriv, only:derivs use evwrite, only:init_evfile,write_evfile,write_evlog use energies, only:compute_energies @@ -147,8 +147,9 @@ subroutine startrun(infile,logfile,evfile,dumpfile,noread) use densityforce, only:densityiterate use linklist, only:set_linklist use boundary_dyn, only:dynamic_bdy,init_dynamic_bdy + use substepping, only:combine_forces_gr #ifdef GR - use part, only:metricderivs + use part, only:metricderivs,metricderivs_ptmass,metrics_ptmass,pxyzu_ptmass use cons2prim, only:prim2consall use eos, only:ieos use extern_gr, only:get_grforce_all,get_tmunu_all,get_tmunu_all_exact @@ -443,7 +444,7 @@ subroutine startrun(infile,logfile,evfile,dumpfile,noread) call init_metric(npart,xyzh,metrics,metricderivs) ! -- The conserved quantites (momentum and entropy) are being computed ! -- directly from the primitive values in the starting dumpfile. - call prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens=.false.) + call prim2consall(npart,xyzh,metrics,vxyzu,pxyzu,use_dens=.false.,dens=dens) write(iprint,*) '' call warning('initial','using preprocessor flag -DPRIM2CONS_FIRST') write(iprint,'(a,/)') ' This means doing prim2cons BEFORE the initial density calculation for this simulation.' @@ -457,12 +458,12 @@ subroutine startrun(infile,logfile,evfile,dumpfile,noread) endif #ifndef PRIM2CONS_FIRST call init_metric(npart,xyzh,metrics,metricderivs) - call prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens=.false.) + call prim2consall(npart,xyzh,metrics,vxyzu,pxyzu,use_dens=.false.,dens=dens) #endif if (iexternalforce > 0 .and. imetric /= imet_minkowski) then call initialise_externalforces(iexternalforce,ierr) if (ierr /= 0) call fatal('initial','error in external force settings/initialisation') - call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,dtextforce) + call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,fext,dtextforce,dens=dens) endif #else if (iexternalforce > 0) then @@ -525,14 +526,28 @@ subroutine startrun(infile,logfile,evfile,dumpfile,noread) if (nptmass > 0) then if (id==master) write(iprint,"(a,i12)") ' nptmass = ',nptmass if (iH2R > 0) call update_ionrates(nptmass,xyzmh_ptmass,h_acc) - ! compute initial sink-sink forces and get timestep - if (use_regnbody) then - call init_subgroup - call group_identify(nptmass,n_group,n_ingroup,n_sing,xyzmh_ptmass,vxyz_ptmass,group_info,bin_info,nmatrix) + if (.not. gr) then + ! compute initial sink-sink forces and get timestep + if (use_regnbody) then + call init_subgroup + call group_identify(nptmass,n_group,n_ingroup,n_sing,xyzmh_ptmass,vxyz_ptmass,group_info,bin_info,nmatrix) + endif + call get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass,epot_sinksink,dtsinksink,& + iexternalforce,time,merge_ij,merge_n,dsdt_ptmass,& + group_info,bin_info) endif +#ifdef GR + ! calculate metric derivatives and the external force caused by the metric on the sink particles + ! this will also return the timestep for sink-sink + call init_metric(nptmass,xyzmh_ptmass,metrics_ptmass,metricderivs_ptmass) + call prim2consall(nptmass,xyzmh_ptmass,metrics_ptmass,& + vxyz_ptmass,pxyzu_ptmass,use_dens=.false.,use_sink=.true.) + call get_grforce_all(nptmass,xyzmh_ptmass,metrics_ptmass,metricderivs_ptmass,& + vxyz_ptmass,fxyz_ptmass,dtextforce,use_sink=.true.) + ! sinks in GR, provide external force due to metric to determine the sink total force call get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass,epot_sinksink,dtsinksink,& - iexternalforce,time,merge_ij,merge_n,dsdt_ptmass,& - group_info,bin_info) + iexternalforce,time,merge_ij,merge_n,dsdt_ptmass) +#endif dtsinksink = C_force*dtsinksink if (id==master) write(iprint,*) 'dt(sink-sink) = ',dtsinksink dtextforce = min(dtextforce,dtsinksink) diff --git a/src/main/inject_windtunnel.f90 b/src/main/inject_windtunnel.f90 index 07f1fa166..98ad6ef1f 100644 --- a/src/main/inject_windtunnel.f90 +++ b/src/main/inject_windtunnel.f90 @@ -19,7 +19,6 @@ module inject ! - handled_layers : *(integer) number of handled BHL wind layers* ! - hold_star : *1: subtract CM velocity of star particles at each timestep* ! - lattice_type : *0: cubic distribution, 1: closepacked distribution* -! - nstar : *No. of particles making up sphere* ! - pres_inf : *ambient pressure (code units)* ! - rho_inf : *ambient density (code units)* ! - v_inf : *wind speed (code units)* @@ -298,10 +297,10 @@ subroutine subtract_star_vcom(nsphere,xyzh,vxyzu) vstar = vstar/real(nbulk) do i=1,nsphere - if (xyzh(1,i) < 2.*Rstar) then + if (xyzh(1,i) < 2.*Rstar) then vxyzu(1:3,i) = vxyzu(1:3,i) - vstar - endif -enddo + endif + enddo end subroutine subtract_star_vcom diff --git a/src/main/part.F90 b/src/main/part.F90 index 6f7655daa..b2ecb91dc 100644 --- a/src/main/part.F90 +++ b/src/main/part.F90 @@ -33,7 +33,7 @@ module part maxphase,maxgradh,maxan,maxdustan,maxmhdan,maxneigh,maxprad,maxp_nucleation,& maxTdust,store_dust_temperature,use_krome,maxp_krome, & do_radiation,gr,maxgr,maxgran,n_nden_phantom,do_nucleation,& - inucleation,itau_alloc,itauL_alloc,use_apr,apr_maxlevel,maxp_apr + inucleation,itau_alloc,itauL_alloc,use_apr,apr_maxlevel,maxp_apr,maxptmassgr use dtypekdtree, only:kdnode #ifdef KROME use krome_user, only: krome_nmols @@ -159,6 +159,7 @@ module part maxeosvars = 7 character(len=*), parameter :: eos_vars_label(maxeosvars) = & (/'pressure ','sound speed','temperature','mu ','H fraction ','metallicity','gamma '/) + ! !--energy_variables ! @@ -187,6 +188,12 @@ module part real, allocatable :: tmunus(:,:,:) !tmunus(0:3,0:3,maxgr) real, allocatable :: sqrtgs(:) ! sqrtg(maxgr) ! +!--sink particles in General relativity +! + real, allocatable :: pxyzu_ptmass(:,:) !pxyz_ptmass(maxvxyzu,maxgr) + real, allocatable :: metrics_ptmass(:,:,:,:) !metrics(0:3,0:3,2,maxgr) + real, allocatable :: metricderivs_ptmass(:,:,:,:) !metricderivs(0:3,0:3,3,maxgr) +! !--sink particles ! integer, parameter :: ihacc = 5 ! accretion radius @@ -473,6 +480,9 @@ subroutine allocate_part call allocate_array('metricderivs', metricderivs, 4, 4, 3, maxgr) call allocate_array('tmunus', tmunus, 4, 4, maxgr) call allocate_array('sqrtgs', sqrtgs, maxgr) + call allocate_array('pxyzu_ptmass', pxyzu_ptmass, maxvxyzu, maxptmassgr) + call allocate_array('metrics_ptmass', metrics_ptmass, 4, 4, 2, maxptmassgr) + call allocate_array('metricderivs_ptmass', metricderivs_ptmass, 4, 4, 3, maxptmassgr) call allocate_array('xyzmh_ptmass', xyzmh_ptmass, nsinkproperties, maxptmass) call allocate_array('vxyz_ptmass', vxyz_ptmass, 3, maxptmass) call allocate_array('fxyz_ptmass', fxyz_ptmass, 4, maxptmass) @@ -567,6 +577,9 @@ subroutine deallocate_part if (allocated(metricderivs)) deallocate(metricderivs) if (allocated(tmunus)) deallocate(tmunus) if (allocated(sqrtgs)) deallocate(sqrtgs) + if (allocated(pxyzu_ptmass)) deallocate(pxyzu_ptmass) + if (allocated(metrics_ptmass)) deallocate(metrics_ptmass) + if (allocated(metricderivs_ptmass)) deallocate(metricderivs_ptmass) if (allocated(xyzmh_ptmass)) deallocate(xyzmh_ptmass) if (allocated(vxyz_ptmass)) deallocate(vxyz_ptmass) if (allocated(fxyz_ptmass)) deallocate(fxyz_ptmass) diff --git a/src/main/partinject.F90 b/src/main/partinject.F90 index 697a46be1..8143981fe 100644 --- a/src/main/partinject.F90 +++ b/src/main/partinject.F90 @@ -196,9 +196,9 @@ subroutine update_injected_particles(npartold,npart,istepfrac,nbinmax,time,dtmax ! after injecting particles, reinitialise metrics on all particles ! call init_metric(npart,xyzh,metrics,metricderivs) - call prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens=.false.) + call prim2consall(npart,xyzh,metrics,vxyzu,pxyzu,use_dens=.false.,dens=dens) if (iexternalforce > 0 .and. imetric /= imet_minkowski) then - call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,dtext_dum) ! Not 100% sure if this is needed here + call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,fext,dtext_dum,dens=dens) ! Not 100% sure if this is needed here endif #endif diff --git a/src/main/ptmass.F90 b/src/main/ptmass.F90 index 4f4c9f995..c79d97064 100644 --- a/src/main/ptmass.F90 +++ b/src/main/ptmass.F90 @@ -336,23 +336,24 @@ subroutine get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass,phitot,dtsinksin #ifdef FINVSQRT use fastmath, only:finvsqrt #endif + use dim, only:gr use externalforces, only:externalforce use extern_geopot, only:get_geopot_force use kernel, only:kernel_softening,radkern use vectorutils, only:unitvec use part, only:igarg,igid,icomp,ihacc,ipert integer, intent(in) :: nptmass + integer, intent(in) :: iexternalforce real, intent(in) :: xyzmh_ptmass(nsinkproperties,nptmass) + real, intent(in) :: ti real, intent(out) :: fxyz_ptmass(4,nptmass) real, intent(out) :: phitot,dtsinksink - integer, intent(in) :: iexternalforce - real, intent(in) :: ti integer, intent(out) :: merge_ij(:),merge_n real, intent(out) :: dsdt_ptmass(3,nptmass) integer, optional, intent(in) :: group_info(4,nptmass) - real, optional, intent(out) :: bin_info(6,nptmass) real, optional, intent(in) :: extrapfac real, optional, intent(in) :: fsink_old(4,nptmass) + real, optional, intent(out) :: bin_info(6,nptmass) real :: xi,yi,zi,pmassi,pmassj,hacci,haccj,fxi,fyi,fzi,phii real :: ddr,dx,dy,dz,rr2,rr2j,dr3,f1,f2 real :: hsoft1,hsoft21,q2i,qi,psoft,fsoft @@ -554,7 +555,7 @@ subroutine get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass,phitot,dtsinksin ! !--apply external forces ! - if (iexternalforce > 0) then + if (iexternalforce > 0 .and. .not. gr) then call externalforce(iexternalforce,xi,yi,zi,0.,ti,fextx,fexty,fextz,phiext,ii=-i) fxi = fxi + fextx fyi = fyi + fexty @@ -595,7 +596,6 @@ subroutine get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass,phitot,dtsinksin fzi = fxyz_ptmass(3,i) phii = fxyz_ptmass(4,i) f2 = fxi*fxi + fyi*fyi + fzi*fzi - !print*,'phi = ',phii,' accel = ',sqrt(f2) ! !--we use an additional tolerance here on the sink-sink timestep ! so that with the default C_force of ~0.25 we get a few diff --git a/src/main/step_leapfrog.F90 b/src/main/step_leapfrog.F90 index a399f495b..032b5a865 100644 --- a/src/main/step_leapfrog.F90 +++ b/src/main/step_leapfrog.F90 @@ -24,8 +24,8 @@ module step_lf_global ! ! :Dependencies: boundary_dyn, cons2prim, cons2primsolver, cooling, ! damping, deriv, dim, extern_gr, growth, io, io_summary, metric_tools, -! mpiutils, options, part, porosity, substepping, timestep, timestep_ind, -! timestep_sts, timing +! mpiutils, options, part, porosity, ptmass, substepping, timestep, +! timestep_ind, timestep_sts, timing ! use dim, only:maxp,maxvxyzu,do_radiation,ind_timesteps use part, only:vpred,Bpred,dustpred,ppred @@ -98,13 +98,14 @@ subroutine step(npart,nactive,t,dtsph,dtextforce,dtnew) iamboundary,get_ntypes,npartoftypetot,apr_level,& dustfrac,dustevol,ddustevol,eos_vars,alphaind,nptmass,& dustprop,ddustprop,dustproppred,pxyzu,dens,metrics,ics,& - filfac,filfacpred,mprev,filfacprev,aprmassoftype,isionised + filfac,filfacpred,mprev,filfacprev,aprmassoftype,isionised,epot_sinksink use options, only:avdecayconst,alpha,ieos,alphamax use deriv, only:derivs - use timestep, only:dterr,bignumber,tolv + use timestep, only:dterr,bignumber,tolv,C_force use mpiutils, only:reduceall_mpi use part, only:nptmass,xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass, & - dsdt_ptmass,fsink_old,ibin_wake,dptmass,linklist_ptmass + dsdt_ptmass,fsink_old,ibin_wake,dptmass,linklist_ptmass, & + pxyzu_ptmass,metrics_ptmass use part, only:n_group,n_ingroup,n_sing,gtgrad,group_info,bin_info,nmatrix use io_summary, only:summary_printout,summary_variable,iosumtvi,iowake, & iosumflrp,iosumflrps,iosumflrc @@ -113,9 +114,9 @@ subroutine step(npart,nactive,t,dtsph,dtextforce,dtnew) use timestep_ind, only:get_dt,nbinmax,decrease_dtmax,dt_too_small use timestep_sts, only:sts_get_dtau_next,use_sts,ibin_sts,sts_it_n use part, only:ibin,ibin_old,twas,iactive,ibin_wake - use part, only:metricderivs + use part, only:metricderivs,metricderivs_ptmass,fxyz_ptmass_sinksink use metric_tools, only:imet_minkowski,imetric - use cons2prim, only:cons2primall + use cons2prim, only:cons2primall,cons2primall_sink use extern_gr, only:get_grforce_all use cooling, only:ufloor,cooling_in_step use timing, only:increment_timer,get_timings,itimer_substep @@ -125,7 +126,8 @@ subroutine step(npart,nactive,t,dtsph,dtextforce,dtnew) use damping, only:idamp use cons2primsolver, only:conservative2primitive,primitive2conservative use substepping, only:substep,substep_gr, & - substep_sph_gr,substep_sph + substep_sph_gr,substep_sph,combine_forces_gr + use ptmass, only:get_accel_sink_sink,get_accel_sink_gas integer, intent(inout) :: npart integer, intent(in) :: nactive @@ -137,6 +139,11 @@ subroutine step(npart,nactive,t,dtsph,dtextforce,dtnew) real :: vxi,vyi,vzi,eni,hdtsph,pmassi real :: alphaloci,source,tdecay1,hi,rhoi,ddenom,spsoundi real :: v2mean,hdti + real :: dtsinksink + real :: fonrmax,poti,dtphi2 + real :: fext_gas(4,npart) + integer :: merge_ij(nptmass) + integer :: merge_n real(kind=4) :: t1,t2,tcpu1,tcpu2 real :: pxi,pyi,pzi,p2i,p2mean real :: dtsph_next,dti,time_now @@ -147,6 +154,7 @@ subroutine step(npart,nactive,t,dtsph,dtextforce,dtnew) ! ! set initial quantities ! + fext_gas = 0. timei = t hdtsph = 0.5*dtsph dterr = bignumber @@ -238,16 +246,38 @@ subroutine step(npart,nactive,t,dtsph,dtextforce,dtnew) !---------------------------------------------------------------------- call get_timings(t1,tcpu1) if (gr) then - if ((iexternalforce > 0 .and. imetric /= imet_minkowski) .or. idamp > 0) then - call cons2primall(npart,xyzh,metrics,pxyzu,vxyzu,dens,eos_vars) - call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,dtextforce) - call substep_gr(npart,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,t) + call cons2primall(npart,xyzh,metrics,pxyzu,vxyzu,dens,eos_vars) + call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,fext,dtextforce,dens=dens) + ! first calculate all the force arrays on sink particles + if (nptmass > 0) then + + call cons2primall_sink(nptmass,xyzmh_ptmass,metrics_ptmass,pxyzu_ptmass,vxyz_ptmass) + call get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass_sinksink,epot_sinksink,dtsinksink,& + iexternalforce,timei,merge_ij,merge_n,dsdt_ptmass) + call get_grforce_all(nptmass,xyzmh_ptmass,metrics_ptmass,metricderivs_ptmass,& + vxyz_ptmass,fxyz_ptmass,dtextforce,use_sink=.true.) + do i=1,nptmass + fxyz_ptmass(1:3,i) = fxyz_ptmass(1:3,i) + fxyz_ptmass_sinksink(1:3,i) + enddo + do i=1,npart + call get_accel_sink_gas(nptmass,xyzh(1,i),xyzh(2,i),xyzh(3,i),xyzh(4,i),xyzmh_ptmass, & + fext(1,i),fext(2,i),fext(3,i),poti,pmassi,fxyz_ptmass,& + dsdt_ptmass,fonrmax,dtphi2,bin_info) + enddo + endif + + if ((iexternalforce > 0 .and. imetric /= imet_minkowski) .or. idamp > 0 .or. nptmass > 0 .or. & + (nptmass > 0 .and. imetric == imet_minkowski)) then + + ! for now use the minimum of the two timesteps as dtextforce + dtextforce = min(dtextforce, C_force*dtsinksink, C_force*sqrt(dtphi2)) + call substep_gr(npart,nptmass,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,t,& + xyzmh_ptmass,vxyz_ptmass,pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass,fxyz_ptmass) else call substep_sph_gr(dtsph,npart,xyzh,vxyzu,dens,pxyzu,metrics) endif else if (nptmass > 0 .or. iexternalforce > 0 .or. h2chemistry .or. cooling_in_step .or. idamp > 0) then - call substep(npart,ntypes,nptmass,dtsph,dtextforce,t,xyzh,vxyzu,& fext,xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass,dsdt_ptmass,& dptmass,linklist_ptmass,fsink_old,nbinmax,ibin_wake,gtgrad, & diff --git a/src/main/substepping.F90 b/src/main/substepping.F90 index 08f5d88a5..8cb7382c6 100644 --- a/src/main/substepping.F90 +++ b/src/main/substepping.F90 @@ -38,6 +38,7 @@ module substepping public :: substep_sph_gr public :: substep public :: get_force + public :: combine_forces_gr,combine_forces_gr_one private @@ -109,38 +110,35 @@ subroutine substep_sph_gr(dt,npart,xyzh,vxyzu,dens,pxyzu,metrics) end subroutine substep_sph_gr -subroutine substep_gr(npart,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,time) - use dim, only:maxptmass,maxp,maxvxyzu,use_apr +subroutine substep_gr(npart,nptmass,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,time,& + xyzmh_ptmass,vxyz_ptmass,pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass,fxyz_ptmass) + use dim, only:maxptmass,maxvxyzu,use_apr use io, only:iverbose,id,master,iprint,warning,fatal - use externalforces, only:externalforce,accrete_particles,update_externalforce - use options, only:iexternalforce - use part, only:maxphase,isdead_or_accreted,iamboundary,igas,iphase,iamtype,& - massoftype,rhoh,ien_type,eos_vars,igamma,itemp,igasP,& - aprmassoftype,apr_level + use part, only:isdead_or_accreted,iamboundary,igas,iamtype,& + massoftype,rhoh,igamma,itemp,igasP use io_summary, only:summary_variable,iosumextr,iosumextt,summary_accrete - use timestep, only:bignumber,C_force,xtol,ptol - use eos, only:equationofstate,ieos + use timestep, only:bignumber + use eos, only:equationofstate use cons2primsolver,only:conservative2primitive use extern_gr, only:get_grforce use metric_tools, only:pack_metric,pack_metricderivs - use damping, only:calc_damp,apply_damp,idamp - integer, intent(in) :: npart,ntypes + use damping, only:calc_damp,apply_damp + integer, intent(in) :: npart,ntypes,nptmass real, intent(in) :: dtsph,time real, intent(inout) :: dtextforce real, intent(inout) :: xyzh(:,:),vxyzu(:,:),fext(:,:),pxyzu(:,:),dens(:),metrics(:,:,:,:),metricderivs(:,:,:,:) - integer :: i,itype,nsubsteps,naccreted,its,ierr,nlive + real, intent(inout) :: xyzmh_ptmass(:,:),vxyz_ptmass(:,:),fxyz_ptmass(:,:) + real, intent(inout) :: pxyzu_ptmass(:,:),metrics_ptmass(:,:,:,:),metricderivs_ptmass(:,:,:,:) + + integer :: itype,nsubsteps,naccreted,nlive,nlive_sinks,naccreted_sinks real :: timei,t_end_step,hdt,pmassi - real :: dt,dtf,dtextforcenew,dtextforce_min - real :: pri,spsoundi,pondensi,tempi,gammai - real, save :: pprev(3),xyz_prev(3),fstar(3),vxyz_star(3),xyz(3),pxyz(3),vxyz(3),fexti(3) - !$omp threadprivate(pprev,xyz_prev,fstar,vxyz_star,xyz,pxyz,vxyz,fexti) - real :: x_err,pmom_err,accretedmass,damp_fac + real :: dt,dtextforcenew,dtextforce_min + real :: accretedmass,damp_fac ! real, save :: dmdt = 0. - logical :: last_step,done,converged,accreted + logical :: last_step,done integer, parameter :: itsmax = 50 integer :: pitsmax,xitsmax real :: perrmax,xerrmax - real :: rhoi,hi,eni,uui,densi pitsmax = 0 xitsmax = 0 @@ -165,6 +163,7 @@ subroutine substep_gr(npart,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metric nsubsteps = 0 dtextforce_min = huge(dt) done = .false. + substeps: do while (timei <= t_end_step .and. .not.done) hdt = 0.5*dt timei = timei + dt @@ -176,126 +175,13 @@ subroutine substep_gr(npart,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metric if (.not.last_step .and. iverbose > 1 .and. id==master) then write(iprint,"(a,f14.6)") '> external forces only : t=',timei endif - !--------------------------- - ! predictor during substeps - !--------------------------- - ! - ! predictor step for external forces, also recompute external forces - ! - !$omp parallel do default(none) & - !$omp shared(npart,xyzh,vxyzu,fext,iphase,ntypes,massoftype) & - !$omp shared(maxphase,maxp,eos_vars,aprmassoftype,apr_level) & - !$omp shared(dt,hdt,xtol,ptol) & - !$omp shared(ieos,pxyzu,dens,metrics,metricderivs,ien_type) & - !$omp private(i,its,spsoundi,tempi,rhoi,hi,eni,uui,densi) & - !$omp private(converged,pmom_err,x_err,pri,ierr,gammai) & - !$omp firstprivate(pmassi,itype) & - !$omp reduction(max:xitsmax,pitsmax,perrmax,xerrmax) & - !$omp reduction(min:dtextforcenew) - predictor: do i=1,npart - xyz(1) = xyzh(1,i) - xyz(2) = xyzh(2,i) - xyz(3) = xyzh(3,i) - hi = xyzh(4,i) - if (.not.isdead_or_accreted(hi)) then - if (ntypes > 1 .and. maxphase==maxp) then - itype = iamtype(iphase(i)) - if (use_apr) then - pmassi = aprmassoftype(itype,apr_level(i)) - else - pmassi = massoftype(itype) - endif - elseif (use_apr) then - pmassi = aprmassoftype(igas,apr_level(i)) - endif - its = 0 - converged = .false. - ! - ! make local copies of array quantities - ! - pxyz(1:3) = pxyzu(1:3,i) - eni = pxyzu(4,i) - vxyz(1:3) = vxyzu(1:3,i) - uui = vxyzu(4,i) - fexti = fext(:,i) - - pxyz = pxyz + hdt*fexti - - !-- unpack thermo variables for the first guess in cons2prim - densi = dens(i) - pri = eos_vars(igasP,i) - gammai = eos_vars(igamma,i) - tempi = eos_vars(itemp,i) - rhoi = rhoh(hi,massoftype(igas)) - - ! Note: grforce needs derivatives of the metric, - ! which do not change between pmom iterations - pmom_iterations: do while (its <= itsmax .and. .not. converged) - its = its + 1 - pprev = pxyz - call conservative2primitive(xyz,metrics(:,:,:,i),vxyz,densi,uui,pri,& - tempi,gammai,rhoi,pxyz,eni,ierr,ien_type) - if (ierr > 0) call warning('cons2primsolver [in substep_gr (a)]','enthalpy did not converge',i=i) - call get_grforce(xyzh(:,i),metrics(:,:,:,i),metricderivs(:,:,:,i),vxyz,densi,uui,pri,fstar) - pxyz = pprev + hdt*(fstar - fexti) - pmom_err = maxval(abs(pxyz - pprev)) - if (pmom_err < ptol) converged = .true. - fexti = fstar - enddo pmom_iterations - if (its > itsmax ) call warning('substep_gr',& - 'max # of pmom iterations',var='pmom_err',val=pmom_err) - pitsmax = max(its,pitsmax) - perrmax = max(pmom_err,perrmax) - - call conservative2primitive(xyz,metrics(:,:,:,i),vxyz,densi,uui,pri,tempi,& - gammai,rhoi,pxyz,eni,ierr,ien_type) - if (ierr > 0) call warning('cons2primsolver [in substep_gr (b)]','enthalpy did not converge',i=i) - xyz = xyz + dt*vxyz - call pack_metric(xyz,metrics(:,:,:,i)) - its = 0 - converged = .false. - vxyz_star = vxyz - ! Note: since particle positions change between iterations - ! the metric and its derivatives need to be updated. - ! cons2prim does not require derivatives of the metric, - ! so those can updated once the iterations are complete - ! in order to reduce the number of computations. - xyz_iterations: do while (its <= itsmax .and. .not. converged) - its = its+1 - xyz_prev = xyz - call conservative2primitive(xyz,metrics(:,:,:,i),vxyz_star,densi,uui,& - pri,tempi,gammai,rhoi,pxyz,eni,ierr,ien_type) - if (ierr > 0) call warning('cons2primsolver [in substep_gr (c)]','enthalpy did not converge',i=i) - xyz = xyz_prev + hdt*(vxyz_star - vxyz) - x_err = maxval(abs(xyz-xyz_prev)) - if (x_err < xtol) converged = .true. - vxyz = vxyz_star - ! UPDATE METRIC HERE - call pack_metric(xyz,metrics(:,:,:,i)) - enddo xyz_iterations - call pack_metricderivs(xyz,metricderivs(:,:,:,i)) - if (its > itsmax ) call warning('substep_gr','Reached max number of x iterations. x_err ',val=x_err) - xitsmax = max(its,xitsmax) - xerrmax = max(x_err,xerrmax) - - ! re-pack arrays back where they belong - xyzh(1:3,i) = xyz(1:3) - pxyzu(1:3,i) = pxyz(1:3) - vxyzu(1:3,i) = vxyz(1:3) - vxyzu(4,i) = uui - fext(:,i) = fexti - dens(i) = densi - eos_vars(igasP,i) = pri - eos_vars(itemp,i) = tempi - eos_vars(igamma,i) = gammai - - ! Skip remainder of update if boundary particle; note that fext==0 for these particles - if (iamboundary(itype)) cycle predictor - endif - enddo predictor - !$omp end parallel do + call predict_gr(xyzh,vxyzu,ntypes,pxyzu,fext,npart,nptmass,dt,timei,hdt, & + dens,metrics,metricderivs,xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass,& + metrics_ptmass,metricderivs_ptmass,pxyzu_ptmass,pitsmax,perrmax, & + xitsmax,xerrmax,dtextforcenew) + if (iverbose >= 2 .and. id==master) then write(iprint,*) '------ Iterations summary: -------------------------------' @@ -311,59 +197,11 @@ subroutine substep_gr(npart,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metric naccreted = 0 nlive = 0 dtextforce_min = bignumber - !$omp parallel default(none) & - !$omp shared(npart,xyzh,metrics,metricderivs,vxyzu,fext,iphase,ntypes,massoftype,hdt,timei) & - !$omp shared(maxphase,maxp,apr_level,aprmassoftype) & - !$omp private(i,accreted) & - !$omp shared(ieos,dens,pxyzu,iexternalforce,C_force) & - !$omp private(pri,pondensi,spsoundi,tempi,dtf) & - !$omp firstprivate(itype,pmassi) & - !$omp reduction(min:dtextforce_min) & - !$omp reduction(+:accretedmass,naccreted,nlive) & - !$omp shared(idamp,damp_fac) - !$omp do - accreteloop: do i=1,npart - if (.not.isdead_or_accreted(xyzh(4,i))) then - if (ntypes > 1 .and. maxphase==maxp) then - itype = iamtype(iphase(i)) - if (use_apr) then - pmassi = aprmassoftype(itype,apr_level(i)) - else - pmassi = massoftype(itype) - endif - ! if (itype==iboundary) cycle accreteloop - elseif (use_apr) then - pmassi = aprmassoftype(igas,apr_level(i)) - endif - - call equationofstate(ieos,pondensi,spsoundi,dens(i),xyzh(1,i),xyzh(2,i),xyzh(3,i),tempi,vxyzu(4,i)) - pri = pondensi*dens(i) - call get_grforce(xyzh(:,i),metrics(:,:,:,i),metricderivs(:,:,:,i),vxyzu(1:3,i),dens(i),vxyzu(4,i),pri,fext(1:3,i),dtf) - dtextforce_min = min(dtextforce_min,C_force*dtf) - - if (idamp > 0) then - call apply_damp(fext(1,i), fext(2,i), fext(3,i), vxyzu(1:3,i), xyzh(1:3,i), damp_fac) - endif - - ! - ! correct v to the full step using only the external force - ! - pxyzu(1:3,i) = pxyzu(1:3,i) + hdt*fext(1:3,i) - ! Do we need call cons2prim here ?? - - if (iexternalforce > 0) then - call accrete_particles(iexternalforce,xyzh(1,i),xyzh(2,i), & - xyzh(3,i),xyzh(4,i),pmassi,timei,accreted,i) - if (accreted) then - accretedmass = accretedmass + pmassi - naccreted = naccreted + 1 - endif - endif - nlive = nlive + 1 - endif - enddo accreteloop - !$omp enddo - !$omp end parallel + call accrete_gr(xyzh,vxyzu,dens,fext,metrics,metricderivs,nlive,naccreted,& + pxyzu,accretedmass,hdt,npart,nptmass,& + ntypes,dtextforce_min,timei,xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass,& + metrics_ptmass,metricderivs_ptmass,& + pxyzu_ptmass,nlive_sinks,naccreted_sinks) if (npart > 2 .and. nlive < 2) then call fatal('step','all particles accreted',var='nlive',ival=nlive) @@ -849,7 +687,7 @@ subroutine get_force(nptmass,npart,nsubsteps,ntypes,timei,dtextforce,xyzh,vxyzu, force_count,extf_vdep_flag,linklist_ptmass,bin_info,group_info,& fsink_old,isionised) use io, only:iverbose,master,id,iprint,warning,fatal - use dim, only:maxp,maxvxyzu,itau_alloc,use_apr + use dim, only:maxp,maxvxyzu,itau_alloc,gr,use_apr use ptmass, only:get_accel_sink_gas,get_accel_sink_sink,merge_sinks, & ptmass_vdependent_correction,n_force_order use options, only:iexternalforce @@ -1219,8 +1057,599 @@ subroutine get_external_force_gas(xi,yi,zi,hi,vxi,vyi,vzi,timei,i,dtextforcenew, fextz = fextz + fextv(3) endif - end subroutine get_external_force_gas + !---------------------------------------------------------------- + !+ + ! routine for prediction substep in GR case + !+ + !---------------------------------------------------------------- +subroutine predict_gr(xyzh,vxyzu,ntypes,pxyzu,fext,npart,nptmass,dt,timei,hdt, & + dens,metrics,metricderivs,& + xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass,& + metrics_ptmass,metricderivs_ptmass,pxyzu_ptmass,& + pitsmax,perrmax,& + xitsmax,xerrmax,dtextforcenew) + + use dim, only:maxptmass,maxp,maxvxyzu,use_apr + use io, only:master,warning,fatal + use externalforces, only:externalforce,accrete_particles,update_externalforce + use part, only:maxphase,isdead_or_accreted,iamboundary,igas,iphase,iamtype,& + massoftype,rhoh,ien_type,eos_vars,igamma,itemp,igasP,& + aprmassoftype,apr_level,epot_sinksink,fxyz_ptmass_sinksink,dsdt_ptmass,& + fsink_old + use io_summary, only:summary_variable,iosumextr,iosumextt,summary_accrete + use timestep, only:bignumber,xtol,ptol + use eos, only:equationofstate,ieos + use cons2primsolver,only:conservative2primitive + use extern_gr, only:get_grforce + use metric_tools, only:pack_metric,pack_metricderivs + use damping, only:calc_damp,apply_damp + use options, only:iexternalforce + use ptmass, only:get_accel_sink_sink,get_accel_sink_gas + + + real, intent(inout) :: xyzh(:,:),vxyzu(:,:),fext(:,:),pxyzu(:,:),dens(:),metrics(:,:,:,:),metricderivs(:,:,:,:) + real, intent(inout) :: xyzmh_ptmass(:,:),vxyz_ptmass(:,:),fxyz_ptmass(:,:),pxyzu_ptmass(:,:) + real, intent(inout) :: metrics_ptmass(:,:,:,:),metricderivs_ptmass(:,:,:,:) + real, intent(in) :: dt,hdt,timei + integer, intent(in) :: npart,ntypes,nptmass + integer, intent(inout) :: pitsmax,xitsmax + real, intent(inout) :: perrmax,xerrmax,dtextforcenew + + integer :: i,its,ierr,itype + integer, parameter :: itsmax = 50 + real :: pmassi + real :: pri,spsoundi,tempi,gammai + real, save :: pprev(3),xyz_prev(3),fstar(3),vxyz_star(3),xyz(3),pxyz(3),vxyz(3),fexti(3) + !$omp threadprivate(pprev,xyz_prev,fstar,vxyz_star,xyz,pxyz,vxyz,fexti) + real :: x_err,pmom_err + ! real, save :: dmdt = 0. + logical :: converged + real :: rhoi,hi,eni,uui,densi,poti + real :: bin_info(6,nptmass) + real :: dtphi2,dtsinksink,fonrmax + integer :: merge_ij(nptmass),merge_n + + pmassi = massoftype(igas) + itype = igas + fsink_old = fxyz_ptmass + fxyz_ptmass = 0. + !---------------------------------------------- + ! calculate acceleration sink-sink + !---------------------------------------------- + call get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass_sinksink,epot_sinksink,dtsinksink,& + iexternalforce,timei,merge_ij,merge_n,dsdt_ptmass) + !---------------------------------------------- + ! predictor during substeps for gas particles + !---------------------------------------------- + ! + ! predictor step for external forces, also recompute external forces + ! + !$omp parallel do default(none) & + !$omp shared(npart,xyzh,vxyzu,fext,iphase,ntypes,massoftype) & + !$omp shared(nptmass,xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass) & + !$omp shared(maxphase,maxp,eos_vars) & + !$omp shared(bin_info,dtphi2,poti,fonrmax) & + !$omp shared(fxyz_ptmass_sinksink) & + !$omp shared(dt,hdt,xtol,ptol,aprmassoftype,apr_level) & + !$omp shared(ieos,pxyzu,dens,metrics,metricderivs,ien_type) & + !$omp shared(pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass) & + !$omp shared(dtsinksink,epot_sinksink,merge_ij,merge_n,dsdt_ptmass,iexternalforce) & + !$omp private(i,its,spsoundi,tempi,rhoi,hi,eni,uui,densi) & + !$omp private(converged,pmom_err,x_err,pri,ierr,gammai) & + !$omp firstprivate(pmassi,itype) & + !$omp reduction(max:xitsmax,pitsmax,perrmax,xerrmax) & + !$omp reduction(min:dtextforcenew) + predictor: do i=1,npart + xyz(1) = xyzh(1,i) + xyz(2) = xyzh(2,i) + xyz(3) = xyzh(3,i) + hi = xyzh(4,i) + if (.not.isdead_or_accreted(hi)) then + if (ntypes > 1 .and. maxphase==maxp) then + itype = iamtype(iphase(i)) + if (use_apr) then + pmassi = aprmassoftype(itype,apr_level(i)) + else + pmassi = massoftype(itype) + endif + elseif (use_apr) then + pmassi = aprmassoftype(igas,apr_level(i)) + endif + + its = 0 + converged = .false. + ! + ! make local copies of array quantities + ! + pxyz(1:3) = pxyzu(1:3,i) + eni = pxyzu(4,i) + vxyz(1:3) = vxyzu(1:3,i) + uui = vxyzu(4,i) + fexti = fext(:,i) + + pxyz = pxyz + hdt*fexti + + !-- unpack thermo variables for the first guess in cons2prim + densi = dens(i) + pri = eos_vars(igasP,i) + gammai = eos_vars(igamma,i) + tempi = eos_vars(itemp,i) + rhoi = rhoh(hi,massoftype(igas)) + + ! Note: grforce needs derivatives of the metric, + ! which do not change between pmom iterations + pmom_iterations: do while (its <= itsmax .and. .not. converged) + its = its + 1 + pprev = pxyz + call conservative2primitive(xyz,metrics(:,:,:,i),vxyz,densi,uui,pri,& + tempi,gammai,rhoi,pxyz,eni,ierr,ien_type) + + if (ierr > 0) call warning('cons2primsolver [in substep_gr (a)]','enthalpy did not converge',i=i) + call get_grforce(xyzh(:,i),metrics(:,:,:,i),metricderivs(:,:,:,i),vxyz,densi,uui,pri,fstar) + + ! calculate force between sink-gas particles + call get_accel_sink_gas(nptmass,xyz(1),xyz(2),xyz(3),hi,xyzmh_ptmass, & + fstar(1),fstar(2),fstar(3),poti,pmassi,fxyz_ptmass,& + dsdt_ptmass,fonrmax,dtphi2,bin_info) + + pxyz = pprev + hdt*(fstar - fexti) + pmom_err = maxval(abs(pxyz - pprev)) + if (pmom_err < ptol) converged = .true. + fexti = fstar + enddo pmom_iterations + if (its > itsmax ) call warning('substep_gr',& + 'max # of pmom iterations',var='pmom_err',val=pmom_err) + pitsmax = max(its,pitsmax) + perrmax = max(pmom_err,perrmax) + + call conservative2primitive(xyz,metrics(:,:,:,i),vxyz,densi,uui,pri,tempi,& + gammai,rhoi,pxyz,eni,ierr,ien_type) + if (ierr > 0) call warning('cons2primsolver [in substep_gr (b)]','enthalpy did not converge',i=i) + xyz = xyz + dt*vxyz + call pack_metric(xyz,metrics(:,:,:,i)) + + its = 0 + converged = .false. + vxyz_star = vxyz + ! Note: since particle positions change between iterations + ! the metric and its derivatives need to be updated. + ! cons2prim does not require derivatives of the metric, + ! so those can updated once the iterations are complete + ! in order to reduce the number of computations. + xyz_iterations: do while (its <= itsmax .and. .not. converged) + its = its+1 + xyz_prev = xyz + call conservative2primitive(xyz,metrics(:,:,:,i),vxyz_star,densi,uui,& + pri,tempi,gammai,rhoi,pxyz,eni,ierr,ien_type) + if (ierr > 0) call warning('cons2primsolver [in substep_gr (c)]','enthalpy did not converge',i=i) + xyz = xyz_prev + hdt*(vxyz_star - vxyz) + x_err = maxval(abs(xyz-xyz_prev)) + if (x_err < xtol) converged = .true. + vxyz = vxyz_star + ! UPDATE METRIC HERE + call pack_metric(xyz,metrics(:,:,:,i)) + enddo xyz_iterations + call pack_metricderivs(xyz,metricderivs(:,:,:,i)) + if (its > itsmax ) call warning('substep_gr','Reached max number of x iterations. x_err ',val=x_err) + xitsmax = max(its,xitsmax) + xerrmax = max(x_err,xerrmax) + + ! re-pack arrays back where they belong + xyzh(1:3,i) = xyz(1:3) + pxyzu(1:3,i) = pxyz(1:3) + vxyzu(1:3,i) = vxyz(1:3) + vxyzu(4,i) = uui + fext(:,i) = fexti + dens(i) = densi + eos_vars(igasP,i) = pri + eos_vars(itemp,i) = tempi + eos_vars(igamma,i) = gammai + + ! Skip remainder of update if boundary particle; note that fext==0 for these particles + if (iamboundary(itype)) cycle predictor + endif + enddo predictor +!$omp end parallel do + + call predict_gr_sink(xyzmh_ptmass,vxyz_ptmass,ntypes,pxyzu_ptmass,fsink_old,fxyz_ptmass,fxyz_ptmass_sinksink,nptmass,& + dt,timei,hdt,metrics_ptmass,metricderivs_ptmass,dtextforcenew,pitsmax,perrmax,& + xitsmax,xerrmax) + +end subroutine predict_gr + + !---------------------------------------------------------------- + !+ + ! routine for prediction substep in GR case + !+ + !---------------------------------------------------------------- +subroutine predict_gr_sink(xyzmh_ptmass,vxyz_ptmass,ntypes,pxyzu_ptmass,fsink_old,fxyz_ptmass,& + fxyz_ptmass_sinksink,nptmass,dt,timei,hdt, & + metrics_ptmass,metricderivs_ptmass,dtextforcenew,pitsmax,perrmax, & + xitsmax,xerrmax) + use dim, only:maxptmass + use io, only:master,warning,fatal + use part, only:epot_sinksink,dsdt_ptmass + use timestep, only:bignumber,xtol,ptol + use cons2primsolver,only:conservative2primitive + use extern_gr, only:get_grforce + use metric_tools, only:pack_metric,pack_metricderivs + use ptmass, only:get_accel_sink_sink + + real, intent(inout) :: xyzmh_ptmass(:,:),vxyz_ptmass(:,:),fxyz_ptmass(:,:),fxyz_ptmass_sinksink(:,:),pxyzu_ptmass(:,:) + real, intent(inout) :: metrics_ptmass(:,:,:,:),metricderivs_ptmass(:,:,:,:),dtextforcenew,fsink_old(:,:) + real, intent(in) :: dt,hdt,timei + integer, intent(in) :: nptmass,ntypes + integer, intent(inout) :: pitsmax,xitsmax + real, intent(inout) :: perrmax,xerrmax + + integer :: i,its,ierr + integer, parameter :: itsmax = 50 + real :: pmassi,xyzhi(4) + real :: pri,tempi,gammai + real, save :: pprev(3),xyz_prev(3),fstar(3),vxyz_star(3),xyz(3),pxyz(3),vxyz(3),fexti(3) + !$omp threadprivate(pprev,xyz_prev,fstar,vxyz_star,xyz,pxyz,vxyz,fexti) + real :: x_err,pmom_err + ! real, save :: dmdt = 0. + logical :: converged + real :: rhoi,hi,eni,uui,densi + integer :: merge_ij(2),merge_n + real :: dtsinksink + + !--------------------------- + ! predictor during substeps + !--------------------------- + ! + ! predictor step for external forces, also recompute external forces + ! + !$omp parallel do default(none) & + !$omp shared(xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass,fxyz_ptmass_sinksink) & + !$omp shared(dt,hdt,xtol,ptol,nptmass) & + !$omp shared(pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass,fsink_old) & + !$omp shared(dtsinksink,epot_sinksink,merge_ij,merge_n,dsdt_ptmass) & + !$omp private(i,its,tempi,rhoi,hi,eni,uui,densi,xyzhi) & + !$omp private(converged,pmom_err,x_err,pri,ierr,gammai,pmassi) & + !$omp reduction(max:xitsmax,pitsmax,perrmax,xerrmax) & + !$omp reduction(min:dtextforcenew) + + predictor: do i=1,nptmass + xyzhi(1) = xyzmh_ptmass(1,i) + xyzhi(2) = xyzmh_ptmass(2,i) + xyzhi(3) = xyzmh_ptmass(3,i) + pmassi = xyzmh_ptmass(4,i) + hi = xyzmh_ptmass(5,i) + + xyz(1) = xyzhi(1) + xyz(2) = xyzhi(2) + xyz(3) = xyzhi(3) + xyzhi(4) = hi + if (pmassi >= 0) then + its = 0 + converged = .false. + ! + ! make local copies of array quantities + ! + pxyz(1:3) = pxyzu_ptmass(1:3,i) + eni = 0. + vxyz(1:3) = vxyz_ptmass(1:3,i) + uui = 0. + fexti = fsink_old(1:3,i) + pxyz = pxyz + hdt*fexti + + !-- unpack thermo variables for the first guess in cons2prim + densi = 1. + pri = 0. + gammai = 0. + tempi = 0. + rhoi = 1. + ! Note: grforce needs derivatives of the metric, + ! which do not change between pmom iterations + pmom_iterations: do while (its <= itsmax .and. .not. converged) + its = its + 1 + pprev = pxyz + call conservative2primitive(xyz,metrics_ptmass(:,:,:,i),vxyz,densi,uui,pri,& + tempi,gammai,rhoi,pxyz,eni,ierr,1) + + if (ierr > 0) call warning('cons2primsolver [in substep_gr (a)]','enthalpy did not converge',i=i) + + call get_grforce(xyzhi,metrics_ptmass(:,:,:,i),metricderivs_ptmass(:,:,:,i),vxyz,densi,uui,pri,fstar) + + ! add forces from curvature on sink, from sink sink interaction and sink-gas interaction + fstar = fstar + fxyz_ptmass_sinksink(1:3,i) + fxyz_ptmass(1:3,i) + + pxyz = pprev + hdt*(fstar - fexti) + pmom_err = maxval(abs(pxyz - pprev)) + if (pmom_err < ptol) converged = .true. + fexti = fstar + enddo pmom_iterations + if (its > itsmax ) call warning('substep_gr',& + 'max # of pmom iterations',var='pmom_err',val=pmom_err) + pitsmax = max(its,pitsmax) + perrmax = max(pmom_err,perrmax) + + call conservative2primitive(xyz,metrics_ptmass(:,:,:,i),vxyz,densi,uui,pri,tempi,& + gammai,rhoi,pxyz,eni,ierr,1) + + if (ierr > 0) call warning('cons2primsolver [in substep_gr (b)]','enthalpy did not converge',i=i) + xyz = xyz + dt*vxyz + call pack_metric(xyz,metrics_ptmass(:,:,:,i)) + + its = 0 + converged = .false. + vxyz_star = vxyz + ! Note: since particle positions change between iterations + ! the metric and its derivatives need to be updated. + ! cons2prim does not require derivatives of the metric, + ! so those can updated once the iterations are complete + ! in order to reduce the number of computations. + xyz_iterations: do while (its <= itsmax .and. .not. converged) + its = its+1 + xyz_prev = xyz + call conservative2primitive(xyz,metrics_ptmass(:,:,:,i),vxyz_star,densi,uui,& + pri,tempi,gammai,rhoi,pxyz,eni,ierr,1) + if (ierr > 0) call warning('cons2primsolver [in substep_gr (c)]','enthalpy did not converge',i=i) + xyz = xyz_prev + hdt*(vxyz_star - vxyz) + x_err = maxval(abs(xyz-xyz_prev)) + if (x_err < xtol) converged = .true. + vxyz = vxyz_star + ! UPDATE METRIC HERE + call pack_metric(xyz,metrics_ptmass(:,:,:,i)) + enddo xyz_iterations + call pack_metricderivs(xyz,metricderivs_ptmass(:,:,:,i)) + if (its > itsmax ) call warning('substep_gr','Reached max number of x iterations. x_err ',val=x_err) + xitsmax = max(its,xitsmax) + xerrmax = max(x_err,xerrmax) + + ! re-pack arrays back where they belong + xyzmh_ptmass(1:3,i) = xyz(1:3) + pxyzu_ptmass(1:3,i) = pxyz(1:3) + vxyz_ptmass(1:3,i) = vxyz(1:3) + fxyz_ptmass(1:3,i) = fexti + + endif + enddo predictor +!$omp end parallel do + +end subroutine predict_gr_sink + + !---------------------------------------------------------------- + !+ + ! routine for accretion step in GR case + !+ + !---------------------------------------------------------------- +subroutine accrete_gr(xyzh,vxyzu,dens,fext,metrics,metricderivs,nlive,naccreted,& + pxyzu,accretedmass,hdt,npart,nptmass,ntypes,dtextforce_min,timei,& + xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass,& + metrics_ptmass,metricderivs_ptmass,& + pxyzu_ptmass,nlive_sinks,naccreted_sinks) + + use dim, only:maxptmass,maxp,maxvxyzu,use_apr + use io, only:master,warning,fatal + use externalforces, only:externalforce,accrete_particles,update_externalforce + use options, only:iexternalforce + use part, only:maxphase,isdead_or_accreted,iamboundary,igas,iphase,iamtype,& + massoftype,rhoh,igamma,itemp,igasP,aprmassoftype,apr_level,& + fxyz_ptmass_sinksink,fsink_old + use io_summary, only:summary_variable,iosumextr,iosumextt,summary_accrete + use timestep, only:bignumber,C_force + use eos, only:equationofstate,ieos + use cons2primsolver,only:conservative2primitive + use extern_gr, only:get_grforce + use metric_tools, only:pack_metric,pack_metricderivs + use damping, only:calc_damp,apply_damp,idamp + use part, only:epot_sinksink + use ptmass, only:get_accel_sink_sink,get_accel_sink_gas + + real, intent(inout) :: xyzh(:,:),vxyzu(:,:),fext(:,:),pxyzu(:,:),dens(:),metrics(:,:,:,:),metricderivs(:,:,:,:) + real, intent(inout) :: xyzmh_ptmass(:,:),vxyz_ptmass(:,:),fxyz_ptmass(:,:),pxyzu_ptmass(:,:) + real, intent(inout) :: metrics_ptmass(:,:,:,:),metricderivs_ptmass(:,:,:,:) + integer, intent(in) :: npart,ntypes,nptmass + integer, intent(inout) :: nlive,naccreted + integer, intent(inout) :: nlive_sinks,naccreted_sinks + real, intent(inout) :: accretedmass + real, intent(in) :: hdt,timei + real, intent(inout) :: dtextforce_min + + logical :: accreted + integer :: i,itype + real :: pmassi + real :: dtf + real :: pri,spsoundi,pondensi,tempi + real :: damp_fac + ! real, save :: dmdt = 0. + integer, parameter :: itsmax = 50 + real :: bin_info(6,nptmass),dsdt_ptmass(3,nptmass) + real :: dtphi2,dtsinksink,fonrmax,poti + integer :: merge_ij(nptmass),merge_n + + pmassi = massoftype(igas) + itype = igas + fsink_old = fxyz_ptmass + fxyz_ptmass = 0. + !---------------------------------------------- + ! calculate acceleration sink-sink + !---------------------------------------------- + call get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_ptmass_sinksink,epot_sinksink,dtsinksink,& + iexternalforce,timei,merge_ij,merge_n,dsdt_ptmass) + + dtextforce_min = min(dtextforce_min,C_force*dtsinksink) + + !$omp parallel default(none) & + !$omp shared(npart,xyzh,metrics,metricderivs,vxyzu,fext,iphase,ntypes,massoftype,hdt,timei) & + !$omp shared(nptmass,xyzmh_ptmass,vxyz_ptmass,fxyz_ptmass) & + !$omp shared(pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass) & + !$omp shared(maxphase,maxp,aprmassoftype,apr_level) & + !$omp shared(dtsinksink,epot_sinksink,merge_ij,merge_n,dsdt_ptmass) & + !$omp shared(bin_info,dtphi2,poti,fonrmax) & + !$omp private(i,accreted) & + !$omp shared(ieos,dens,pxyzu,iexternalforce,C_force) & + !$omp private(pri,pondensi,spsoundi,tempi,dtf) & + !$omp firstprivate(itype,pmassi) & + !$omp reduction(min:dtextforce_min) & + !$omp reduction(+:accretedmass,naccreted,nlive) & + !$omp shared(idamp,damp_fac) + !$omp do + accreteloop: do i=1,npart + if (.not.isdead_or_accreted(xyzh(4,i))) then + if (ntypes > 1 .and. maxphase==maxp) then + itype = iamtype(iphase(i)) + if (use_apr) then + else + pmassi = massoftype(itype) + endif + ! if (itype==iboundary) cycle accreteloop + elseif (use_apr) then + pmassi = aprmassoftype(igas,apr_level(i)) + endif + + call equationofstate(ieos,pondensi,spsoundi,dens(i),xyzh(1,i),xyzh(2,i),xyzh(3,i),tempi,vxyzu(4,i)) + pri = pondensi*dens(i) + + call get_grforce(xyzh(:,i),metrics(:,:,:,i),metricderivs(:,:,:,i),vxyzu(1:3,i),dens(i),vxyzu(4,i),pri,fext(1:3,i),dtf) + call get_accel_sink_gas(nptmass,xyzh(1,i),xyzh(2,i),xyzh(3,i),xyzh(4,i),xyzmh_ptmass, & + fext(1,i),fext(2,i),fext(3,i),poti,pmassi,fxyz_ptmass,& + dsdt_ptmass,fonrmax,dtphi2,bin_info) + + dtextforce_min = min(dtextforce_min,C_force*dtf,C_force*sqrt(dtphi2)) + + if (idamp > 0) then + call apply_damp(fext(1,i), fext(2,i), fext(3,i), vxyzu(1:3,i), xyzh(1:3,i), damp_fac) + endif + + ! + ! correct v to the full step using only the external force + ! + pxyzu(1:3,i) = pxyzu(1:3,i) + hdt*fext(1:3,i) + ! Do we need call cons2prim here ?? + + if (iexternalforce > 0) then + call accrete_particles(iexternalforce,xyzh(1,i),xyzh(2,i), & + xyzh(3,i),xyzh(4,i),pmassi,timei,accreted,i) + if (accreted) then + accretedmass = accretedmass + pmassi + naccreted = naccreted + 1 + endif + endif + nlive = nlive + 1 + endif + enddo accreteloop + !$omp enddo + !$omp end parallel + call accrete_gr_sink(xyzmh_ptmass,vxyz_ptmass,fsink_old,fxyz_ptmass,fxyz_ptmass_sinksink,& + metrics_ptmass,metricderivs_ptmass,nlive_sinks,naccreted_sinks,pxyzu_ptmass,& + accretedmass,hdt,nptmass,dtextforce_min,timei,dtsinksink) +end subroutine accrete_gr + + !---------------------------------------------------------------- + !+ + ! routine for accretion step in GR case + !+ + !---------------------------------------------------------------- +subroutine accrete_gr_sink(xyzmh_ptmass,vxyz_ptmass,fsink_old,fxyz_ptmass,fxyz_ptmass_sinksink,metrics_ptmass,metricderivs_ptmass,& + nlive_sinks,naccreted_sinks,& + pxyzu_ptmass,accretedmass,hdt,nptmass,dtextforce_min,timei,dtsinksink) + use part, only:ihsoft + use externalforces, only:accrete_particles + use options, only:iexternalforce + use timestep, only:bignumber,C_force + use cons2primsolver,only:conservative2primitive + use extern_gr, only:get_grforce + real, intent(inout) :: xyzmh_ptmass(:,:),vxyz_ptmass(:,:),fxyz_ptmass(:,:),pxyzu_ptmass(:,:),fsink_old(:,:) + real, intent(inout) :: metrics_ptmass(:,:,:,:),metricderivs_ptmass(:,:,:,:),fxyz_ptmass_sinksink(:,:) + integer, intent(in) :: nptmass + integer, intent(inout) :: nlive_sinks,naccreted_sinks + real, intent(inout) :: accretedmass + real, intent(in) :: hdt,timei,dtsinksink + real, intent(inout) :: dtextforce_min + + logical :: accreted + integer :: i + real :: xyzhi(4),pmassi,densi,pri + real :: dtf,hsofti + ! real, save :: dmdt = 0. + integer, parameter :: itsmax = 50 + + !$omp parallel default(none) & + !$omp shared(nptmass,xyzmh_ptmass,metrics_ptmass,metricderivs_ptmass,vxyz_ptmass,fxyz_ptmass,hdt,timei) & + !$omp shared(dtsinksink,fxyz_ptmass_sinksink) & + !$omp private(i,accreted) & + !$omp shared(pxyzu_ptmass,iexternalforce,C_force) & + !$omp private(dtf,xyzhi,hsofti,pmassi,pri,densi) & + !$omp reduction(min:dtextforce_min) & + !$omp reduction(+:accretedmass,naccreted_sinks,nlive_sinks) + !$omp do + accreteloop: do i=1,nptmass + pri = 0. + densi = 1. + + ! add this force due to the curvature of the metric. + xyzhi(1:3) = xyzmh_ptmass(1:3,i) + + ! if a sink particle is already eaten by the black hole, skip it... + pmassi = xyzmh_ptmass(4,i) + if (pmassi < 0.) cycle accreteloop + ! + ! the smoothing length is used inside get_grforce to set the timestep based + ! on h/abs(dp/dt), but for sink particles this is meaningless unless + ! a softening length is set + ! + hsofti = xyzmh_ptmass(ihsoft,i) + xyzhi(4) = xyzmh_ptmass(5,i) + if (hsofti > 0.) xyzhi(4) = hsofti + ! add force from sink-gas interaction to the sink-sink interaction array + fxyz_ptmass_sinksink(:,i) = fxyz_ptmass_sinksink(:,i) + fxyz_ptmass(:,i) + ! calculate force due to curvature + call get_grforce(xyzhi,metrics_ptmass(:,:,:,i),metricderivs_ptmass(:,:,:,i),vxyz_ptmass(1:3,i),& + densi,0.,pri,fxyz_ptmass(1:3,i),dtf) + ! get total force on sinks + fxyz_ptmass(:,i) = fxyz_ptmass(:,i) + fxyz_ptmass_sinksink(:,i) + + dtextforce_min = min(dtextforce_min,C_force*dtf) + ! + ! correct v to the full step using only the external force + ! + pxyzu_ptmass(1:3,i) = pxyzu_ptmass(1:3,i) + hdt*fxyz_ptmass(1:3,i) + if (pmassi < 0.) then + ! + ! sending the mass twice here is deliberate, as an accreted sink particle is indicated by + ! a negative mass, unlike gas particles which are flagged with a negative smoothing length + ! + call accrete_particles(iexternalforce,xyzmh_ptmass(1,i),xyzmh_ptmass(2,i), & + xyzmh_ptmass(3,i),xyzmh_ptmass(4,i),xyzmh_ptmass(4,i),timei,accreted) + if (accreted) then + accretedmass = accretedmass + abs(xyzmh_ptmass(4,i)) + naccreted_sinks = naccreted_sinks + 1 + endif + endif + nlive_sinks = nlive_sinks + 1 + + enddo accreteloop + !$omp enddo + !$omp end parallel + +end subroutine accrete_gr_sink + +subroutine combine_forces_gr(nptmass,fsinks,fgr) + real, intent(in) :: fsinks(:,:) + integer, intent(in) :: nptmass + + real, intent(inout) :: fgr(:,:) + + integer :: i + + do i=1,nptmass + fgr(:,i) = fsinks(:,i) + fgr(:,i) + enddo +end subroutine combine_forces_gr + + +subroutine combine_forces_gr_one(fsink,fgr) + real, intent(in) :: fsink(:) + real, intent(inout) :: fgr(:) + + fgr(:) = fgr(:) + fsink(:) + +end subroutine combine_forces_gr_one end module substepping diff --git a/src/setup/setup_grtde.f90 b/src/setup/setup_grtde.f90 index 1fb776d80..0957c20ea 100644 --- a/src/setup/setup_grtde.f90 +++ b/src/setup/setup_grtde.f90 @@ -10,30 +10,52 @@ module setup ! ! :References: None ! -! :Owner: David Liptai +! :Owner: Megha Sharma ! ! :Runtime parameters: -! - beta : *penetration factor* -! - dumpsperorbit : *number of dumps per orbit* -! - ecc : *eccentricity (1 for parabolic)* -! - mhole : *mass of black hole (solar mass)* -! - norbits : *number of orbits* -! - relax : *relax star into hydrostatic equilibrium* -! - theta : *inclination of orbit (degrees)* +! - beta : *penetration factor* +! - dumpsperorbit : *number of dumps per orbit* +! - ecc_bh : *eccentricity (1 for parabolic)* +! - mhole : *mass of black hole (solar mass)* +! - norbits : *number of orbits* +! - nstar : *number of stars to set* +! - provide_params : *initial conditions* +! - relax : *relax star into hydrostatic equilibrium* +! - theta_bh : *inclination of orbit (degrees)* +! - vx1 : *vel x star 1* +! - vx2 : *vel x star 2* +! - vy1 : *vel y star 1* +! - vy2 : *vel y star 2* +! - vz1 : *vel z star 1* +! - vz2 : *vel z star 2* +! - x1 : *pos x star 1* +! - x2 : *pos x star 2* +! - y1 : *pos y star 1* +! - y2 : *pos y star 2* +! - z1 : *pos z star 1* +! - z2 : *pos z star 2* ! ! :Dependencies: eos, externalforces, gravwaveutils, infile_utils, io, ! kernel, metric, mpidomain, options, part, physcon, relaxstar, -! setbinary, setstar, setup_params, systemutils, timestep, units, -! vectorutils +! setbinary, setorbit, setstar, setup_params, systemutils, timestep, +! units, vectorutils ! - use setstar, only:star_t + + use setstar, only:star_t + use setorbit, only:orbit_t + use metric, only:mass1,a implicit none public :: setpart - real :: mhole,beta,ecc,norbits,theta - integer :: dumpsperorbit - logical :: relax - type(star_t) :: star + real :: mhole,beta,ecc_bh,norbits,theta_bh + real :: x1,y1,z1,x2,y2,z2 + real :: vx1,vy1,vz1,vx2,vy2,vz2 + integer :: dumpsperorbit,nstar + logical :: relax,write_profile + logical :: provide_params + integer, parameter :: max_stars = 2 + type(star_t) :: star(max_stars) + type(orbit_t) :: orbit private @@ -46,14 +68,14 @@ module setup !---------------------------------------------------------------- subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact,time,fileprefix) use part, only:nptmass,xyzmh_ptmass,vxyz_ptmass,ihacc,ihsoft,igas,& - gravity,eos_vars,rad,gr + gravity,eos_vars,rad,gr,nsinkproperties use setbinary, only:set_binary - use setstar, only:set_star,shift_star,set_defaults_star - use units, only:set_units,umass,udist + use setorbit, only:set_defaults_orbit,set_orbit + use setstar, only:shift_star,set_defaults_stars,set_stars,shift_stars + use units, only:set_units,umass,udist,unit_velocity use physcon, only:solarm,pi,solarr use io, only:master,fatal,warning use timestep, only:tmax,dtmax - use metric, only:mass1,a use eos, only:ieos,X_in,Z_in use kernel, only:hfact_default use mpidomain, only:i_belong @@ -64,6 +86,7 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, use systemutils, only:get_command_option use options, only:iexternalforce use units, only:in_code_units + use, intrinsic :: ieee_arithmetic integer, intent(in) :: id integer, intent(inout) :: npart integer, intent(out) :: npartoftype(:) @@ -75,11 +98,16 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, real, intent(out) :: vxyzu(:,:) character(len=120) :: filename integer :: ierr,np_default - logical :: iexist,write_profile,use_var_comp + integer :: nptmass_in + integer :: i + logical :: iexist,use_var_comp real :: rtidal,rp,semia,period,hacc1,hacc2 real :: vxyzstar(3),xyzstar(3) - real :: r0,vel,lorentz,mstar,rstar + real :: r0,vel,lorentz real :: vhat(3),x0,y0 + real :: semi_maj_val + real :: mstars(max_stars),rstars(max_stars),haccs(max_stars) + real :: xyzmh_ptmass_in(nsinkproperties,2),vxyz_ptmass_in(3,2),angle ! !-- general parameters ! @@ -87,7 +115,9 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, time = 0. polyk = 1.e-10 ! <== uconst gamma = 5./3. - ieos = 2 + angle = 0. + xyzmh_ptmass_in(:,:) = 0. + vxyz_ptmass_in(:,:) = 0. if (.not.gravity) call fatal('setup','recompile with GRAVITY=yes') ! !-- space available for injected gas particles @@ -97,25 +127,28 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, xyzh(:,:) = 0. vxyzu(:,:) = 0. nptmass = 0 + nstar = 1 ! !-- Default runtime parameters ! mhole = 1.e6 ! (solar masses) call set_units(mass=mhole*solarm,c=1.d0,G=1.d0) !--Set central mass to M=1 in code units - call set_defaults_star(star) - star%m = '1.*msun' - star%r = '1.*solarr' + call set_defaults_stars(star) + call set_defaults_orbit(orbit) + star(:)%m = '1.*msun' + star(:)%r = '1.*solarr' np_default = 1e6 star%np = int(get_command_option('np',default=np_default)) ! can set default value with --np=1e5 flag (mainly for testsuite) - star%iprofile = 2 +! star%iprofile = 2 beta = 5. - ecc = 0.8 + ecc_bh = 0.8 norbits = 5. dumpsperorbit = 100 - theta = 0. + theta_bh = 0. write_profile = .true. use_var_comp = .false. relax = .true. + ! !-- Read runtime parameters from setup file ! @@ -130,89 +163,136 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, endif stop endif - ! - !--set up and relax a star + !--set nstar/nptmass stars around the BH. This would also relax the star. ! - call set_star(id,master,star,xyzh,vxyzu,eos_vars,rad,npart,npartoftype,& - massoftype,hfact,xyzmh_ptmass,vxyz_ptmass,nptmass,ieos,gamma,& - X_in,Z_in,relax,use_var_comp,write_profile,& - rhozero,npart_total,i_belong,ierr) + call set_stars(id,master,nstar,star,xyzh,vxyzu,eos_vars,rad,npart,npartoftype,& + massoftype,hfact,xyzmh_ptmass,vxyz_ptmass,nptmass,ieos,gamma,& + X_in,Z_in,relax,use_var_comp,write_profile,& + rhozero,npart_total,i_belong,ierr) + + do i=1,nstar + rstars(i) = in_code_units(star(i)%r,ierr,unit_type='length') + if (ierr /= 0) call fatal('setup','could not convert rstar to code units',i=i) + mstars(i) = in_code_units(star(i)%m,ierr,unit_type='mass') + if (ierr /= 0) call fatal('setup','could not convert mstar to code units',i=i) + haccs(i) = in_code_units(star(i)%hacc,ierr,unit_type='mass') + if (ierr /= 0) call fatal('setup','could not convert hacc to code units',i=i) + enddo + + if (star(1)%iprofile == 0 .and. nstar == 1) then + xyzmh_ptmass_in(4,1) = mstars(1) + xyzmh_ptmass_in(5,1) = haccs(1) + endif - if (ierr /= 0) call fatal('setup','errors in set_star') + ! + !--set the stars around each other first if nstar > 1 (Assuming binary system) + ! + if (nstar > 1 .and. (.not. provide_params)) then + nptmass_in = 0 + call set_orbit(orbit,mstars(1),mstars(2),haccs(1),haccs(2),& + xyzmh_ptmass_in,vxyz_ptmass_in,nptmass_in,(id==master),ierr) - rstar = in_code_units(star%r,ierr,unit_type='length') - if (ierr /= 0) call fatal('setup','could not convert rstar to code units') - mstar = in_code_units(star%m,ierr,unit_type='mass') - if (ierr /= 0) call fatal('setup','could not convert mstar to code units') + if (ierr /= 0) call fatal ('setup_binary','error in call to set_orbit') + if (ierr /= 0) call fatal('setup','errors in set_star') + endif ! - !--place star into orbit + !--place star / stars into orbit ! - rtidal = rstar*(mass1/mstar)**(1./3.) - rp = rtidal/beta - accradius1_hard = 5.*mass1 - accradius1 = accradius1_hard + ! Calculate tidal radius + if (nstar == 1) then + ! for single star around the BH, the tidal radius is given by + ! RT = rr * (MM / mm)**(1/3) where rr is rstar, MM is mass of BH and mm is mass of star + rtidal = rstars(1) * (mass1/mstars(1))**(1./3.) + rp = rtidal/beta + else + semi_maj_val = in_code_units(orbit%elems%semi_major_axis,ierr,unit_type='length') + ! for a binary, tidal radius is given by + ! orbit.an * (3 * MM / mm)**(1/3) where mm is mass of binary and orbit.an is semi-major axis of binary + rtidal = semi_maj_val * (3.*mass1 / (mstars(1) + mstars(2)))**(1./3.) + rp = rtidal/beta + endif + + if (gr) then + accradius1_hard = 5.*mass1 + accradius1 = accradius1_hard + else + if (mass1 /= 0.) then + accradius1_hard = 6. + accradius1 = accradius1_hard + endif + endif + a = 0. - theta = theta*pi/180. + theta_bh = theta_bh*pi/180. - print*, 'mstar', mstar - print*, 'rstar', rstar print*, 'umass', umass print*, 'udist', udist + print*, 'uvel', unit_velocity print*, 'mass1', mass1 print*, 'tidal radius', rtidal print*, 'beta', beta + print*, accradius1_hard, "accradius1_hard",mass1,"mass1" + + if (.not. provide_params) then + do i = 1, nstar + print*, 'mstar of star ',i,' is: ', mstars(i) + print*, 'rstar of star ',i,' is: ', rstars(i) + enddo + + xyzstar = 0. + vxyzstar = 0. + period = 0. + + if (ecc_bh<1.) then + ! + !-- Set a binary orbit given the desired orbital parameters to get the position and velocity of the star + ! + semia = rp/(1.-ecc_bh) + period = 2.*pi*sqrt(semia**3/mass1) + hacc1 = rstars(1)/1.e8 ! Something small so that set_binary doesnt warn about Roche lobe + hacc2 = hacc1 + ! apocentre = rp*(1.+ecc_bh)/(1.-ecc_bh) + ! trueanom = acos((rp*(1.+ecc_bh)/r0 - 1.)/ecc_bh)*180./pi + call set_binary(mass1,mstars(1),semia,ecc_bh,hacc1,hacc2,xyzmh_ptmass,vxyz_ptmass,nptmass,ierr,& + posang_ascnode=0.,arg_peri=90.,incl=0.,f=-180.) + vxyzstar(:) = vxyz_ptmass(1:3,2) + xyzstar(:) = xyzmh_ptmass(1:3,2) + nptmass = 0 + + call rotatevec(xyzstar,(/0.,1.,0./),-theta_bh) + call rotatevec(vxyzstar,(/0.,1.,0./),-theta_bh) + + elseif (abs(ecc_bh-1.) < tiny(0.)) then + ! + !-- Setup a parabolic orbit + ! + r0 = 10.*rtidal ! A default starting distance from the black hole. + period = 2.*pi*sqrt(r0**3/mass1) !period not defined for parabolic orbit, so just need some number + y0 = -2.*rp + r0 + x0 = sqrt(r0**2 - y0**2) + xyzstar(:) = (/-x0,y0,0./) + vel = sqrt(2.*mass1/r0) + vhat = (/2.*rp,-x0,0./)/sqrt(4.*rp**2 + x0**2) + vxyzstar(:) = vel*vhat + if (rtidal == 0.) then + vxyzstar(:) = (/0.,0.,0./) + endif + + call rotatevec(xyzstar,(/0.,1.,0./),theta_bh) + call rotatevec(vxyzstar,(/0.,1.,0./),theta_bh) + + else + call fatal('setup','please choose a valid eccentricity (01.1) call warning('setup','Lorentz factor of star greater than 1.1, density may not be correct') - else - call fatal('setup','please choose a valid eccentricity (01.1) call warning('setup','Lorentz factor of star greater than 1.1, density may not be correct') - - tmax = norbits*period - dtmax = period/dumpsperorbit - if (id==master) then print "(/,a)", ' STAR SETUP:' print "(a,3f10.3)" ,' Position = ',xyzstar @@ -221,24 +301,46 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, print "(a,1f10.3)" ,' Polytropic gamma = ',gamma print "(a,3f10.3,/)",' Pericentre = ',rp endif + ! + !--shift stars / sink particles + ! + if (provide_params) then + xyzmh_ptmass_in(1:3,1) = (/x1,y1,z1/) + xyzmh_ptmass_in(1:3,2) = (/x2,y2,z2/) + vxyz_ptmass_in(:,1) = (/vx1, vy1, vz1/) + vxyz_ptmass_in(:,2) = (/vx2, vy2, vz2/) + + xyzmh_ptmass_in(4,1) = mstars(1) + xyzmh_ptmass_in(5,1) = haccs(1) - call shift_star(npart,npartoftype,xyzh,vxyzu,x0=xyzstar,v0=vxyzstar) + xyzmh_ptmass_in(4,2) = mstars(2) + xyzmh_ptmass_in(5,2) = haccs(2) + else + do i = 1, nstar + xyzmh_ptmass_in(1:3,i) = xyzmh_ptmass_in(1:3,i) + xyzstar(:) + vxyz_ptmass_in(1:3,i) = vxyz_ptmass_in(1:3,i) + vxyzstar(:) + enddo + endif + call shift_stars(nstar,star,xyzmh_ptmass_in(1:3,1:nstar),vxyz_ptmass_in(1:3,1:nstar),& + xyzh,vxyzu,xyzmh_ptmass,vxyz_ptmass,npart,& + npartoftype,nptmass) + if (id==master) print "(/,a,i10,/)",' Number of particles setup = ',npart ! - ! set a few options for the input file + !--set a few options for the input file ! calc_gravitwaves = .true. - if (abs(ecc-1.) > epsilon(0.)) then - theta_gw = theta*180./pi + if (abs(ecc_bh-1.) > epsilon(0.)) then + theta_gw = theta_bh*180./pi else - theta_gw = -theta*180./pi + theta_gw = -theta_bh*180./pi endif if (.not.gr) iexternalforce = 1 - - if (npart == 0) call fatal('setup','no particles setup') + ! We have ignored the following error message. + !if (npart == 0) call fatal('setup','no particles setup') if (ierr /= 0) call fatal('setup','ERROR during setup') end subroutine setpart @@ -248,25 +350,35 @@ end subroutine setpart ! subroutine write_setupfile(filename) use infile_utils, only:write_inopt - use setstar, only:write_options_star + use setstar, only:write_options_star,write_options_stars use relaxstar, only:write_options_relax + use setorbit, only:write_options_orbit + use eos, only:ieos + character(len=*), intent(in) :: filename integer :: iunit print "(a)",' writing setup options file '//trim(filename) open(newunit=iunit,file=filename,status='replace',form='formatted') write(iunit,"(a)") '# input file for tidal disruption setup' - call write_options_star(star,iunit) - call write_inopt(relax,'relax','relax star into hydrostatic equilibrium',iunit) - if (relax) call write_options_relax(iunit) - - write(iunit,"(/,a)") '# options for black hole and orbit' - call write_inopt(mhole, 'mhole', 'mass of black hole (solar mass)',iunit) - call write_inopt(beta, 'beta', 'penetration factor', iunit) - call write_inopt(ecc, 'ecc', 'eccentricity (1 for parabolic)', iunit) - call write_inopt(norbits, 'norbits', 'number of orbits', iunit) - call write_inopt(dumpsperorbit,'dumpsperorbit','number of dumps per orbit', iunit) - call write_inopt(theta, 'theta', 'inclination of orbit (degrees)', iunit) + call write_inopt(provide_params,'provide_params','initial conditions',iunit) + call write_inopt(mhole, 'mhole', 'mass of black hole (solar mass)', iunit) + if (.not. provide_params) then + call write_options_stars(star,relax,write_profile,ieos,iunit,nstar) + write(iunit,"(/,a)") '# options for black hole and orbit' + call write_inopt(beta, 'beta', 'penetration factor', iunit) + call write_inopt(ecc_bh, 'ecc_bh', 'eccentricity (1 for parabolic)', iunit) + call write_inopt(norbits, 'norbits', 'number of orbits', iunit) + call write_inopt(dumpsperorbit,'dumpsperorbit','number of dumps per orbit', iunit) + call write_inopt(theta_bh, 'theta_bh', 'inclination of orbit (degrees)', iunit) + if (nstar > 1) then + call write_options_orbit(orbit,iunit) + endif + else + write(iunit,"(/,a)") '# provide inputs for the binary system' + call write_params(iunit) + endif + close(iunit) end subroutine write_setupfile @@ -274,10 +386,12 @@ end subroutine write_setupfile subroutine read_setupfile(filename,ierr) use infile_utils, only:open_db_from_file,inopts,read_inopt,close_db use io, only:error - use setstar, only:read_options_star + use setstar, only:read_options_star,read_options_stars use relaxstar, only:read_options_relax use physcon, only:solarm,solarr - use units, only:set_units + use units, only:set_units,umass + use setorbit, only:read_options_orbit + use eos, only:ieos character(len=*), intent(in) :: filename integer, intent(out) :: ierr integer, parameter :: iunit = 21 @@ -291,20 +405,28 @@ subroutine read_setupfile(filename,ierr) ! !--read black hole mass and use it to define code units ! + call read_inopt(provide_params,'provide_params',db,errcount=nerr) call read_inopt(mhole,'mhole',db,min=0.,errcount=nerr) - call set_units(mass=mhole*solarm,c=1.d0,G=1.d0) !--Set central mass to M=1 in code units +! call set_units(mass=mhole*solarm,c=1.d0,G=1.d0) !--Set central mass to M=1 in code units + ! This ensures that we can run simulations with BH's as massive as 1e9 msun. + ! A BH of mass 1e9 msun would be 1e3 in code units when umass is 1e6*solar masses. + mass1 = mhole*solarm/umass ! !--read star options and convert to code units ! - call read_options_star(star,db,nerr) - call read_inopt(relax,'relax',db,errcount=nerr) - if (relax) call read_options_relax(db,nerr) - - call read_inopt(beta, 'beta', db,min=0.,errcount=nerr) - call read_inopt(ecc, 'ecc', db,min=0.,max=1.,errcount=nerr) - call read_inopt(norbits, 'norbits', db,min=0.,errcount=nerr) - call read_inopt(dumpsperorbit, 'dumpsperorbit', db,min=0 ,errcount=nerr) - call read_inopt(theta, 'theta', db, errcount=nerr) + if (.not. provide_params) then + call read_options_stars(star,ieos,relax,write_profile,db,nerr,nstar) + call read_inopt(beta, 'beta', db,min=0.,errcount=nerr) + call read_inopt(ecc_bh, 'ecc_bh', db,min=0.,max=1.,errcount=nerr) + call read_inopt(norbits, 'norbits', db,min=0.,errcount=nerr) + call read_inopt(dumpsperorbit, 'dumpsperorbit', db,min=0 ,errcount=nerr) + call read_inopt(theta_bh, 'theta_bh', db, errcount=nerr) + if (nstar > 1) then + call read_options_orbit(orbit,db,nerr) + endif + else + call read_params(db,nerr) + endif call close_db(db) if (nerr > 0) then print "(1x,i2,a)",nerr,' error(s) during read of setup file: re-writing...' @@ -313,4 +435,43 @@ subroutine read_setupfile(filename,ierr) end subroutine read_setupfile +subroutine write_params(iunit) + use infile_utils, only:write_inopt + integer, intent(in) :: iunit + + call write_inopt(x1, 'x1', 'pos x star 1', iunit) + call write_inopt(y1, 'y1', 'pos y star 1', iunit) + call write_inopt(z1, 'z1', 'pos z star 1', iunit) + call write_inopt(x2, 'x2', 'pos x star 2', iunit) + call write_inopt(y2, 'y2', 'pos y star 2', iunit) + call write_inopt(z2, 'z2', 'pos z star 2', iunit) + call write_inopt(vx1, 'vx1', 'vel x star 1', iunit) + call write_inopt(vy1, 'vy1', 'vel y star 1', iunit) + call write_inopt(vz1, 'vz1', 'vel z star 1', iunit) + call write_inopt(vx2, 'vx2', 'vel x star 2', iunit) + call write_inopt(vy2, 'vy2', 'vel y star 2', iunit) + call write_inopt(vz2, 'vz2', 'vel z star 2', iunit) + +end subroutine write_params + +subroutine read_params(db,nerr) + use infile_utils, only:inopts,read_inopt + type(inopts), allocatable, intent(inout) :: db(:) + integer, intent(inout) :: nerr + + call read_inopt(x1, 'x1', db,errcount=nerr) + call read_inopt(y1, 'y1', db,errcount=nerr) + call read_inopt(z1, 'z1', db,errcount=nerr) + call read_inopt(x2, 'x2', db,errcount=nerr) + call read_inopt(y2, 'y2', db,errcount=nerr) + call read_inopt(z2, 'z2', db,errcount=nerr) + call read_inopt(vx1, 'vx1', db,errcount=nerr) + call read_inopt(vy1, 'vy1', db,errcount=nerr) + call read_inopt(vz1, 'vz1', db,errcount=nerr) + call read_inopt(vx2, 'vx2', db,errcount=nerr) + call read_inopt(vy2, 'vy2', db,errcount=nerr) + call read_inopt(vz2, 'vz2', db,errcount=nerr) + +end subroutine read_params + end module setup diff --git a/src/setup/setup_windtunnel.f90 b/src/setup/setup_windtunnel.f90 index 0bbc21f56..c83dfd870 100644 --- a/src/setup/setup_windtunnel.f90 +++ b/src/setup/setup_windtunnel.f90 @@ -18,7 +18,6 @@ module setup ! - gamma : *adiabatic index* ! - handled_layers : *number of handled layers* ! - lattice_type : *0: cubic, 1: close-packed cubic* -! - nstar : *number of particles resolving gas sphere* ! - pres_inf : *wind pressure / dyn cm^2* ! - rho_inf : *wind density / g cm^-3* ! - v_inf : *wind speed / km s^-1* @@ -27,8 +26,9 @@ module setup ! - wind_radius : *injection radius in units of Rstar* ! ! :Dependencies: dim, eos, extern_densprofile, infile_utils, inject, io, -! kernel, mpidomain, part, physcon, rho_profile, setstar_utils, setunits, -! setup_params, table_utils, timestep, unifdis, units +! kernel, mpidomain, part, physcon, relaxstar, rho_profile, +! setstar_utils, setunits, setup_params, table_utils, timestep, unifdis, +! units ! use io, only:master,fatal use inject, only:init_inject,nstar,Rstar,lattice_type,handled_layers,& @@ -168,9 +168,9 @@ subroutine setpart(id,npart,npartoftype,xyzh,massoftype,vxyzu,polyk,gamma,hfact, vxyzu(4,i) = presi / ( (gamma-1.) * densi) enddo - deallocate(r,den,pres) + deallocate(r,den,pres) endif - + print*, "udist = ", udist, "; umass = ", umass, "; utime = ", utime end subroutine setpart diff --git a/src/tests/test_gr.f90 b/src/tests/test_gr.f90 index bdc1ad0fa..544ddbb5f 100644 --- a/src/tests/test_gr.f90 +++ b/src/tests/test_gr.f90 @@ -165,7 +165,9 @@ end subroutine test_inccirc !----------------------------------------------------------------------- subroutine integrate_geodesic(tmax,dt,xyz,vxyz,angmom0,angmom) use io, only:iverbose - use part, only:igas,npartoftype,massoftype,set_particle_type,get_ntypes,ien_type + use part, only:igas,npartoftype,massoftype,set_particle_type,get_ntypes,ien_type,& + xyzmh_ptmass,vxyz_ptmass,pxyzu_ptmass,metrics_ptmass,& + metricderivs_ptmass,fxyz_ptmass,nptmass use substepping, only:substep_gr use eos, only:ieos use cons2prim, only:prim2consall @@ -208,8 +210,8 @@ subroutine integrate_geodesic(tmax,dt,xyz,vxyz,angmom0,angmom) ien_type = 1 call init_metric(npart,xyzh,metrics,metricderivs) - call prim2consall(npart,xyzh,metrics,vxyzu,dens,pxyzu,use_dens=.false.) - call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,dens,fext,dtextforce) + call prim2consall(npart,xyzh,metrics,vxyzu,pxyzu,use_dens=.false.,dens=dens) + call get_grforce_all(npart,xyzh,metrics,metricderivs,vxyzu,fext,dtextforce,dens=dens) call calculate_angmom(xyzh(1:3,1),metrics(:,:,:,1),massi,vxyzu(1:3,1),angmom0) nsteps = 0 @@ -217,7 +219,9 @@ subroutine integrate_geodesic(tmax,dt,xyz,vxyz,angmom0,angmom) nsteps = nsteps + 1 time = time + dt dtextforce = blah - call substep_gr(npart,ntypes,dt,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,time) + ! call substep_gr(npart,ntypes,dt,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,time) + call substep_gr(npart,nptmass,ntypes,dt,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,time,& + xyzmh_ptmass,vxyz_ptmass,pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass,fxyz_ptmass) enddo call calculate_angmom(xyzh(1:3,1),metrics(:,:,:,1),massi,vxyzu(1:3,1),angmom) diff --git a/src/tests/test_ptmass.f90 b/src/tests/test_ptmass.f90 index da7f7dac6..70668904d 100644 --- a/src/tests/test_ptmass.f90 +++ b/src/tests/test_ptmass.f90 @@ -14,11 +14,12 @@ module testptmass ! ! :Runtime parameters: None ! -! :Dependencies: HIIRegion, boundary, centreofmass, checksetup, deriv, dim, -! energies, eos, eos_HIIR, extern_binary, externalforces, gravwaveutils, -! io, kdtree, kernel, mpiutils, options, part, physcon, ptmass, random, +! :Dependencies: HIIRegion, boundary, centreofmass, checksetup, cons2prim, +! deriv, dim, energies, eos, eos_HIIR, extern_binary, extern_gr, +! externalforces, gravwaveutils, io, kdtree, kernel, metric, +! metric_tools, mpiutils, options, part, physcon, ptmass, random, ! setbinary, setdisc, spherical, step_lf_global, stretchmap, subgroup, -! testutils, timestep, timing, units +! substepping, testutils, timestep, timing, units ! use testutils, only:checkval,update_test_scores implicit none @@ -31,7 +32,7 @@ module testptmass subroutine test_ptmass(ntests,npass,string) use io, only:id,master,iskfile use eos, only:polyk,gamma - use part, only:nptmass + use part, only:nptmass,gr use options, only:iexternalforce,alpha use ptmass, only:use_fourthorder,set_integration_precision character(len=*), intent(in) :: string @@ -41,6 +42,7 @@ subroutine test_ptmass(ntests,npass,string) integer :: itmp,ierr,itest,istart logical :: do_test_binary,do_test_accretion,do_test_createsink,do_test_softening logical :: do_test_chinese_coin,do_test_merger,do_test_potential,do_test_HII,do_test_SDAR + logical :: do_test_binary_gr logical :: testall if (id==master) write(*,"(/,a,/)") '--> TESTING PTMASS MODULE' @@ -54,11 +56,14 @@ subroutine test_ptmass(ntests,npass,string) do_test_chinese_coin = .false. do_test_HII = .false. do_test_SDAR = .false. + do_test_binary_gr = .false. testall = .false. istart = 1 select case(trim(string)) case('ptmassbinary') do_test_binary = .true. + case('ptmassgenrel') + do_test_binary_gr = .true. case('ptmassaccrete') do_test_accretion = .true. case('ptmasscreatesink') @@ -80,7 +85,6 @@ subroutine test_ptmass(ntests,npass,string) do_test_HII = .true. case('ptmassSDAR') do_test_SDAR = .true. - case default testall = .true. end select @@ -91,6 +95,14 @@ subroutine test_ptmass(ntests,npass,string) gamma = 1. iexternalforce = 0 alpha = 0.01 + ! + ! Test for sink particles in GR + ! + if ((do_test_binary_gr .or. testall) .and. gr) then + call test_sink_binary_gr(ntests,npass,string) + return + endif + do itest=istart,2 ! ! select order of integration @@ -119,6 +131,7 @@ subroutine test_ptmass(ntests,npass,string) ! Test sink particle mergers ! if (do_test_merger .or. testall) call test_merger(ntests,npass) + enddo ! ! Test of sink particle potentials @@ -264,9 +277,9 @@ subroutine test_binary(ntests,npass,string) hacc1 = 0.35 hacc2 = 0.35 C_force = 0.25 + t = 0. if (itest==3) C_force = 0.25 omega = sqrt((m1+m2)/a**3) - t = 0. call set_units(mass=1.d0,dist=1.d0,G=1.d0) call set_binary(m1,m2,a,ecc,hacc1,hacc2,xyzmh_ptmass,vxyz_ptmass,nptmass,ierr,verbose=.false.) if (ierr /= 0) nerr = nerr + 1 @@ -448,6 +461,166 @@ subroutine test_binary(ntests,npass,string) end subroutine test_binary +!----------------------------------------------------------------------- +!+ +! Test that binary setup in GR using sink particles is OK. +!+ +!----------------------------------------------------------------------- +subroutine test_sink_binary_gr(ntests,npass,string) + use io, only:id,master,iverbose + use part, only:init_part,npart,npartoftype,nptmass,xyzmh_ptmass,vxyz_ptmass,& + epot_sinksink,metrics_ptmass,metricderivs_ptmass,pxyzu_ptmass,& + fxyz_ptmass,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,& + fext + use timestep, only:C_force,dtextforce + use physcon, only:solarm,pi + use units, only:set_units + use setbinary, only:set_binary + use metric, only:mass1 + use checksetup, only:check_setup + use testutils, only:checkval,checkvalf,update_test_scores + use ptmass, only:get_accel_sink_sink + use metric_tools, only:init_metric + use cons2prim, only:prim2consall + use extern_gr, only:get_grforce_all + use substepping, only:combine_forces_gr + use energies, only:angtot,etot,totmom,compute_energies,epot + use substepping, only:substep_gr + integer, intent(inout) :: ntests,npass + character(len=*), intent(in) :: string + real :: fxyz_sinksink(4,2),dsdt_sinksink(3,2) ! we only use 2 sink particles in the tests here + real :: m1,m2,a,ecc,hacc1,hacc2,t,dt,tol_en + real :: dtsinksink,tol,omega,errmax,dis + real :: angmomin,etotin,totmomin,dtsph,dtorb,vphi + integer :: ierr,nerr,nfailed(6),nwarn,nsteps,i,ntypes + integer :: merge_ij(2),merge_n,norbits + character(len=20) :: dumpfile + ! + !--no gas particles + ! + call init_part() + ! + !--set quantities + ! + npartoftype = 0 + npart = 0 + nptmass = 0 + m1 = 1.e-6 + m2 = 1.e-6 + a = 2.35 ! udist in GR is 1.48E+11. 5 Rsun in code units + ecc = 0. ! eccentricity of binary orbit + hacc1 = 0.75 ! 0.35 rsun in code units + hacc2 = 0.75 + mass1 = 0. ! set BH mass as 0. So the metric becomes Minkowski + t = 0. + iverbose = 0 + ! chose a very small value because a value of 0.35 was resulting in distance - distance_init of 1.e-3 + ! but using a small timestep resulted in values smaller than equal to 1.e-4 + C_force = 0.25 + tol = epsilon(0.) + omega = sqrt((m1+m2)/a**3) + vphi = a*omega + ! set sinks around each other + call set_units(mass=1.e6*solarm,c=1.d0,G=1.d0) + call set_binary(m1,m2,a,ecc,hacc1,hacc2,xyzmh_ptmass,vxyz_ptmass,nptmass,ierr,verbose=.false.) + dis = norm2(xyzmh_ptmass(1:3,1) - xyzmh_ptmass(1:3,2)) + + if (ierr /= 0) nerr = nerr + 1 + + ! check the setup is ok + nfailed = 0 + call check_setup(nerr,nwarn) + call checkval(nerr,0,0,nfailed(1),'no errors during setting sink binary orbit') + call update_test_scores(ntests,nfailed,npass) + ! + !--initialise forces and test that the curvature contribution is 0. when mass1 is 0. + ! + if (id==master) then + + call init_metric(nptmass,xyzmh_ptmass,metrics_ptmass,metricderivs_ptmass) + call prim2consall(nptmass,xyzmh_ptmass,metrics_ptmass,& + vxyz_ptmass,pxyzu_ptmass,use_dens=.false.,use_sink=.true.) + ! sinks in GR, provide external force due to metric to determine the sink total force + call get_accel_sink_sink(nptmass,xyzmh_ptmass,fxyz_sinksink,epot_sinksink,& + dtsinksink,0,0.,merge_ij,merge_n,dsdt_sinksink) + call get_grforce_all(nptmass,xyzmh_ptmass,metrics_ptmass,metricderivs_ptmass,& + vxyz_ptmass,fxyz_ptmass,dtextforce,use_sink=.true.) + call combine_forces_gr(nptmass,fxyz_sinksink,fxyz_ptmass) + + ! Test the force calculated is same as sink-sink because there is no curvature. + + call checkval(fxyz_sinksink(1,1), fxyz_ptmass(1,1),tol,nfailed(1),'x force term for sink 1') + call checkval(fxyz_sinksink(2,1), fxyz_ptmass(2,1),tol,nfailed(2),'y force term for sink 1') + call checkval(fxyz_sinksink(3,1), fxyz_ptmass(3,1),tol,nfailed(3),'z force term for sink 1') + call checkval(fxyz_sinksink(1,2), fxyz_ptmass(1,2),tol,nfailed(4),'x force term for sink 2') + call checkval(fxyz_sinksink(2,2), fxyz_ptmass(2,2),tol,nfailed(5),'y force term for sink 2') + call checkval(fxyz_sinksink(3,2), fxyz_ptmass(3,2),tol,nfailed(6),'z force term for sink 2') + + call update_test_scores(ntests,nfailed(1:3),npass) + call update_test_scores(ntests,nfailed(3:6),npass) + + endif + ! + !--check energy and angular momentum of the system + ! + dtextforce = min(C_force*dtsinksink,dtextforce) + dt = dtextforce + call compute_energies(t) + etotin = etot + totmomin = totmom + angmomin = angtot + + call checkval(epot,-m1*m2/a,epsilon(0.),nfailed(1),'potential energy') + call update_test_scores(ntests,nfailed,npass) + ! + !--check initial angular momentum on the two sinks is correct + ! + call checkval(angtot,m1*m2*sqrt(a/(m1 + m2)),1e6*epsilon(0.),nfailed(2),'angular momentum') + call update_test_scores(ntests,nfailed,npass) + ! + !--check initial total energy of the two sinks is correct + !--using Virial Theorem for the test + ! + call checkval(etot,epot*0.5,epsilon(0.),nfailed(3),'total energy') + call update_test_scores(ntests,nfailed,npass) + ! + !--determine number of steps per orbit for information + ! + dtorb = 2.*pi/omega + dt = dtorb + norbits = 100 + nsteps = norbits*nint(dtorb/dt) + errmax = 0. + dumpfile='test_00000' + ntypes = 2 + + do i=1,nsteps + dtsph = dt + call substep_gr(npart,nptmass,ntypes,dtsph,dtextforce,xyzh,vxyzu,pxyzu,dens,metrics,metricderivs,fext,t,& + xyzmh_ptmass,vxyz_ptmass,pxyzu_ptmass,metrics_ptmass,metricderivs_ptmass,fxyz_ptmass) + call compute_energies(t) + errmax = max(errmax,abs(etot - etotin)) + t = t + dt + dis = norm2(xyzmh_ptmass(1:3,1) - xyzmh_ptmass(1:3,2)) + enddo + ! + !--check the radius of the orbit does not change + ! + call checkval(dis,a,7.e-4,nfailed(1),"radius of orbit") + call update_test_scores(ntests,nfailed,npass) + ! + !--check energy, linear and angular momentum conservation + ! + tol_en = 1.e-13 + call compute_energies(t) + call checkval(angtot,angmomin,tol_en,nfailed(1),'angular momentum') + call checkval(totmom,totmomin,tol_en,nfailed(2),'linear momentum') + call checkval(etotin+errmax,etotin,tol_en,nfailed(3),'total energy') + do i=1,3 + call update_test_scores(ntests,nfailed(i:i),npass) + enddo + +end subroutine test_sink_binary_gr !----------------------------------------------------------------------- !+ ! Test softening between sink particles. Run a binary orbit @@ -987,6 +1160,7 @@ subroutine test_merger(ntests,npass) use timestep, only:dtmax use mpiutils, only:bcast_mpi,reduce_in_place_mpi use energies, only:compute_energies,angtot,totmom,mtot + integer, intent(inout) :: ntests,npass integer, parameter :: max_to_test = 100 logical, parameter :: print_sink_paths = .false. ! print sink paths in the merger test @@ -1003,7 +1177,7 @@ subroutine test_merger(ntests,npass) nptmass = 2 npart = 0 h_acc = 0.1 - h_soft_sinksink = h_acc + h_soft_sinksink = h_acc r_merge_uncond = 2.*h_acc ! sinks will unconditionally merge if they touch r_merge_cond = 4.*h_acc ! sinks will merge if bound within this radius r_merge_uncond2 = r_merge_uncond**2 diff --git a/src/utils/analysis_common_envelope.f90 b/src/utils/analysis_common_envelope.f90 index 7843a5bb2..227a329eb 100644 --- a/src/utils/analysis_common_envelope.f90 +++ b/src/utils/analysis_common_envelope.f90 @@ -15,9 +15,10 @@ module analysis ! :Runtime parameters: None ! ! :Dependencies: centreofmass, dim, dust_formation, energies, eos, -! eos_gasradrec, eos_mesa, extern_corotate, io, ionization_mod, kernel, -! mesa_microphysics, part, physcon, prompting, ptmass, setbinary, -! sortutils, table_utils, units, vectorutils +! eos_gasradrec, eos_idealplusrad, eos_mesa, extern_corotate, io, +! ionization_mod, kernel, mesa_microphysics, part, physcon, prompting, +! ptmass, radiation_utils, setbinary, sortutils, table_utils, units, +! vectorutils ! use part, only:xyzmh_ptmass,vxyz_ptmass,nptmass,poten,ihsoft,ihacc,& @@ -1370,7 +1371,7 @@ subroutine output_extra_quantities(time,dumpfile,npart,particlemass,xyzh,vxyzu) .or. quants==9 .or. quants==10 .or. quants==13) req_gas_energy = any(quants==1 .or. quants==2 .or. quants==3) req_thermal_energy = any(quants==1 .or. quants==3) - + if (any(quants==6 .or. quants==8)) then sinkcom_xyz = (xyzmh_ptmass(1:3,1)*xyzmh_ptmass(4,1) + xyzmh_ptmass(1:3,2)*xyzmh_ptmass(4,2)) & / (xyzmh_ptmass(4,1) + xyzmh_ptmass(4,2)) @@ -1385,7 +1386,7 @@ subroutine output_extra_quantities(time,dumpfile,npart,particlemass,xyzh,vxyzu) endif if (any(quants==10) .and. dump_number==0) allocate(init_entropy(npart)) - + if (any(quants==13)) call set_abundances ! set initial abundances to get mass_per_H @@ -1851,7 +1852,7 @@ subroutine recombination_tau(time,npart,particlemass,xyzh,vxyzu) kappa_part(i) = kappa ! In cgs units call ionisation_fraction(rho_part(i)*unit_density,eos_vars(itemp,i),X_in,1.-X_in-Z_in,xh0,xh1,xhe0,xhe1,xhe2) call calc_gas_energies(particlemass,poten(i),xyzh(:,i),vxyzu(:,i),rad(:,i),xyzmh_ptmass,phii,& - epoti,ekini,egasi,eradi,ereci,dum) + epoti,ekini,egasi,eradi,ereci,dum) call calc_thermal_energy(particlemass,ieos,xyzh(:,i),vxyzu(:,i),ponrhoi*rho_part(i),eos_vars(itemp,i),ethi) etoti = ekini + epoti + ethi if ((xh0 > recomb_th) .and. (.not. prev_recombined(i)) .and. (etoti < 0.)) then ! Recombination event and particle is still bound @@ -3776,7 +3777,7 @@ subroutine calc_gas_energies(particlemass,poten,xyzh,vxyzu,rad,xyzmh_ptmass,phii egasi = vxyzu(4)*particlemass egasradi = egasi + eradi case(10) ! not tested - eradi = 0. ! not implemented + eradi = 0. ! not implemented egasi = 0. ! not implemented call equationofstate(ieos,ponrhoi,spsoundi,rhoi,xyzh(1),xyzh(2),xyzh(3),tempi,vxyzu(4)) presi = ponrhoi*rhoi diff --git a/src/utils/analysis_kepler.f90 b/src/utils/analysis_kepler.f90 index e057510cb..8fbab5e9a 100644 --- a/src/utils/analysis_kepler.f90 +++ b/src/utils/analysis_kepler.f90 @@ -70,7 +70,7 @@ subroutine do_analysis(dumpfile,numfile,xyzh,vxyzu,pmass,npart,time,iunit) 'radius', & 'cell density', & 'cell temperature', & - 'cell radial momentum', & + 'cell radial vel', & 'angular vel (x)', & !ang velocity x component 'angular vel (y)', & !ang velocity y component 'angular vel (z)', & !velocity z component @@ -93,8 +93,9 @@ subroutine do_analysis(dumpfile,numfile,xyzh,vxyzu,pmass,npart,time,iunit) end subroutine do_analysis !---------------------------------------------------------------- !+ - ! This subroutine returns the position and velocity of a - ! particle wrt to the centre of star/max density point + ! This subroutine bins the particles + ! Max density particle is considered as the centre of the remanant + ! !+ !---------------------------------------------------------------- subroutine phantom_to_kepler_arrays(xyzh,vxyzu,pmass,npart,time,density,rad_grid,mass_enclosed,bin_mass,& @@ -106,7 +107,7 @@ subroutine phantom_to_kepler_arrays(xyzh,vxyzu,pmass,npart,time,density,rad_grid use sortutils, only : set_r2func_origin,indexxfunc,r2func_origin use eos, only : equationofstate,entropy,X_in,Z_in,gmw,init_eos use physcon, only : kb_on_mh,kboltz,atomic_mass_unit,avogadro,gg,pi,pc,years - use orbits_data, only : escape + use orbits_data, only : escape,semimajor_axis,period_star use linalg , only : inverse integer,intent(in) :: npart,numfile integer,intent(out) :: ibin,columns_compo @@ -122,10 +123,10 @@ subroutine phantom_to_kepler_arrays(xyzh,vxyzu,pmass,npart,time,density,rad_grid real :: pos(3),vel(3),kinetic_i,potential_i,energy_i,vel_mag,pos_mag,pos_next(3),vel_next(3),vel_mag_next,pos_mag_next integer ::particle_bound_bh,last_particle_with_neg_e,energy_verified_no,index_val,i_next,iu1,iu2,iu3,i_prev integer,allocatable :: index_particle_star(:),array_particle_j(:),array_bh_j(:) - integer :: dummy_size,dummy_bins,number_per_bin,count_particles,number_bins,no_particles,big_bins_no,tot_binned_particles + integer :: dummy_size,dummy_bins=5000,number_per_bin,count_particles,number_bins,no_particles,big_bins_no,tot_binned_particles real :: density_i,density_sum,rad_inner,rad_outer,radius_star logical :: double_the_no,escape_star - real :: omega_particle,omega_bin,pos_com(3),vel_com(3),pos_mag_star,vel_mag_star + real :: omega_particle,omega_bin,pos_mag_star,vel_mag_star real :: eni_input,u_i,temperature_i,temperature_sum,mu real :: ponrhoi,spsoundi,rad_vel_i,momentum_i,rad_mom_sum real :: bhmass,pos_prev(3),vel_prev(3),pos_mag_prev,vel_mag_prev @@ -133,173 +134,266 @@ subroutine phantom_to_kepler_arrays(xyzh,vxyzu,pmass,npart,time,density,rad_grid real,allocatable :: A_array(:), Z_array(:) real,allocatable :: interpolate_comp(:,:),composition_i(:),composition_sum(:) real :: ke_star,u_star,total_star,distance_from_bh,vel_from_bh,vel_at_infinity + real :: period_val + real,allocatable :: count_particles_temp(:),temp_array_new(:),temp_array_diff(:),temp_all_particles(:) + real :: max_temp=8000.,temp_cut=0. + real, dimension(200) :: temp_array_test + logical :: temp_found=.false. + integer :: count_loops_temp = 0 + real,allocatable :: temp_npart(:),den_npart(:),pos_npart(:),vel_npart(:),pos_vec_npart(:,:),vel_vec_npart(:,:) + real,allocatable :: h_npart(:),tot_eng_npart(:),ke_npart(:),pe_npart(:) + integer,allocatable :: sorted_index_npart(:),bound_index(:),sorted_index(:) + real :: pos_i,vel_i,pos_vec_i(3),vel_vec_i(3),ke_i,pe_i,tot_e_sum + real :: tot_rem_mass,pos_com(3),vel_com(3),pos_com_mag,vel_com_mag + integer :: index_sort,double_count + real,allocatable :: pos_wrt_bh(:,:),vel_wrt_bh(:,:),interp_comp_npart(:,:) + real :: vphi_i,R_mag_i,vphi_sum,R_vec(2),vphi_avg,omega_vec(3),rad_cyl,breakup + + ! use adiabatic EOS ieos = 2 call init_eos(ieos,ierr) gmw=0.61 - bhmass=1. + ! Set mass of black hole in code units + bhmass = 1 + ! Set initial cut based on temperature as zero K + temp_cut = 0. + allocate(temp_all_particles(npart)) ! performing a loop to determine maximum density particle position do j = 1,npart den_all(j) = rhoh(xyzh(4,j),pmass) enddo + + ! Save the location of max density particle location = maxloc(den_all,dim=1) - print*,location,"location of max density" - ! Determining centre of star as max density particle + print*,location,"LOCATION OF MAX" + ! Determining centre of star as max density particle. xpos(:) = xyzh(1:3,location) vpos(:) = vxyzu(1:3,location) distance_from_bh = sqrt(dot_product(xpos(:),xpos(:))) vel_from_bh = sqrt(dot_product(vpos(:),vpos(:))) - print*,"******************" - print*,distance_from_bh*udist,"distance from bh",vel_from_bh*unit_velocity,"velfrom bh" - print*,"*******************" - ! sorting particles + + ! sorting particles by radius. Letting the max density particle be the centre of the star. + ! This here for npart particles call set_r2func_origin(xpos(1),xpos(2),xpos(3)) call indexxfunc(npart,r2func_origin,xyzh,iorder) + + ! Get the composition array for all the particles call composition_array(interpolate_comp,columns_compo,comp_label) - call particles_bound_to_star(xpos,vpos,xyzh,vxyzu,pmass,npart,iorder,energy_verified_no,last_particle_with_neg_e,array_particle_j,array_bh_j,interpolate_comp,columns_compo,comp_label,numfile) - call assign_atomic_mass_and_number(comp_label,A_array,Z_array) - print*,array_particle_j(energy_verified_no),"Last particle indes",last_particle_with_neg_e - print*,energy_verified_no,"energy_verified_no",size(array_particle_j) + + ! Call the following function to obatin important information about each particle + call calculate_npart_quantities(npart,iorder,numfile,xyzh,vxyzu,pmass,xpos,vpos,comp_label,& + interpolate_comp,columns_compo,temp_npart,den_npart,pos_npart,vel_npart,& + pos_vec_npart,vel_vec_npart,tot_eng_npart,sorted_index_npart,ke_npart,pe_npart,& + pos_wrt_bh,vel_wrt_bh,h_npart,interp_comp_npart) + + ! This determines the particles bound to the star. Also removes the streams from the data + call particles_bound_to_star(pos_npart,temp_npart,tot_eng_npart,npart,sorted_index_npart,bound_index,sorted_index,energy_verified_no,& + last_particle_with_neg_e,ke_npart,pe_npart,den_npart) + + ! This determins how many particles would be added to the bins. We set a min no of bins as 500 in the code and using that along with the particles + ! that we consider as being part of the remnant implies that the model returns what the max number of particles I would have to add to a bin to get 500 bins call particles_per_bin(energy_verified_no,number_per_bin) - tot_binned_particles = 0 big_bins_no = number_per_bin + tot_binned_particles = 0 no_particles = 1 - dummy_bins = 5000 ibin = 1 double_the_no = .True. - + print*,size(bound_index),"BOUND INDEX ARRAY",energy_verified_no,"energy verified no" + ! Define bins arrays allocate(density(dummy_bins),rad_grid(dummy_bins),mass_enclosed(dummy_bins),bin_mass(dummy_bins),temperature(dummy_bins),rad_vel(dummy_bins),angular_vel_3D(3,dummy_bins)) + allocate(composition_i(columns_compo),composition_sum(columns_compo),composition_kepler(columns_compo,dummy_bins)) + + print*,"WORKED1" density_sum = 0. temperature_sum = 0. rad_mom_sum = 0. L_sum(:) = 0. I_sum(:,:) = 0. count_particles = 0 - allocate(composition_i(columns_compo)) - allocate(composition_sum(columns_compo)) - allocate(composition_kepler(columns_compo,dummy_bins)) composition_sum(:) = 0. composition_i(:) = 0. - - ! writing files with angular velocity info - open(1,file="particleOmega.info") - write(1,*) "[pos]"," ","[omega]" - open(2,file="binOmega.info") - write(2,*) "[pos]"," ","[omega]" - open(3,file="radius_of_bins.info") - write(3,*) "[ibin]"," ","[rad_inner]"," ","[rad_outer]"," ","[Position rad next]"," ","[particles in bin]" + pos_com(:) = 0. + vel_com(:) = 0. + tot_e_sum = 0. + vphi_sum = 9. + ! Write a comp file that includes informtation about the remnant only write(output,"(a4,i5.5)") 'compo',numfile open(4,file=output) - write(4,"(25(a22,1x))") & + write(4,"(32(a22,1x))") & "i", & "ibin", & "radius", & "x", & "y", & + "z", & + "radial_vel",& + 'temp',& + 'density',& + comp_label,& + 'omega',& + 'breakup',& + 'j',& + 'index_sort' + + + + ! this will determine when sorted indices are part of the star. We would also need the normal i indicies of the sorted particles + ! Using this we can determine which sorted particles are part of the array and then use the sorted information to calculate all the + ! quantities we require for the project + print*,size(bound_index),"bound index size",energy_verified_no,"energy_verified_no" + open(14,file="big_loop_clean.txt") + write(output,"(a4,i5.5)") 'vphi',numfile + open(10,file=output) + write(10,"(2(a22,1x))") & + "rad",& + "vphi" + + write(output,"(a4,i5.5)") 'vbra',numfile + open(111,file=output) + write(111,"(2(a22,1x))") & + "rad",& + "vbreak" + write(output,"(a4,i5.5)") 'rot_i',numfile + open(11,file=output) + open(41,file="remnant") + write(41,"(6(a22,1x))") & "x", & - comp_label - - pos_com(:) = 0. - vel_com(:) = 0. - ! Now we calculate the different quantities of the particles and bin them - do j=1,energy_verified_no - i = iorder(array_particle_j(j)) - if (j /= energy_verified_no) then - i_next = iorder(array_particle_j(j+1)) - else - i_next = iorder(array_particle_j(j)) - endif - - call particle_pos_and_vel_wrt_centre(xpos,vpos,xyzh,vxyzu,pos,vel,i,pos_mag,vel_mag) - - ! calculating centre of mass position and velocity wrt black hole - pos_com(:) = pos_com(:) + xyzh(1:3,i)*pmass - vel_com(:) = vel_com(:) + vxyzu(1:3,i)*pmass - - if (j /= energy_verified_no) then - call particle_pos_and_vel_wrt_centre(xpos,vpos,xyzh,vxyzu,pos_next,vel_next,i_next,pos_mag_next,vel_mag_next) - endif - + "y", & + "z", & + "m", & + "h", & + 'rho' + + do i = 1,energy_verified_no + ! get the sorted index of bound particles + j = bound_index(i) + index_sort = sorted_index(i) count_particles = count_particles + 1 - if (count_particles == 1) then - rad_inner = pos_mag - !print*,j,"j","first",rad_inner,"rad_inner",count_particles + ! Calculate the position and velocity VEC of COM + pos_com(:) = pos_com(:) + xyzh(1:3,index_sort)*pmass + vel_com(:) = vel_com(:) + vxyzu(1:3,index_sort)*pmass + + ! Obtain the values for each particle that is bound/ part of remnant + density_i = den_npart(j) + temperature_i = temp_npart(j) + pos_i = pos_npart(j) + vel_i = vel_npart(j) + pos_vec_i(:) = pos_vec_npart(:,j) + vel_vec_i(:) = vel_vec_npart(:,j) + R_vec(:) = pos_vec_i(1:2) + R_mag_i = norm2(R_vec) + ke_i = ke_npart(j) + pe_i = pe_npart(i) + write(14,*) i,j,pos_i,vel_i,pos_vec_i(1)*udist,pos_vec_i(2)*udist,pos_vec_i(3)*udist,temperature_i,density_i*unit_density,sorted_index(i) + + ! Calculate the angular velocity in cylindrical coordinates + vphi_i = vel_vec_i(1)*(-pos_vec_i(2)/R_mag_i) + vel_vec_i(2)*(pos_vec_i(1)/R_mag_i) + vphi_i = vphi_i/R_mag_i + + ! Position magnitude of the next bound particle + if (i /= energy_verified_no) then + pos_mag_next = pos_npart(j+1) endif - !print*,pos_mag_next-pos_mag,"difference in pos mag of next and current particle",i,"i",i_next,"i_next",j,"j",j+1,"jnext" - call no_per_bin(j,count_particles,double_the_no,number_per_bin,big_bins_no,energy_verified_no,pos_mag_next,rad_inner) - if (number_per_bin == count_particles) then - rad_outer = pos_mag - endif ! composition if (columns_compo /= 0) then - composition_i(:) = interpolate_comp(:,i) + composition_i(:) = interp_comp_npart(:,j) endif - composition_sum(:) = composition_sum(:) + composition_i(:) - - write(4,'(i9,1x,i5,1x,23(e18.10,1x))') & - i, & - ibin, & - pos_mag*udist, & - pos(1)*udist, & - pos(2)*udist, & - pos(3)*udist, & - composition_i(:) - ! calculate mean molecular weight that is required by the eos module using - ! the mass fractions for each particle. - ! do not consider neutron which is the first element of the composition_i array. - - call calculate_mu(A_array,Z_array,composition_i,columns_compo,mu) - - gmw = 1./mu - ! Density - density_i = rhoh(xyzh(4,i),pmass) - density_sum = density_sum + density_i - ! Temperature - u_i = vxyzu(4,i) - eni_input = u_i - call equationofstate(ieos,ponrhoi,spsoundi,density_i,xyzh(1,i),xyzh(2,i),xyzh(3,i),tempi=temperature_i,eni=eni_input) - temperature_sum = temperature_sum + temperature_i - - ! Radial momentum - ! we skip the first particle as its the one that exists at the center of - ! star and hence will give infinite rad_vel as rad = 0. - if (pos_mag > 0.) then - rad_vel_i = dot_product(vel(:),pos(:))/pos_mag - momentum_i = rad_vel_i*pmass - rad_mom_sum = rad_mom_sum + momentum_i + if (index_sort == 13) then + print*,composition_i(:),"compo in big look",j,"j",i,"i",index_sort,"index_sort2" endif + ! Calculate extra quantities + if (pos_i > 0.) then + ! Radial velocity + rad_vel_i = dot_product(vel_vec_i(:),pos_vec_i)/pos_i + momentum_i = rad_vel_i*pmass + endif ! Angular momentum - call cross_product3D(pos(:),vel(:),Li(:)) + call cross_product3D(pos_vec_i(:),vel_vec_i(:),Li(:)) L_i(:) = Li(:)*pmass - L_sum(:) = L_sum(:) + L_i(:) - if (pos_mag == 0.) then + ! Moment of Inertia Matrix + call moment_of_inertia(pos_vec_i,pos_i,pmass,i_matrix) + + if (pos_i == 0.) then omega_particle = 0. else - omega_particle = sqrt(dot_product(Li(:)/(pos_mag**2),Li(:)/(pos_mag**2))) + omega_vec(:) = Li(:)/pos_i**2 + omega_particle = norm2(omega_vec) endif + breakup = ((gg*i*pmass*umass)/(pos_i*udist)**3)**(0.5) + write(11,*) pos_i,omega_particle/utime,vphi_i/utime,pos_vec_i(1),pos_vec_i(2),pos_vec_i(3) + write(4,'(i9,1x,i5,1x,27(e18.10,1x),1x,i10,1x,i10)') & + i, & + ibin, & + pos_i*udist, & + pos_vec_i(1)*udist, & + pos_vec_i(2)*udist, & + pos_vec_i(3)*udist, & + rad_vel_i,& + temperature_i,& + density_i*unit_density,& + composition_i(:),& + omega_particle/utime,& + breakup,& + j,& + index_sort + + write(41,'(6(e18.10,1x))') & + pos_vec_i(1), & + pos_vec_i(2), & + pos_vec_i(3), & + pmass, & + h_npart(j), & + density_i + + ! Count particles keeps track of particles in a bin. + ! Rad_inner is the radius of the first particle that is added to a bin + if (count_particles == 1) then + rad_inner = pos_i + endif + ! Calculate how many particles will go in a bin + call no_per_bin(i,count_particles,double_the_no,number_per_bin,big_bins_no,energy_verified_no,pos_mag_next,rad_inner,double_count) + + ! We sum the quantities we want to save for the particles + density_sum = density_sum + density_i + temperature_sum = temperature_sum + temperature_i + rad_mom_sum = rad_mom_sum + momentum_i + L_sum(:) = L_sum(:) + L_i(:) + I_sum(:,:) = I_sum(:,:) + i_matrix(:,:) + composition_sum(:) = composition_sum(:) + composition_i(:) + tot_e_sum = ke_i + pe_i + tot_e_sum + vphi_sum = vphi_sum + vphi_i + + ! We check id the count_particles is the same as the number_per_bin + ! If true then we save the bin information + !if (count_particles==number_per_bin .or. i==energy_verified_no) then + if (count_particles==number_per_bin) then + ! Total particles binned. Should be the same as energy_verified_no at the end + tot_binned_particles = tot_binned_particles+count_particles - write(1,*)pos_mag,omega_particle - ! Moment of inertia - call moment_of_inertia(pos,pos_mag,pmass,i_matrix) - I_sum(:,:) = I_sum(:,:) + i_matrix(:,:) + ! Calculate the bin quantities + call radius_of_remnant(bound_index,count_particles,number_per_bin,i,energy_verified_no,pos_npart,radius_star,pos_vec_npart,rad_cyl) - if (count_particles==number_per_bin .or. j==energy_verified_no) then - tot_binned_particles = tot_binned_particles+count_particles - call radius_of_remnant(array_particle_j,count_particles,number_per_bin,j,energy_verified_no,xpos,vpos,xyzh,vxyzu,iorder,pos_mag,radius_star) rad_grid(ibin) = radius_star density(ibin) = density_sum/count_particles mass_enclosed(ibin) = tot_binned_particles*pmass bin_mass(ibin) = count_particles*pmass + ! Change the temperature of particles if its < 1.e3 to 1.e3 + if (temperature_sum < 1.e3) then + print*,"THIS BIN HAS TEMP LESS THAN 1000 K",temperature_sum + endif temperature(ibin) = max(temperature_sum/count_particles,1e3) rad_vel(ibin) = rad_mom_sum/bin_mass(ibin) !Radial vel of each bin is summation(vel_rad_i*m_i)/summation(m_i) if (count_particles == 1) then - if (pos_mag==0.) then + if (rad_grid(ibin)==0.) then + angular_vel_3D(:,ibin) = L_sum(:) else - angular_vel_3D(:,ibin) = L_sum(:) / (pos_mag**2*pmass) + + angular_vel_3D(:,ibin) = L_sum(:) / (pos_i**2*pmass) endif else inverse_of_i = inverse(I_sum, 3) @@ -308,10 +402,16 @@ subroutine phantom_to_kepler_arrays(xyzh,vxyzu,pmass,npart,time,density,rad_grid omega = reshape(matrix_result,(/3/)) angular_vel_3D(:,ibin) = omega endif - omega_bin = sqrt(dot_product(angular_vel_3D(:,ibin),angular_vel_3D(:,ibin))) - write(2,*)pos_mag,omega_bin composition_kepler(:,ibin) = composition_sum(:)/count_particles - write(3,*) ibin,rad_inner,rad_outer,pos_mag_next,count_particles + vphi_avg = vphi_sum/count_particles + breakup = ((gg*mass_enclosed(ibin)*umass)/(rad_grid(ibin)*udist)**3)**(0.5) + if (norm2(angular_vel_3D(:,ibin)) > 0) then + write(10,*) udist*rad_grid(ibin),norm2(angular_vel_3D(:,ibin))/utime + write(111,*) udist*rad_grid(ibin),breakup + endif + + !print*,count_particles,"count particles",ibin,"ibin",rad_grid(ibin),"rad",number_per_bin,"number per bin" + ! Reset the sum values count_particles = 0 density_sum = 0. temperature_sum = 0. @@ -319,49 +419,134 @@ subroutine phantom_to_kepler_arrays(xyzh,vxyzu,pmass,npart,time,density,rad_grid L_sum(:) = 0. I_sum(:,:) = 0. composition_sum(:) = 0. + vphi_sum = 0. ibin = ibin+1 + number_per_bin = big_bins_no endif enddo - print*,iu1,"iu",iu2,"iu2" - close(1) - close(2) - close(3) + close(111) close(4) + close(14) + close(10) + close(11) + close(41) + ! We want to set the radial and angular velocity of the first bin as the same as the second bin so that they are not zero + angular_vel_3D(:,1) = angular_vel_3D(:,2) + rad_vel(1) = rad_vel(2) ibin = ibin-1 - print*,mass_enclosed(ibin)*umass,"enclodsed mass",pos_com,"pos com" - print*,rad_grid(ibin),"Radius MAX" - pos_com(:) = pos_com(:)/mass_enclosed(ibin) - print*,pos_com,"pos of com" - vel_com(:) = vel_com(:)/mass_enclosed(ibin) - print*,pos_com,"pos_com",xpos,"xpos",vel_com,"vel com",vpos,"vpos" - print*,ibin,"ibin",tot_binned_particles - pos_mag_star = sqrt(dot_product(pos_com,pos_com)) - vel_mag_star = sqrt(dot_product(vel_com,vel_com)) - print*,"bhmass*umass",bhmass*umass - print*,"-------------------------------------------------------------------------" - print*,escape(vel_mag_star*unit_velocity,bhmass*umass,pos_mag_star*udist) - print*,"-------------------------------------------------------------------------" - ke_star = 0.5*(vel_mag_star*unit_velocity)**2 - u_star = -gg*(bhmass*umass)/(pos_mag_star*udist) - print*,"--------------" - print*,bhmass*umass,"BH mass", pos_mag_star*udist, "Pos Mag star",vel_mag_star*unit_velocity,"Vel Mag Star" - print*,"--------------" - total_star = u_star+ke_star - print*,umass,"umass",gg,"gg" - print*,total_star,"total_star",u_star,"ustar",ke_star,"ke_star" - print*,mass_enclosed(ibin),"/mass_enclosed(ibin)",mass_enclosed(ibin)*umass - vel_at_infinity = sqrt(2.*total_star) - if (isnan(vel_at_infinity)) then - vel_at_infinity = 0. - endif - print*,vel_at_infinity*1.e-5,"vel at infinity in Km/s" - print*,umass,"umass",udist,"udist",unit_density,"unit_density",unit_velocity,"unit_velocity",utime,"utime" - ! write information to the dump_info file - call write_compo_wrt_bh(xyzh,vxyzu,xpos,vpos,pmass,npart,iorder,array_bh_j,interpolate_comp,columns_compo,comp_label,energy_verified_no,last_particle_with_neg_e) + tot_rem_mass = mass_enclosed(ibin) + + ! Get the COM pos and vel magnitudes + call determine_pos_vel_com(vel_com,pos_com,pos_com_mag,vel_com_mag,tot_rem_mass) + print*,pos_i,"Radius of last particle in code units" + print*,pos_com,"POS COM",vel_com,"VEL COM" + print*,xpos,"XPOS",vpos,"VPOS",mass_enclosed(ibin)*umass,"mass" + print*,norm2(xpos),norm2(pos_com),"pos mag",norm2(pos_com)/170.33676805,"how far from rt?" + print*,norm2(vpos),norm2(vel_com),"vel mag" + ! Next we calculate the energy for the COM and determine if its bound or unbound + call determine_bound_unbound(vel_com,pos_com,pos_com_mag,vel_com_mag,bhmass,tot_rem_mass,pmass,& + total_star,ke_star,u_star,vel_at_infinity) + print*,tot_e_sum,"TOT E SUM" call write_dump_info(numfile,density(1),temperature(1),mass_enclosed(ibin),xpos,rad_grid(ibin),distance_from_bh,& - pos_mag_star,vel_mag_star,total_star,ke_star,u_star,time,vel_at_infinity) + pos_com_mag,vel_com_mag,total_star,ke_star,u_star,time,vel_at_infinity) + end subroutine phantom_to_kepler_arrays + !---------------------------------------------------------------- + !+ + ! This subroutine returns the magntitude of the COM pos and vel + !+ + !---------------------------------------------------------------- +subroutine determine_pos_vel_com(vel_com,pos_com,pos_com_mag,vel_com_mag,tot_rem_mass) + real,intent(inout),dimension(3) :: vel_com,pos_com + real,intent(in) :: tot_rem_mass + real,intent(out) :: vel_com_mag,pos_com_mag + + ! Divide the pos_com and vel_com with the total mass enclosed + pos_com(:) = pos_com(:)/tot_rem_mass + vel_com(:) = vel_com(:)/tot_rem_mass + + pos_com_mag = norm2(pos_com) + vel_com_mag = norm2(vel_com) + +end subroutine determine_pos_vel_com + !---------------------------------------------------------------- + !+ + ! This subroutine returns if remnant is bound or unbound + !+ + !---------------------------------------------------------------- +subroutine determine_bound_unbound(vel_com,pos_com,pos_com_mag,vel_com_mag,bhmass,tot_rem_mass,pmass,& + tot_energy_remnant_com,ke_star,pe_star,vel_at_infinity) + use units , only : udist,umass,unit_velocity + use physcon,only : gg + + real,intent(in) :: vel_com_mag,pos_com_mag,bhmass,tot_rem_mass,pmass + real,intent(in) :: pos_com(3),vel_com(3) + real,intent(out):: ke_star,pe_star,tot_energy_remnant_com,vel_at_infinity + real :: bhmass_cgs,rem_mass + real :: period_val,vel_com_cgs(3),pos_com_cgs(3) + real :: er, ar + + bhmass_cgs = bhmass*umass + rem_mass = tot_rem_mass*umass + vel_com_cgs(:) = vel_com(:)*unit_velocity + pos_com_cgs(:) = pos_com(:)*udist + ! Check if Total specific Energy of COM is < 0 or not (in cgs units) + ke_star = 0.5*(vel_com_mag*unit_velocity)**2 + pe_star = -gg*bhmass_cgs/(pos_com_mag*udist) + tot_energy_remnant_com = ke_star + pe_star + print*,vel_com_cgs,"CGS vel com",pos_com_cgs,"CGS pos com" + + if (tot_energy_remnant_com < 0.) then + print*, "REMNANT IS BOUND TO THE BLACKHOLE",tot_energy_remnant_com,"energy val" + call determine_orbital_params(rem_mass,bhmass_cgs,pos_com_cgs,vel_com_cgs,period_val) + ar = -gg*0.5*(bhmass_cgs + rem_mass)/tot_energy_remnant_com + er = 1 - (56.77892268*udist)/ar + print*,"******************" + print*,ar/1.496e13,"ar",er,"er" + elseif (tot_energy_remnant_com == 0.) then + print*, "Parabolic orbit!" + else + print*, "REMNANT IS UNBOUND" + call determine_inf_vel(tot_energy_remnant_com,vel_at_infinity) + print*,"VELOCITY OF REMNANT IN kms/s :",vel_at_infinity*1e-5 + ar = gg*0.5*(bhmass_cgs + rem_mass)/tot_energy_remnant_com + er = 1 + (56.77892268*udist)/ar + print*,"******************" + print*,ar/1.496e13,"ar",er,"er" + endif + + print*,pmass*(0.5*vel_com_mag**2 - (1/pos_com_mag)),"ENERGY OF COM" +end subroutine determine_bound_unbound + !---------------------------------------------------------------- + !+ + ! This subroutine returns the vel infinity for the remnant + ! if its unbound + !+ + !---------------------------------------------------------------- +subroutine determine_orbital_params(rem_mass,bhmass_cgs,pos_com,vel_com,period_val) + use orbits_data, only : escape,semimajor_axis,period_star,eccentricity_star + real,intent(in) :: rem_mass,bhmass_cgs,pos_com(3),vel_com(3) + real,intent(out):: period_val + real :: ecc_val + + ecc_val = eccentricity_star(rem_mass,bhmass_cgs,pos_com,vel_com) + print*,ecc_val,"ECCENTRICITY VALUE!!!!",rem_mass,"rem mass", bhmass_cgs,"bhmass cgs",pos_com,"com pos",vel_com,"com vel" + period_val = period_star(rem_mass,bhmass_cgs,pos_com,vel_com) + print*,period_val,"PERIOD OF STAR" + +end subroutine determine_orbital_params + !---------------------------------------------------------------- + !+ + ! This subroutine returns the oribital properties + !+ + !---------------------------------------------------------------- +subroutine determine_inf_vel(tot_energy_remnant_com,vel_at_infinity) + real,intent(in) :: tot_energy_remnant_com + real,intent(out):: vel_at_infinity + + vel_at_infinity = sqrt(2.*tot_energy_remnant_com) +end subroutine determine_inf_vel !---------------------------------------------------------------- !+ ! This subroutine returns the position and velocity of a @@ -386,122 +571,84 @@ end subroutine particle_pos_and_vel_wrt_centre ! This subroutine returns which particles are bound to the star !+ !---------------------------------------------------------------- -subroutine particles_bound_to_star(xpos,vpos,xyzh,vxyzu,pmass,npart,iorder,energy_verified_no,last_particle_with_neg_e,array_particle_j,array_bh_j,interpolate_comp,columns_compo,comp_label,numfile) - use units , only : udist,umass,unit_velocity,unit_energ - use vectorutils, only : cross_product3D - use part, only : rhoh,poten - use centreofmass, only : get_centreofmass - use sortutils, only : set_r2func_origin,indexxfunc,r2func_origin - use eos, only : equationofstate,entropy,X_in,Z_in,gmw,init_eos - use physcon, only : gg - - integer,intent(in) :: npart,iorder(:),numfile - real,intent(in) :: xyzh(:,:),vxyzu(:,:),xpos(:),vpos(:) - real,intent(in) :: pmass - character(len=20),intent(in) :: comp_label(:) - real,intent(in) :: interpolate_comp(:,:) - integer,intent(in) :: columns_compo - integer,intent(out) :: energy_verified_no,last_particle_with_neg_e - integer,allocatable,intent(out) :: array_particle_j(:),array_bh_j(:) - - character(len=120) :: output - integer,allocatable :: index_particle_star(:),index_particle_bh(:) - integer :: i,j,dummy_size,index_val,particle_bound_bh,index_val_bh,count_val,count_val_unbound,count_bound_both - real :: potential_wrt_bh,kinetic_wrt_bh,tot_wrt_bh,pos(3),vel(3) - real :: potential_i, kinetic_i,energy_i,pos_mag,vel_mag - logical :: bound_to_bh,bound_to_star - real,allocatable :: composition_i(:) - - bound_to_bh = .false. - bound_to_star = .false. - particle_bound_bh = 0 +subroutine particles_bound_to_star(pos_npart,temp_npart,tot_eng_npart,npart,sorted_index_npart,bound_index,sorted_index,bound_particles_no,& + last_particle_with_neg_e,ke_npart,pe_npart,den_npart) + + real,intent(in) :: temp_npart(:),tot_eng_npart(:),ke_npart(:),pe_npart(:),pos_npart(:),den_npart(:) + integer,intent(in) :: sorted_index_npart(:) + integer,intent(in) :: npart + + integer,allocatable,intent(out) :: bound_index(:),sorted_index(:) + integer,intent(out) :: bound_particles_no,last_particle_with_neg_e + integer :: energy_verified_no,i + real,allocatable :: index_particle_star(:),temp_bound(:),temp_particles(:) + integer,allocatable :: index_bound(:),index_bound_sorted(:),index_bound_new(:) + real :: max_temp=8000.,index_val + integer :: count_loops_temp=0 + logical :: temp_found,implement_temp_cut + real :: temp_cut + + ! Implement temp cut would try to remove the strems. But if you only want + ! to consider what is bound based on energy condition set this parameter to False + implement_temp_cut = .true. + bound_particles_no = 0 + temp_found = .false. energy_verified_no = 0 - index_val = 1 - index_val_bh = 1 - dummy_size = 1e8 - count_val = 0 - count_val_unbound = 0 - count_bound_both = 0 - - write(output,"(a8,i5.5)") 'compfull',numfile - open(5,file=output) - write(5,"(18(a22,1x))") & - comp_label - - allocate(index_particle_star(dummy_size),index_particle_bh(dummy_size)) - allocate(composition_i(columns_compo)) - do j = 1, npart - - i = iorder(j) !Access the rank of each particle in radius. - potential_wrt_bh = -(gg*umass*pmass*umass)/(sqrt(dot_product(xyzh(1:3,i),xyzh(1:3,i)))*udist) - kinetic_wrt_bh = 0.5*pmass*umass*dot_product(vxyzu(1:3,i),vxyzu(1:3,i))*unit_velocity**2 - tot_wrt_bh = potential_wrt_bh+ kinetic_wrt_bh+vxyzu(4,i)*pmass*unit_energ - if (tot_wrt_bh < 0.) then - bound_to_bh = .True. - endif - if (columns_compo /= 0) then - composition_i(:) = interpolate_comp(:,i) - endif - write(5,'(18(e18.10,1x))') & - composition_i(:) - !the position of the particle is calculated by subtracting the point of - !highest density. - !xyzh is position wrt the black hole present at origin. - call particle_pos_and_vel_wrt_centre(xpos,vpos,xyzh,vxyzu,pos,vel,i,pos_mag,vel_mag) - - !calculate the position which is the location of the particle. - potential_i = poten(i) - kinetic_i = 0.5*pmass*vel_mag**2 - - energy_i = potential_i + kinetic_i + vxyzu(4,i)*pmass - + allocate(index_particle_star(npart),index_bound(npart),temp_particles(npart)) + open(unit=10,file="particle_index_clean") + ! Use the sorted array information and check the energy condition first + do i=1,npart !if energy is less than 0, we have bound system. We can accept these particles. - if (energy_i < 0. .and. kinetic_i < 0.5*abs(potential_i)) then - bound_to_star = .True. + if (tot_eng_npart(i) < 0. .and. ke_npart(i) < 0.5*abs(pe_npart(i))) then + write(10,*) i,temp_npart(i),pos_npart(i),sorted_index_npart(i) energy_verified_no = energy_verified_no + 1 - last_particle_with_neg_e = j - index_particle_star(index_val) = j - index_val = index_val+1 + ! Save the index of these particles + ! this is because sometimes even if a particle is farther it could be could but the one before could be unbound + last_particle_with_neg_e = i + index_particle_star(energy_verified_no) = sorted_index_npart(i) + index_bound(energy_verified_no) = i + temp_particles(energy_verified_no) = temp_npart(i) + print*,"YES BOUND",i,"i" endif - - if (bound_to_bh == .True. .and. bound_to_star == .false.) then - count_val = count_val + 1 - index_particle_bh(index_val_bh) = j - particle_bound_bh = particle_bound_bh +1 - index_val_bh = index_val_bh+1 - endif - if (bound_to_bh == .True. .and. bound_to_star == .True.) then - count_bound_both = count_bound_both + 1 - endif - - if (bound_to_bh == .false. .and. bound_to_star == .false.) then - count_val_unbound = count_val_unbound + 1 - endif - bound_to_bh = .false. - bound_to_star = .false. - enddo - close(5) - print*,"===================================" - print*,count_val,"count val",count_val_unbound,"unbound count",count_bound_both,"count_bound_both" - print*,"====================================" - !next we save the index of particles which are part of star into a new array - allocate(array_particle_j(energy_verified_no)) - do i=1,energy_verified_no - array_particle_j(i) = index_particle_star(i) enddo - print*,"-------" - print*,npart,"npart in determing particles" - print*,"-------" - ! we save the index of the particles bound to the SMBH - allocate(array_bh_j(particle_bound_bh)) - do i=1,particle_bound_bh - array_bh_j(i) = index_particle_bh(i) + close(10) + allocate(temp_bound(energy_verified_no), index_bound_sorted(energy_verified_no),index_bound_new(energy_verified_no)) + do i = 1,energy_verified_no + temp_bound(i) = temp_particles(i) + ! This is the sorted index + index_bound_sorted(i) = index_particle_star(i) + index_bound_new(i) = index_bound(i) enddo - - print*,"--------" - print*,particle_bound_bh,"particle bound to the bh",size(array_bh_j),"array bh j" - print*,"Size of array with particles",size(array_particle_j) - print*,"--------" + if (implement_temp_cut) then + ! next we loop over the bound particles based on energy condition to find the temp_cut + ! As the models would need ages to evolve and I can not do that due to how slow some models run, we have streams around the remnants + ! Hence, we bin the temperature particles and try to find the cut in temperature + ! But using a temperature cut of 8000 K implies that if I use a model that has streams at high temperature (>1e4 K) because the remnant has just formed + ! then I would not get rid of the correct particles + ! Hence, we keep looping until the temperature being returned is the same as the max_temp + count_loops_temp = count_loops_temp + 1 + call calculate_temp_cut(temp_bound,energy_verified_no,temp_cut,max_temp,temp_found,count_loops_temp,den_npart) + max_temp = max_temp + 1000 + + allocate(bound_index(energy_verified_no),sorted_index(energy_verified_no)) + ! use temp_cut to ignore the streams + do i = 1,energy_verified_no + if (temp_bound(i) > temp_cut) then + bound_particles_no = bound_particles_no + 1 + ! Save the sorted array indices only + bound_index(bound_particles_no) = index_bound_new(i) + sorted_index(bound_particles_no) = index_bound_sorted(i) + if (sorted_index(bound_particles_no) == 13) then + print*, bound_index(bound_particles_no),"bound_index(bound_particles_no)" + endif + endif + enddo + else + bound_particles_no = energy_verified_no + allocate(bound_index(energy_verified_no),sorted_index(energy_verified_no)) + bound_index(:) = index_bound_new(:) + sorted_index(:) = index_bound_sorted(:) + endif end subroutine particles_bound_to_star !---------------------------------------------------------------- !+ @@ -518,7 +665,7 @@ subroutine particles_per_bin(energy_verified_no,number_per_bin) number_bins = 500 number_per_bin = (energy_verified_no/number_bins) if (mod(energy_verified_no,number_bins) /= 0) then - number_per_bin = 1 + number_per_bin + number_per_bin = 1+ number_per_bin endif end subroutine particles_per_bin @@ -528,59 +675,87 @@ end subroutine particles_per_bin ! on some conditions !+ !---------------------------------------------------------------- -subroutine no_per_bin(j,count_particles,double_the_no,number_per_bin,big_bins_no,energy_verified_no,pos_mag_next,rad_inner) - integer,intent(inout) :: number_per_bin +subroutine no_per_bin(j,count_particles,double_the_no,number_per_bin,big_bins_no,energy_verified_no,pos_mag_next,rad_inner,double_count) + integer,intent(inout) :: number_per_bin,double_count logical,intent(inout) :: double_the_no integer,intent(in) :: count_particles,big_bins_no,j,energy_verified_no real,intent(in) :: pos_mag_next,rad_inner + real,parameter :: min_no=5 + integer :: i + real :: avg_val,diff_val + + avg_val = (pos_mag_next+rad_inner)/2 + diff_val = (pos_mag_next-rad_inner) + open(15,file="rad_to_bin",status='old',action='write',iostat=i) + if (i /= 0) then + ! File does not exist, create it + open(unit=15,file="rad_to_bin",status='new',action='write',iostat=i) + endif if (j==1) then number_per_bin = 1 + double_count = 1 elseif (double_the_no==.True. .and. count_particles==1) then - number_per_bin = number_per_bin*2 + double_count = double_count*2 + number_per_bin = double_count if (number_per_bin >= big_bins_no) then number_per_bin = big_bins_no double_the_no = .False. endif else - if (pos_mag_next - rad_inner > 0.1) then - number_per_bin=count_particles - if (number_per_bin < 10) then - number_per_bin = 10 + if (double_the_no == .False. .and. j /= count_particles) then + if (100*(pos_mag_next-rad_inner)/rad_inner > 30) then + !print*,(((pos_mag_next-rad_inner)/rad_inner)*100),"per inc",j,"j",count_particles,"count_particles" + write(15,*) pos_mag_next,rad_inner,j,number_per_bin + number_per_bin=count_particles + !if (number_per_bin < min_no) then + ! number_per_bin = min_no + ! endif endif endif endif if (j==energy_verified_no) then number_per_bin = count_particles endif - end subroutine no_per_bin !---------------------------------------------------------------- !+ ! This subroutine returns radius of the remnant !+ !---------------------------------------------------------------- -subroutine radius_of_remnant(array_particle_j,count_particles,number_per_bin,j,energy_verified_no,xpos,vpos,xyzh,vxyzu,iorder,pos_mag,radius_star) - integer,intent(in) :: count_particles,number_per_bin,j,energy_verified_no,iorder(:),array_particle_j(:) - real,intent(in) :: xyzh(:,:),vxyzu(:,:),xpos(:),vpos(:),pos_mag - real,intent(out) :: radius_star - - real :: pos_mag_next,vel_mag_next,pos_next(3),vel_next(3) - integer :: i_next +subroutine radius_of_remnant(bound_index,count_particles,number_per_bin,i,energy_verified_no,pos_npart,radius_star,pos_vec_npart,rad_cyl) + integer,intent(in) :: count_particles,number_per_bin,i,energy_verified_no,bound_index(:) + real,intent(in) :: pos_npart(:),pos_vec_npart(:,:) + real,intent(out) :: radius_star,rad_cyl + + real :: pos_mag_next,pos_mag + integer :: index_val_next,index_val + real :: pos_cyl,pos_cyl_next + real :: pos_cyl_vec(3),pos_cyl_vec_next(3) + + index_val = bound_index(i) + index_val_next = bound_index(i+1) + pos_mag = pos_npart(index_val) + pos_cyl_vec(:) = pos_vec_npart(:,index_val) + pos_cyl = sqrt(pos_cyl_vec(1)**2 + pos_cyl_vec(2)**2) + if (count_particles == number_per_bin .and. i /= energy_verified_no) then + pos_mag_next = pos_npart(index_val_next) + pos_cyl_vec_next(:) = pos_vec_npart(:,index_val_next) - if (count_particles==number_per_bin .and. j /= energy_verified_no) then - i_next = iorder(array_particle_j(j+1)) - call particle_pos_and_vel_wrt_centre(xpos,vpos,xyzh,vxyzu,pos_next,vel_next,i_next,pos_mag_next,vel_mag_next) radius_star = (pos_mag+pos_mag_next)/2 + pos_cyl_next = sqrt(pos_cyl_vec_next(1)**2 + pos_cyl_vec_next(2)**2) + rad_cyl = (pos_cyl + pos_cyl_next)/2 else radius_star = pos_mag + rad_cyl = pos_cyl endif +! print*,norm2(pos_cyl_vec),"mag of pos_cyl vector",pos_mag,"pos_mag" end subroutine radius_of_remnant !---------------------------------------------------------------- !+ -! This subroutine calculates the moment of inertia +! This subroutine calculates the moment of inertia of each particle !+ !---------------------------------------------------------------- subroutine moment_of_inertia(pos,pos_mag,pmass,i_matrix) @@ -597,7 +772,107 @@ subroutine moment_of_inertia(pos,pos_mag,pmass,i_matrix) i_matrix = pmass*(pos_mag**2*delta - result_matrix) end subroutine moment_of_inertia + !---------------------------------------------------------------- + !+ + ! This subroutine calculates the temperature of all particles + ! by sorting them out with radius + ! Density is also sorted and saved. Along with the radius + !+ + !---------------------------------------------------------------- +subroutine calculate_npart_quantities(npart,iorder,numfile,xyzh,vxyzu,pmass,xpos,vpos,comp_label,& + interpolate_comp,columns_compo,temp_npart,den_npart,pos_npart,vel_npart,& + pos_vec_npart,vel_vec_npart,tot_eng_npart,sorted_index_npart,ke_npart,pe_npart,& + pos_wrt_bh,vel_wrt_bh,h_npart,interp_comp_npart) + + use units , only : udist,umass,unit_velocity,unit_energ + use vectorutils, only : cross_product3D + use part, only : rhoh,poten + use sortutils, only : set_r2func_origin,indexxfunc,r2func_origin + use eos, only : equationofstate,entropy,X_in,Z_in,gmw,init_eos + use physcon, only : gg + + integer,intent(in) :: npart,iorder(:),numfile + real,intent(in) :: xyzh(:,:),vxyzu(:,:) + real,intent(in) :: pmass + real,intent(inout) :: xpos(:),vpos(:) + character(len=20),intent(in) :: comp_label(:) + real,intent(in) :: interpolate_comp(:,:) + integer,intent(in) :: columns_compo + real,allocatable,intent(out) :: temp_npart(:),den_npart(:),pos_npart(:),vel_npart(:),pos_wrt_bh(:,:),vel_wrt_bh(:,:),h_npart(:) + real,allocatable,intent(out) :: pos_vec_npart(:,:),vel_vec_npart(:,:),tot_eng_npart(:) + real,allocatable,intent(out) :: ke_npart(:),pe_npart(:),interp_comp_npart(:,:) + integer,allocatable,intent(out) :: sorted_index_npart(:) + + integer :: i,j,ierr,ieos + real :: pos(3),vel(3) + real :: potential_i, kinetic_i,energy_i,pos_mag,vel_mag + real :: density_i,temperature_i,eni_input,u_i + real :: ponrhoi,spsoundi,mu + real,allocatable :: composition_i(:) + real,allocatable :: A_array(:), Z_array(:) + + ieos = 2 + gmw = 0.61 + call init_eos(ieos,ierr) + allocate(composition_i(columns_compo)) + call assign_atomic_mass_and_number(comp_label,A_array,Z_array) + ! Allocate arrays to save the sorted index,density,temperature,radius,total energy of particle wrt centre, velocity_npart + allocate(temp_npart(npart),den_npart(npart),pos_npart(npart),vel_npart(npart),sorted_index_npart(npart),tot_eng_npart(npart),ke_npart(npart),pe_npart(npart)) + allocate(pos_vec_npart(3,npart),vel_vec_npart(3,npart),pos_wrt_bh(3,npart),vel_wrt_bh(3,npart),h_npart(npart),interp_comp_npart(columns_compo,npart)) + + do j = 1, npart + !Access the rank of each particle in radius and save the sorted index + i = iorder(j) + sorted_index_npart(j) = i + + !if (columns_compo /= 0) then + ! composition_i(:) = interpolate_comp(:,i) + !endif + !the position of the particle is calculated by subtracting the point of + !highest density. + !xyzh is position wrt the black hole present at origin. + call particle_pos_and_vel_wrt_centre(xpos,vpos,xyzh,vxyzu,pos,vel,i,pos_mag,vel_mag) + !calculate the position which is the location of the particle. + potential_i = poten(i) + kinetic_i = 0.5*pmass*vel_mag**2 + density_i = rhoh(xyzh(4,i),pmass) + energy_i = potential_i + kinetic_i + vxyzu(4,i)*pmass + print*,potential_i,"POTENTIAL I",kinetic_i,"Kinetic I" + + ! composition + if (columns_compo /= 0) then + composition_i(:) = interpolate_comp(:,i) + endif + if (i == 13) then + print*,composition_i(:),"compo",i,"i before",j,"j" + endif + ! calculate mean molecular weight that is required by the eos module using + ! the mass fractions for each particle. + ! do not consider neutron which is the first element of the composition_i array. + call calculate_mu(A_array,Z_array,composition_i,columns_compo,mu) + gmw = 1./mu + u_i = vxyzu(4,i) + eni_input = u_i + call equationofstate(ieos,ponrhoi,spsoundi,density_i,xyzh(1,i),xyzh(2,i),xyzh(3,i),tempi=temperature_i,eni=eni_input) + ! Save the information for each particle that we need + den_npart(j) = density_i + temp_npart(j) = temperature_i + pos_npart(j) = pos_mag + vel_npart(j) = vel_mag + vel_vec_npart(:,j) = vel(:) + pos_vec_npart(:,j) = pos(:) + tot_eng_npart(j) = energy_i + ke_npart(j) = kinetic_i + pe_npart(j) = potential_i + pos_wrt_bh(:,j) = xyzh(1:3,i) + vel_wrt_bh(:,j) = vxyzu(1:3,i) + h_npart(j) = xyzh(4,i) + + interp_comp_npart(:,j) = interpolate_comp(:,i) + enddo + +end subroutine calculate_npart_quantities !---------------------------------------------------------------- !+ ! This routine reads the output file that contains composition @@ -624,7 +899,7 @@ subroutine composition_array(interpolate_comp,columns_compo,comp_label) n_rows = 0 iexist = .false. - filename = 'kepler.comp' + filename = 'tde.comp' !First check if kepler.comp exists. !This file will only be generated if KEPLER file had composition stored in it. inquire(file=filename,exist=iexist) @@ -795,7 +1070,12 @@ subroutine calculate_mu(A_array,Z_array,composition_i,columns_compo,mu) end subroutine calculate_mu - +!---------------------------------------------------------------- +!+ +! This routine updates the dump_info file with the information +! for full dumps files +!+ +!---------------------------------------------------------------- subroutine write_dump_info(fileno,density,temperature,mass,xpos,rad,distance,pos_mag_star,vel_mag_star,& tot_energy,kinetic_energy,potential_energy,time,vel_at_infinity) @@ -827,7 +1107,7 @@ subroutine write_dump_info(fileno,density,temperature,mass,xpos,rad,distance,pos stop endif ! Write headers to file - write(file_id,'(16(a22,1x))') & + write(file_id,'(17(a22,1x))') & "FileNo", & "Density",& "Temperature",& @@ -843,10 +1123,11 @@ subroutine write_dump_info(fileno,density,temperature,mass,xpos,rad,distance,pos "specKE",& "specPE",& "time",& - "Escape_in" + "Escape_in",& + "Accretion_r" endif - write(file_id,'(i5,1x,15(e18.10,1x))')fileno,density*unit_density,temperature,mass*umass,xpos(1)*udist,xpos(2)*udist,xpos(3)*udist,rad*udist,distance*udist,pos_mag_star*udist,& - vel_mag_star*unit_velocity,tot_energy,kinetic_energy,potential_energy,time*utime,vel_at_infinity*1e-5 + write(file_id,'(i5,1x,16(e18.10,1x))')fileno,density*unit_density,temperature,mass*umass,xpos(1)*udist,xpos(2)*udist,xpos(3)*udist,rad*udist,distance*udist,pos_mag_star*udist,& + vel_mag_star*unit_velocity,tot_energy,kinetic_energy,potential_energy,time*utime,vel_at_infinity*1e-5,(mass*umass)/(time/(365*24*3600)*utime) close(file_id) end subroutine write_dump_info @@ -898,4 +1179,139 @@ subroutine write_compo_wrt_bh(xyzh,vxyzu,xpos,vpos,pmass,npart,iorder,array_bh_j end subroutine write_compo_wrt_bh + !---------------------------------------------------------------- + !+ + ! This subroutine is to get the temperature cut + ! + !+ + !---------------------------------------------------------------- +subroutine calculate_temp_cut(temperature_array,count_bound,temp_cut,max_temp,temp_found,count_loops_temp,density_array) + real,intent(in) :: temperature_array(:),max_temp,density_array(:) + integer,intent(in) :: count_bound,count_loops_temp + real,intent(out) :: temp_cut + integer :: i,count_possible_temp,m + integer,parameter :: nbins=20000 + real, dimension(nbins)::temp_array_test + real,allocatable :: avg_density(:) + real,allocatable :: temp_array_new(:),count_particles_temp(:),diff_count_particles(:),diff2_count_particles(:),diff3_count_particles(:),array_input(:) + real :: temp_start,count_temp_particles=0,dtemp + integer :: index_val,avg_inde + real :: mean,variance,std,cut_off + real :: count_cut,count_cut_index,lower_limit,upper_limit + logical, intent(inout) :: temp_found + + + ! First we create an array of possible temperature from max_temp to 0 with a step size of 100. + temp_start = 0. + dtemp = 100. + + count_cut_index = 0 + count_cut = 0. + count_possible_temp=1+(max_temp/dtemp) + + ! Create array with the temperatures ranging from 0 to max_temp + do m=1,nbins + if (temp_start <= max_temp) then + temp_array_test(m) = temp_start + temp_start = temp_start + dtemp + endif + enddo + + ! Allocate arrays to save the number of particles per bin + allocate(temp_array_new(count_possible_temp),count_particles_temp(count_possible_temp), array_input(count_possible_temp),avg_density(count_possible_temp)) + + count_particles_temp(:) = 0 + + ! Next we create the same size array as count_possible_temp + do m=1,count_possible_temp + temp_array_new(m) = temp_array_test(m) + enddo + + ! this will count the particles for each temperature and then save them into a new array + do i =1,count_bound + do m=1,size(temp_array_new)-1 + if (temperature_array(i) >= temp_array_new(m) .and. temperature_array(i) < temp_array_new(m+1) ) then + count_temp_particles = count_particles_temp(m) + 1 + count_particles_temp(m) = count_temp_particles + avg_density(m) = density_array(i) + endif + enddo + enddo + + print*,"***-------------------------------------" + print*,temp_array_new,"TEMP ARRAY",size(temp_array_new) + print*,count_particles_temp,"COUNT PARTICLES TEMP",size(count_particles_temp) + print*,avg_density,"AVG DENSITY FOR EACH BIN" + print*,"***-------------------------------------" + ! Calculate the mean, std of the data + call statistics(count_particles_temp,mean,variance,std) + + ! Using 2 sigma as the data sample is small to determine the outlier + cut_off = std*2 + lower_limit = mean - cut_off + upper_limit = mean + cut_off + + + ! This loops and find the last element which is outside the limits based on 2 sigma + do i=1,size(count_particles_temp) + if (count_particles_temp(i) > upper_limit .or. count_particles_temp(i) < lower_limit) then + count_cut = count_particles_temp(i) + count_cut_index = i + endif + enddo + print*,count_cut,"count cut first",count_cut_index,"count_cut_index" + ! this starts from the cound_cut_index found earlier but then tries to make sure that the cut is done when the gaussian bins + ! have less than 5% particles compared to the max_temp_cut found above + do i=count_cut_index,size(count_particles_temp) + if ((count_particles_temp(i)/count_cut)*100 < 1.) then + count_cut = count_particles_temp(i) + print*,count_cut,"count_cut",(count_particles_temp(i)/count_cut)*100,"(count_particles_temp(i)/count_cut)*100" + count_cut_index = i + exit + endif + enddo + + !print*,count_cut_index,"final cut index" + + ! Define the temperature to cut the model at + temp_cut = temp_array_new(count_cut_index) + + if (temp_cut /= max_temp) then + temp_found = .true. + endif + + ! If we get the temp_cut as 0. K and the count_loops_temp is 1, then we accept that as a true value + if (temp_cut == 0.0 .and. count_loops_temp /= 1) then + temp_found = .false. + endif + print*,temp_cut,"TEMP CUT" +end subroutine calculate_temp_cut + +! -------------------------------------------------------------------- +! This subroutine calculates the mean, variance and standard deviation +! +! -------------------------------------------------------------------- +subroutine statistics(array_data,mean,variance,std) + real,allocatable,intent(in) :: array_data(:) + real,intent(out) :: mean,variance + integer :: size_array,i + real :: var,sum_val,std + + sum_val = 0. + var = 0. + size_array = size(array_data) + do i=1,size_array + sum_val = sum_val + array_data(i) + enddo + mean = sum_val/size_array + + do i=1,size_array + var = var + (array_data(i) - mean)**2 + enddo + + variance = var/(size_array-1) + std = sqrt(variance) + +end subroutine statistics + end module analysis diff --git a/src/utils/utils_orbits.f90 b/src/utils/utils_orbits.f90 index a92cd3da9..502fcc331 100644 --- a/src/utils/utils_orbits.f90 +++ b/src/utils/utils_orbits.f90 @@ -65,7 +65,7 @@ function hvector(pos_vec,vel_vec) real,intent(in) :: pos_vec(3),vel_vec(3) real,dimension(3) :: hvector - call cross_product3D(vel_vec,pos_vec,hvector) + call cross_product3D(pos_vec,vel_vec,hvector) end function hvector