orbitingcylinders.f90

self-gravitating orbiting cylinder test for spectral solver

Author
Manuel Jung

This part is adapted (and translated) from the PhD-thesis from Jung (2016, [jung2016] ). It contains two different types of tests with cylinders. Both originally come from Chan (2006, [chan2006] ):

  1. A static simulation of the gravitational potential in a
    • razor-thin disk.
    • gaussian distribution in vertical direction.
  2. A dynamic evolution of the cylinders.

Density-Potential Pairs

This part compares the solution of the potential by the gravitational spectral solver with an existing analytical result. Since the module only calculates the potential within the simulation area, the potential produced by the boundaries need to be neglectable. However, the potential from outside the potential can calculated with additional gravitational modules.

Assuming a density distributions around a central point

\[ \Sigma_{(r_k,\varphi_k)}(r,\varphi) = \frac{1}{2 \pi \sigma^2} \exp{\left( - \frac{R_k^2}{\sigma} \right)}, \]

with \( \sigma \) a measurement for the width of and \( R_k \) the distance of a coordinate point form the center of the distribution. For a razor thin disk, this yields the potential (see [chan2006]):

\[ \Phi_{(r_k, \varphi_k)} = - \frac{G}{\sigma} \left(I_0(y_k)K_1(y_k) - I_1(y_k)K_0(y_k)\right), \]

where \( y_k = \frac{R_k}{2 \sigma} \). \( I_n \) and \( K_n \) are the modified Bessel functions of first and second kind with integral order (see functions ).

The density distribution in our example is

\[ \Sigma(r,\varphi) = 2 \Sigma_{(1,10^{-3})}(r,\varphi) + 0.5 \Sigma_{(1,\pi+10^{-3})} (r,\varphi) + \Sigma_{(0.9,\frac{3}{4}\pi)} (r,\varphi), \]

with the according potential

\[ \Phi(r,\varphi) = 2 \Phi_{(1,10^{-3})}(r,\varphi) + 0.5 \Phi_{(1,\pi+10^{-3})} (r,\varphi) + \Phi_{(0.9,\frac{3}{4}\pi)} (r,\varphi). \]

Additionally, the implementation is tested for a vertical gaussian distribution, with a scale height similar to \( \sigma =0.1 \). with the same setup as above. This gives us a solution for the potential

\[ \Phi(r_k, \varphi_k) = - \frac{1}{R_k} \mathrm{erf}\left( \frac{R_k}{\sqrt{2} \sigma} \right). \]

Below the relative error for the potential is shown, solved for a razor-thin disk (left) and a vertical gaussian distribution (right). For the first case we see a maximum error of \( 10^{-3} \) at steep gradients and \( 10^{-5} \) for the second case.

Relative error

Self-Gravitating Rotating Cylinders

A time-dependent test from [chan2006], which generates two cylinders with gaussian density distribution in polar grid placed opposite towards each other. A background field adds a rigid rotation. The thereby arising centrifugal force has to be balanced from the self-gravitation of the mass-distribution. This is a very sophisticated setup for the accuracy of the acceleration by self-gravitation and the general angular-momentum conservation. The setup is

\[ \varrho(r,\varphi) = \frac{10^{-2}}{\pi \left( r_{\mathrm{max}}^2 - r_{\mathrm{min}}^2 \right)} + 0.99 \left( \frac{1}{2 \pi \sigma^2} \exp{\left(- \frac{R_1}{2 \sigma^2} \right)} + \frac{1}{2 \pi \sigma} \exp{\left(- \frac{R_2}{2 \sigma^2} \right)}\right) \\ p(r,\varphi) = \frac{10^{-2}}{\pi \left( r_{\mathrm{max}}^2 - r_{\mathrm{min}}^2 \right)} + \frac{G}{2 \pi \sigma^2} \left(\mathrm{Ei}\left(- \frac{R_1}{\sigma^2} \right) - \mathrm{Ei}\left(- \frac{R_1}{2 \sigma^2} \right) + \mathrm{Ei}\left(- \frac{R_2}{\sigma^2} \right) - \mathrm{Ei}\left(- \frac{R_2}{2 \sigma^2} \right)\right), \]

and \( \mathbf{v} = r \mathbf{e}_{\varphi} \). The function \( \mathrm{Ei}(x) := \int_{-\infty}^x \frac{\exp{t}}{2} \mathrm{d}t \) is the exponential integral, \( R_1 \), \( R_2 \) are the radii from the center of the cylinders and \( \sigma = 0.1 \) a measure for spreading of the cylinders.

