Skip to content

Commit

Permalink
(grdisc) added set_orbit functionality for setting up orbits in diffe…
Browse files Browse the repository at this point in the history
…rent parameter sets; add helper routines to set up multiple stars; added stars to grdisc setup for qpes
  • Loading branch information
danieljprice committed May 17, 2024
1 parent eae7a47 commit 8a57204
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 111 deletions.
2 changes: 1 addition & 1 deletion build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ phantomsetup: setup
SRCSETUP= prompting.f90 utils_omp.F90 setup_params.f90 \
set_dust_options.f90 set_units.f90 \
density_profiles.f90 readwrite_kepler.f90 readwrite_mesa.f90 \
set_slab.f90 set_disc.F90 \
set_slab.f90 set_disc.F90 set_orbit.f90 \
set_cubic_core.f90 set_fixedentropycore.f90 set_softened_core.f90 \
set_star_utils.f90 relax_star.f90 set_star.f90 set_hierarchical.f90 \
set_vfield.f90 set_Bfield.f90 \
Expand Down
13 changes: 7 additions & 6 deletions src/setup/set_binary.f90
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module setbinary
end interface get_eccentricity_vector

real, parameter :: pi = 4.*atan(1.)
real, parameter :: deg_to_rad = pi/180.
integer, parameter :: &
ierr_m1 = 1, &
ierr_m2 = 2, &
Expand Down Expand Up @@ -186,19 +187,19 @@ subroutine set_binary(m1,m2,semimajoraxis,eccentricity, &
if (present(posang_ascnode) .and. present(arg_peri) .and. present(incl)) then
! Campbell elements
ecc = eccentricity
omega = arg_peri*pi/180.
omega = arg_peri*deg_to_rad
! our conventions here are Omega is measured East of North
big_omega = posang_ascnode*pi/180. + 0.5*pi
inc = incl*pi/180.
big_omega = posang_ascnode*deg_to_rad + 0.5*pi
inc = incl*deg_to_rad

if (present(f)) then
! get eccentric, parabolic or hyperbolic anomaly from true anomaly
! (https://en.wikipedia.org/wiki/Eccentric_anomaly#From_the_true_anomaly)
theta = f*pi/180.
theta = f*deg_to_rad
E = get_E_from_true_anomaly(theta,ecc)
elseif (present(mean_anomaly)) then
! get eccentric anomaly from mean anomaly by solving Kepler equation
bigM = mean_anomaly*pi/180.
bigM = mean_anomaly*deg_to_rad
E = get_E_from_mean_anomaly(bigM,ecc)
else
! set binary at apastron
Expand Down Expand Up @@ -324,7 +325,7 @@ subroutine set_binary(m1,m2,semimajoraxis,eccentricity, &
! rotate if inclination is non-zero
!
if (present(incl) .and. .not.(present(arg_peri) .and. present(posang_ascnode))) then
xangle = incl*pi/180.
xangle = incl*deg_to_rad
cosi = cos(xangle)
sini = sin(xangle)
do i=i1,i2
Expand Down
270 changes: 270 additions & 0 deletions src/setup/set_orbit.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
!--------------------------------------------------------------------------!
! The Phantom Smoothed Particle Hydrodynamics code, by Daniel Price et al. !
! Copyright (c) 2007-2024 The Authors (see AUTHORS) !
! See LICENCE file for usage and distribution conditions !
! http://phantomsph.github.io/ !
!--------------------------------------------------------------------------!
module setorbit
!
! Generic procedure for setting up two body orbits with
! different parameter sets for the orbital elements
!
! The current options are:
! 0) Campbell elements for bound or unbound orbit (aeiOwf)
! 1) Flyby parameters (periapsis, initial separation, argument of periapsis, inclination)
! 2) position and velocity for both bodies

! While Campbell elements can be used for unbound orbits, they require
! specifying the true anomaly at the start of the simulation. This is
! not always easy to determine, so the flyby option is provided as an
! alternative. There one specifies the initial separation instead, however
! the choice of angles is more restricted
!
! :References: None
!
! :Owner: Daniel Price
!
! :Runtime parameters: None
!
! :Dependencies: physcon
!
implicit none
public :: set_orbit
public :: set_defaults_orbit,write_options_orbit,read_options_orbit
public :: orbit_t
!
! define data types with options needed
! to setup an orbit
!
type campbell_elems
character(len=20) :: semi_major_axis ! string because can specific units
real :: e ! eccentricity
real :: i ! inclination
real :: O ! position angle of the ascending node
real :: w ! argument of periapsis
real :: f ! initial true anomaly
end type campbell_elems

type posvel_elems
real :: x1(3) ! position of body 1
real :: v1(3) ! velocity of body 1
real :: x2(3) ! position of body 2
real :: v2(3) ! velocity of body 2
end type posvel_elems

type flyby_elems
character(len=20) :: rp ! pericentre distance in arbitrary units
real :: d ! initial separation
real :: O ! position angle of the ascending node
real :: i ! inclination
end type flyby_elems

