Afivo 0.3
All Classes Namespaces Functions Variables Pages
m_af_output.f90
1!> This module contains routines for writing output files with Afivo. The Silo
2!> format should probably be used for larger files, especially in 3D.
4#include "cpp_macros.h"
5 use m_af_types
6
7 implicit none
8 private
9
10 integer, parameter :: af_dat_file_version = 3
11
12 abstract interface
13 subroutine subr_add_vars(box, new_vars, n_var)
14 import
15 type(box_t), intent(in) :: box
16 integer, intent(in) :: n_var
17 real(dp) :: &
18 new_vars(DTIMES(0:box%n_cell+1), n_var)
19 end subroutine subr_add_vars
20
21 ! Subroutine that reads/writes unformatted data
22 subroutine subr_other_data(my_unit)
23 integer, intent(in) :: my_unit
24 end subroutine subr_other_data
25 end interface
26
27 public :: af_write_tree
28 public :: af_read_tree
29 public :: af_tree_copy_variable
30 public :: af_write_vtk
31 public :: af_write_numpy
32#if NDIM > 1
33 public :: af_write_plane
34#endif
35 public :: af_write_silo
36 public :: af_write_line
37
38contains
39
40 !> Write full tree in binary format
41 subroutine af_write_tree(tree, filename, write_other_data)
42 type(af_t), intent(in) :: tree !< Tree to write out
43 character(len=*), intent(in) :: filename !< Filename for the output
44 procedure(subr_other_data), optional :: write_other_data
45 integer :: my_unit, lvl, id, n
46 character(len=400) :: fname
47
48 fname = trim(filename) // ".dat"
49
50 open(newunit=my_unit, file=trim(fname), form='unformatted', &
51 access='stream', status='replace')
52
53 write(my_unit) af_dat_file_version
54 write(my_unit) tree%ready
55 write(my_unit) tree%box_limit
56 write(my_unit) tree%highest_lvl
57 write(my_unit) tree%highest_id
58 write(my_unit) tree%n_cell
59 write(my_unit) tree%n_var_cell
60 write(my_unit) tree%n_var_face
61 write(my_unit) tree%coord_t
62 write(my_unit) tree%coarse_grid_size
63 write(my_unit) tree%periodic
64 write(my_unit) tree%r_base
65 write(my_unit) tree%dr_base
66
67 write(my_unit) tree%cc_names
68 write(my_unit) tree%fc_names
69 write(my_unit) tree%cc_num_copies
70 write(my_unit) tree%cc_write_output
71 write(my_unit) tree%cc_write_binary
72 write(my_unit) tree%fc_write_binary
73
74 write(my_unit) tree%n_removed_ids
75 write(my_unit) tree%removed_ids(1:tree%n_removed_ids)
76
77 ! Skip methods (these have to be set again)
78
79 do lvl = 1, tree%highest_lvl
80 write(my_unit) size(tree%lvls(lvl)%ids)
81 write(my_unit) tree%lvls(lvl)%ids
82
83 write(my_unit) size(tree%lvls(lvl)%leaves)
84 write(my_unit) tree%lvls(lvl)%leaves
85
86 write(my_unit) size(tree%lvls(lvl)%parents)
87 write(my_unit) tree%lvls(lvl)%parents
88 end do
89
90 do id = 1, tree%highest_id
91 associate(box => tree%boxes(id))
92 write(my_unit) box%in_use !< is the box in use?
93 if (.not. box%in_use) cycle
94
95 write(my_unit) box%n_cell !< number of cells per dimension
96 write(my_unit) box%n_bc !< Number of boundary conditions
97 write(my_unit) box%n_stencils !< Number of stencils
98 write(my_unit) box%lvl !< level of the box
99 write(my_unit) box%tag !< for the user
100 write(my_unit) box%ix !< index in the domain
101 write(my_unit) box%parent !< index of parent in box list
102 write(my_unit) box%children
103 write(my_unit) box%neighbors
104 write(my_unit) box%neighbor_mat
105 write(my_unit) box%dr !< width/height of a cell
106 write(my_unit) box%r_min !< min coords. of box
107 write(my_unit) box%coord_t !< Coordinate type (e.g. Cartesian)
108
109 do n = 1, tree%n_var_cell
110 if (tree%cc_write_binary(n)) then
111 write(my_unit) box%cc(dtimes(:), n)
112 end if
113 end do
114
115 do n = 1, tree%n_var_face
116 if (tree%fc_write_binary(n)) then
117 write(my_unit) box%fc(dtimes(:), :, n)
118 end if
119 end do
120
121 ! Write boundary conditions
122 if (box%n_bc > 0) then
123 write(my_unit) box%bc_index_to_nb
124 write(my_unit) box%nb_to_bc_index
125 write(my_unit) box%bc_type
126 write(my_unit) box%bc_val
127 write(my_unit) box%bc_coords
128 end if
129
130 ! Write stencils
131 if (box%n_stencils > 0) then
132 do n = 1, box%n_stencils
133 associate(stncl => box%stencils(n))
134 write(my_unit) stncl%key
135 write(my_unit) stncl%shape
136 write(my_unit) stncl%stype
137 write(my_unit) stncl%cylindrical_gradient
138
139 if (allocated(stncl%c)) then
140 write(my_unit) size(stncl%c)
141 write(my_unit) stncl%c
142 else
143 write(my_unit) 0
144 end if
145
146 if (allocated(stncl%v)) then
147 write(my_unit) size(stncl%v, 1)
148 write(my_unit) stncl%v
149 else
150 write(my_unit) 0
151 end if
152
153 if (allocated(stncl%f)) then
154 write(my_unit) 1
155 write(my_unit) stncl%f
156 else
157 write(my_unit) 0
158 end if
159
160 if (allocated(stncl%bc_correction)) then
161 write(my_unit) 1
162 write(my_unit) stncl%bc_correction
163 else
164 write(my_unit) 0
165 end if
166
167 if (allocated(stncl%sparse_ix)) then
168 write(my_unit) size(stncl%sparse_ix, 2)
169 write(my_unit) stncl%sparse_ix
170 else
171 write(my_unit) 0
172 end if
173
174 if (allocated(stncl%sparse_v)) then
175 write(my_unit) size(stncl%sparse_v, 1)
176 write(my_unit) stncl%sparse_v
177 else
178 write(my_unit) 0
179 end if
180 end associate
181 end do
182 end if
183 end associate
184 end do
185
186 write(my_unit) present(write_other_data)
187
188 if (present(write_other_data)) then
189 call write_other_data(my_unit)
190 end if
191
192 close(my_unit)
193 print *, "af_write_tree: written " // trim(fname)
194 end subroutine af_write_tree
195
196 !> Read full tree in binary format
197 subroutine af_read_tree(tree, filename, read_other_data)
198 use m_af_core, only: af_init_box
199 type(af_t), intent(inout) :: tree !< Tree to read in
200 character(len=*), intent(in) :: filename !< Filename for the input
201 procedure(subr_other_data), optional :: read_other_data
202 integer :: my_unit, lvl, n, id, version, k, m, nc
203 logical :: other_data_present
204
205 open(newunit=my_unit, file=trim(filename), form='unformatted', &
206 access='stream', status='old', action='read')
207
208 read(my_unit) version
209 if (version /= af_dat_file_version) then
210 print *, "File version read:", version
211 print *, "File version required:", af_dat_file_version
212 error stop "af_read_tree: incompatible file versions"
213 end if
214
215 read(my_unit) tree%ready
216 read(my_unit) tree%box_limit
217 read(my_unit) tree%highest_lvl
218 read(my_unit) tree%highest_id
219 read(my_unit) tree%n_cell
220 read(my_unit) tree%n_var_cell
221 read(my_unit) tree%n_var_face
222 read(my_unit) tree%coord_t
223 read(my_unit) tree%coarse_grid_size
224 read(my_unit) tree%periodic
225 read(my_unit) tree%r_base
226 read(my_unit) tree%dr_base
227
228 nc = tree%n_cell
229
230 ! Skip methods (these have to be set again)
231 if (.not. allocated(tree%cc_auto_vars)) &
232 allocate(tree%cc_auto_vars(0))
233 if (.not. allocated(tree%cc_func_vars)) &
234 allocate(tree%cc_func_vars(0))
235
236 read(my_unit) tree%cc_names
237 read(my_unit) tree%fc_names
238 read(my_unit) tree%cc_num_copies
239 read(my_unit) tree%cc_write_output
240 read(my_unit) tree%cc_write_binary
241 read(my_unit) tree%fc_write_binary
242
243 allocate(tree%removed_ids(tree%box_limit))
244 read(my_unit) tree%n_removed_ids
245 read(my_unit) tree%removed_ids(1:tree%n_removed_ids)
246
247 do lvl = 1, tree%highest_lvl
248 read(my_unit) n
249 allocate(tree%lvls(lvl)%ids(n))
250 read(my_unit) tree%lvls(lvl)%ids
251
252 read(my_unit) n
253 allocate(tree%lvls(lvl)%leaves(n))
254 read(my_unit) tree%lvls(lvl)%leaves
255
256 read(my_unit) n
257 allocate(tree%lvls(lvl)%parents(n))
258 read(my_unit) tree%lvls(lvl)%parents
259 end do
260
261 do lvl = tree%highest_lvl+1, af_max_lvl
262 allocate(tree%lvls(lvl)%ids(0))
263 allocate(tree%lvls(lvl)%leaves(0))
264 allocate(tree%lvls(lvl)%parents(0))
265 end do
266
267 allocate(tree%boxes(tree%box_limit))
268
269 do id = 1, tree%highest_id
270 associate(box => tree%boxes(id))
271 read(my_unit) box%in_use !< is the box in use?
272 if (.not. box%in_use) cycle
273
274 read(my_unit) box%n_cell !< number of cells per dimension
275 read(my_unit) box%n_bc !< Number of boundary conditions
276 read(my_unit) box%n_stencils !< Number of stencils
277 read(my_unit) box%lvl !< level of the box
278 read(my_unit) box%tag !< for the user
279 read(my_unit) box%ix !< index in the domain
280 read(my_unit) box%parent !< index of parent in box list
281 read(my_unit) box%children
282 read(my_unit) box%neighbors
283 read(my_unit) box%neighbor_mat
284 read(my_unit) box%dr !< width/height of a cell
285 read(my_unit) box%r_min !< min coords. of box
286 read(my_unit) box%coord_t !< Coordinate type (e.g. Cartesian)
287
288 call af_init_box(tree, id)
289
290 do n = 1, tree%n_var_cell
291 if (tree%cc_write_binary(n)) then
292 read(my_unit) box%cc(dtimes(:), n)
293 end if
294 end do
295
296 do n = 1, tree%n_var_face
297 if (tree%fc_write_binary(n)) then
298 read(my_unit) box%fc(dtimes(:), :, n)
299 end if
300 end do
301
302 ! Read boundary conditions
303 if (box%n_bc > 0) then
304 read(my_unit) box%bc_index_to_nb
305 read(my_unit) box%nb_to_bc_index
306 read(my_unit) box%bc_type
307 read(my_unit) box%bc_val
308 read(my_unit) box%bc_coords
309 end if
310
311 ! Read stencils
312 if (box%n_stencils > 0) then
313 allocate(box%stencils(box%n_stencils))
314
315 do n = 1, box%n_stencils
316 associate(stncl => box%stencils(n))
317 read(my_unit) stncl%key
318 read(my_unit) stncl%shape
319 read(my_unit) stncl%stype
320 read(my_unit) stncl%cylindrical_gradient
321
322 read(my_unit) k
323 if (k > 0) then
324 allocate(stncl%c(k))
325 read(my_unit) stncl%c
326 end if
327
328 read(my_unit) k
329 if (k > 0) then
330 allocate(stncl%v(k, dtimes(nc)))
331 read(my_unit) stncl%v
332 end if
333
334 read(my_unit) k
335 if (k > 0) then
336 allocate(stncl%f(dtimes(nc)))
337 read(my_unit) stncl%f
338 end if
339
340 read(my_unit) k
341 if (k > 0) then
342 allocate(stncl%bc_correction(dtimes(nc)))
343 read(my_unit) stncl%bc_correction
344 end if
345
346 read(my_unit) k
347 if (k > 0) then
348 allocate(stncl%sparse_ix(ndim, k))
349 read(my_unit) stncl%sparse_ix
350 end if
351
352 read(my_unit) m
353 if (k > 0 .and. m > 0) then
354 allocate(stncl%sparse_v(m, k))
355 read(my_unit) stncl%sparse_v
356 end if
357 end associate
358 end do
359 end if
360 end associate
361 end do
362
363 read(my_unit) other_data_present
364
365 if (present(read_other_data)) then
366 if (other_data_present) then
367 call read_other_data(my_unit)
368 else
369 error stop "af_read_tree: other data is not present"
370 end if
371 end if
372
373 close(my_unit)
374 end subroutine af_read_tree
375
376 subroutine af_tree_copy_variable(tree_from, ivs_from, tree_to, ivs_to)
377 use m_af_interp
378 type(af_t), intent(in) :: tree_from !< Copy from this grid
379 integer, intent(in) :: ivs_from(:) !< From these variable
380 type(af_t), intent(inout) :: tree_to !< Copy to this grid
381 integer, intent(in) :: ivs_to(:) !< To these variable
382 integer :: lvl, id, n, nc, i, j, k
383 real(dp) :: rr(ndim)
384 logical :: success
385
386 !$omp parallel private(lvl, id, n, nc, i, j, k, rr)
387 do lvl = 1, tree_to%highest_lvl
388 !$omp do
389 do n = 1, size(tree_to%lvls(lvl)%leaves)
390 id = tree_to%lvls(lvl)%leaves(n)
391 nc = tree_to%boxes(id)%n_cell
392
393 do kji_do(1, nc)
394 rr = af_r_cc(tree_to%boxes(id), [ijk])
395 tree_to%boxes(id)%cc(ijk, ivs_to) = &
396 af_interp1(tree_from, rr, [ivs_from], success)
397 if (.not. success) error stop "af_tree_copy_variable: interpolation error"
398 end do; close_do
399 end do
400 !$omp end do
401 end do
402 !$omp end parallel
403
404 end subroutine af_tree_copy_variable
405
406 !> Write line data in a text file
407 subroutine af_write_line(tree, filename, ivs, r_min, r_max, n_points)
408 use m_af_interp, only: af_interp1
409 type(af_t), intent(in) :: tree !< Tree to write out
410 character(len=*), intent(in) :: filename !< Filename for the vtk file
411 integer, intent(in) :: ivs(:) !< Variables to write
412 real(dp), intent(in) :: r_min(ndim) !< Minimum coordinate of line
413 real(dp), intent(in) :: r_max(ndim) !< Maximum coordinate of line
414 integer, intent(in) :: n_points !< Number of points along line
415
416 integer, parameter :: my_unit = 100
417 character(len=400) :: fname
418 integer :: i, n_cc
419 real(dp) :: r(ndim), dr_vec(ndim)
420 real(dp), allocatable :: line_data(:, :)
421 logical :: success
422
423 n_cc = size(ivs)
424 dr_vec = (r_max - r_min) / max(1, n_points-1)
425
426 allocate(line_data(n_cc+ndim, n_points))
427
428 !$omp parallel do private(r)
429 do i = 1, n_points
430 r = r_min + (i-1) * dr_vec
431 line_data(1:ndim, i) = r
432 line_data(ndim+1:ndim+n_cc, i) = af_interp1(tree, r, ivs, success)
433 if (.not. success) error stop "af_write_line: interpolation error"
434 end do
435 !$omp end parallel do
436
437 fname = trim(filename) // ".txt"
438
439 ! Write header
440 open(my_unit, file=trim(fname), action="write")
441#if NDIM == 1
442 write(my_unit, '(A)', advance="no") "# x"
443#elif NDIM == 2
444 write(my_unit, '(A)', advance="no") "# x y"
445#elif NDIM == 3
446 write(my_unit, '(A)', advance="no") "# x y z"
447#endif
448 do i = 1, n_cc
449 write(my_unit, '(A)', advance="no") " "//trim(tree%cc_names(ivs(i)))
450 end do
451 write(my_unit, '(A)') ""
452
453 ! Write data
454 do i = 1, n_points
455 write(my_unit, *) line_data(:, i)
456 end do
457
458 close(my_unit)
459 end subroutine af_write_line
460
461#if NDIM > 1
462 !> Write data in a plane (2D) to a VTK ASCII file. In 3D, r_min and r_max
463 !> should have one identical coordinate (i.e., they differ in two
464 !> coordinates).
465 subroutine af_write_plane(tree, filename, ivs, r_min, r_max, n_pixels)
466 use m_af_interp, only: af_interp1
467 type(af_t), intent(in) :: tree !< Tree to write out
468 character(len=*), intent(in) :: filename !< Filename for the vtk file
469 integer, intent(in) :: ivs(:) !< Variables to write
470 real(dp), intent(in) :: r_min(ndim) !< Minimum coordinate of plane
471 real(dp), intent(in) :: r_max(ndim) !< Maximum coordinate of plane
472 integer, intent(in) :: n_pixels(2) !< Number of pixels in the plane
473
474 integer, parameter :: my_unit = 100
475 character(len=100) :: fmt_string
476 character(len=400) :: fname
477 integer :: i, j, n_cc, dim_unused, n_points(3)
478 real(dp) :: r(ndim), dvec(3)
479 real(dp) :: v1(ndim), v2(ndim)
480 real(dp), allocatable :: pixel_data(:, :, :)
481 logical :: success
482
483 n_cc = size(ivs)
484#if NDIM == 2
485 dvec(1:2) = r_max(1:2) - r_min(1:2)
486 dvec(3) = 0
487 dim_unused = 3
488 n_points = [n_pixels(1), n_pixels(2), 1]
489 v1 = [dvec(1), 0.0_dp] / (n_pixels(1) - 1)
490 v2 = [0.0_dp, dvec(2)] / (n_pixels(2) - 1)
491#elif NDIM == 3
492 dvec = r_max - r_min
493 dim_unused = minloc(abs(dvec), 1)
494
495 select case (dim_unused)
496 case (1)
497 v1 = [0.0_dp, dvec(2), 0.0_dp] / (n_pixels(1) - 1)
498 v2 = [0.0_dp, 0.0_dp, dvec(3)] / (n_pixels(2) - 1)
499 n_points = [1, n_pixels(1), n_pixels(2)]
500 case (2)
501 v1 = [dvec(1), 0.0_dp, 0.0_dp] / (n_pixels(1) - 1)
502 v2 = [0.0_dp, 0.0_dp, dvec(3)] / (n_pixels(2) - 1)
503 n_points = [n_pixels(1), 1, n_pixels(2)]
504 case (3)
505 v1 = [dvec(1), 0.0_dp, 0.0_dp] / (n_pixels(1) - 1)
506 v2 = [0.0_dp, dvec(2), 0.0_dp] / (n_pixels(2) - 1)
507 n_points = [n_pixels(1), n_pixels(2), 1]
508 end select
509#endif
510
511 allocate(pixel_data(n_cc, n_pixels(1), n_pixels(2)))
512
513 !$omp parallel do private(i, r)
514 do j = 1, n_pixels(2)
515 do i = 1, n_pixels(1)
516 r = r_min + (i-1) * v1 + (j-1) * v2
517 pixel_data(:, i, j) = af_interp1(tree, r, ivs, success)
518 if (.not. success) error stop "af_write_plane: interpolation error"
519 end do
520 end do
521 !$omp end parallel do
522
523 ! Construct format string. Write one row at a time
524 write(fmt_string, '(A,I0,A)') '(', n_pixels(1), 'E20.8)'
525
526 ! Construct file name
527 fname = trim(filename) // ".vtk"
528
529 open(my_unit, file=trim(fname), action="write")
530 write(my_unit, '(A)') "# vtk DataFile Version 2.0"
531 write(my_unit, '(A)') trim(filename)
532 write(my_unit, '(A)') "ASCII"
533 write(my_unit, '(A)') "DATASET STRUCTURED_POINTS"
534 write(my_unit, '(A,3I10)') "DIMENSIONS ", n_points
535#if NDIM == 2
536 write(my_unit, '(A,3E20.8)') "ORIGIN ", [r_min(1), r_min(2), 0.0_dp]
537 write(my_unit, '(A,3E20.8)') "SPACING ", &
538 [v1(1) + v2(1), v1(2) + v2(2), 0.0_dp]
539#elif NDIM == 3
540 write(my_unit, '(A,3E20.8)') "ORIGIN ", r_min
541 write(my_unit, '(A,3E20.8)') "SPACING ", v1 + v2
542#endif
543 write(my_unit, '(A,2I0)') "POINT_DATA ", product(n_points)
544 do i = 1, n_cc
545 write(my_unit, '(A)') "SCALARS " // &
546 trim(tree%cc_names(ivs(i))) // " double 1"
547 write(my_unit, '(A)') "LOOKUP_TABLE default"
548 write(my_unit, trim(fmt_string)) pixel_data(i, :, :)
549 end do
550 close(my_unit)
551 end subroutine af_write_plane
552#endif
553
554 !> Write the cell centered data of a tree to a vtk unstructured file. Only the
555 !> leaves of the tree are used
556 subroutine af_write_vtk(tree, filename, n_cycle, time, ixs_cc, &
557 add_vars, add_names)
558 use m_vtk
559
560 type(af_t), intent(in) :: tree !< Tree to write out
561 character(len=*), intent(in) :: filename !< Filename for the vtk file
562 integer, intent(in), optional :: n_cycle !< Cycle-number for vtk file (counter)
563 real(dp), intent(in), optional :: time !< Time for output file
564 integer, intent(in), optional :: ixs_cc(:) !< Only include these cell variables
565 procedure(subr_add_vars), optional :: add_vars !< Optional routine to add extra variables
566 character(len=*), intent(in), optional :: add_names(:) !< Names of extra variables
567
568 integer :: lvl, bc, bn, n, n_cells, n_nodes
569 integer :: ig, ijk, id, n_ix, c_ix, n_grids
570 integer :: cell_ix, node_ix, n_cycle_val
571 integer :: n_cc, n_add
572 integer, parameter :: n_ch = af_num_children
573 integer :: nodes_per_box, cells_per_box
574 real(dp) :: time_val
575 real(dp), allocatable :: coords(:), cc_vars(:,:)
576 integer, allocatable :: offsets(:), connects(:)
577 integer, allocatable :: cell_types(:), icc_val(:)
578 type(vtk_t) :: vtkf
579 character(len=400) :: fname
580 character(len=100), allocatable :: var_names(:)
581 real(dp), allocatable :: cc(dtimes(:), :)
582#if NDIM == 3
583 integer :: bn2
584#endif
585
586 if (.not. tree%ready) error stop "Tree not ready"
587 time_val = 0.0_dp; if (present(time)) time_val = time
588 n_cycle_val = 0; if (present(n_cycle)) n_cycle_val = n_cycle
589 n_add = 0; if (present(add_names)) n_add = size(add_names)
590
591 if (present(add_names) .neqv. present(add_vars)) &
592 stop "af_write_vtk: both arguments (add_names, add_vars) needed"
593
594 if (present(ixs_cc)) then
595 if (maxval(ixs_cc) > tree%n_var_cell .or. &
596 minval(ixs_cc) < 1) stop "af_write_vtk: wrong indices given (ixs_cc)"
597 allocate(icc_val(size(ixs_cc)))
598 icc_val = ixs_cc
599 else
600 call get_output_vars(tree, icc_val)
601 end if
602
603 n_cc = size(icc_val)
604
605 allocate(var_names(n_cc+n_add))
606 var_names(1:n_cc) = tree%cc_names(icc_val)
607
608 if (present(add_names)) then
609 var_names(n_cc+1:n_cc+n_add) = add_names(:)
610 end if
611
612 bc = tree%n_cell ! number of Box Cells
613 bn = tree%n_cell + 1 ! number of Box Nodes
614 nodes_per_box = bn**ndim
615 cells_per_box = bc**ndim
616
617 allocate(cc(dtimes(0:bc+1), n_cc + n_add))
618
619 n_grids = 0
620 do lvl = 1, tree%highest_lvl
621 n_grids = n_grids + size(tree%lvls(lvl)%leaves)
622 end do
623 n_nodes = nodes_per_box * n_grids
624 n_cells = cells_per_box * n_grids
625
626 allocate(coords(ndim * n_nodes))
627 allocate(cc_vars(n_cells, n_cc+n_add))
628 allocate(offsets(cells_per_box * n_grids))
629 allocate(cell_types(cells_per_box * n_grids))
630 allocate(connects(n_ch * cells_per_box * n_grids))
631
632#if NDIM == 1
633 cell_types = 3 ! VTK line type
634#elif NDIM == 2
635 cell_types = 8 ! VTK pixel type
636#elif NDIM == 3
637 bn2 = bn**2
638 cell_types = 11 ! VTK voxel type
639#endif
640
641 ig = 0
642 do lvl = 1, tree%highest_lvl
643 do n = 1, size(tree%lvls(lvl)%leaves)
644 id = tree%lvls(lvl)%leaves(n)
645 ig = ig + 1
646 cell_ix = (ig-1) * cells_per_box
647 node_ix = (ig-1) * nodes_per_box
648
649#if NDIM == 1
650 cc(:, 1:n_cc) = tree%boxes(id)%cc(:, icc_val)
651
652 if (present(add_vars)) then
653 call add_vars(tree%boxes(id), &
654 cc(:, n_cc+1:n_cc+n_add), n_add)
655 end if
656
657 do i = 1, bn
658 n_ix = node_ix + i
659 coords(n_ix) = tree%boxes(id)%r_min(1) + &
660 (i-1) * tree%boxes(id)%dr(1)
661 end do
662
663 do i = 1, bc
664 ! In vtk, indexing starts at 0, so subtract 1
665 n_ix = node_ix + i - 1
666 c_ix = cell_ix + i
667 cc_vars(c_ix, :) = cc(i, :)
668 offsets(c_ix) = af_num_children * c_ix
669 connects(n_ch*(c_ix-1)+1:n_ch*c_ix) = [n_ix, n_ix+1]
670 end do
671#elif NDIM == 2
672 cc(:, :, 1:n_cc) = tree%boxes(id)%cc(:, :, icc_val)
673
674 if (present(add_vars)) then
675 call add_vars(tree%boxes(id), &
676 cc(:, :, n_cc+1:n_cc+n_add), n_add)
677 end if
678
679 do j = 1, bn
680 do i = 1, bn
681 n_ix = 2 * (node_ix + (j-1) * bn + i)
682 coords(n_ix-1:n_ix) = tree%boxes(id)%r_min + &
683 [i-1,j-1] * tree%boxes(id)%dr
684 end do
685 end do
686
687 do j = 1, bc
688 do i = 1, bc
689 ! In vtk, indexing starts at 0, so subtract 1
690 n_ix = node_ix + (j-1) * bn + i - 1
691 c_ix = cell_ix + (j-1) * bc + i
692 cc_vars(c_ix, :) = cc(i, j, :)
693 offsets(c_ix) = af_num_children * c_ix
694 connects(n_ch*(c_ix-1)+1:n_ch*c_ix) = [n_ix, n_ix+1, n_ix+bn, n_ix+bn+1]
695 end do
696 end do
697#elif NDIM == 3
698 cc(:, :, :, 1:n_cc) = tree%boxes(id)%cc(:, :, :, icc_val)
699
700 if (present(add_vars)) then
701 call add_vars(tree%boxes(id), &
702 cc(:, :, :, n_cc+1:n_cc+n_add), n_add)
703 end if
704
705 do k = 1, bn
706 do j = 1, bn
707 do i = 1, bn
708 n_ix = 3 * (node_ix + (k-1) * bn2 + (j-1) * bn + i)
709 coords(n_ix-2:n_ix) = tree%boxes(id)%r_min + &
710 [i-1,j-1,k-1] * tree%boxes(id)%dr
711 end do
712 end do
713 end do
714
715 do k = 1, bc
716 do j = 1, bc
717 do i = 1, bc
718 ! In vtk, indexing starts at 0, so subtract 1
719 n_ix = node_ix + (k-1) * bn2 + &
720 (j-1) * bn + i - 1
721 c_ix = cell_ix + (k-1) * bc**2 + &
722 (j-1) * bc + i
723 cc_vars(c_ix, :) = cc(i, j, k, :)
724 offsets(c_ix) = 8 * c_ix
725 connects(n_ch*(c_ix-1)+1:n_ch*c_ix) = &
726 [n_ix, n_ix+1, n_ix+bn, n_ix+bn+1, &
727 n_ix+bn2, n_ix+bn2+1, n_ix+bn2+bn, n_ix+bn2+bn+1]
728 end do
729 end do
730 end do
731#endif
732 end do
733 end do
734
735 fname = trim(filename) // ".vtu"
736
737 call vtk_ini_xml(vtkf, trim(fname), 'UnstructuredGrid')
738 call vtk_dat_xml(vtkf, "UnstructuredGrid", .true.)
739 call vtk_unstr_geo_xml(vtkf, coords, n_nodes, n_cells, ndim, n_cycle_val, time_val)
740 call vtk_unstr_con_xml(vtkf, connects, offsets, cell_types, n_cells)
741 call vtk_dat_xml(vtkf, "CellData", .true.)
742
743 do n = 1, n_cc + n_add
744 call vtk_var_r8_xml(vtkf, trim(var_names(n)), cc_vars(:, n), n_cells)
745 end do
746
747 call vtk_dat_xml(vtkf, "CellData", .false.)
748 call vtk_unstr_geo_xml_close(vtkf)
749 call vtk_dat_xml(vtkf, "UnstructuredGrid", .false.)
750 call vtk_end_xml(vtkf)
751 print *, "af_write_vtk: written " // trim(fname)
752 end subroutine af_write_vtk
753
754 !> Write uniform data interpolated from a region to a .npy or .npz numpy file.
755 !> The format is determined based on the extension of filename
756 subroutine af_write_numpy(tree, filename, r_min, r_max, n_points, &
757 n_cycle, time, ixs_cc)
758 use m_npy
759 use m_af_interp, only: af_interp1
760
761 type(af_t), intent(inout) :: tree !< Tree to save
762 !> Filename, possible extensions: .npz, .npy
763 character(len=*), intent(in) :: filename
764 integer, intent(in), optional :: n_points(ndim) !< Number of points to use
765 real(dp), intent(in), optional :: r_min(ndim) !< Minimum coordinates
766 real(dp), intent(in), optional :: r_max(ndim) !< Maximum coordinates
767 integer, intent(in), optional :: n_cycle !< Cycle-number (counter)
768 real(dp), intent(in), optional :: time !< Time
769 integer, intent(in), optional :: ixs_cc(:) !< Only include these cell variables
770
771 integer :: n, ijk, id, lvl, id_guess, nx(ndim)
772 integer :: n_cycle_val, n_cc, nc
773 logical :: success
774 real(dp) :: time_val, dr(ndim), r(ndim)
775 real(dp) :: rmin(ndim), rmax(ndim)
776 integer, allocatable :: icc_val(:)
777 character(len=af_nlen), allocatable :: var_names(:)
778 character(len=400) :: fname
779 real(dp), allocatable :: cc(dtimes(:), :)
780
781 if (.not. tree%ready) error stop "Tree not ready"
782 nc = tree%n_cell
783 time_val = 0.0_dp; if (present(time)) time_val = time
784 n_cycle_val = 0; if (present(n_cycle)) n_cycle_val = n_cycle
785 rmin = tree%r_base
786 if (present(r_min)) rmin = r_min
787 rmax = tree%r_base + tree%dr_base * tree%coarse_grid_size
788 if (present(r_max)) rmax = r_max
789
790 if (present(ixs_cc)) then
791 if (maxval(ixs_cc) > tree%n_var_cell .or. &
792 minval(ixs_cc) < 1) stop "af_write_vtk: wrong indices given (ixs_cc)"
793 allocate(icc_val(size(ixs_cc)))
794 icc_val = ixs_cc
795 else
796 call get_output_vars(tree, icc_val)
797 end if
798 n_cc = size(icc_val)
799
800 if (present(n_points)) then
801 nx = n_points
802 else
803 ! Determine highest fully refined grid level
804 outer: do lvl = 1, tree%highest_lvl-1
805 do i = 1, size(tree%lvls(lvl)%leaves)
806 id = tree%lvls(lvl)%leaves(i)
807
808 ! If there is a leave in the region, the highest is found
809 if (all(tree%boxes(id)%r_min <= rmax .and. &
810 tree%boxes(id)%r_min + nc * &
811 tree%boxes(id)%dr >= rmin)) exit outer
812 end do
813 end do outer
814
815 ! Use same grid spacing as the grid
816 nx = nint((rmax - rmin)/af_lvl_dr(tree, lvl))
817 end if
818
819 dr = (rmax - rmin)/nx
820
821 allocate(var_names(n_cc))
822 var_names(1:n_cc) = tree%cc_names(icc_val)
823 allocate(cc(dindex(nx), n_cc))
824
825 id_guess = -1
826 !$omp parallel do private(IJK, r) firstprivate(id_guess)
827#if NDIM == 1
828 do i = 1, nx(1)
829 r = rmin + ([ijk] - 0.5_dp) * dr
830 cc(ijk, :) = af_interp1(tree, r, icc_val, success, id_guess)
831 if (.not. success) error stop "af_write_plane: interpolation error"
832 end do
833#elif NDIM == 2
834 do j = 1, nx(2)
835 do i = 1, nx(1)
836 r = rmin + ([ijk] - 0.5) * dr
837 cc(ijk, :) = af_interp1(tree, r, icc_val, success, id_guess)
838 if (.not. success) error stop "af_write_plane: interpolation error"
839 end do
840 end do
841#elif NDIM == 3
842 do k = 1, nx(3)
843 do j = 1, nx(2)
844 do i = 1, nx(1)
845 r = rmin + ([ijk] - 0.5) * dr
846 cc(ijk, :) = af_interp1(tree, r, icc_val, success, id_guess)
847 if (.not. success) error stop "af_write_plane: interpolation error"
848 end do
849 end do
850 end do
851#endif
852 !$omp end parallel do
853
854 n = len_trim(filename)
855
856 ! Check last four characters of filename
857 select case (filename(n-3:n))
858 case ('.npy')
859 call save_npy(filename, cc)
860 case ('.npz')
861 call remove_file(filename) ! Clear npz file
862
863 ! Add variables separately
864 do n = 1, n_cc
865 fname = filename(:n-4)//trim(var_names(n))//'.npy'
866 call save_npy(fname, cc(dtimes(:), n))
867 call add_to_zip(filename, fname, .false., var_names(n))
868 end do
869
870 fname = filename(:n-4)//'nx.npy'
871 call save_npy(fname, nx)
872 call add_to_zip(filename, fname, .false., 'nx')
873
874 fname = filename(:n-4)//'r_min.npy'
875 call save_npy(fname, rmin)
876 call add_to_zip(filename, fname, .false., 'r_min')
877
878 fname = filename(:n-4)//'r_max.npy'
879 call save_npy(fname, rmax)
880 call add_to_zip(filename, fname, .false., 'r_max')
881
882 fname = filename(:n-4)//'dr.npy'
883 call save_npy(fname, dr)
884 call add_to_zip(filename, fname, .false., 'dr')
885
886 fname = filename(:n-4)//'coord_t.npy'
887 call save_npy(fname, [tree%coord_t])
888 call add_to_zip(filename, fname, .false., 'coord_t')
889
890 fname = filename(:n-4)//'time_cycle.npy'
891 call save_npy(fname, [time_val, real(n_cycle_val, dp)])
892 call add_to_zip(filename, fname, .false., 'time_cycle')
893 case default
894 error stop "Unknown file extension"
895 end select
896
897 print *, "af_write_numpy: written " // trim(filename)
898 end subroutine af_write_numpy
899
900#if NDIM == 1
901 !> Note: a 1D version is present below, and seems to work, but its output
902 !> cannot be visualized by Visit. That's why we use curve output for 1D cases.
903 subroutine af_write_silo(tree, filename, n_cycle, time, ixs_cc, &
904 add_vars, add_names, max_lvl)
905 use m_write_silo
906 use m_mrgrnk
907
908 type(af_t), intent(in) :: tree !< Tree to write out
909 character(len=*) :: filename !< Filename for the vtk file
910 integer, intent(in), optional :: n_cycle !< Cycle-number for vtk file (counter)
911 real(dp), intent(in), optional :: time !< Time for output file
912 integer, intent(in), optional :: ixs_cc(:) !< Only include these cell variables
913 procedure(subr_add_vars), optional :: add_vars !< Optional routine to add extra variables
914 character(len=*), intent(in), optional :: add_names(:) !< Names of extra variables
915 !> Maximum refinement level for output
916 integer, intent(in), optional :: max_lvl
917 integer :: n, i, id, ix, lvl, n_boxes
918 integer :: dbix, n_cc, n_leaves, nc
919 integer :: highest_lvl
920 integer, allocatable :: icc_val(:), id_leaves(:)
921 integer, allocatable :: id_sorted_ix(:)
922 real(dp), allocatable :: xmin_leaves(:)
923 real(dp), allocatable :: xdata(:), ydata(:, :)
924 character(len=af_nlen) :: yname
925 character(len=400) :: fname
926
927 highest_lvl = tree%highest_lvl
928 if (present(max_lvl)) highest_lvl = min(max_lvl, tree%highest_lvl)
929
930 if (present(ixs_cc)) then
931 if (maxval(ixs_cc) > tree%n_var_cell .or. &
932 minval(ixs_cc) < 1) stop "af_write_silo: wrong indices given (ixs_cc)"
933 allocate(icc_val(size(ixs_cc)))
934 icc_val = ixs_cc
935 else
936 call get_output_vars(tree, icc_val)
937 end if
938
939 if (present(add_vars) .or. present(add_names)) &
940 error stop "add_vars / add_names not implemented in 1D"
941
942 n_cc = size(icc_val)
943 nc = tree%n_cell
944
945 ! Count number of leaves
946 n = 0
947 do lvl = 1, highest_lvl
948 if (lvl < highest_lvl) then
949 n = n + size(tree%lvls(lvl)%leaves)
950 else
951 n = n + size(tree%lvls(lvl)%ids)
952 end if
953 end do
954 n_leaves = n
955
956 allocate(ydata(n_leaves * nc, n_cc))
957 allocate(xdata(n_leaves * nc))
958 allocate(id_leaves(n_leaves))
959 allocate(xmin_leaves(n_leaves))
960 allocate(id_sorted_ix(n_leaves))
961
962 ! Store ids of all leaves
963 n = 0
964 do lvl = 1, highest_lvl
965 if (lvl < highest_lvl) then
966 n_boxes = size(tree%lvls(lvl)%leaves)
967 id_leaves(n+1:n+n_boxes) = tree%lvls(lvl)%leaves
968 else
969 n_boxes = size(tree%lvls(lvl)%ids)
970 id_leaves(n+1:n+n_boxes) = tree%lvls(lvl)%ids
971 end if
972 n = n + n_boxes
973 end do
974
975 ! Get minimum coordinate of each leave
976 xmin_leaves = tree%boxes(id_leaves)%r_min(1)
977
978 ! Sort by xmin
979 call mrgrnk(xmin_leaves, id_sorted_ix)
980
981 do n = 1, n_leaves
982 ix = (n-1) * nc
983 id = id_leaves(id_sorted_ix(n))
984 ydata(ix+1:ix+nc, :) = tree%boxes(id)%cc(1:nc, icc_val)
985 do i = 1, nc
986 xdata(ix+i:ix+i) = af_r_cc(tree%boxes(id), [i])
987 end do
988 end do
989
990 fname = trim(filename) // ".silo"
991 call silo_create_file(trim(fname), dbix)
992 call silo_set_time_varying(dbix)
993
994 do n = 1, n_cc
995 yname = tree%cc_names(icc_val(n))
996 call silo_add_curve(dbix, yname, xdata, ydata(:, n))
997 end do
998
999 call silo_close_file(dbix)
1000 print *, "af_write_silo: written " // trim(fname)
1001 end subroutine af_write_silo
1002#else
1003 !> Write the cell centered data of a tree to a Silo file. Only the
1004 !> leaves of the tree are used
1005 subroutine af_write_silo(tree, filename, n_cycle, time, ixs_cc, &
1006 add_vars, add_names, add_curve_names, add_curve_dat, max_lvl)
1007 use m_write_silo
1008
1009 type(af_t), intent(in) :: tree !< Tree to write out
1010 character(len=*) :: filename !< Filename for the vtk file
1011 integer, intent(in), optional :: n_cycle !< Cycle-number for vtk file (counter)
1012 real(dp), intent(in), optional :: time !< Time for output file
1013 integer, intent(in), optional :: ixs_cc(:) !< Only include these cell variables
1014 procedure(subr_add_vars), optional :: add_vars !< Optional routine to add extra variables
1015 character(len=*), intent(in), optional :: add_names(:), add_curve_names(:) !< Names of extra variables or curves
1016 real(dp), intent(in), optional :: add_curve_dat(:, :, :) !< Data for additional curves (#curves, x/y-dimensions, #points)
1017 !> Maximum refinement level for output. Note that the user has to ensure the
1018 !> coarse grid data is up to date
1019 integer, intent(in), optional :: max_lvl
1020
1021 character(len=*), parameter :: grid_name = "gg", block_prefix = "blk_"
1022 character(len=*), parameter :: amr_name = "mesh", meshdir = "data"
1023 character(len=100), allocatable :: grid_list(:), grid_list_block(:)
1024 character(len=100), allocatable :: var_list(:, :), var_names(:)
1025 character(len=400) :: fname
1026 integer :: lvl, i, id, i_grid, iv, nc, n_grids_max
1027 integer :: n_cc, n_add, dbix, highest_lvl
1028 integer :: nx, nx_prev, ix
1029 integer :: n_cycle_val, ncurve, ncurve_add
1030 integer :: lo(ndim), hi(ndim), vlo(ndim), vhi(ndim)
1031 integer :: blo(ndim), bhi(ndim)
1032 logical :: lo_bnd(ndim), hi_bnd(ndim)
1033 integer, allocatable :: ids(:), nb_ids(:), icc_val(:)
1034 logical, allocatable :: box_done(:)
1035 real(dp) :: dr(ndim), r_min(ndim), time_val
1036 integer, allocatable :: box_list(dtimes(:)), new_box_list(dtimes(:))
1037 real(dp), allocatable :: var_data(dtimes(:),:), cc(dtimes(:), :)
1038#if NDIM > 1
1039 integer :: ny, ny_prev, iy
1040#endif
1041#if NDIM > 2
1042 integer :: nz, nz_prev, iz
1043#endif
1044
1045 if (.not. tree%ready) stop "Tree not ready"
1046 time_val = 0.0_dp; if (present(time)) time_val = time
1047 n_cycle_val = 0; if (present(n_cycle)) n_cycle_val = n_cycle
1048 n_add = 0; if (present(add_names)) n_add = size(add_names)
1049
1050 if (present(add_names) .neqv. present(add_vars)) &
1051 stop "af_write_silo: both arguments (add_names, add_vars) needed"
1052
1053 if (present(add_curve_names) .neqv. present(add_curve_dat)) &
1054 stop "af_write_silo: both arguments (add_curve_names, add_curve_dat) needed"
1055
1056 if (present(add_curve_names)) then
1057 if (size(add_curve_names, 1) .ne. size(add_curve_dat, 1)) &
1058 stop "af_write_silo: number of curve names and data do not agree"
1059 end if
1060
1061 if (present(ixs_cc)) then
1062 if (maxval(ixs_cc) > tree%n_var_cell .or. &
1063 minval(ixs_cc) < 1) stop "af_write_silo: wrong indices given (ixs_cc)"
1064 allocate(icc_val(size(ixs_cc)))
1065 icc_val = ixs_cc
1066 else
1067 call get_output_vars(tree, icc_val)
1068 end if
1069
1070 highest_lvl = tree%highest_lvl
1071 if (present(max_lvl)) highest_lvl = min(max_lvl, tree%highest_lvl)
1072
1073 n_cc = size(icc_val)
1074
1075 allocate(var_names(n_cc+n_add))
1076 var_names(1:n_cc) = tree%cc_names(icc_val)
1077
1078 if (present(add_names)) then
1079 var_names(n_cc+1:n_cc+n_add) = add_names(:)
1080 end if
1081
1082 nc = tree%n_cell
1083 n_grids_max = 0
1084 do lvl = 1, highest_lvl
1085 if (lvl < highest_lvl) then
1086 n_grids_max = n_grids_max + size(tree%lvls(lvl)%leaves)
1087 else
1088 n_grids_max = n_grids_max + size(tree%lvls(lvl)%ids)
1089 end if
1090 end do
1091
1092 allocate(grid_list(n_grids_max))
1093 allocate(grid_list_block(n_grids_max))
1094 allocate(var_list(n_cc+n_add, n_grids_max))
1095 allocate(box_done(tree%highest_id))
1096 box_done = .false.
1097
1098 allocate(cc(dtimes(0:nc+1), n_cc + n_add))
1099
1100 fname = trim(filename) // ".silo"
1101 call silo_create_file(trim(fname), dbix)
1102 call silo_set_time_varying(dbix)
1103 call silo_mkdir(dbix, meshdir)
1104
1105 ! Adding additional curve objects
1106 if (present(add_curve_names)) then
1107 ncurve_add = size(add_curve_dat, 1)
1108 do ncurve = 1, ncurve_add
1109 call silo_add_curve(dbix, add_curve_names(ncurve), &
1110 add_curve_dat(ncurve, 1, :), add_curve_dat(ncurve, 2, :))
1111 end do
1112 end if
1113
1114 i_grid = 0
1115
1116 do lvl = 1, highest_lvl
1117 do i = 1, size(tree%lvls(lvl)%ids)
1118 id = tree%lvls(lvl)%ids(i)
1119
1120 if (box_done(id)) cycle
1121
1122 ! Skip non-leaf boxes except for at the highest level
1123 if (lvl < highest_lvl .and. af_has_children(tree%boxes(id))) cycle
1124
1125 i_grid = i_grid + 1
1126
1127 ! Find largest rectangular box including id and other leaves that
1128 ! haven't been written yet
1129#if NDIM == 1
1130 allocate(box_list(1))
1131 box_list(1) = id
1132 box_done(id) = .true.
1133 nx = 1
1134
1135 do
1136 nx_prev = nx
1137
1138 ! Check whether we can extend to the -x direction
1139 ids = box_list(1:1)
1140 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowx)
1141 if (all(nb_ids > af_no_box)) then
1142 if (.not. any(box_done(nb_ids)) .and. &
1143 tree%boxes(nb_ids(1))%ix(1) < tree%boxes(ids(1))%ix(1) .and. &
1144 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1145 nx = nx + 1
1146 allocate(new_box_list(nx))
1147 new_box_list(1:1) = nb_ids
1148 new_box_list(2:) = box_list
1149 box_list = new_box_list
1150 box_done(nb_ids) = .true.
1151 deallocate(new_box_list)
1152 end if
1153 end if
1154
1155 ! Check whether we can extend to the +x direction
1156 ids = box_list(nx:nx)
1157 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highx)
1158 if (all(nb_ids > af_no_box)) then
1159 if (.not. any(box_done(nb_ids)) .and. &
1160 tree%boxes(nb_ids(1))%ix(1) > tree%boxes(ids(1))%ix(1) .and. &
1161 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1162 nx = nx + 1
1163 allocate(new_box_list(nx))
1164 new_box_list(nx:nx) = nb_ids
1165 new_box_list(1:nx-1) = box_list
1166 box_list = new_box_list
1167 box_done(nb_ids) = .true.
1168 deallocate(new_box_list)
1169 end if
1170 end if
1171
1172 if (nx == nx_prev) exit
1173 end do
1174
1175 ! Check for (periodic) boundaries (this could give problems for
1176 ! complex geometries, e.g. a triangle block)
1177 id = box_list(1)
1178 lo_bnd = af_is_phys_boundary(tree%boxes, id, af_low_neighbs)
1179
1180 id = box_list(nx)
1181 hi_bnd = af_is_phys_boundary(tree%boxes, id, af_high_neighbs)
1182
1183 lo(:) = 1
1184 where (.not. lo_bnd) lo = lo - 1
1185
1186 hi = [nx] * nc
1187 where (.not. hi_bnd) hi = hi + 1
1188
1189 ! Include ghost cells around internal boundaries
1190 allocate(var_data(lo(1):hi(1), n_cc+n_add))
1191
1192 do ix = 1, nx
1193 id = box_list(ix)
1194
1195 cc(:, 1:n_cc) = tree%boxes(id)%cc(:, icc_val)
1196 if (present(add_vars)) then
1197 call add_vars(tree%boxes(id), &
1198 cc(:, n_cc+1:n_cc+n_add), n_add)
1199 end if
1200
1201 ! Include ghost cells on internal block boundaries
1202 blo = 1
1203 where ([ix] == 1 .and. .not. lo_bnd) blo = 0
1204
1205 bhi = nc
1206 where ([ix] == [nx] .and. .not. hi_bnd) bhi = nc+1
1207
1208 vlo = blo + ([ix]-1) * nc
1209 vhi = bhi + ([ix]-1) * nc
1210
1211 var_data(vlo(1):vhi(1), :) = cc(blo(1):bhi(1), :)
1212 end do
1213
1214 id = box_list(1)
1215 dr = tree%boxes(id)%dr
1216 r_min = tree%boxes(id)%r_min - (1 - lo) * dr
1217
1218 write(grid_list(i_grid), "(A,I0)") meshdir // '/' // grid_name, i_grid
1219 call silo_add_grid(dbix, grid_list(i_grid), 1, &
1220 hi - lo + 2, r_min, dr, 1-lo, hi - [nx] * nc)
1221 write(grid_list_block(i_grid), "(A,I0)") meshdir // '/' // block_prefix &
1222 // grid_name, i_grid
1223 call silo_add_grid(dbix, grid_list_block(i_grid), 1, [nx+1], &
1224 tree%boxes(id)%r_min, nc*dr, [0], [0])
1225
1226 do iv = 1, n_cc+n_add
1227 write(var_list(iv, i_grid), "(A,I0)") meshdir // '/' // &
1228 trim(var_names(iv)) // "_", i_grid
1229 call silo_add_var(dbix, var_list(iv, i_grid), grid_list(i_grid), &
1230 pack(var_data(:, iv), .true.), hi-lo+1)
1231 end do
1232
1233 deallocate(var_data)
1234 deallocate(box_list)
1235#elif NDIM == 2
1236 allocate(box_list(1,1))
1237 box_list(1,1) = id
1238 box_done(id) = .true.
1239 nx = 1
1240 ny = 1
1241
1242 do
1243 nx_prev = nx
1244 ny_prev = ny
1245
1246 ! Check whether we can extend to the -x direction
1247 ids = box_list(1, :)
1248 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowx)
1249 if (all(nb_ids > af_no_box)) then
1250 if (.not. any(box_done(nb_ids)) .and. &
1251 tree%boxes(nb_ids(1))%ix(1) < tree%boxes(ids(1))%ix(1) .and. &
1252 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1253 nx = nx + 1
1254 allocate(new_box_list(nx, ny))
1255 new_box_list(1, :) = nb_ids
1256 new_box_list(2:, :) = box_list
1257 box_list = new_box_list
1258 box_done(nb_ids) = .true.
1259 deallocate(new_box_list)
1260 end if
1261 end if
1262
1263 ! Check whether we can extend to the +x direction
1264 ids = box_list(nx, :)
1265 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highx)
1266 if (all(nb_ids > af_no_box)) then
1267 if (.not. any(box_done(nb_ids)) .and. &
1268 tree%boxes(nb_ids(1))%ix(1) > tree%boxes(ids(1))%ix(1) .and. &
1269 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1270 nx = nx + 1
1271 allocate(new_box_list(nx, ny))
1272 new_box_list(nx, :) = nb_ids
1273 new_box_list(1:nx-1, :) = box_list
1274 box_list = new_box_list
1275 box_done(nb_ids) = .true.
1276 deallocate(new_box_list)
1277 end if
1278 end if
1279
1280 ! Check whether we can extend to the -y direction
1281 ids = box_list(:, 1)
1282 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowy)
1283 if (all(nb_ids > af_no_box)) then
1284 if (.not. any(box_done(nb_ids)) .and. &
1285 tree%boxes(nb_ids(1))%ix(2) < tree%boxes(ids(1))%ix(2) .and. &
1286 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1287 ny = ny + 1
1288 allocate(new_box_list(nx, ny))
1289 new_box_list(:, 1) = nb_ids
1290 new_box_list(:, 2:) = box_list
1291 box_list = new_box_list
1292 box_done(nb_ids) = .true.
1293 deallocate(new_box_list)
1294 end if
1295 end if
1296
1297 ! Check whether we can extend to the +y direction
1298 ids = box_list(:, ny)
1299 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highy)
1300 if (all(nb_ids > af_no_box)) then
1301 if (.not. any(box_done(nb_ids)) .and. &
1302 tree%boxes(nb_ids(1))%ix(2) > tree%boxes(ids(1))%ix(2) .and. &
1303 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1304 ny = ny + 1
1305 allocate(new_box_list(nx, ny))
1306 new_box_list(:, ny) = nb_ids
1307 new_box_list(:, 1:ny-1) = box_list
1308 box_list = new_box_list
1309 box_done(nb_ids) = .true.
1310 deallocate(new_box_list)
1311 end if
1312 end if
1313
1314 if (nx == nx_prev .and. ny == ny_prev) exit
1315 end do
1316
1317 ! Check for (periodic) boundaries (this could give problems for
1318 ! complex geometries, e.g. a triangle block)
1319 id = box_list(1, 1)
1320 lo_bnd = af_is_phys_boundary(tree%boxes, id, af_low_neighbs)
1321
1322 id = box_list(nx, ny)
1323 hi_bnd = af_is_phys_boundary(tree%boxes, id, af_high_neighbs)
1324
1325 lo(:) = 1
1326 where (.not. lo_bnd) lo = lo - 1
1327
1328 hi = [nx, ny] * nc
1329 where (.not. hi_bnd) hi = hi + 1
1330
1331 ! Include ghost cells around internal boundaries
1332 allocate(var_data(lo(1):hi(1), lo(2):hi(2), n_cc+n_add))
1333
1334 do ix = 1, nx
1335 do iy = 1, ny
1336 id = box_list(ix, iy)
1337
1338 cc(:, :, 1:n_cc) = tree%boxes(id)%cc(:, :, icc_val)
1339 if (present(add_vars)) then
1340 call add_vars(tree%boxes(id), &
1341 cc(:, :, n_cc+1:n_cc+n_add), n_add)
1342 end if
1343
1344 ! Include ghost cells on internal block boundaries
1345 blo = 1
1346 where ([ix, iy] == 1 .and. .not. lo_bnd) blo = 0
1347
1348 bhi = nc
1349 where ([ix, iy] == [nx, ny] .and. .not. hi_bnd) bhi = nc+1
1350
1351 vlo = blo + ([ix, iy]-1) * nc
1352 vhi = bhi + ([ix, iy]-1) * nc
1353
1354 var_data(vlo(1):vhi(1), vlo(2):vhi(2), :) = &
1355 cc(blo(1):bhi(1), blo(2):bhi(2), :)
1356 end do
1357 end do
1358
1359 id = box_list(1, 1)
1360 dr = tree%boxes(id)%dr
1361 r_min = tree%boxes(id)%r_min - (1 - lo) * dr
1362
1363 write(grid_list(i_grid), "(A,I0)") meshdir // '/' // grid_name, i_grid
1364 call silo_add_grid(dbix, grid_list(i_grid), 2, &
1365 hi - lo + 2, r_min, dr, 1-lo, hi - [nx, ny] * nc, n_cycle_val)
1366 write(grid_list_block(i_grid), "(A,I0)") meshdir // '/' // block_prefix &
1367 // grid_name, i_grid
1368 call silo_add_grid(dbix, grid_list_block(i_grid), 2, [nx+1, ny+1], &
1369 tree%boxes(id)%r_min, nc*dr, [0, 0], [0, 0], n_cycle_val)
1370
1371 do iv = 1, n_cc+n_add
1372 write(var_list(iv, i_grid), "(A,I0)") meshdir // '/' // &
1373 trim(var_names(iv)) // "_", i_grid
1374 call silo_add_var(dbix, var_list(iv, i_grid), grid_list(i_grid), &
1375 pack(var_data(:, :, iv), .true.), hi-lo+1, n_cycle_val)
1376 end do
1377
1378 deallocate(var_data)
1379 deallocate(box_list)
1380#elif NDIM == 3
1381 allocate(box_list(1,1,1))
1382 box_list(1,1,1) = id
1383 box_done(id) = .true.
1384 nx = 1
1385 ny = 1
1386 nz = 1
1387
1388 do
1389 nx_prev = nx
1390 ny_prev = ny
1391 nz_prev = nz
1392
1393 ! Check whether we can extend to the -x direction
1394 ids = pack(box_list(1, :, :), .true.)
1395 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowx)
1396 if (all(nb_ids > af_no_box)) then
1397 if (.not. any(box_done(nb_ids)) .and. &
1398 tree%boxes(nb_ids(1))%ix(1) < tree%boxes(ids(1))%ix(1) .and. &
1399 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1400 nx = nx + 1
1401 allocate(new_box_list(nx, ny, nz))
1402 new_box_list(1, :, :) = reshape(nb_ids, [ny, nz])
1403 new_box_list(2:, :, :) = box_list
1404 box_list = new_box_list
1405 box_done(nb_ids) = .true.
1406 deallocate(new_box_list)
1407 end if
1408 end if
1409
1410 ! Check whether we can extend to the +x direction
1411 ids = pack(box_list(nx, :, :), .true.)
1412 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highx)
1413 if (all(nb_ids > af_no_box)) then
1414 if (.not. any(box_done(nb_ids)) .and. &
1415 tree%boxes(nb_ids(1))%ix(1) > tree%boxes(ids(1))%ix(1) .and. &
1416 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1417 nx = nx + 1
1418 allocate(new_box_list(nx, ny, nz))
1419 new_box_list(nx, :, :) = reshape(nb_ids, [ny, nz])
1420 new_box_list(1:nx-1, :, :) = box_list
1421 box_list = new_box_list
1422 box_done(nb_ids) = .true.
1423 deallocate(new_box_list)
1424 end if
1425 end if
1426
1427 ! Check whether we can extend to the -y direction
1428 ids = pack(box_list(:, 1, :), .true.)
1429 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowy)
1430 if (all(nb_ids > af_no_box)) then
1431 if (.not. any(box_done(nb_ids)) .and. &
1432 tree%boxes(nb_ids(1))%ix(2) < tree%boxes(ids(1))%ix(2) .and. &
1433 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1434 ny = ny + 1
1435 allocate(new_box_list(nx, ny, nz))
1436 new_box_list(:, 1, :) = reshape(nb_ids, [nx, nz])
1437 new_box_list(:, 2:, :) = box_list
1438 box_list = new_box_list
1439 box_done(nb_ids) = .true.
1440 deallocate(new_box_list)
1441 end if
1442 end if
1443
1444 ! Check whether we can extend to the +y direction
1445 ids = pack(box_list(:, ny, :), .true.)
1446 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highy)
1447 if (all(nb_ids > af_no_box)) then
1448 if (.not. any(box_done(nb_ids)) .and. &
1449 tree%boxes(nb_ids(1))%ix(2) > tree%boxes(ids(1))%ix(2) .and. &
1450 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1451 ny = ny + 1
1452 allocate(new_box_list(nx, ny, nz))
1453 new_box_list(:, ny, :) = reshape(nb_ids, [nx, nz])
1454 new_box_list(:, 1:ny-1, :) = box_list
1455 box_list = new_box_list
1456 box_done(nb_ids) = .true.
1457 deallocate(new_box_list)
1458 end if
1459 end if
1460
1461 ! Check whether we can extend to the -z direction
1462 ids = pack(box_list(:, :, 1), .true.)
1463 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowz)
1464 if (all(nb_ids > af_no_box)) then
1465 if (.not. any(box_done(nb_ids)) .and. &
1466 tree%boxes(nb_ids(1))%ix(3) < tree%boxes(ids(1))%ix(3) .and. &
1467 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1468 nz = nz + 1
1469 allocate(new_box_list(nx, ny, nz))
1470 new_box_list(:, :, 1) = reshape(nb_ids, [nx, ny])
1471 new_box_list(:, :, 2:) = box_list
1472 box_list = new_box_list
1473 box_done(nb_ids) = .true.
1474 deallocate(new_box_list)
1475 end if
1476 end if
1477
1478 ! Check whether we can extend to the +z direction
1479 ids = pack(box_list(:, :, nz), .true.)
1480 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highz)
1481 if (all(nb_ids > af_no_box)) then
1482 if (.not. any(box_done(nb_ids)) .and. &
1483 tree%boxes(nb_ids(1))%ix(3) > tree%boxes(ids(1))%ix(3) .and. &
1484 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1485 nz = nz + 1
1486 allocate(new_box_list(nx, ny, nz))
1487 new_box_list(:, :, nz) = reshape(nb_ids, [nx, ny])
1488 new_box_list(:, :, 1:nz-1) = box_list
1489 box_list = new_box_list
1490 box_done(nb_ids) = .true.
1491 deallocate(new_box_list)
1492 end if
1493 end if
1494
1495 if (nx == nx_prev .and. ny == ny_prev .and. nz == nz_prev) exit
1496 end do
1497
1498 ! Check for (periodic) boundaries (this could give problems for
1499 ! complex geometries, e.g. a triangle block)
1500 id = box_list(1, 1, 1)
1501 lo_bnd = af_is_phys_boundary(tree%boxes, id, af_low_neighbs)
1502
1503 id = box_list(nx, ny, nz)
1504 hi_bnd = af_is_phys_boundary(tree%boxes, id, af_high_neighbs)
1505
1506 lo(:) = 1
1507 where (.not. lo_bnd) lo = lo - 1
1508
1509 hi = [nx, ny, nz] * nc
1510 where (.not. hi_bnd) hi = hi + 1
1511
1512 ! Include ghost cells around internal boundaries
1513 allocate(var_data(lo(1):hi(1), lo(2):hi(2), lo(3):hi(3), n_cc+n_add))
1514
1515 do iz = 1, nz
1516 do ix = 1, nx
1517 do iy = 1, ny
1518 id = box_list(ix, iy, iz)
1519 cc(:, :, :, 1:n_cc) = tree%boxes(id)%cc(:, :, :, icc_val)
1520 if (present(add_vars)) then
1521 call add_vars(tree%boxes(id), &
1522 cc(:, :, :, n_cc+1:n_cc+n_add), n_add)
1523 end if
1524
1525 ! Include ghost cells on internal block boundaries
1526 blo = 1
1527 where ([ix, iy, iz] == 1 .and. .not. lo_bnd) blo = 0
1528
1529 bhi = nc
1530 where ([ix, iy, iz] == [nx, ny, nz] &
1531 .and. .not. hi_bnd) bhi = nc+1
1532
1533 vlo = blo + ([ix, iy, iz]-1) * nc
1534 vhi = bhi + ([ix, iy, iz]-1) * nc
1535
1536 var_data(vlo(1):vhi(1), vlo(2):vhi(2), vlo(3):vhi(3), :) = &
1537 cc(blo(1):bhi(1), blo(2):bhi(2), blo(3):bhi(3), :)
1538 end do
1539 end do
1540 end do
1541
1542 id = box_list(1, 1, 1)
1543 dr = tree%boxes(id)%dr
1544 r_min = tree%boxes(id)%r_min - (1 - lo) * dr
1545
1546 write(grid_list(i_grid), "(A,I0)") meshdir // '/' // grid_name, i_grid
1547 call silo_add_grid(dbix, grid_list(i_grid), 3, &
1548 hi - lo + 2, r_min, dr, 1-lo, hi-[nx, ny, nz]*nc, n_cycle_val)
1549 write(grid_list_block(i_grid), "(A,I0)") meshdir // '/' // block_prefix // &
1550 grid_name, i_grid
1551 call silo_add_grid(dbix, grid_list_block(i_grid), 3, [nx+1, ny+1, nz+1], &
1552 tree%boxes(id)%r_min, nc*dr, [0, 0, 0], [0, 0, 0], n_cycle_val)
1553
1554 do iv = 1, n_cc+n_add
1555 write(var_list(iv, i_grid), "(A,I0)") meshdir // '/' // &
1556 trim(var_names(iv)) // "_", i_grid
1557 call silo_add_var(dbix, var_list(iv, i_grid), grid_list(i_grid), &
1558 pack(var_data(:, :, :, iv), .true.), hi-lo+1, n_cycle_val)
1559 end do
1560
1561 deallocate(var_data)
1562 deallocate(box_list)
1563#endif
1564
1565 end do
1566 end do
1567
1568 call silo_set_mmesh_grid(dbix, amr_name, grid_list(1:i_grid), &
1569 n_cycle_val, time_val)
1570 do iv = 1, n_cc+n_add
1571 call silo_set_mmesh_var(dbix, trim(var_names(iv)), amr_name, &
1572 var_list(iv, 1:i_grid), n_cycle_val, time_val)
1573 end do
1574
1575 call silo_set_mmesh_grid(dbix, block_prefix // amr_name, &
1576 grid_list_block(1:i_grid), n_cycle_val, time_val)
1577 call silo_close_file(dbix)
1578 print *, "af_write_silo: written " // trim(fname)
1579 end subroutine af_write_silo
1580#endif
1581
1582 subroutine get_output_vars(tree, ix_out)
1583 type(af_t), intent(in) :: tree
1584 integer, allocatable, intent(inout) :: ix_out(:)
1585 integer :: n, i
1586
1587 n = count(tree%cc_write_output(1:tree%n_var_cell))
1588 allocate(ix_out(n))
1589
1590 n = 0
1591 do i = 1, tree%n_var_cell
1592 if (tree%cc_write_output(i)) then
1593 n = n + 1
1594 ix_out(n) = i
1595 end if
1596 end do
1597 end subroutine get_output_vars
1598
1599end module m_af_output
This module contains the core routines of Afivo, namely those that deal with initializing and changin...
Definition: m_af_core.f90:3
This module contains routines related to interpolation, which can interpolate 'to' the grid and 'from...
Definition: m_af_interp.f90:4
This module contains routines for writing output files with Afivo. The Silo format should probably be...
Definition: m_af_output.f90:3
This module contains the basic types and constants that are used in the NDIM-dimensional version of A...
Definition: m_af_types.f90:3
Definition: m_npy.f90:1
This file is a modification of code found in Lib_VTK_IO.
Definition: m_vtk.f90:9
This module contains wrapper functions to simplify writing Silo files.
Definition: m_write_silo.f90:3
Type which stores all the boxes and levels, as well as some information about the number of boxes,...
Definition: m_af_types.f90:326
The basic building block of afivo: a box with cell-centered and face centered data,...
Definition: m_af_types.f90:286