vortex3d.f90
Go to the documentation of this file.
1!#############################################################################
2!# #
3!# fosite - 3D hydrodynamical simulation program #
4!# module: vortex2d.f90 #
5!# #
6!# Copyright (C) 2006-2024 #
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!----------------------------------------------------------------------------!
32!----------------------------------------------------------------------------!
33PROGRAM vortex3d
34 USE fosite_mod
35#include "tap.h"
36 IMPLICIT NONE
37 !--------------------------------------------------------------------------!
38 ! simulation parameters
39 REAL, PARAMETER :: tsim = 30.0 ! simulation stop time
40 REAL, PARAMETER :: gamma = 1.4 ! ratio of specific heats
41 REAL, PARAMETER :: csiso = &
42 0.0 ! non-isothermal simulation
43! 1.127 ! isothermal simulation
44 ! with CSISO as sound speed
45 INTEGER, PARAMETER :: fargo= 0 ! 0: disables fargo transport
46 ! 1: dynamic background velocity field,
47 ! 2: fixed background velocity field
48 ! initial condition (dimensionless units)
49 REAL, PARAMETER :: rhoinf = 1. ! ambient density
50 REAL, PARAMETER :: pinf = 1. ! ambient pressure
51 REAL, PARAMETER :: vstr = 5.0 ! nondimensional vortex strength
52 REAL, PARAMETER :: uinf = 0.0 ! cartesian components of constant
53 REAL, PARAMETER :: vinf = 0.0 ! global velocity field
54 REAL, PARAMETER :: winf = 0.0
55 REAL, PARAMETER :: x0 = 0.0 ! vortex position (cart. coords.)
56 REAL, PARAMETER :: y0 = 0.0
57 REAL, PARAMETER :: z0 = 0.0
58 REAL, PARAMETER :: r0 = 1.0 ! size of vortex
59 REAL, PARAMETER :: omega = 0.1 ! angular speed of rotational frame
60 ! around [X0,Y0]
61 ! mesh settings
62 INTEGER, PARAMETER :: mgeo = cylindrical
63 INTEGER, PARAMETER :: xres = 40 ! x-resolution
64 INTEGER, PARAMETER :: yres = 40 ! y-resolution
65 INTEGER, PARAMETER :: zres = 1 ! z-resolution
66 REAL, PARAMETER :: rmin = 1.0e-2 ! inner radius for polar grids
67 REAL, PARAMETER :: rmax = 5.0 ! outer radius
68 REAL, PARAMETER :: zmin = -1.0 ! extent in z-direction
69 REAL, PARAMETER :: zmax = 1.0
70 REAL, PARAMETER :: gpar = 1.0 ! geometry scaling parameter !
71 ! physics settings
72!!$ LOGICAL, PARAMETER :: WITH_IAR = .TRUE. ! use EULER2D_IAMROT
73 LOGICAL, PARAMETER :: with_iar = .false.
74
75 ! output parameters
76 INTEGER, PARAMETER :: onum = 10 ! number of output data sets
77 CHARACTER(LEN=256), PARAMETER & ! output data dir
78 :: odir = './'
79 CHARACTER(LEN=256), PARAMETER & ! output data file name
80 :: ofname = 'vortex3d'
81 !--------------------------------------------------------------------------!
82 CLASS(fosite),ALLOCATABLE :: sim
83 CLASS(marray_compound), POINTER :: pvar_init => null()
84 REAL, DIMENSION(:), ALLOCATABLE :: sum_numer, sum_denom, sigma
85 LOGICAL :: ok
86 INTEGER :: n,den,vel
87 !--------------------------------------------------------------------------!
88
89 ALLOCATE(sim)
90 CALL sim%InitFosite()
91
92#ifdef PARALLEL
93 IF (sim%GetRank().EQ.0) THEN
94#endif
95tap_plan(3)
96#ifdef PARALLEL
97 END IF
98#endif
99
100 CALL makeconfig(sim, sim%config)
101 CALL sim%Setup()
102 CALL initdata(sim%Mesh, sim%Physics, sim%Timedisc)
103 CALL sim%Physics%new_statevector(pvar_init,primitive)
104 pvar_init%data1d(:) = sim%Timedisc%pvar%data1d(:)
105 CALL sim%Run()
106 ok = .NOT.sim%aborted
107
108 ALLOCATE(sum_numer(sim%Physics%VNUM),sum_denom(sim%Physics%VNUM),sigma(sim%Physics%VNUM))
109 ! use L1 norm to estimate the deviation from the exact stationary solution given
110 ! in the initial condition:
111 ! Σ |pvar - pvar_exact| / Σ |pvar_exact|
112 DO n=1,sim%Physics%VNUM
113 sum_numer(n) = sum(abs(sim%Timedisc%pvar%data2d(:,n)-pvar_init%data2d(:,n)),mask=sim%Mesh%without_ghost_zones%mask1d)
114 sum_denom(n) = sum(abs(pvar_init%data2d(:,n)),mask=sim%Mesh%without_ghost_zones%mask1d)
115 END DO
116! PRINT *,"Rank: ",Sim%GetRank(),ACHAR(10),"numer=",sum_numer,ACHAR(10),"denom=",sum_denom,ACHAR(10),"sigma=",sigma
117
118#ifdef PARALLEL
119 IF (sim%GetRank().GT.0) THEN
120 CALL mpi_reduce(sum_numer,sum_numer,sim%Physics%VNUM,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
121 CALL mpi_reduce(sum_denom,sum_denom,sim%Physics%VNUM,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
122 ELSE
123 CALL mpi_reduce(mpi_in_place,sum_numer,sim%Physics%VNUM,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
124 CALL mpi_reduce(mpi_in_place,sum_denom,sim%Physics%VNUM,default_mpi_real,mpi_sum,0,mpi_comm_world,sim%ierror)
125 END IF
126#endif
127 WHERE (abs(sum_denom(:)).LE.2*tiny(sum_denom))
128 sigma(:) = 0.0
129 ELSEWHERE
130 sigma(:) = sum_numer(:) / sum_denom(:)
131 END WHERE
132
133 den = sim%Physics%DENSITY
134 vel = sim%Physics%YVELOCITY
135
136#ifdef PARALLEL
137 IF (sim%GetRank().EQ.0) THEN
138#endif
139! PRINT *,"numer=",sum_numer,ACHAR(10)," denom=",sum_denom,ACHAR(10)," sigma=",sigma
140tap_check_small(sigma(den),0.01,"density deviation < 1%")
141tap_check_small(sigma(vel),0.1,"azimuthal velocity deviation < 10%")
142tap_check(ok,"stoptime reached")
143tap_done
144! skip radial velocity deviation, because the exact value is 0
145#ifdef PARALLEL
146 END IF
147#endif
148
149 CALL sim%Finalize()
150 DEALLOCATE(sim,pvar_init,sum_numer,sum_denom,sigma)
151
152CONTAINS
153
154 SUBROUTINE makeconfig(Sim, config)
155 USE functions, ONLY : asinh
156 IMPLICIT NONE
157 !------------------------------------------------------------------------!
158 TYPE(fosite) :: Sim
159 TYPE(dict_typ),POINTER :: config
160 !------------------------------------------------------------------------!
161 ! Local variable declaration
162 INTEGER :: bc(6)
163 TYPE(dict_typ), POINTER :: mesh, physics, boundary, datafile, &
164 timedisc, fluxes, sources, rotframe
165 REAL :: x1,x2,y1,y2,z1,z2
166 !------------------------------------------------------------------------!
167 INTENT(INOUT) :: sim
168 !------------------------------------------------------------------------!
169 ! mesh settings and boundary conditions
170 SELECT CASE(mgeo)
171 CASE(cylindrical)
172 x1 = rmin
173 x2 = rmax
174 y1 = 0.0
175 y2 = 2.0*pi
176 IF (zres.GT.1) THEN
177 z1 = zmin
178 z2 = zmax
179 ELSE
180 z1 = 0
181 z2 = 0
182 END IF
183 bc(west) = axis
184 bc(east) = reflecting
185 bc(south) = periodic
186 bc(north) = periodic
187 bc(bottom) = periodic
188 bc(top) = periodic
189 CASE DEFAULT
190 CALL sim%Error("vortex3d::MakeConfig","geometry currently not supported")
191 END SELECT
192
193 !mesh settings
194 mesh => dict( &
195 "meshtype" / midpoint, &
196 "geometry" / mgeo, &
197 "omega" / omega, &
198 "fargo/method" / fargo, &
199 "decomposition" / (/ -1, 1, -1/), & ! do not decompose along 2nd dimension with FARGO!
200 "inum" / xres, &
201 "jnum" / yres, &
202 "knum" / zres, &
203 "xmin" / x1, &
204 "xmax" / x2, &
205 "ymin" / y1, &
206 "ymax" / y2, &
207 "zmin" / z1, &
208 "zmax" / z2, &
209 "gparam" / gpar )
210
211 ! boundary conditions
212 boundary => dict( &
213 "western" / bc(west), &
214 "eastern" / bc(east), &
215 "southern" / bc(south), &
216 "northern" / bc(north), &
217 "bottomer" / bc(bottom), &
218 "topper" / bc(top) )
219
220 ! physics settings
221 IF (csiso.GT.tiny(csiso)) THEN
222 physics => dict("problem" / euler_isotherm, &
223 "cs" / csiso) ! isothermal sound speed !
224 ELSE
225! IF (WITH_IAR) THEN
226! ! REMARK: the optimal softening parameter depends on mesh geometry, limiter and
227! ! possibly other settings; modify this starting with the default of 1.0, if the
228! ! results show odd behaviour near the center of rotation; larger values increase
229! ! softening; 0.5 give reasonable results for PP limiter on cartesian mesh
230! physics => Dict("problem" / EULER2D_IAMT, &
231! "centrot_x" / X0,"centrot_y" / Y0, & ! center of rotation !
232! "softening" / 0.5, & ! softening parameter !
233! "gamma" / GAMMA ) ! ratio of specific heats !
234! ELSE
235 physics => dict("problem" / euler, &
236 "gamma" / gamma)
237! END IF
238 END IF
239
240 ! flux calculation and reconstruction method
241 fluxes => dict( &
242 "fluxtype" / kt, &
243! "order" / CONSTANT, &
244 "order" / linear, &
245 "variables" / primitive, &
246 "limiter" / vanleer, & ! PP limiter gives better results than
247 "theta" / 1.0e-20, & ! should be < EPS for PP limiter
248 "output/slopes" / 0)
249
250 ! activate inertial forces due to rotating frame if OMEGA > 0
251 NULLIFY(sources)
252 IF (omega.GT.tiny(omega)) THEN
253 rotframe => dict("stype" / rotating_frame, &
254 "x" / x0, &
255 "y" / y0)
256 sources => dict("rotframe" / rotframe)
257 END IF
258
259 ! time discretization settings
260 timedisc => dict(&
261 "method" / modified_euler, &
262 "order" / 3, &
263 "cfl" / 0.4, &
264 "stoptime" / tsim, &
265 "dtlimit" / 1.0e-4, &
266 "maxiter" / 1000000, &
267 "rhstype" / 0)
268
269 ! initialize data input/output
270 datafile => dict(&
271 "fileformat" / vtk, &
272 "filename" / (trim(odir) // trim(ofname)), &
273 "count" / onum)
274
275 config => dict("mesh" / mesh, &
276 "physics" / physics, &
277 "boundary" / boundary, &
278 "fluxes" / fluxes, &
279 "timedisc" / timedisc, &
280 "datafile" / datafile )
281
282 ! add sources terms
283 IF (ASSOCIATED(sources)) &
284 CALL setattr(config, "sources", sources)
285
286 END SUBROUTINE makeconfig
287
288
289 SUBROUTINE initdata(Mesh,Physics,Timedisc)
290 IMPLICIT NONE
291 !------------------------------------------------------------------------!
292 CLASS(physics_base),INTENT(IN) :: Physics
293 CLASS(mesh_base),INTENT(IN) :: Mesh
294 CLASS(timedisc_base),INTENT(INOUT):: Timedisc
295 !------------------------------------------------------------------------!
296 ! Local variable declaration
297 INTEGER :: n
298 REAL, DIMENSION(Mesh%IGMIN:Mesh%IGMAX,Mesh%JGMIN:Mesh%JGMAX,Mesh%KGMIN:Mesh%KGMAX) &
299 :: radius,dist_axis,domega
300 REAL, DIMENSION(Mesh%IGMIN:Mesh%IGMAX,Mesh%JGMIN:Mesh%JGMAX,Mesh%KGMIN:Mesh%KGMAX,3) &
301 :: posvec,ephi
302 REAL :: csinf
303 !------------------------------------------------------------------------!
304 IF (abs(x0).LE.tiny(x0).AND.abs(y0).LE.tiny(y0).AND.abs(z0).LE.tiny(z0)) THEN
305 ! no shift of rotational axis
306 radius(:,:,:) = mesh%radius%bcenter(:,:,:)
307 posvec(:,:,:,:) = mesh%posvec%bcenter(:,:,:,:)
308 ELSE
309 ! axis is assumed to be parallel to the cartesian z-axis
310 ! but shifted in the plane perpendicular to it
311 ! compute curvilinear components of shift vector
312 posvec(:,:,:,1) = x0
313 posvec(:,:,:,2) = y0
314 posvec(:,:,:,3) = 0.0
315 CALL mesh%geometry%Convert2Curvilinear(mesh%bcenter,posvec,posvec)
316 ! subtract the result from the position vector:
317 ! this gives you the curvilinear components of all vectors pointing
318 ! from the point mass to the bary center of any cell on the mesh
319 posvec(:,:,:,:) = mesh%posvec%bcenter(:,:,:,:) - posvec(:,:,:,:)
320 ! compute its absolute value
321 radius(:,:,:) = sqrt(posvec(:,:,:,1)**2+posvec(:,:,:,2)**2+posvec(:,:,:,3)**2)
322 END IF
323 ! distance to axis of rotation
324 dist_axis(:,:,:) = sqrt(posvec(:,:,:,1)**2+posvec(:,:,:,2)**2)
325
326 ! curvilinear components of azimuthal unit vector
327 ! (maybe with respect to shifted origin)
328 ! from ephi = ez x er = ez x posvec/radius = ez x (rxi*exi + reta*eeta)/r
329 ! = rxi/r*(ez x exi) + reta/r*(ez x eeta) = rxi/r*eeta - reta/r*exi
330 ! because (ez,exi,eeta) is right handed orthonormal set of basis vectors
331 ephi(:,:,:,1) = -posvec(:,:,:,2)/radius(:,:,:)
332 ephi(:,:,:,2) = posvec(:,:,:,1)/radius(:,:,:)
333 ephi(:,:,:,3) = 0.0
334
335 ! initial condition
336 ! angular velocity
337 domega(:,:,:) = 0.5*vstr/pi*exp(0.5*(1.-(dist_axis(:,:,:)/r0)**2))
338 SELECT TYPE(pvar => timedisc%pvar)
339 TYPE IS(statevector_eulerisotherm) ! isothermal HD
340 ! density
341 pvar%density%data3d(:,:,:) = rhoinf * exp(-0.5*(r0*domega(:,:,:)/csiso)**2)
342 ! velocities
343 DO n=1,physics%VDIM
344 pvar%velocity%data4d(:,:,:,n) = (domega(:,:,:)-mesh%OMEGA)*dist_axis(:,:,:)*ephi(:,:,:,n)
345 END DO
346 SELECT CASE(mesh%fargo%GetType())
347 CASE(1,2)
348 SELECT CASE(mesh%fargo%GetDirection())
349 CASE(1)
350 timedisc%w(:,:) = pvar%velocity%data4d(mesh%IMIN,:,:,1)
351 CASE(2)
352 timedisc%w(:,:) = pvar%velocity%data4d(:,mesh%JMIN,:,2)
353 CASE(3)
354 timedisc%w(:,:) = pvar%velocity%data4d(:,mesh%KMIN,:,3)
355 END SELECT
356 END SELECT
357 TYPE IS(statevector_euler) ! non-isothermal HD
358 csinf = sqrt(gamma*pinf/rhoinf) ! sound speed at infinity (isentropic vortex)
359 ! density
360 ! ATTENTION: there's a factor of 1/PI missing in the density
361 ! formula eq. (3.3) in [1]
362 pvar%density%data3d(:,:,:) = rhoinf * (1.0 - &
363 0.5*(gamma-1.0)*(r0*domega/csinf)**2 )**(1./(gamma-1.))
364 ! pressure
365 pvar%pressure%data1d(:) = pinf * (pvar%density%data1d(:)/rhoinf)**gamma
366 ! velocities
367 DO n=1,physics%VDIM
368 pvar%velocity%data4d(:,:,:,n) = (domega(:,:,:)-mesh%OMEGA)*dist_axis(:,:,:)*ephi(:,:,:,n)
369 END DO
370 SELECT CASE(mesh%fargo%GetType())
371 CASE(1,2)
372 SELECT CASE(mesh%fargo%GetDirection())
373 CASE(1)
374 timedisc%w(:,:) = pvar%velocity%data4d(mesh%IMIN,:,:,1)
375 CASE(2)
376 timedisc%w(:,:) = pvar%velocity%data4d(:,mesh%JMIN,:,2)
377 CASE(3)
378 timedisc%w(:,:) = pvar%velocity%data4d(:,:,mesh%KMIN,3)
379 END SELECT
380 END SELECT
381 END SELECT
382
383 CALL physics%Convert2Conservative(timedisc%pvar,timedisc%cvar)
384
385
386! ! compute curvilinear components of constant background velocity field
387! ! and add to the vortex velocity field
388! IF (ABS(UINF).GT.TINY(UINF).OR.ABS(VINF).GT.TINY(VINF).AND.ABS(WINF).LE.TINY(WINF)) THEN
389! v0(:,:,:,1) = UINF
390! v0(:,:,:,2) = VINF
391! v0(:,:,:,3) = WINF
392! CALL Mesh%geometry%Convert2Curvilinear(Mesh%bcenter,v0,v0)
393! Timedisc%pvar%data4d(:,:,:,Physics%XVELOCITY:Physics%ZVELOCITY) = &
394! Timedisc%pvar%data4d(:,:,:,Physics%XVELOCITY:Physics%ZVELOCITY) + v0(:,:,:,1:3)
395! END IF
396!
397!
398 CALL mesh%Info(" DATA-----> initial condition: 3D vortex")
399 END SUBROUTINE initdata
400
401END PROGRAM vortex3d
subroutine initdata(Mesh, Physics, Fluxes, Timedisc)
Definition: bondi2d.f90:274
subroutine makeconfig(Sim, config)
Definition: bondi2d.f90:165
Mathematical functions.
Definition: functions.f90:48
elemental real function, public gamma(x)
Compute the Gamma function for arguments != 0,-1,-2,..
Definition: functions.f90:1133
real, parameter pi
Definition: functions.f90:52
elemental real function, public asinh(x)
inverse hyperbolic sine function
Definition: functions.f90:100
pure real function, dimension(size(ephir%data2d, dim=1)) omega(ephir, velocity)
main fosite class
Definition: fosite.f90:71
program vortex3d
Definition: vortex3d.f90:33