Afivo  0.3
particles_gravity_3d.f90
1 
5 program particles_gravity_3d
6  use m_a3_all
7 
8  implicit none
9 
10  integer :: n
11  integer, parameter :: box_size = 8
12  integer, parameter :: n_particles = 100*1000
13  integer, parameter :: particles_per_cell = 100
14  integer, parameter :: max_refinement_lvl = 7
15  integer, parameter :: i_phi = 1
16  integer, parameter :: i_rho = 2
17  integer, parameter :: i_tmp = 3
18  integer, parameter :: i_f(3) = [(i_tmp+n, n=1,3)]
19 
20  real(dp), parameter :: force_to_accel = 1.0_dp
21 
22  real(dp), parameter :: domain_len = 1.0_dp
23  real(dp), parameter :: mean_density = 1.0_dp
24  real(dp), parameter :: particle_weight = domain_len**3 / n_particles
25  real(dp), parameter :: dr = domain_len / box_size
26  real(dp), parameter :: t_end = 5.0_dp
27  real(dp), parameter :: dt_output = 5e-2_dp
28  real(dp) :: dt = 1.0e-2_dp
29  real(dp) :: dt_prev
30 
31  real(dp), allocatable :: coordinates(:, :)
32  real(dp), allocatable :: velocities(:, :)
33  integer, allocatable :: ids(:)
34  real(dp), allocatable :: weights(:)
35 
36  integer :: n_output
37  type(a3_t) :: tree
38  type(mg3_t) :: mg
39  type(ref_info_t) :: refine_info
40  integer :: id, ix_list(3, 1)
41  integer :: nb_list(2*3, 1)
42  integer :: count_rate, wc_start, wc_end
43  character(len=100) :: fname
44  real(dp) :: max_vel, time
45 
46  print *, "Running particles_gravity_3d"
47  print *, "Number of threads", af_get_max_threads()
48 
49  ! Initialize tree
50  call a3_init(tree, & ! Tree to initialize
51  box_size, & ! A box contains box_size**DIM cells
52  n_var_cell=i_f(3), & ! Number of cell-centered variables
53  n_var_face=0, & ! Number of face-centered variables
54  dr=dr, & ! Distance between cells on base level
55  coarsen_to=2, &
56  cc_names=["phi", "rho", "tmp", "fx ", "fy ", "fz "] & ! Variable names
57  )
58 
59  ! Set up geometry
60  id = 1
61  ix_list(:, id) = 1 ! Set index of box
62  nb_list(:, id) = 1 ! Fully periodic
63 
64  mg%i_phi = i_phi ! Solution variable
65  mg%i_rhs = i_rho ! Right-hand side variable
66  mg%i_tmp = i_tmp ! Variable for temporary space
67  mg%sides_bc => a3_bc_neumann_zero ! Not used
68  mg%subtract_mean = .true.
69 
70  call mg3_init_mg(mg)
71 
72  ! Create the base mesh, using the box indices and their neighbor information
73  call a3_set_base(tree, 1, ix_list, nb_list)
74 
75  ! Set default methods (boundary condition is not actually used due to
76  ! periodicity)
77  call a3_set_cc_methods(tree, i_phi, a3_bc_neumann_zero, a3_gc_interp)
78  call a3_set_cc_methods(tree, i_rho, a3_bc_neumann_zero, a3_gc_interp)
79  do n = 1, 3
80  call a3_set_cc_methods(tree, i_f(n), a3_bc_neumann_zero, a3_gc_interp)
81  end do
82 
83  allocate(coordinates(3, n_particles))
84  allocate(velocities(3, n_particles))
85  allocate(weights(n_particles))
86  allocate(ids(n_particles))
87 
88  call random_number(coordinates(:, :))
89  call random_number(velocities(:, :))
90  velocities(:, :) = 0.0_dp
91  weights(:) = particle_weight
92 
93  do n = 1, 100
94  call a3_tree_clear_cc(tree, i_rho)
95  call a3_particles_to_grid(tree, i_rho, coordinates, weights, &
96  n_particles, 1, ids)
97  call a3_adjust_refinement(tree, refine_routine, refine_info, 2)
98  if (refine_info%n_add == 0) exit
99  end do
100 
101  ! Compute initial gravitational potential
102  call mg3_fas_fmg(tree, mg, .false., .false.)
103  call mg3_fas_fmg(tree, mg, .false., .true.)
104 
105  time = 0.0_dp
106  n = 0
107  n_output = 0
108  dt_prev = dt
109 
110  call system_clock(wc_start, count_rate)
111  do while (time < t_end)
112  n = n + 1
113  call push_particles(coordinates, velocities, n_particles, dt)
114 
115  ! Compute force
116  call a3_tree_clear_cc(tree, i_rho)
117  call a3_particles_to_grid(tree, i_rho, coordinates, weights, &
118  n_particles, 1, ids)
119  call a3_loop_box(tree, subtract_mean_density)
120 
121  call mg3_fas_vcycle(tree, mg, .false.)
122  call mg3_fas_vcycle(tree, mg, .true.)
123 
124  call a3_loop_box(tree, compute_forces)
125  call a3_gc_tree(tree, i_f)
126 
127  call update_velocities(tree, coordinates, velocities, &
128  ids, n_particles, dt)
129 
130  time = time + dt
131  max_vel = sqrt(maxval(sum(velocities**2, dim=1)))
132  dt = min(1.1 * dt_prev, dt_output, &
133  0.5_dp * a3_min_dr(tree) / max_vel)
134  dt_prev = dt
135  call compute_total_energy(tree, coordinates, velocities, &
136  ids, n_particles)
137 
138  if (modulo(n, 5) == 0) then
139  call a3_adjust_refinement(tree, refine_routine, refine_info, 2)
140  end if
141 
142  if (n_output * dt_output <= time) then
143  call a3_tree_clear_cc(tree, i_rho)
144  call a3_particles_to_grid(tree, i_rho, coordinates, weights, &
145  n_particles, 1, ids)
146  n_output = n_output + 1
147  write(fname, "(A,I4.4)") "particles_gravity_3d_", n_output
148  call a3_write_silo(tree, fname, n_output, time, dir="output")
149  print *, "Time", time, "n_cells", a3_num_cells_used(tree)
150  end if
151  end do
152 
153  call system_clock(wc_end, count_rate)
154  print *, "Total time: ", (wc_end-wc_start) / real(count_rate, dp), " seconds"
155 
156 contains
157 
158  subroutine compute_total_energy(tree, x, v, ids, n_particles)
159  type(a3_t), intent(in) :: tree
160  integer, intent(in) :: n_particles
161  real(dp), intent(in) :: x(3, n_particles)
162  real(dp), intent(in) :: v(3, n_particles)
163  integer, intent(in) :: ids(n_particles)
164  real(dp) :: sum_ken, sum_phi, phi(1)
165  integer :: n
166 
167  sum_ken = 0.0_dp
168  sum_phi = 0.0_dp
169 
170  do n = 1, n_particles
171  phi = a3_interp1(tree, x(:, n), [i_phi], 1, ids(n))
172  sum_ken = sum_ken + 0.5_dp * sum(v(:, n)**2)
173  sum_phi = sum_phi + 0.5_dp * phi(1)
174  end do
175  print *, "sum energy", sum_ken, sum_phi, sum_ken+sum_phi
176  end subroutine compute_total_energy
177 
178  subroutine push_particles(x, v, n_particles, dt)
179  integer, intent(in) :: n_particles
180  real(dp), intent(inout) :: x(3, n_particles)
181  real(dp), intent(in) :: v(3, n_particles)
182  real(dp), intent(in) :: dt
183  integer :: n
184 
185  !$omp parallel do
186  do n = 1, n_particles
187  x(:, n) = x(:, n) + dt * v(:, n)
188  x(:, n) = modulo(x(:, n), domain_len)
189  end do
190  !$omp end parallel do
191  end subroutine push_particles
192 
193  subroutine update_velocities(tree, x, v, ids, n_particles, dt)
194  type(a3_t), intent(in) :: tree
195  integer, intent(in) :: n_particles
196  real(dp), intent(in) :: x(3, n_particles)
197  real(dp), intent(inout) :: v(3, n_particles)
198  integer, intent(inout) :: ids(:)
199  real(dp), intent(in) :: dt
200  integer :: n
201  real(dp) :: a(3)
202 
203  !$omp parallel do private(a)
204  do n = 1, n_particles
205  a = a3_interp1(tree, x(:, n), i_f, &
206  3, ids(n))
207  v(:, n) = v(:, n) + dt * a * force_to_accel
208  end do
209  !$omp end parallel do
210  end subroutine update_velocities
211 
212  subroutine refine_routine(box, cell_flags)
213  type(box3_t), intent(in) :: box
214  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell, box%n_cell)
215  real(dp) :: rho_refine, rho_derefine
216  integer :: nc
217 
218  nc = box%n_cell
219  rho_refine = particles_per_cell * particle_weight / box%dr**3
220  rho_derefine = rho_refine / (2**3 * 4)
221 
222  where (box%cc(1:nc, 1:nc, 1:nc, i_rho) > rho_refine)
223  cell_flags(:, :, :) = af_do_ref
224  elsewhere (box%cc(1:nc, 1:nc, 1:nc, i_rho) < rho_derefine)
225  cell_flags(:, :, :) = af_rm_ref
226  elsewhere
227  cell_flags(:, :, :) = af_keep_ref
228  end where
229 
230  if (box%lvl >= max_refinement_lvl) then
231  where (cell_flags == af_do_ref)
232  cell_flags = af_keep_ref
233  end where
234  end if
235 
236  end subroutine refine_routine
237 
238  subroutine compute_forces(box)
239  type(box3_t), intent(inout) :: box
240  integer :: i, j, k, nc
241  real(dp) :: fac
242 
243  nc = box%n_cell
244  fac = -0.5_dp / box%dr
245 
246  do k = 1, nc; do j = 1, nc; do i = 1, nc
247  box%cc(i, j, k, i_f(1)) = fac * &
248  (box%cc(i+1, j, k, i_phi) - box%cc(i-1, j, k, i_phi))
249  box%cc(i, j, k, i_f(2)) = fac * &
250  (box%cc(i, j+1, k, i_phi) - box%cc(i, j-1, k, i_phi))
251  box%cc(i, j, k, i_f(3)) = fac * &
252  (box%cc(i, j, k+1, i_phi) - box%cc(i, j, k-1, i_phi))
253  end do; end do; end do
254 
255  end subroutine compute_forces
256 
257  subroutine subtract_mean_density(box)
258  type(box3_t), intent(inout) :: box
259 
260  box%cc(:, :, :, i_rho) = box%cc(:, :, :, i_rho) - mean_density
261  end subroutine subtract_mean_density
262 
263 end program particles_gravity_3d
Module which contains all Afivo modules, so that a user does not have to include them separately...
Definition: m_a3_all.f90:4