bondi3d.f90
Go to the documentation of this file.
1 !#############################################################################
2 !# #
3 !# fosite - 3D hydrodynamical simulation program #
4 !# module: bondi3d.f90 #
5 !# #
6 !# Copyright (C) 2006-2018 #
7 !# Tobias Illenseer <tillense@astrophysik.uni-kiel.de> #
8 !# #
9 !# This program is free software; you can redistribute it and/or modify #
10 !# it under the terms of the GNU General Public License as published by #
11 !# the Free Software Foundation; either version 2 of the License, or (at #
12 !# your option) any later version. #
13 !# #
14 !# This program is distributed in the hope that it will be useful, but #
15 !# WITHOUT ANY WARRANTY; without even the implied warranty of #
16 !# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or #
17 !# NON INFRINGEMENT. See the GNU General Public License for more #
18 !# details. #
19 !# #
20 !# You should have received a copy of the GNU General Public License #
21 !# along with this program; if not, write to the Free Software #
22 !# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #
23 !# #
24 !#############################################################################
25 
26 !----------------------------------------------------------------------------!
41 !----------------------------------------------------------------------------!
42 PROGRAM bondi3d
43  USE fosite_mod
44 #include "tap.h"
45  IMPLICIT NONE
46  !--------------------------------------------------------------------------!
47  ! general constants
48  REAL, PARAMETER :: msun = 1.989e+30 ! solar mass [kg] !
49  REAL, PARAMETER :: gn = 6.67408e-11 ! gravitional constant (SI) !
50  ! simulation parameters
51  REAL, PARAMETER :: tsim = 20.0 ! simulation time [TAU] !
52  REAL, PARAMETER :: accmass = 1.0*msun ! mass of the accreting object !
53  REAL, PARAMETER :: gamma = 1.4 ! ratio of specific heats !
54  ! boundary conditions
55  REAL, PARAMETER :: rhoinf = 3.351e-17 ! density at infinity [kg/m^3] !
56  REAL, PARAMETER :: csinf = 537.0 ! sound speed at infinity [m/s]!
57  ! mesh settings
58  INTEGER, PARAMETER :: mgeo = spherical ! geometry of the mesh !
59  INTEGER, PARAMETER :: xres = 100 ! x-resolution !
60  INTEGER, PARAMETER :: yres = 4 ! y-resolution !
61  INTEGER, PARAMETER :: zres = 4 ! z-resolution !
62  REAL, PARAMETER :: rin = 0.1 ! inner/outer radii in terms of!
63  REAL, PARAMETER :: rout = 2.0 ! the Bondi radius RB, ROUT > 1!
64  ! output parameters
65  INTEGER, PARAMETER :: onum = 10 ! number of output data sets
66  CHARACTER(LEN=256), PARAMETER & ! output data dir
67  :: odir = './'
68  CHARACTER(LEN=256), PARAMETER & ! output data file name
69  :: ofname = 'bondi3d'
70  ! some derives quandities
71  REAL :: rb ! Bondi radius
72  REAL :: tau ! free fall time scale
73  !--------------------------------------------------------------------------!
74  CLASS(fosite), ALLOCATABLE :: sim
75  REAL, DIMENSION(:), ALLOCATABLE :: sigma
76  REAL :: sum_numer, sum_denom
77  INTEGER :: n,den,vel,pre
78  LOGICAL :: ok
79  !--------------------------------------------------------------------------!
80 
81  ALLOCATE(sim)
82  CALL sim%InitFosite()
83 
84 #ifdef PARALLEL
85  IF (sim%GetRank().EQ.0) THEN
86 #endif
87 tap_plan(4)
88 #ifdef PARALLEL
89  END IF
90 #endif
91 
92  CALL makeconfig(sim, sim%config)
93 ! CALL PrintDict(Sim%config)
94  CALL sim%Setup()
95  ! set initial condition
96  CALL initdata(sim%Mesh,sim%Physics,sim%Timedisc)
97  ! compute numerical solution
98  CALL sim%Run()
99  ok = .NOT.sim%aborted
100  ! compare with exact solution if requested
101  IF (ASSOCIATED(sim%Timedisc%solution)) THEN
102  ALLOCATE(sigma(sim%Physics%VNUM))
103  DO n=1,sim%Physics%VNUM
104  ! use L1 norm to estimate the deviation from the exact solution:
105  ! Σ |pvar - pvar_exact| / Σ |pvar_exact|
106  sum_numer = sum(abs(sim%Timedisc%pvar%data4d(sim%Mesh%IMIN:sim%Mesh%IMAX,&
107  sim%Mesh%JMIN:sim%Mesh%JMAX,sim%Mesh%KMIN:sim%Mesh%KMAX,n) &
108  -sim%Timedisc%solution%data4d(sim%Mesh%IMIN:sim%Mesh%IMAX,&
109  sim%Mesh%JMIN:sim%Mesh%JMAX,sim%Mesh%KMIN:sim%Mesh%KMAX,n)))
110  sum_denom = sum(abs(sim%Timedisc%solution%data4d(sim%Mesh%IMIN:sim%Mesh%IMAX,&
111  sim%Mesh%JMIN:sim%Mesh%JMAX,sim%Mesh%KMIN:sim%Mesh%KMAX,n)))
112 #ifdef PARALLEL
113  IF (sim%GetRank().GT.0) THEN
114  CALL mpi_reduce(sum_numer,sum_numer,1,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
115  CALL mpi_reduce(sum_denom,sum_denom,1,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
116  ELSE
117  CALL mpi_reduce(mpi_in_place,sum_numer,1,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
118  CALL mpi_reduce(mpi_in_place,sum_denom,1,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
119 #endif
120  sigma(n) = sum_numer / sum_denom
121 #ifdef PARALLEL
122  END IF
123 #endif
124  END DO
125  ELSE
126  sigma(:) = 0.0
127  END IF
128 
129  den = sim%Physics%DENSITY
130  vel = sim%Physics%XVELOCITY
131  pre = sim%Physics%PRESSURE
132 
133 #ifdef PARALLEL
134  IF (sim%GetRank().EQ.0) THEN
135 #endif
136 tap_check(ok,"stoptime reached")
137 ! These lines are very long if expanded. So we can't indent it or it will be cropped.
138 tap_check_small(sigma(den),4.0e-02,"density deviation < 4%")
139 tap_check_small(sigma(vel),4.0e-02,"radial velocity deviation < 4%")
140 ! skip azimuthal velocity deviation, because exact value is 0
141 tap_check_small(sigma(pre),6.0e-02,"pressure deviation < 6%")
142 tap_done
143 #ifdef PARALLEL
144  END IF
145 #endif
146 
147  IF (ALLOCATED(sigma)) DEALLOCATE(sigma)
148  CALL sim%Finalize()
149  DEALLOCATE(sim)
150 
151 CONTAINS
152 
153  SUBROUTINE makeconfig(Sim, config)
154  IMPLICIT NONE
155  !------------------------------------------------------------------------!
156  CLASS(fosite) :: Sim
157  TYPE(Dict_TYP), POINTER :: config
158  !------------------------------------------------------------------------!
159  ! Local variable declaration
160  INTEGER :: bc(6)
161  TYPE(Dict_TYP), POINTER :: mesh, physics, boundary, datafile, &
162  grav, pmass, timedisc, fluxes
163  REAL :: x1,x2,y1,y2,z1,z2
164  !------------------------------------------------------------------------!
165  INTENT(INOUT) :: sim
166  !------------------------------------------------------------------------!
167  ! derived constants
168  rb = gn * accmass / csinf**2 ! bondi radius [m] !
169  tau = rb / csinf ! free fall time scale [s] !
170 
171  ! geometry dependent setttings
172  SELECT CASE(mgeo)
173  CASE(spherical)
174  x1 = rin * rb
175  x2 = rout * rb
176  y1 = 0.0
177  y2 = pi
178  z1 = 0.0
179  z2 = 2*pi
180  bc(west) = absorbing
181  bc(east) = custom
182  bc(south) = axis
183  bc(north) = axis
184  bc(bottom) = periodic
185  bc(top) = periodic
186  CASE DEFAULT
187  CALL sim%Physics%Error("bondi3d::MakeConfig","geometry currently not supported")
188  END SELECT
189 
190  mesh => dict( &
191  "meshtype" / midpoint, &
192  "geometry" / mgeo, &
193  "inum" / xres, &
194  "jnum" / yres, &
195  "knum" / zres, &
196  "xmin" / x1, &
197  "xmax" / x2, &
198  "ymin" / y1, &
199  "ymax" / y2, &
200  "zmin" / z1, &
201  "zmax" / z2, &
202  "output/radius" / 1, &
203  "gparam" / rb)
204 
205  ! boundary conditions
206  boundary => dict( &
207  "western" / bc(west), &
208  "eastern" / bc(east), &
209  "southern" / bc(south), &
210  "northern" / bc(north), &
211  "bottomer" / bc(bottom), &
212  "topper" / bc(top))
213 
214  ! physics settings
215  physics => dict( &
216  "problem" / euler, &
217  "gamma" / gamma)
218 
219  ! flux calculation and reconstruction method
220  fluxes => dict( &
221  "order" / linear, &
222  "fluxtype" / kt, &
223  "variables" / primitive, &
224  "limiter" / monocent, &
225  "theta" / 1.2)
226 
227  ! gravity term due to a point mass
228  pmass => dict( &
229  "gtype" / pointmass, &
230  "mass" / accmass, &
231  "outbound" / 0)
232 
233  ! source term due to all gravity terms
234  grav => dict( &
235  "stype" / gravity, &
236  "pmass" / pmass)
237 
238  ! time discretization settings
239  timedisc => dict( &
240  "method" / modified_euler, &
241  "order" / 3, &
242  "cfl" / 0.4, &
243  "stoptime" / (tsim * tau), &
244  "dtlimit" / (1.0e-6 * tau), &
245  "output/solution" / 1, &
246  "maxiter" / 20000)
247 
248  ! initialize data input/output
249  datafile => dict( &
250  "fileformat" / vtk, &
251  "filename" / (trim(odir) // trim(ofname)), &
252  "count" / onum)
253 
254  config => dict( &
255  "mesh" / mesh, &
256  "physics" / physics, &
257  "boundary" / boundary, &
258  "fluxes" / fluxes, &
259  "sources/grav" / grav, &
260  "timedisc" / timedisc, &
261  "datafile" / datafile)
262  END SUBROUTINE makeconfig
263 
264 
265  SUBROUTINE initdata(Mesh,Physics,Timedisc)
266  IMPLICIT NONE
267  !------------------------------------------------------------------------!
268  CLASS(physics_base), INTENT(IN) :: Physics
269  CLASS(mesh_base), INTENT(IN) :: Mesh
270  CLASS(timedisc_base), INTENT(INOUT) :: Timedisc
271  !------------------------------------------------------------------------!
272  ! Local variable declaration
273  REAL :: r,rho,vr,cs2
274  INTEGER :: i,j,k,l
275  CHARACTER(LEN=64) :: info_str
276  !------------------------------------------------------------------------!
277  ! initial condition
278  SELECT TYPE(pvar => timedisc%pvar)
279  TYPE IS(statevector_euler)
280  ! constant density and pressure, vanishing velocity
281  pvar%density%data1d(:) = rhoinf
282  pvar%velocity%data1d(:) = 0.
283  pvar%pressure%data1d(:) = rhoinf * csinf**2 / gamma
284  CLASS DEFAULT
285  CALL physics%Error("bondi3d::InitData","only non-isothermal HD supported")
286  END SELECT
287 
288  CALL physics%Convert2Conservative(timedisc%pvar,timedisc%cvar)
289 
290  ! store exact stationary solution if requested,
291  ! i.e. output/solution == 1 in timedisc config
292  IF (ASSOCIATED(timedisc%solution)) THEN
293  ! compute the stationary 2D planar bondi solution
294  SELECT TYPE(pvar => timedisc%solution)
295  TYPE IS(statevector_euler)
296  DO k=mesh%KMIN,mesh%KMAX
297  DO j=mesh%JMIN,mesh%JMAX
298  DO i=mesh%IMIN,mesh%IMAX
299  r = mesh%radius%bcenter(i,j,k)
300  CALL bondi(r/rb,gamma,rhoinf,csinf,rho,vr)
301  cs2 = csinf**2 * (rho/rhoinf)**(gamma-1.0)
302  pvar%density%data3d(i,j,k) = rho
303  ! set the minimum of the exact velocity to some value > 0
304  ! for comparison with the numerical results
305 !NEC$ UNROLL(3)
306  DO l=1,physics%VDIM
307  pvar%velocity%data4d(i,j,k,l) = sign(&
308  max(epsilon(cs2),abs(vr*mesh%posvec%bcenter(i,j,k,l)/r)), &
309  vr*mesh%posvec%bcenter(i,j,k,l))
310  END DO
311  pvar%pressure%data3d(i,j,k) = rho * cs2 / gamma
312  END DO
313  END DO
314  END DO
315  END SELECT
316  END IF
317 
318  ! boundary condition: subsonic inflow according to Bondi's solution
319  ! calculate Bondi solution for y=ymin..ymax at xmax
320  SELECT TYPE(beast => timedisc%Boundary%boundary(east)%p)
321  CLASS IS (boundary_custom)
322  ! this tells the boundary routine the actual boundary condition for each variable
323  CALL beast%SetCustomBoundaries(mesh,physics, &
324  (/custom_fixed,custom_nograd,custom_fixed,custom_fixed,custom_fixed/))
325  DO k=mesh%KMIN,mesh%KMAX
326  DO j=mesh%JMIN,mesh%JMAX
327  DO i=1,mesh%GNUM
328  ! get distance to the origin for each boundary cell
329  r = mesh%radius%bcenter(mesh%IMAX+i,j,k)
330  CALL bondi(r/rb,gamma,rhoinf,csinf,rho,vr)
331  cs2 = csinf**2 * (rho/rhoinf)**(gamma-1.0)
332  beast%data(i,j,k,physics%DENSITY) = rho
333  DO l=1,physics%VDIM
334  beast%data(i,j,k,physics%XVELOCITY+l-1) &
335  = vr*mesh%posvec%bcenter(mesh%IMAX+i,j,k,l)/r
336  END DO
337  beast%data(i,j,k,physics%PRESSURE) = rho * cs2 / gamma
338  END DO
339  END DO
340  END DO
341  END SELECT
342 
343  WRITE(info_str,"(ES9.3)") rb
344  CALL mesh%Info(" " // "Bondi radius: " &
345  // trim(info_str) // " m")
346  WRITE(info_str,"(ES9.3)") tau
347  CALL mesh%Info(" " // "Free fall time: " &
348  // trim(info_str) // " s")
349  END SUBROUTINE initdata
350 
351 
352  SUBROUTINE bondi(r,gamma,rhoinf,csinf,rho,vr)
353  USE roots
354  IMPLICIT NONE
355  !------------------------------------------------------------------------!
356  ! computes the Bondi solution for spherically symmetric accretion !
357  ! uses funcd, GetRoot !
358  ! INPUT paramter: !
359  ! r : radius in units of the Bondi radius r_b = G*M/csinf**2 !
360  ! gamma : ratio of specific heats (1 < gamma < 5/3) !
361  ! rhoinf: density at infinity !
362  ! csinf : speed of sound at infinity !
363  ! OUTPUT paramter: !
364  ! rho : density @ r !
365  ! vr : radial velocity @ r !
366  !------------------------------------------------------------------------!
367  REAL, INTENT(IN) :: r,gamma,rhoinf,csinf
368  REAL, INTENT(OUT) :: rho,vr
369  !------------------------------------------------------------------------!
370  INTERFACE
371  PURE SUBROUTINE funcd(x,fx,dfx,plist)
372  IMPLICIT NONE
373  REAL, INTENT(IN) :: x
374  REAL, INTENT(IN), DIMENSION(:), OPTIONAL :: plist
375  REAL, INTENT(OUT) :: fx,dfx
376  END SUBROUTINE funcd
377  END INTERFACE
378  !------------------------------------------------------------------------!
379  REAL, PARAMETER :: xacc = 1.0e-6 ! accuracy for root finding
380  REAL :: gp1,gm1,g35,rc,chi,lambda,psi,gr
381  INTEGER :: err
382  COMMON /funcd_parameter/ gm1, gr
383  !------------------------------------------------------------------------!
384  ! for convenience
385  gm1 = gamma - 1.0
386  gp1 = gamma + 1.0
387  g35 = 0.5 * (5.0-3.0*gamma)
388 
389  ! critical radius
390  rc = 0.5 * g35
391  ! critical dimensionless accretion rate
392  lambda = 0.25 * g35**(-g35/gm1)
393 
394  ! Newton-Raphson to solve Bondis equations for psi
395  chi = r**2 / lambda
396  gr = chi**(2.*gm1/gp1) * (1./gm1 + 1./r)
397  IF (r.LT.rc) THEN
398  CALL getroot_newton(funcd,1.0,gr,psi,err)
399  ELSE
400  CALL getroot_newton(funcd,1.0e-6,1.0,psi,err)
401  END IF
402 
403  ! return values
404  rho = rhoinf * chi**(-2./gp1) / psi ! density
405  vr = -csinf * chi**(-gm1/gp1) * psi ! radial velocity
406  END SUBROUTINE bondi
407 
408 
409 END PROGRAM bondi3d
410 
411 
412 ! for exact Bondi solution at the outer boundary
413 PURE SUBROUTINE funcd(y,fy,dfy,plist)
414  IMPLICIT NONE
415  !------------------------------------------------------------------------!
416  REAL, INTENT(IN) :: y
417  REAL, INTENT(OUT) :: fy,dfy
418  REAL, INTENT(IN), DIMENSION(:), OPTIONAL :: plist
419  !------------------------------------------------------------------------!
420  REAL :: gm1,gr
421  COMMON /funcd_parameter/ gm1,gr
422  !------------------------------------------------------------------------!
423  fy = 0.5*y*y + y**(-gm1) / gm1 - gr
424  dfy = y - y**(-gm1-1.)
425 END SUBROUTINE funcd
pure subroutine funcd(y, fy, dfy, plist)
Definition: bondi2d.f90:416
subroutine, public getroot_newton(funcd, x1, x2, root, error, plist, xm, iterations)
Definition: roots.f90:247
subroutine initdata(Mesh, Physics, Fluxes, Timedisc)
Definition: bondi2d.f90:274
main fosite class
Definition: fosite.f90:71
subroutine makeconfig(Sim, config)
Definition: bondi2d.f90:165
program bondi3d
Program and data initialization for 3D Bondi accretion.
Definition: bondi3d.f90:42
root finding subroutines
Definition: roots.f90:45
subroutine bondi(r, gamma, rhoinf, csinf, rho, vr)
Definition: bondi2d.f90:363