Afivo  0.3
implicit_diffusion_3d.f90

An implicit diffusion example, showing how the multigrid methods can be used to solve the diffusion equation with a backward Euler scheme.

1 
6 program implicit_diffusion_3d
7  use m_a3_all
8 
9  implicit none
10 
11  integer, parameter :: box_size = 8
12  integer, parameter :: i_phi = 1
13  integer, parameter :: i_rhs = 2
14  integer, parameter :: i_tmp = 3
15  integer, parameter :: i_err = 4
16 
17  real(dp), parameter :: domain_len = 2 * acos(-1.0_dp)
18  real(dp), parameter :: dr = domain_len / box_size
19  real(dp), parameter :: diffusion_coeff = 1.0_dp
20  real(dp), parameter :: solution_modes(3) = [1, 1, 0]
21 
22  type(a3_t) :: tree
23  type(mg3_t) :: mg
24  type(ref_info_t) :: refine_info
25  integer :: id
26  integer :: ix_list(3, 1)
27  integer :: nb_list(a3_num_neighbors, 1)
28  integer :: time_steps, output_cnt
29  real(dp) :: dt, time, end_time
30  character(len=100) :: fname
31 
32  print *, "Running implicit_diffusion_3d"
33  print *, "Number of threads", af_get_max_threads()
34 
35  ! Initialize tree
36  call a3_init(tree, box_size, n_var_cell=4, n_var_face=1, dr=dr, &
37  cc_names=["phi", "rhs", "tmp", "err"])
38 
39  ! Set up geometry
40  id = 1
41  ix_list(:, id) = [1, 1, 1] ! Set index of box
42  nb_list(:, id) = 1 ! Periodic domain
43 
44  ! Create the base mesh, using the box indices and their neighbor information
45  call a3_set_base(tree, 1, ix_list, nb_list)
46  call a3_print_info(tree)
47 
48  ! Set up the initial conditions
49  do
50 
51  ! For each box, set the initial conditions
52  call a3_loop_box(tree, set_initial_condition)
53 
54  ! Fill ghost cells for variables i_phi on the sides of all boxes, using
55  ! a3_gc_interp on refinement boundaries: Interpolation between fine
56  ! points and coarse neighbors to fill ghost cells near refinement
57  ! boundaries.
58  ! Fill ghost cells near physical boundaries using Dirichlet zero
59 
60  call a3_gc_tree(tree, i_phi, a3_gc_interp, a3_bc_dirichlet_zero)
61 
62  ! Adjust the refinement of a tree using refine_routine (see below) for grid
63  ! refinement.
64  ! Routine a3_adjust_refinement sets the bit af_bit_new_children for each box
65  ! that is refined. On input, the tree should be balanced. On output,
66  ! the tree is still balanced, and its refinement is updated (with at most
67  ! one level per call).
68  call a3_adjust_refinement(tree, & ! tree
69  refine_routine, & ! Refinement function
70  refine_info) ! Information about refinement
71 
72  ! If no new boxes have been added, exit the loop
73  if (refine_info%n_add == 0) exit
74  end do
75 
76  mg%i_phi = i_phi ! Solution variable
77  mg%i_rhs = i_rhs ! Right-hand side variable
78  mg%i_tmp = i_tmp ! Variable for temporary space
79  mg%sides_bc => a3_bc_dirichlet_zero ! Method for boundary conditions
80 
81  ! The methods defined below implement a backward Euler method for the heat
82  ! equation, by changing the elliptic operator for the multigrid procedure.
83  mg%box_op => box_op_diff
84  mg%box_gsrb => box_gsrb_diff
85 
86  ! This routine does not initialize the multigrid fields boxes%i_phi,
87  ! boxes%i_rhs and boxes%i_tmp. These fields will be initialized at the
88  ! first call of mg3_fas_fmg
89  call mg3_init_mg(mg)
90 
91  output_cnt = 0
92  time = 0
93  end_time = 2.0_dp
94  time_steps = 0
95  time = 0
96  dt = 0.1_dp
97 
98  ! Starting simulation
99  do
100  time_steps = time_steps + 1
101 
102  output_cnt = output_cnt + 1
103  write(fname, "(A,I0)") "implicit_diffusion_3d_", output_cnt
104 
105  ! Call procedure set_error (see below) for each box in tree, with argument time
106  call a3_loop_box_arg(tree, set_error, [time])
107 
108  ! Write the cell centered data of tree to a vtk unstructured file fname.
109  ! Only the leaves of the tree are used
110  call a3_write_vtk(tree, trim(fname), output_cnt, time, dir="output")
111 
112  if (time > end_time) exit
113 
114  ! Call set_rhs (see below) for each box in tree
115  call a3_loop_box(tree, set_rhs)
116 
117  ! Perform a FAS-FMG cycle (full approximation scheme, full multigrid).
118  call mg3_fas_fmg(tree, & ! Tree to do multigrid on
119  mg, & ! Multigrid options
120  set_residual = .true., & ! If true, store residual in i_tmp
121  have_guess = .true.) ! If false, start from phi = 0
122 
123  time = time + dt
124  end do
125 
126 contains
127 
129  subroutine refine_routine(box, cell_flags)
130  type(box3_t), intent(in) :: box
131  integer, intent(out) :: cell_flags(box%n_cell, box%n_cell, box%n_cell)
132 
133  if (box%dr > 2e-2_dp * domain_len) then
134  cell_flags = af_do_ref
135  else
136  cell_flags = af_keep_ref
137  end if
138  end subroutine refine_routine
139 
141  subroutine set_initial_condition(box)
142  type(box3_t), intent(inout) :: box
143  integer :: i, j, k, nc
144  real(dp) :: rr(3)
145 
146  nc = box%n_cell
147 
148  do k = 0, nc+1; do j = 0, nc+1; do i = 0, nc+1
149  rr = a3_r_cc(box, [i, j, k])
150  box%cc(i, j, k, i_phi) = solution(rr, 0.0_dp)
151  end do; end do; end do
152  end subroutine set_initial_condition
153 
155  subroutine set_error(box, time)
156  type(box3_t), intent(inout) :: box
157  real(dp), intent(in) :: time(:)
158  integer :: i, j, k, nc
159  real(dp) :: rr(3)
160 
161  nc = box%n_cell
162  do k = 1, nc; do j = 1, nc; do i = 1, nc
163  rr = a3_r_cc(box, [i, j, k])
164  box%cc(i, j, k, i_err) = &
165  box%cc(i, j, k, i_phi) - solution(rr, time(1))
166  end do; end do; end do
167  end subroutine set_error
168 
169  function solution(rr, t) result(sol)
170  real(dp), intent(in) :: rr(3), t
171  real(dp) :: sol, tmp(3)
172 
173  tmp = solution_modes * rr
174  sol = 1 + product(cos(tmp)) * &
175  exp(-sum(solution_modes**2) * diffusion_coeff * t)
176  end function solution
177 
179  subroutine set_rhs(box)
180  type(box3_t), intent(inout) :: box
181  integer :: nc
182 
183  nc = box%n_cell
184  box%cc(1:nc, 1:nc, 1:nc, i_rhs) = box%cc(1:nc, 1:nc, 1:nc, i_phi)
185  end subroutine set_rhs
186 
187  ! Compute L * phi, where L corresponds to (D * dt * nabla^2 - 1)
188  subroutine box_op_diff(box, i_out, mg)
189  type(box3_t), intent(inout) :: box
190  integer, intent(in) :: i_out
191  type(mg3_t), intent(in) :: mg
192  integer :: i, j, k, nc, i_phi
193  real(dp) :: tmp
194 
195  nc = box%n_cell
196  tmp = diffusion_coeff * dt / box%dr**2
197  i_phi = mg%i_phi
198 
199  do k = 1, nc; do j = 1, nc; do i = 1, nc
200  box%cc(i, j, k, i_out) = tmp * ((6 + 1/tmp) * &
201  box%cc(i, j, k, i_phi) &
202  - box%cc(i-1, j, k, i_phi) &
203  - box%cc(i+1, j, k, i_phi) &
204  - box%cc(i, j-1, k, i_phi) &
205  - box%cc(i, j+1, k, i_phi) &
206  - box%cc(i, j, k-1, i_phi) &
207  - box%cc(i, j, k+1, i_phi))
208  end do; end do; end do
209  end subroutine box_op_diff
210 
211  ! Locally solve L * phi = rhs, where L corresponds to (D * dt * nabla^2 - 1)
212  subroutine box_gsrb_diff(box, redblack_cntr, mg)
213  type(box3_t), intent(inout) :: box
214  integer, intent(in) :: redblack_cntr
215  type(mg3_t), intent(in) :: mg
216  integer :: i, j, k, i0, nc, i_phi, i_rhs
217  real(dp) :: tmp
218 
219  tmp = diffusion_coeff * dt / box%dr**2
220  nc = box%n_cell
221  i_phi = mg%i_phi
222  i_rhs = mg%i_rhs
223 
224  ! The parity of redblack_cntr determines which cells we use. If
225  ! redblack_cntr is even, we use the even cells and vice versa.
226  do k = 1, nc
227  do j = 1, nc
228  i0 = 2 - iand(ieor(redblack_cntr, k+j), 1)
229  do i = i0, nc, 2
230  box%cc(i, j, k, i_phi) = 1 / (6 + 1/tmp) * ( &
231  box%cc(i+1, j, k, i_phi) + box%cc(i-1, j, k, i_phi) + &
232  box%cc(i, j+1, k, i_phi) + box%cc(i, j-1, k, i_phi) + &
233  box%cc(i, j, k+1, i_phi) + box%cc(i, j, k-1, i_phi) + &
234  1/tmp * box%cc(i, j, k, i_rhs))
235  end do
236  end do
237  end do
238  end subroutine box_gsrb_diff
239 
240 end program implicit_diffusion_3d