!
! generic type handling all options
!
type orbit_t
integer :: itype
type(campbell_elems) :: elems
type(flyby_elems) :: flyby
type(posvel_elems) :: posvel
end type orbit_t

private

contains

!----------------------------------------------------------------
!+
! default parameters for orbit type
!+
!----------------------------------------------------------------
subroutine set_defaults_orbit(orbit)
type(orbit_t), intent(out) :: orbit

orbit%itype = 0
orbit%elems%semi_major_axis = '10.'
orbit%elems%e = 0.0
orbit%elems%i = 0.0
orbit%elems%O = 0.0
orbit%elems%w = 270. ! argument of periapsis
orbit%elems%f = 180. ! start orbit at apocentre

orbit%flyby%rp = '10.'
orbit%flyby%d = 100.0
orbit%flyby%O = 0.0
orbit%flyby%i = 0.0

orbit%posvel%x1 = 0.0
orbit%posvel%v1 = 0.0
orbit%posvel%x2 = 0.0
orbit%posvel%v2 = 0.0
orbit%posvel%x1(1) = 10.0
orbit%posvel%x2(1) = -10.0
orbit%posvel%v1(2) = 1.0
orbit%posvel%v2(2) = -1.0

end subroutine set_defaults_orbit

!----------------------------------------------------------------
!+
! setup for two body orbit
!+
!----------------------------------------------------------------
subroutine set_orbit(orbit,m1,m2,hacc1,hacc2,xyzmh_ptmass,vxyz_ptmass,nptmass,verbose,ierr,omega_corotate)
use physcon, only:days
use units, only:in_code_units,is_time_unit,utime
use setbinary, only:set_binary,get_a_from_period
use setflyby, only:set_flyby
type(orbit_t), intent(in) :: orbit
real, intent(in) :: m1,m2,hacc1,hacc2
real, intent(inout) :: xyzmh_ptmass(:,:),vxyz_ptmass(:,:)
integer, intent(inout) :: nptmass
logical, intent(in) :: verbose
integer, intent(out) :: ierr
real, intent(out), optional :: omega_corotate
real :: rp,a

ierr = 0
select case(orbit%itype)
case(2)
! body 1
xyzmh_ptmass(1:3,nptmass+1) = orbit%posvel%x1(1:3)
xyzmh_ptmass(4,nptmass+1) = m1
xyzmh_ptmass(5,nptmass+1) = hacc1
vxyz_ptmass(1:3,nptmass+1) = orbit%posvel%v1(1:3)
! body 2
xyzmh_ptmass(1:3,nptmass+2) = orbit%posvel%x2(1:3)
xyzmh_ptmass(4,nptmass+2) = m2
xyzmh_ptmass(5,nptmass+2) = hacc2
vxyz_ptmass(1:3,nptmass+2) = orbit%posvel%v2(1:3)
case(1)
rp = in_code_units(orbit%flyby%rp,ierr)

call set_flyby(m1,m2,rp,orbit%flyby%d,hacc1,hacc2,xyzmh_ptmass, &
vxyz_ptmass,nptmass,ierr,orbit%flyby%O,orbit%flyby%i,verbose=verbose)
case default
!
!--if a is negative or is given time units, interpret this as a period
!
a = in_code_units(orbit%elems%semi_major_axis,ierr)
if (is_time_unit(orbit%elems%semi_major_axis) .and. ierr == 0) then
a = -abs(a)
print "(a,g0,a,g0,a)",' Using PERIOD = ',abs(a),' = ',abs(a)*utime/days,' days'
endif
if (a < 0.) a = get_a_from_period(m1,m2,abs(a))
!
!--now setup orbit using sink particles
!
if (present(omega_corotate)) then
call set_binary(m1,m2,a,orbit%elems%e,hacc1,hacc2,&
xyzmh_ptmass,vxyz_ptmass,nptmass,ierr,omega_corotate,&
posang_ascnode=orbit%elems%O,arg_peri=orbit%elems%w,&
incl=orbit%elems%i,f=orbit%elems%f,verbose=verbose)
else
call set_binary(m1,m2,a,orbit%elems%e,hacc1,hacc2,&
xyzmh_ptmass,vxyz_ptmass,nptmass,ierr,&
posang_ascnode=orbit%elems%O,arg_peri=orbit%elems%w,&
incl=orbit%elems%i,f=orbit%elems%f,verbose=verbose)
endif
end select

end subroutine set_orbit

!----------------------------------------------------------------
!+
! write options to .setup file
!+
!----------------------------------------------------------------
subroutine write_options_orbit(orbit,iunit,label)
use infile_utils, only:write_inopt
type(orbit_t), intent(in) :: orbit
integer, intent(in) :: iunit
character(len=*), intent(in), optional :: label
character(len=10) :: c

! append optional label e.g. '1', '2'
c = ''
if (present(label)) c = trim(adjustl(label))

