Afivo 0.3
Loading...
Searching...
No Matches
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 n_grids_max = n_grids_max + size(tree%lvls(lvl)%leaves)
1086 end do
1087
1088 allocate(grid_list(n_grids_max))
1089 allocate(grid_list_block(n_grids_max))
1090 allocate(var_list(n_cc+n_add, n_grids_max))
1091 allocate(box_done(tree%highest_id))
1092 box_done = .false.
1093
1094 allocate(cc(dtimes(0:nc+1), n_cc + n_add))
1095
1096 fname = trim(filename) // ".silo"
1097 call silo_create_file(trim(fname), dbix)
1098 call silo_set_time_varying(dbix)
1099 call silo_mkdir(dbix, meshdir)
1100
1101 ! Adding additional curve objects
1102 if (present(add_curve_names)) then
1103 ncurve_add = size(add_curve_dat, 1)
1104 do ncurve = 1, ncurve_add
1105 call silo_add_curve(dbix, add_curve_names(ncurve), &
1106 add_curve_dat(ncurve, 1, :), add_curve_dat(ncurve, 2, :))
1107 end do
1108 end if
1109
1110 i_grid = 0
1111
1112 do lvl = 1, highest_lvl
1113 do i = 1, size(tree%lvls(lvl)%leaves)
1114 id = tree%lvls(lvl)%leaves(i)
1115
1116 if (box_done(id)) cycle
1117
1118 i_grid = i_grid + 1
1119
1120 ! Find largest rectangular box including id and other leaves that
1121 ! haven't been written yet
1122#if NDIM == 1
1123 allocate(box_list(1))
1124 box_list(1) = id
1125 box_done(id) = .true.
1126 nx = 1
1127
1128 do
1129 nx_prev = nx
1130
1131 ! Check whether we can extend to the -x direction
1132 ids = box_list(1:1)
1133 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowx)
1134 if (all(nb_ids > af_no_box)) then
1135 if (.not. any(box_done(nb_ids)) .and. &
1136 tree%boxes(nb_ids(1))%ix(1) < tree%boxes(ids(1))%ix(1) .and. &
1137 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1138 nx = nx + 1
1139 allocate(new_box_list(nx))
1140 new_box_list(1:1) = nb_ids
1141 new_box_list(2:) = box_list
1142 box_list = new_box_list
1143 box_done(nb_ids) = .true.
1144 deallocate(new_box_list)
1145 end if
1146 end if
1147
1148 ! Check whether we can extend to the +x direction
1149 ids = box_list(nx:nx)
1150 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highx)
1151 if (all(nb_ids > af_no_box)) then
1152 if (.not. any(box_done(nb_ids)) .and. &
1153 tree%boxes(nb_ids(1))%ix(1) > tree%boxes(ids(1))%ix(1) .and. &
1154 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1155 nx = nx + 1
1156 allocate(new_box_list(nx))
1157 new_box_list(nx:nx) = nb_ids
1158 new_box_list(1:nx-1) = box_list
1159 box_list = new_box_list
1160 box_done(nb_ids) = .true.
1161 deallocate(new_box_list)
1162 end if
1163 end if
1164
1165 if (nx == nx_prev) exit
1166 end do
1167
1168 ! Check for (periodic) boundaries (this could give problems for
1169 ! complex geometries, e.g. a triangle block)
1170 id = box_list(1)
1171 lo_bnd = af_is_phys_boundary(tree%boxes, id, af_low_neighbs)
1172
1173 id = box_list(nx)
1174 hi_bnd = af_is_phys_boundary(tree%boxes, id, af_high_neighbs)
1175
1176 lo(:) = 1
1177 where (.not. lo_bnd) lo = lo - 1
1178
1179 hi = [nx] * nc
1180 where (.not. hi_bnd) hi = hi + 1
1181
1182 ! Include ghost cells around internal boundaries
1183 allocate(var_data(lo(1):hi(1), n_cc+n_add))
1184
1185 do ix = 1, nx
1186 id = box_list(ix)
1187
1188 cc(:, 1:n_cc) = tree%boxes(id)%cc(:, icc_val)
1189 if (present(add_vars)) then
1190 call add_vars(tree%boxes(id), &
1191 cc(:, n_cc+1:n_cc+n_add), n_add)
1192 end if
1193
1194 ! Include ghost cells on internal block boundaries
1195 blo = 1
1196 where ([ix] == 1 .and. .not. lo_bnd) blo = 0
1197
1198 bhi = nc
1199 where ([ix] == [nx] .and. .not. hi_bnd) bhi = nc+1
1200
1201 vlo = blo + ([ix]-1) * nc
1202 vhi = bhi + ([ix]-1) * nc
1203
1204 var_data(vlo(1):vhi(1), :) = cc(blo(1):bhi(1), :)
1205 end do
1206
1207 id = box_list(1)
1208 dr = tree%boxes(id)%dr
1209 r_min = tree%boxes(id)%r_min - (1 - lo) * dr
1210
1211 write(grid_list(i_grid), "(A,I0)") meshdir // '/' // grid_name, i_grid
1212 call silo_add_grid(dbix, grid_list(i_grid), 1, &
1213 hi - lo + 2, r_min, dr, 1-lo, hi - [nx] * nc)
1214 write(grid_list_block(i_grid), "(A,I0)") meshdir // '/' // block_prefix &
1215 // grid_name, i_grid
1216 call silo_add_grid(dbix, grid_list_block(i_grid), 1, [nx+1], &
1217 tree%boxes(id)%r_min, nc*dr, [0], [0])
1218
1219 do iv = 1, n_cc+n_add
1220 write(var_list(iv, i_grid), "(A,I0)") meshdir // '/' // &
1221 trim(var_names(iv)) // "_", i_grid
1222 call silo_add_var(dbix, var_list(iv, i_grid), grid_list(i_grid), &
1223 var_data(:, iv), hi-lo+1)
1224 end do
1225
1226 deallocate(var_data)
1227 deallocate(box_list)
1228#elif NDIM == 2
1229 allocate(box_list(1,1))
1230 box_list(1,1) = id
1231 box_done(id) = .true.
1232 nx = 1
1233 ny = 1
1234
1235 do
1236 nx_prev = nx
1237 ny_prev = ny
1238
1239 ! Check whether we can extend to the -x direction
1240 ids = box_list(1, :)
1241 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowx)
1242 if (all(nb_ids > af_no_box)) then
1243 if (.not. any(box_done(nb_ids)) .and. &
1244 tree%boxes(nb_ids(1))%ix(1) < tree%boxes(ids(1))%ix(1) .and. &
1245 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1246 nx = nx + 1
1247 allocate(new_box_list(nx, ny))
1248 new_box_list(1, :) = nb_ids
1249 new_box_list(2:, :) = box_list
1250 box_list = new_box_list
1251 box_done(nb_ids) = .true.
1252 deallocate(new_box_list)
1253 end if
1254 end if
1255
1256 ! Check whether we can extend to the +x direction
1257 ids = box_list(nx, :)
1258 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highx)
1259 if (all(nb_ids > af_no_box)) then
1260 if (.not. any(box_done(nb_ids)) .and. &
1261 tree%boxes(nb_ids(1))%ix(1) > tree%boxes(ids(1))%ix(1) .and. &
1262 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1263 nx = nx + 1
1264 allocate(new_box_list(nx, ny))
1265 new_box_list(nx, :) = nb_ids
1266 new_box_list(1:nx-1, :) = box_list
1267 box_list = new_box_list
1268 box_done(nb_ids) = .true.
1269 deallocate(new_box_list)
1270 end if
1271 end if
1272
1273 ! Check whether we can extend to the -y direction
1274 ids = box_list(:, 1)
1275 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowy)
1276 if (all(nb_ids > af_no_box)) then
1277 if (.not. any(box_done(nb_ids)) .and. &
1278 tree%boxes(nb_ids(1))%ix(2) < tree%boxes(ids(1))%ix(2) .and. &
1279 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1280 ny = ny + 1
1281 allocate(new_box_list(nx, ny))
1282 new_box_list(:, 1) = nb_ids
1283 new_box_list(:, 2:) = box_list
1284 box_list = new_box_list
1285 box_done(nb_ids) = .true.
1286 deallocate(new_box_list)
1287 end if
1288 end if
1289
1290 ! Check whether we can extend to the +y direction
1291 ids = box_list(:, ny)
1292 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highy)
1293 if (all(nb_ids > af_no_box)) then
1294 if (.not. any(box_done(nb_ids)) .and. &
1295 tree%boxes(nb_ids(1))%ix(2) > tree%boxes(ids(1))%ix(2) .and. &
1296 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1297 ny = ny + 1
1298 allocate(new_box_list(nx, ny))
1299 new_box_list(:, ny) = nb_ids
1300 new_box_list(:, 1:ny-1) = box_list
1301 box_list = new_box_list
1302 box_done(nb_ids) = .true.
1303 deallocate(new_box_list)
1304 end if
1305 end if
1306
1307 if (nx == nx_prev .and. ny == ny_prev) exit
1308 end do
1309
1310 ! Check for (periodic) boundaries (this could give problems for
1311 ! complex geometries, e.g. a triangle block)
1312 id = box_list(1, 1)
1313 lo_bnd = af_is_phys_boundary(tree%boxes, id, af_low_neighbs)
1314
1315 id = box_list(nx, ny)
1316 hi_bnd = af_is_phys_boundary(tree%boxes, id, af_high_neighbs)
1317
1318 lo(:) = 1
1319 where (.not. lo_bnd) lo = lo - 1
1320
1321 hi = [nx, ny] * nc
1322 where (.not. hi_bnd) hi = hi + 1
1323
1324 ! Include ghost cells around internal boundaries
1325 allocate(var_data(lo(1):hi(1), lo(2):hi(2), n_cc+n_add))
1326
1327 do ix = 1, nx
1328 do iy = 1, ny
1329 id = box_list(ix, iy)
1330
1331 cc(:, :, 1:n_cc) = tree%boxes(id)%cc(:, :, icc_val)
1332 if (present(add_vars)) then
1333 call add_vars(tree%boxes(id), &
1334 cc(:, :, n_cc+1:n_cc+n_add), n_add)
1335 end if
1336
1337 ! Include ghost cells on internal block boundaries
1338 blo = 1
1339 where ([ix, iy] == 1 .and. .not. lo_bnd) blo = 0
1340
1341 bhi = nc
1342 where ([ix, iy] == [nx, ny] .and. .not. hi_bnd) bhi = nc+1
1343
1344 vlo = blo + ([ix, iy]-1) * nc
1345 vhi = bhi + ([ix, iy]-1) * nc
1346
1347 var_data(vlo(1):vhi(1), vlo(2):vhi(2), :) = &
1348 cc(blo(1):bhi(1), blo(2):bhi(2), :)
1349 end do
1350 end do
1351
1352 id = box_list(1, 1)
1353 dr = tree%boxes(id)%dr
1354 r_min = tree%boxes(id)%r_min - (1 - lo) * dr
1355
1356 write(grid_list(i_grid), "(A,I0)") meshdir // '/' // grid_name, i_grid
1357 call silo_add_grid(dbix, grid_list(i_grid), 2, &
1358 hi - lo + 2, r_min, dr, 1-lo, hi - [nx, ny] * nc, n_cycle_val)
1359 write(grid_list_block(i_grid), "(A,I0)") meshdir // '/' // block_prefix &
1360 // grid_name, i_grid
1361 call silo_add_grid(dbix, grid_list_block(i_grid), 2, [nx+1, ny+1], &
1362 tree%boxes(id)%r_min, nc*dr, [0, 0], [0, 0], n_cycle_val)
1363
1364 do iv = 1, n_cc+n_add
1365 write(var_list(iv, i_grid), "(A,I0)") meshdir // '/' // &
1366 trim(var_names(iv)) // "_", i_grid
1367 call silo_add_var(dbix, var_list(iv, i_grid), grid_list(i_grid), &
1368 pack(var_data(:, :, iv), .true.), hi-lo+1, n_cycle_val)
1369 end do
1370
1371 deallocate(var_data)
1372 deallocate(box_list)
1373#elif NDIM == 3
1374 allocate(box_list(1,1,1))
1375 box_list(1,1,1) = id
1376 box_done(id) = .true.
1377 nx = 1
1378 ny = 1
1379 nz = 1
1380
1381 do
1382 nx_prev = nx
1383 ny_prev = ny
1384 nz_prev = nz
1385
1386 ! Check whether we can extend to the -x direction
1387 ids = pack(box_list(1, :, :), .true.)
1388 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowx)
1389 if (all(nb_ids > af_no_box)) then
1390 if (.not. any(box_done(nb_ids)) .and. &
1391 tree%boxes(nb_ids(1))%ix(1) < tree%boxes(ids(1))%ix(1) .and. &
1392 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1393 nx = nx + 1
1394 allocate(new_box_list(nx, ny, nz))
1395 new_box_list(1, :, :) = reshape(nb_ids, [ny, nz])
1396 new_box_list(2:, :, :) = box_list
1397 box_list = new_box_list
1398 box_done(nb_ids) = .true.
1399 deallocate(new_box_list)
1400 end if
1401 end if
1402
1403 ! Check whether we can extend to the +x direction
1404 ids = pack(box_list(nx, :, :), .true.)
1405 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highx)
1406 if (all(nb_ids > af_no_box)) then
1407 if (.not. any(box_done(nb_ids)) .and. &
1408 tree%boxes(nb_ids(1))%ix(1) > tree%boxes(ids(1))%ix(1) .and. &
1409 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1410 nx = nx + 1
1411 allocate(new_box_list(nx, ny, nz))
1412 new_box_list(nx, :, :) = reshape(nb_ids, [ny, nz])
1413 new_box_list(1:nx-1, :, :) = box_list
1414 box_list = new_box_list
1415 box_done(nb_ids) = .true.
1416 deallocate(new_box_list)
1417 end if
1418 end if
1419
1420 ! Check whether we can extend to the -y direction
1421 ids = pack(box_list(:, 1, :), .true.)
1422 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowy)
1423 if (all(nb_ids > af_no_box)) then
1424 if (.not. any(box_done(nb_ids)) .and. &
1425 tree%boxes(nb_ids(1))%ix(2) < tree%boxes(ids(1))%ix(2) .and. &
1426 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1427 ny = ny + 1
1428 allocate(new_box_list(nx, ny, nz))
1429 new_box_list(:, 1, :) = reshape(nb_ids, [nx, nz])
1430 new_box_list(:, 2:, :) = box_list
1431 box_list = new_box_list
1432 box_done(nb_ids) = .true.
1433 deallocate(new_box_list)
1434 end if
1435 end if
1436
1437 ! Check whether we can extend to the +y direction
1438 ids = pack(box_list(:, ny, :), .true.)
1439 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highy)
1440 if (all(nb_ids > af_no_box)) then
1441 if (.not. any(box_done(nb_ids)) .and. &
1442 tree%boxes(nb_ids(1))%ix(2) > tree%boxes(ids(1))%ix(2) .and. &
1443 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1444 ny = ny + 1
1445 allocate(new_box_list(nx, ny, nz))
1446 new_box_list(:, ny, :) = reshape(nb_ids, [nx, nz])
1447 new_box_list(:, 1:ny-1, :) = box_list
1448 box_list = new_box_list
1449 box_done(nb_ids) = .true.
1450 deallocate(new_box_list)
1451 end if
1452 end if
1453
1454 ! Check whether we can extend to the -z direction
1455 ids = pack(box_list(:, :, 1), .true.)
1456 nb_ids = tree%boxes(ids)%neighbors(af_neighb_lowz)
1457 if (all(nb_ids > af_no_box)) then
1458 if (.not. any(box_done(nb_ids)) .and. &
1459 tree%boxes(nb_ids(1))%ix(3) < tree%boxes(ids(1))%ix(3) .and. &
1460 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1461 nz = nz + 1
1462 allocate(new_box_list(nx, ny, nz))
1463 new_box_list(:, :, 1) = reshape(nb_ids, [nx, ny])
1464 new_box_list(:, :, 2:) = box_list
1465 box_list = new_box_list
1466 box_done(nb_ids) = .true.
1467 deallocate(new_box_list)
1468 end if
1469 end if
1470
1471 ! Check whether we can extend to the +z direction
1472 ids = pack(box_list(:, :, nz), .true.)
1473 nb_ids = tree%boxes(ids)%neighbors(af_neighb_highz)
1474 if (all(nb_ids > af_no_box)) then
1475 if (.not. any(box_done(nb_ids)) .and. &
1476 tree%boxes(nb_ids(1))%ix(3) > tree%boxes(ids(1))%ix(3) .and. &
1477 .not. any(af_has_children(tree%boxes(nb_ids)))) then
1478 nz = nz + 1
1479 allocate(new_box_list(nx, ny, nz))
1480 new_box_list(:, :, nz) = reshape(nb_ids, [nx, ny])
1481 new_box_list(:, :, 1:nz-1) = box_list
1482 box_list = new_box_list
1483 box_done(nb_ids) = .true.
1484 deallocate(new_box_list)
1485 end if
1486 end if
1487
1488 if (nx == nx_prev .and. ny == ny_prev .and. nz == nz_prev) exit
1489 end do
1490
1491 ! Check for (periodic) boundaries (this could give problems for
1492 ! complex geometries, e.g. a triangle block)
1493 id = box_list(1, 1, 1)
1494 lo_bnd = af_is_phys_boundary(tree%boxes, id, af_low_neighbs)
1495
1496 id = box_list(nx, ny, nz)
1497 hi_bnd = af_is_phys_boundary(tree%boxes, id, af_high_neighbs)
1498
1499 lo(:) = 1
1500 where (.not. lo_bnd) lo = lo - 1
1501
1502 hi = [nx, ny, nz] * nc
1503 where (.not. hi_bnd) hi = hi + 1
1504
1505 ! Include ghost cells around internal boundaries
1506 allocate(var_data(lo(1):hi(1), lo(2):hi(2), lo(3):hi(3), n_cc+n_add))
1507
1508 do iz = 1, nz
1509 do ix = 1, nx
1510 do iy = 1, ny
1511 id = box_list(ix, iy, iz)
1512 cc(:, :, :, 1:n_cc) = tree%boxes(id)%cc(:, :, :, icc_val)
1513 if (present(add_vars)) then
1514 call add_vars(tree%boxes(id), &
1515 cc(:, :, :, n_cc+1:n_cc+n_add), n_add)
1516 end if
1517
1518 ! Include ghost cells on internal block boundaries
1519 blo = 1
1520 where ([ix, iy, iz] == 1 .and. .not. lo_bnd) blo = 0
1521
1522 bhi = nc
1523 where ([ix, iy, iz] == [nx, ny, nz] &
1524 .and. .not. hi_bnd) bhi = nc+1
1525
1526 vlo = blo + ([ix, iy, iz]-1) * nc
1527 vhi = bhi + ([ix, iy, iz]-1) * nc
1528
1529 var_data(vlo(1):vhi(1), vlo(2):vhi(2), vlo(3):vhi(3), :) = &
1530 cc(blo(1):bhi(1), blo(2):bhi(2), blo(3):bhi(3), :)
1531 end do
1532 end do
1533 end do
1534
1535 id = box_list(1, 1, 1)
1536 dr = tree%boxes(id)%dr
1537 r_min = tree%boxes(id)%r_min - (1 - lo) * dr
1538
1539 write(grid_list(i_grid), "(A,I0)") meshdir // '/' // grid_name, i_grid
1540 call silo_add_grid(dbix, grid_list(i_grid), 3, &
1541 hi - lo + 2, r_min, dr, 1-lo, hi-[nx, ny, nz]*nc, n_cycle_val)
1542 write(grid_list_block(i_grid), "(A,I0)") meshdir // '/' // block_prefix // &
1543 grid_name, i_grid
1544 call silo_add_grid(dbix, grid_list_block(i_grid), 3, [nx+1, ny+1, nz+1], &
1545 tree%boxes(id)%r_min, nc*dr, [0, 0, 0], [0, 0, 0], n_cycle_val)
1546
1547 do iv = 1, n_cc+n_add
1548 write(var_list(iv, i_grid), "(A,I0)") meshdir // '/' // &
1549 trim(var_names(iv)) // "_", i_grid
1550 call silo_add_var(dbix, var_list(iv, i_grid), grid_list(i_grid), &
1551 pack(var_data(:, :, :, iv), .true.), hi-lo+1, n_cycle_val)
1552 end do
1553
1554 deallocate(var_data)
1555 deallocate(box_list)
1556#endif
1557
1558 end do
1559 end do
1560
1561 call silo_set_mmesh_grid(dbix, amr_name, grid_list(1:i_grid), &
1562 n_cycle_val, time_val)
1563 do iv = 1, n_cc+n_add
1564 call silo_set_mmesh_var(dbix, trim(var_names(iv)), amr_name, &
1565 var_list(iv, 1:i_grid), n_cycle_val, time_val)
1566 end do
1567
1568 call silo_set_mmesh_grid(dbix, block_prefix // amr_name, &
1569 grid_list_block(1:i_grid), n_cycle_val, time_val)
1570 call silo_close_file(dbix)
1571 print *, "af_write_silo: written " // trim(fname)
1572 end subroutine af_write_silo
1573#endif
1574
1575 subroutine get_output_vars(tree, ix_out)
1576 type(af_t), intent(in) :: tree
1577 integer, allocatable, intent(inout) :: ix_out(:)
1578 integer :: n, i
1579
1580 n = count(tree%cc_write_output(1:tree%n_var_cell))
1581 allocate(ix_out(n))
1582
1583 n = 0
1584 do i = 1, tree%n_var_cell
1585 if (tree%cc_write_output(i)) then
1586 n = n + 1
1587 ix_out(n) = i
1588 end if
1589 end do
1590 end subroutine get_output_vars
1591
1592end 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...
This module contains routines for writing output files with Afivo. The Silo format should probably be...
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.
Type which stores all the boxes and levels, as well as some information about the number of boxes,...
The basic building block of afivo: a box with cell-centered and face centered data,...