Additional Settings:
angular velocity: \( \Omega = 1.0 \)
heat capacity ratio: \( \gamma = \frac{5}{3} \)
resolution: \( N_r \times N_{\varphi} = 256 \times 1024 \)
simulation time: \( t_{\mathrm{sim}} = 100 \)
gravitational constant: \( G = 1.0 \)
\( r_{\mathrm{min}}, r_{\mathrm{max}} = 0.2, 1.8 \)

The images below show the results at \( t = 0, 100 \). The cylinders retain their shape very well. Within \( 100 \) orbits of the cylinders in the rotating frame there is a loss of \( 0.06 \% \) of total angular momentum. Compared with [chan2006], we have a \( 200 \times \) better angular momentum conservation in one time step.

colormesh of density
azimuthal cut through density field
colormesh of pressure

References:

1 !#############################################################################
2 !# #
3 !# fosite - 3D hydrodynamical simulation program #
4 !# module: orbitingcylinders.f90 #
5 !# #
6 !# Copyright (C) 2013-2018 #
7 !# Manuel Jung <mjung@astrophysik.uni-kiel.de> #
8 !# Jannes Klee <jklee@astrophysik.uni-kiel.de> #
9 !# #
10 !# This program is free software; you can redistribute it and/or modify #
11 !# it under the terms of the GNU General Public License as published by #
12 !# the Free Software Foundation; either version 2 of the License, or (at #
13 !# your option) any later version. #
14 !# #
15 !# This program is distributed in the hope that it will be useful, but #
16 !# WITHOUT ANY WARRANTY; without even the implied warranty of #
17 !# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or #
18 !# NON INFRINGEMENT. See the GNU General Public License for more #
19 !# details. #
20 !# #
21 !# You should have received a copy of the GNU General Public License #
22 !# along with this program; if not, write to the Free Software #
23 !# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #
24 !# #
25 !#############################################################################
26 
27 !----------------------------------------------------------------------------!
152 !----------------------------------------------------------------------------!
153 !#define HAVE_FGSL
154 PROGRAM orbitingcylinders
155  USE fosite_mod
156 #ifdef HAVE_FGSL
157  USE fgsl
158 #endif
160  IMPLICIT NONE
161  !--------------------------------------------------------------------------!
162  ! problem setup
163  REAL, PARAMETER :: sigma = 0.1
164  REAL, DIMENSION(2), PARAMETER :: x1 = (/ 1.0, 0. /)
165  REAL, DIMENSION(2), PARAMETER :: x2 = (/ 1.0, pi /)
166  REAL, DIMENSION(2), PARAMETER :: x3 = (/ 0.9, 3./4.*pi /)
167  REAL, DIMENSION(2), PARAMETER :: x4 = (/ 1.0, -pi/2. /)
168 
169  ! general constants
170  REAL, PARAMETER :: GN = 1.
171  REAL, PARAMETER :: P0 = 1.
172  REAL, PARAMETER :: P = 10.
173  REAL, PARAMETER :: t = p*p0
174 
175  REAL, PARAMETER :: flaring = 0.05
176  REAL, PARAMETER :: RG = 8.31
177  REAL, PARAMETER :: MU = 6.02e-04
178  REAL, PARAMETER :: GAMMA = 5./3.
179 
180  REAL, PARAMETER :: RMIN = 0.4
181  REAL, PARAMETER :: RMAX = 2.0
182  INTEGER, PARAMETER :: YRES = 128
183  INTEGER, PARAMETER :: XRES = yres/4
184  INTEGER, PARAMETER :: ONUM = p * 1
185  REAL, PARAMETER :: omega = 1.0
186  !--------------------------------------------------------------------------!
187  CLASS(fosite), ALLOCATABLE :: Sim
188  INTEGER :: green
189  REAL, DIMENSION(:,:,:), POINTER :: numpot, anapot
190  !--------------------------------------------------------------------------!
191 
192  ALLOCATE(sim)
193  DO green=3,3
194 
195 
196  CALL sim%InitFosite()
197  CALL makeconfig(sim, sim%config)
198 
199  SELECT CASE(green)
200  CASE(3)
201  CALL setattr(sim%config, "/mesh/rmin", 0.2)
202  CALL setattr(sim%config, "/mesh/rmax", 1.8)
203  CALL setattr(sim%config, "/mesh/inum", 64)
204  CALL setattr(sim%config, "/mesh/jnum", 192)
205  END SELECT
206 
207  CALL sim%Setup()
208  CALL initdata(sim%Timedisc,sim%Mesh,sim%Physics,sim%Sources)
209 
210  SELECT CASE(green)
211  CASE(1,2)
212  CALL sim%FirstStep()
213  print *,"max local relative error: ",maxval(abs((numpot-anapot)/anapot))
214  print *,"global relative error: ",sum(abs(numpot-anapot))/sum(abs(anapot))
215  CASE(3)
216  CALL sim%Run()
217  CALL sim%Finalize()
218  END SELECT
219  END DO
220  DEALLOCATE(sim)
221 
222 
223 CONTAINS
224 
225  SUBROUTINE makeconfig(Sim, config)
226  IMPLICIT NONE
227  !------------------------------------------------------------------------!
228  CLASS(fosite), INTENT(INOUT) :: Sim
229  TYPE(Dict_TYP),POINTER :: config
230  !------------------------------------------------------------------------!
231  ! Local variable declaration
232  TYPE(Dict_TYP),POINTER :: mesh,boundary,timedisc,datafile,&
233  fluxes,physics,grav,rotframe,sources
234  !------------------------------------------------------------------------!
235  CHARACTER(LEN=1) :: str
236  !------------------------------------------------------------------------!
237  physics => dict( &
238  "problem" / euler,&
239  "mu" / mu, &
240  "gamma" / gamma, &
241  "units" / geometrical)
242 
243  fluxes => dict( &
244  "fluxtype" / kt, &!HLLC, &
245  "order" / linear, &
246  "variables" / primitive, &
247  "limiter" / monocent, &
248  "theta" / 1.2)
249 
250  mesh => dict( &
251  "meshtype" / midpoint, &
252  "geometry" / cylindrical, &
253  "xmin" / rmin, &
254  "xmax" / rmax, &
255 ! "geometry" / LOGPOLAR, &
256 ! "xmin" / LOG(RMIN/1.), &
257 ! "xmax" / LOG(RMAX/1.), &
258  "gparam" / 1., &
259  "omega" / omega, &
260  "inum" / xres, &
261  "jnum" / yres, &
262  "knum" / 1, &
263  "ymin" / (0.), &
264  "ymax" / (2.*pi), &
265  "zmin" / (0.), &
266  "zmax" / (0.), &
267  "decomposition" / (/ -1, 1/))!, &
268 ! "output/dl" / 1, &
269 ! "output/radius" / 1, &
270 ! "output/volume" / 1 )
271 
272  boundary => dict( &
273  "western" / noslip, &
274  "eastern" / noslip, &
275  "southern" / periodic, &
276  "northern" / periodic, &
277  "bottomer" / reflecting, &
278  "topper" / reflecting)
279 
280  grav => dict( &
281  "stype" / gravity, &
282  "output/accel" / 1, &
283  "self/gtype" / spectral, &
284  "self/green" / green, &
285  "self/sigma" / sigma)
286 ! "self/output/potential" / 1)
287 
288  rotframe => dict( &
289  "stype" / rotating_frame, &
290  "x" / 0.0, &
291  "y" / 0.0, &
292  "z" / 0.0)
293 
294  sources => dict( &
295 ! "rotframe" / rotframe, &
296  "grav" / grav)
297 
298  timedisc => dict( &
299  "method" / ssprk, &
300  "rhstype" / 1, &
301 ! "dtmax" / 8., &
302 ! "fargo" / 1, &
303 ! "method" / RK_FEHLBERG, &
304 ! "order" / 5, &
305  "tol_rel" / 1.0e-3, &
306  "cfl" / 0.3, &
307  "stoptime" / t, &
308  "dtlimit" / 1.0e-10, &
309  "maxiter" / 2000000000, &
310  "output/solution" / 1)
311 
312  WRITE(str,'(I1)')green
313 
314  datafile => dict( &
315  "fileformat" / vtk, &
316  "filename" / ("orbitingcylinders_" // str), &
317  "count" / onum)
318 
319  config => dict( &
320  "physics" / physics, &
321  "fluxes" / fluxes, &
322  "mesh" / mesh, &
323  "boundary" / boundary, &
324  "sources" / sources, &
325  "timedisc" / timedisc, &
326  "datafile" / datafile)
327 
328  END SUBROUTINE makeconfig
329 
330 
331  SUBROUTINE initdata(Timedisc,Mesh,Physics,Sources)
332  IMPLICIT NONE
333  !------------------------------------------------------------------------!
334  CLASS(timedisc_base), INTENT(INOUT) :: Timedisc
335  CLASS(mesh_base), INTENT(IN) :: Mesh
336  CLASS(physics_base), INTENT(INOUT) :: Physics
337  CLASS(sources_base), POINTER :: Sources
338  !------------------------------------------------------------------------!
339  ! Local variable declaration
340  INTEGER :: i,j,k,dir,ig
341  REAL,DIMENSION(Mesh%IGMIN:Mesh%IGMAX,Mesh%JGMIN:Mesh%JGMAX,Mesh%KGMIN:Mesh%KGMAX) :: r,r1,r2,r3
342  CLASS(sources_base), POINTER :: sp
343  CLASS(sources_gravity), POINTER :: gp
344  !------------------------------------------------------------------------!
345  r = mesh%radius%bcenter
346  sp => sources
347  DO
348  IF (ASSOCIATED(sp).EQV..false.) RETURN
349  SELECT TYPE(sp)
350  CLASS IS(sources_gravity)
351  gp => sp
352  EXIT
353  END SELECT
354  sp => sp%next
355  END DO
356 
357 
358  anapot => timedisc%solution%data4d(mesh%IMIN:mesh%IMAX,mesh%JMIN:mesh%JMAX,mesh%KMIN:mesh%KMAX,1)
359  numpot => gp%glist%pot(mesh%IMIN:mesh%IMAX,mesh%JMIN:mesh%JMAX,mesh%KMIN:mesh%KMAX,1)
360 
361  SELECT CASE(green)
362  CASE(1)
363  r1 = rsq(mesh,mesh%radius%bcenter,x1)
364  r2 = rsq(mesh,mesh%radius%bcenter,x3)
365  r3 = rsq(mesh,mesh%radius%bcenter,x4)
366  timedisc%pvar%data4d(:,:,:,physics%DENSITY) &
367  = 2.0 / (2.*pi*sigma**2) * exp(-sqrt(r1)/sigma) &
368  + 0.5 / (2.*pi*sigma**2) * exp(-sqrt(r2)/sigma) &
369  + 1.0 / (2.*pi*sigma**2) * exp(-sqrt(r3)/sigma)
370 
371  r1 = sqrt(rsq(mesh,mesh%radius%faces(:,:,:,1),x1)) / (2.*sigma)
372  r2 = sqrt(rsq(mesh,mesh%radius%faces(:,:,:,1),x3)) / (2.*sigma)
373  r3 = sqrt(rsq(mesh,mesh%radius%faces(:,:,:,1),x4)) / (2.*sigma)
374  DO k=mesh%KGMIN,mesh%KGMAX
375  DO j=mesh%JGMIN,mesh%JGMAX
376  DO i=mesh%IGMIN,mesh%IGMAX
377  timedisc%solution%data4d(i,j,k,1) &
378 #ifdef HAVE_FGSL
379  = -2.0 * r1(i,j,k)/sigma*(fgsl_sf_bessel_ic0(r1(i,j,k))*fgsl_sf_bessel_kc1(r1(i,j,k))&
380  -fgsl_sf_bessel_ic1(r1(i,j,k))*fgsl_sf_bessel_kc0(r1(i,j,k))) &
381  -0.5 * r2(i,j,k)/sigma*(fgsl_sf_bessel_ic0(r2(i,j,k))*fgsl_sf_bessel_kc1(r2(i,j,k))&
382  -fgsl_sf_bessel_ic1(r2(i,j,k))*fgsl_sf_bessel_kc0(r2(i,j,k))) &
383  -1.0 * r3(i,j,k)/sigma*(fgsl_sf_bessel_ic0(r3(i,j,k))*fgsl_sf_bessel_kc1(r3(i,j,k))&
384  -fgsl_sf_bessel_ic1(r3(i,j,k))*fgsl_sf_bessel_kc0(r3(i,j,k)))
385 #else
386  = -2.0 * r1(i,j,k)/sigma*(bessel_i0(r1(i,j,k))*bessel_k1(r1(i,j,k))&
387  -bessel_i1(r1(i,j,k))*bessel_k0(r1(i,j,k))) &
388  -0.5 * r2(i,j,k)/sigma*(bessel_i0(r2(i,j,k))*bessel_k1(r2(i,j,k))&
389  -bessel_i1(r2(i,j,k))*bessel_k0(r2(i,j,k))) &
390  -1.0 * r3(i,j,k)/sigma*(bessel_i0(r3(i,j,k))*bessel_k1(r3(i,j,k))&
391  -bessel_i1(r3(i,j,k))*bessel_k0(r3(i,j,k)))
392 #endif
393  END DO
394  END DO
395  END DO
396 
397  timedisc%pvar%data4d(:,:,:,physics%PRESSURE) = 1.0
398  timedisc%pvar%data4d(:,:,:,physics%XVELOCITY) = 0.0
399  timedisc%pvar%data4d(:,:,:,physics%YVELOCITY) = 0.0
400  CASE(2)
401  r1 = rsq(mesh,mesh%radius%bcenter,x1)
402  r2 = rsq(mesh,mesh%radius%bcenter,x3)
403  r3 = rsq(mesh,mesh%radius%bcenter,x4)
404  timedisc%pvar%data4d(:,:,:,physics%DENSITY) &
405  = 2.0 / (2.*pi*sigma**2) * exp(-r1/(2.*sigma**2)) &
406  + 0.5 / (2.*pi*sigma**2) * exp(-r2/(2.*sigma**2)) &
407  + 1.0 / (2.*pi*sigma**2) * exp(-r3/(2.*sigma**2))
408 
409  r1 = rsq(mesh,mesh%radius%faces(:,:,:,1),x1)
410  r2 = rsq(mesh,mesh%radius%faces(:,:,:,1),x3)
411  r3 = rsq(mesh,mesh%radius%faces(:,:,:,1),x4)
412 
413  timedisc%solution%data4d(:,:,:,1) &
414  = - 2.0 / sqrt(r1) * erf(sqrt(r1 / 2.) / sigma) &
415  - 0.5 / sqrt(r2) * erf(sqrt(r2 / 2.) / sigma) &
416  - 1.0 / sqrt(r3) * erf(sqrt(r3 / 2.) / sigma)
417  timedisc%pvar%data4d(:,:,:,physics%PRESSURE) = 1.0
418  timedisc%pvar%data4d(:,:,:,physics%XVELOCITY) = 0.0
419  timedisc%pvar%data4d(:,:,:,physics%YVELOCITY) = 0.0
420  CASE(3)
421  r1 = rsq(mesh,mesh%curv%bcenter,x1)
422  r2 = rsq(mesh,mesh%curv%bcenter,x2)
423  timedisc%pvar%data4d(:,:,:,physics%DENSITY) = 0.02/(3.2*pi) &
424  + 0.99 * ( exp(-0.5*(r1/sigma**2))/(2.*pi*sigma**2) &
425  + exp(-0.5*(r2/sigma**2))/(2.*pi*sigma**2))
426 
427  timedisc%pvar%data4d(:,:,:,physics%PRESSURE) = 0.02/(3.2*pi) &
428  + gn / (2.*pi*sigma**2) &
429  * ( ei(-(r1/sigma**2)) - ei(-0.5*(r1/sigma**2)) &
430  + ei(-(r2/sigma**2)) - ei(-0.5*(r2/sigma**2)))
431 
432  timedisc%pvar%data4d(:,:,:,physics%XVELOCITY) = 0.0
433  timedisc%pvar%data4d(:,:,:,physics%YVELOCITY) = r - r*omega
434  END SELECT
435 
436  DO dir=west,east
437  SELECT TYPE(bound => timedisc%Boundary%boundary(dir)%p)
438  CLASS IS (boundary_noslip)
439  DO k=mesh%KMIN,mesh%KMAX
440  DO j=mesh%JMIN,mesh%JMAX
441  DO ig=1,mesh%GNUM
442  SELECT CASE (dir)
443  CASE(west)
444  i = mesh%IMIN-ig
445  CASE(east)
446  i = mesh%IMAX+ig
447  END SELECT
448  bound%data(ig,j,k,physics%YVELOCITY) &
449  = timedisc%pvar%data4d(i,j,k,physics%YVELOCITY)
450  END DO
451  END DO
452  END DO
453  END SELECT
454  END DO
455 
456  CALL physics%Convert2Conservative(timedisc%pvar,timedisc%cvar)
457  END SUBROUTINE initdata
458 
459  FUNCTION rsq(Mesh,r,x) RESULT(res)
460  IMPLICIT NONE
461  !------------------------------------------------------------------------!
462  CLASS(mesh_base) :: Mesh
463  REAL, DIMENSION(2) :: x
464  REAL, DIMENSION(Mesh%IGMIN:Mesh%IGMAX,Mesh%JGMIN:Mesh%JGMAX,Mesh%KGMIN:Mesh%KGMAX) :: r
465  REAL, DIMENSION(Mesh%IGMIN:Mesh%IGMAX,Mesh%JGMIN:Mesh%JGMAX,Mesh%KGMIN:Mesh%KGMAX) :: res
466  !------------------------------------------------------------------------!
467  INTENT(IN) :: x
468  !------------------------------------------------------------------------!
469  res = r**2 + x(1)**2 - 2.*r*x(1) * cos(mesh%curv%bcenter(:,:,:,2)-x(2))
470  END FUNCTION rsq
471 
472 END PROGRAM orbitingcylinders