write(iunit,"(/,a)") '# orbit '//trim(c)
call write_inopt(orbit%itype,'itype'//trim(c),'type of orbital elements (0=aeiOwf,1=flyby,2=posvel)',iunit)
select case(orbit%itype)
case(2)
call write_inopt(orbit%posvel%x1(1),'x1'//trim(c),'x position body 1',iunit)
call write_inopt(orbit%posvel%x1(2),'y1'//trim(c),'y position body 1',iunit)
call write_inopt(orbit%posvel%x1(3),'z1'//trim(c),'z position body 1',iunit)
call write_inopt(orbit%posvel%v1(1),'vx1'//trim(c),'x velocity body 1',iunit)
call write_inopt(orbit%posvel%v1(2),'vy1'//trim(c),'y velocity body 1',iunit)
call write_inopt(orbit%posvel%v1(3),'vz1'//trim(c),'z velocity body 1',iunit)
call write_inopt(orbit%posvel%x2(1),'x2'//trim(c),'x position body 2',iunit)
call write_inopt(orbit%posvel%x2(2),'y2'//trim(c),'y position body 2',iunit)
call write_inopt(orbit%posvel%x2(3),'z2'//trim(c),'z position body 2',iunit)
call write_inopt(orbit%posvel%v2(1),'vx2'//trim(c),'x velocity body 2',iunit)
call write_inopt(orbit%posvel%v2(2),'vy2'//trim(c),'y velocity body 2',iunit)
call write_inopt(orbit%posvel%v2(3),'vz2'//trim(c),'z velocity body 2',iunit)
case(1)
call write_inopt(orbit%flyby%rp,'rp'//trim(c),'pericentre distance',iunit)
call write_inopt(orbit%flyby%d,'d'//trim(c),'initial separation [same units as rp]',iunit)
call write_inopt(orbit%flyby%O,'O'//trim(c),'position angle of the ascending node',iunit)
call write_inopt(orbit%flyby%i,'i'//trim(c),'inclination',iunit)
case default
call write_inopt(orbit%elems%semi_major_axis,'a'//trim(c),&
'semi-major axis (e.g. 1 au), period (e.g. 10*days) or rp if e=1',iunit)
call write_inopt(orbit%elems%e,'ecc'//trim(c),'eccentricity',iunit)
call write_inopt(orbit%elems%i,'inc'//trim(c),'inclination (deg)',iunit)
call write_inopt(orbit%elems%O,'O'//trim(c),'position angle of ascending node (deg)',iunit)
call write_inopt(orbit%elems%w,'w'//trim(c),'argument of periapsis (deg)',iunit)
call write_inopt(orbit%elems%f,'f'//trim(c),'initial true anomaly (180=apoastron)',iunit)
end select

end subroutine write_options_orbit

!----------------------------------------------------------------
!+
! read options from .setup file
!+
!----------------------------------------------------------------
subroutine read_options_orbit(orbit,db,nerr,label)
use infile_utils, only:inopts,read_inopt
type(orbit_t), intent(out) :: orbit
type(inopts), allocatable, intent(inout) :: db(:)
integer, intent(inout) :: nerr
character(len=*), intent(in), optional :: label
character(len=10) :: c

! append optional label e.g. '1', '2'
c = ''
if (present(label)) c = trim(adjustl(label))

call read_inopt(orbit%itype,'itype'//trim(c),db,errcount=nerr,min=0,max=2)
select case(orbit%itype)
case(2)
call read_inopt(orbit%posvel%x1(1),'x1'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%x1(2),'y1'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%x1(3),'z1'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%v1(1),'vx1'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%v1(2),'vy1'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%v1(3),'vz1'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%x2(1),'x2'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%x2(2),'y2'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%x2(3),'z2'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%v2(1),'vx2'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%v2(2),'vy2'//trim(c),db,errcount=nerr)
call read_inopt(orbit%posvel%v2(3),'vz2'//trim(c),db,errcount=nerr)
case(1)
call read_inopt(orbit%flyby%rp,'rp'//trim(c),db,errcount=nerr)
call read_inopt(orbit%flyby%d,'d'//trim(c),db,errcount=nerr)
call read_inopt(orbit%flyby%O,'O'//trim(c),db,errcount=nerr)
call read_inopt(orbit%flyby%i,'i'//trim(c),db,errcount=nerr)
case default
call read_inopt(orbit%elems%semi_major_axis,'a'//trim(c),db,errcount=nerr)
call read_inopt(orbit%elems%e,'ecc'//trim(c),db,min=0.,errcount=nerr)
call read_inopt(orbit%elems%i,'inc'//trim(c),db,errcount=nerr)
call read_inopt(orbit%elems%O,'O'//trim(c),db,errcount=nerr)
call read_inopt(orbit%elems%w,'w'//trim(c),db,errcount=nerr)
call read_inopt(orbit%elems%f,'f'//trim(c),db,errcount=nerr)
end select

end subroutine read_options_orbit

end module setorbit
Loading

0 comments on commit 8a57204

Please sign in to comment.