Neko 1.99.7
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
boundary_data_writer_simcomp.f90
Go to the documentation of this file.
1! Copyright (c) 2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
36 use num_types, only : rp
37 use json_module, only : json_file
41 use time_state, only : time_state_t
42 use case, only : case_t
43 use field, only : field_t
44 use field_list, only : field_list_t
45 use registry, only : neko_registry
46 use coefs, only : coef_t
47 use vector, only : vector_t
48 use device, only : device_to_host
49 use math, only : copy
50 use tensor, only : trsp
51 use matrix, only : matrix_t
53 use file, only : file_t
54 use csv_file, only : csv_file_t
55 use hdf5_file, only : hdf5_file_t
56 use ale_manager, only : neko_ale
57 use logger, only : neko_log, log_size
61 use mpi_f08, only : mpi_allreduce, mpi_gather, mpi_gatherv, mpi_exscan, &
62 mpi_integer, mpi_sum
63 implicit none
64 private
65
73 type(coef_t), pointer :: coef => null()
75 type(boundary_data_t) :: bdata
77 integer, allocatable :: zone_indices(:)
79 type(field_list_t) :: fields
81 character(len=NEKO_VARNAME_LEN), allocatable :: col_names(:)
83 type(vector_t) :: work
85 integer :: n_local = 0
87 integer :: n_global = 0
89 integer :: n_cols = 0
91 real(kind=rp), allocatable :: local_buffer(:,:)
93 real(kind=rp), allocatable :: local_buffer_t(:,:)
95 real(kind=rp), allocatable :: global_buffer(:,:)
97 integer, allocatable :: recvcounts(:)
99 integer, allocatable :: displs(:)
100 integer, allocatable :: recvcounts_c(:), displs_c(:)
102 type(matrix_t) :: mat_out
104 type(vector_t) :: vec_out
106 type(file_t) :: fout
108 real(kind=rp) :: start_time = 0.0_rp
110 logical :: mesh_has_changed = .false.
112 logical :: geometry_in_data = .false.
114 logical :: output_normals = .false.
116 logical :: output_area = .false.
119 logical :: append_out = .true.
120 contains
122 procedure, pass(this) :: init => boundary_data_writer_init_from_json
123 generic :: init_from_components => &
124 init_from_controllers, init_from_controllers_properties
125 procedure, pass(this) :: init_from_controllers => &
127 procedure, pass(this) :: init_from_controllers_properties => &
129 procedure, private, pass(this) :: init_common => &
132 procedure, pass(this) :: free => boundary_data_writer_free
134 procedure, pass(this) :: compute_ => boundary_data_writer_compute
136 procedure, private, pass(this) :: gather_column => &
139
140contains
141
145 subroutine boundary_data_writer_init_from_json(this, json, case)
146 class(boundary_data_writer_t), intent(inout), target :: this
147 type(json_file), intent(inout) :: json
148 class(case_t), intent(inout), target :: case
149 character(len=:), allocatable :: name, output_filename
150 character(len=NEKO_VARNAME_LEN), allocatable :: fields(:)
151 integer, allocatable :: zone_indices(:)
152 real(kind=rp) :: start_time
153 logical :: output_normals
154 logical :: output_area
155 logical :: append_out
156 logical :: user_set_compute
157
158 call this%free()
159
160 call json_get_or_default(json, "name", name, "boundary_data_writer")
161
162 user_set_compute = json%valid_path("compute_control") .or. &
163 json%valid_path("compute_value")
164
165 call this%init_base(json, case)
166
167 if (user_set_compute) then
168 call neko_warning("boundary_data_writer: 'compute_control' and " // &
169 "'compute_value' are ignored. Use 'output_control' and " // &
170 "'output_value' to set how often the data is written.")
171 end if
172 call this%compute_controller%init(case%time%start_time, &
173 case%time%end_time, "tsteps", 1.0_rp)
174
175 call json_get(json, "zone_indices", zone_indices)
176 call json_get(json, "output_filename", output_filename)
177 call json_get_or_default(json, "output_normals", output_normals, .true.)
178 call json_get_or_default(json, "output_area", output_area, .true.)
179 call json_get_or_default(json, "append_output", append_out, .true.)
180 call json_get_or_default(json, "start_time", start_time, 0.0_rp)
181
182 call json_get(json, "fields", fields)
183
184 call this%init_common(name, case%fluid%c_Xh, zone_indices, fields, &
185 output_filename, output_normals, output_area, append_out, &
186 start_time)
187
189
205 subroutine boundary_data_writer_init_from_controllers(this, name, case, &
206 order, preprocess_controller, compute_controller, output_controller, &
207 coef, zone_indices, fields, output_filename, output_normals, &
208 output_area, append_out, start_time)
209 class(boundary_data_writer_t), intent(inout) :: this
210 character(len=*), intent(in) :: name
211 class(case_t), intent(inout), target :: case
212 integer, intent(in) :: order
213 type(time_based_controller_t), intent(in) :: preprocess_controller
214 type(time_based_controller_t), intent(in) :: compute_controller
215 type(time_based_controller_t), intent(in) :: output_controller
216 type(coef_t), intent(inout), target :: coef
217 integer, intent(in) :: zone_indices(:)
218 character(len=*), intent(in) :: fields(:)
219 character(len=*), intent(in) :: output_filename
220 logical, intent(in) :: output_normals
221 logical, intent(in) :: output_area
222 logical, intent(in) :: append_out
223 real(kind=rp), intent(in) :: start_time
224
225 call this%free()
226
227 call this%init_base_from_components(case, order, preprocess_controller, &
228 compute_controller, output_controller)
229 call this%init_common(name, coef, zone_indices, fields, &
230 output_filename, output_normals, output_area, append_out, start_time)
231
233
254 name, case, order, preprocess_control, preprocess_value, &
255 compute_control, compute_value, output_control, output_value, coef, &
256 zone_indices, fields, output_filename, output_normals, output_area, &
257 append_out, start_time)
258 class(boundary_data_writer_t), intent(inout) :: this
259 character(len=*), intent(in) :: name
260 class(case_t), intent(inout), target :: case
261 integer, intent(in) :: order
262 character(len=*), intent(in) :: preprocess_control
263 real(kind=rp), intent(in) :: preprocess_value
264 character(len=*), intent(in) :: compute_control
265 real(kind=rp), intent(in) :: compute_value
266 character(len=*), intent(in) :: output_control
267 real(kind=rp), intent(in) :: output_value
268 type(coef_t), intent(inout), target :: coef
269 integer, intent(in) :: zone_indices(:)
270 character(len=*), intent(in) :: fields(:)
271 character(len=*), intent(in) :: output_filename
272 logical, intent(in) :: output_normals
273 logical, intent(in) :: output_area
274 logical, intent(in) :: append_out
275 real(kind=rp), intent(in) :: start_time
276
277 call this%free()
278
279 call this%init_base_from_components(case, order, preprocess_control, &
280 preprocess_value, compute_control, compute_value, output_control, &
281 output_value)
282 call this%init_common(name, coef, zone_indices, fields, &
283 output_filename, output_normals, output_area, append_out, start_time)
284
286
297 subroutine boundary_data_writer_init_common(this, name, coef, zone_indices, &
298 fields, output_filename, output_normals, output_area, append_out, &
299 start_time)
300 class(boundary_data_writer_t), intent(inout) :: this
301 character(len=*), intent(in) :: name
302 type(coef_t), intent(inout), target :: coef
303 integer, intent(in) :: zone_indices(:)
304 character(len=*), intent(in) :: fields(:)
305 character(len=*), intent(in) :: output_filename
306 logical, intent(in) :: output_normals
307 logical, intent(in) :: output_area
308 logical, intent(in) :: append_out
309 real(kind=rp), intent(in) :: start_time
310 character(len=LOG_SIZE) :: log_buf
311 character(len=80) :: suffix
312 integer :: i, col, ierr, offset, n_geom
313 logical :: ale_enabled
314
315 this%name = name
316 this%coef => coef
317 this%start_time = start_time
318 this%output_normals = output_normals
319 this%output_area = output_area
320 this%append_out = append_out
321
322 ! Zones
323 allocate(this%zone_indices(size(zone_indices)))
324 this%zone_indices = zone_indices
325
326 ! Fields
327 if (size(fields) .eq. 0) then
328 call neko_error("boundary_data_writer requires at least one entry " // &
329 "in 'fields'")
330 end if
331
332 call this%fields%init(size(fields))
333 do i = 1, size(fields)
334 this%fields%items(i)%ptr => &
335 neko_registry%get_field_by_name(trim(fields(i)))
336 end do
337
338 ale_enabled = .false.
339 if (associated(neko_ale)) then
340 if (neko_ale%active) ale_enabled = .true.
341 end if
342
343 ! Whether the mesh geometry varies during the run.
344 this%mesh_has_changed = .false.
345 if (ale_enabled) then
346 this%mesh_has_changed = .true.
347 end if
348
349 ! When the mesh moves the geometry accompanies every sample, so that
350 ! each output carries the coordinates it corresponds to.
351 this%geometry_in_data = this%mesh_has_changed
352
353 ! only_facets
354 call this%bdata%init(this%coef, this%zone_indices)
355
356 this%n_local = this%bdata%n_local
357 this%n_global = this%bdata%n_global
358
359 call this%work%init(this%n_local)
360
361 if (neko_bcknd_device .eq. 1) then
362 call this%bdata%x%copy_from(device_to_host, .false.)
363 call this%bdata%y%copy_from(device_to_host, .false.)
364 call this%bdata%z%copy_from(device_to_host, .false.)
365 call this%bdata%n_x%copy_from(device_to_host, .false.)
366 call this%bdata%n_y%copy_from(device_to_host, .false.)
367 call this%bdata%n_z%copy_from(device_to_host, .false.)
368 call this%bdata%area%copy_from(device_to_host, .true.)
369 end if
370
371 ! Column layout
372 n_geom = 0
373 if (this%geometry_in_data) then
374 n_geom = 3
375 if (this%output_normals) n_geom = n_geom + 3
376 if (this%output_area) n_geom = n_geom + 1
377 end if
378
379 this%n_cols = n_geom + this%fields%size()
380
381 allocate(this%col_names(this%n_cols))
382 col = 0
383 if (this%geometry_in_data) then
384 col = col + 1
385 this%col_names(col) = "x"
386 col = col + 1
387 this%col_names(col) = "y"
388 col = col + 1
389 this%col_names(col) = "z"
390 if (this%output_normals) then
391 col = col + 1
392 this%col_names(col) = "n_x"
393 col = col + 1
394 this%col_names(col) = "n_y"
395 col = col + 1
396 this%col_names(col) = "n_z"
397 end if
398 if (this%output_area) then
399 col = col + 1
400 this%col_names(col) = "area"
401 end if
402 end if
403 do i = 1, this%fields%size()
404 col = col + 1
405 this%col_names(col) = trim(fields(i))
406 end do
407
408 allocate(this%local_buffer(this%n_local, this%n_cols))
409 this%local_buffer = 0.0_rp
410
411 ! For CSV.
412 allocate(this%local_buffer_t(this%n_cols, this%n_local))
413 this%local_buffer_t = 0.0_rp
414
415 ! Offsets for the gather onto rank zero.
416 allocate(this%recvcounts(pe_size))
417 allocate(this%displs(pe_size))
418 this%recvcounts = 0
419 this%displs = 0
420
421 call mpi_gather(this%n_local, 1, mpi_integer, this%recvcounts, 1, &
422 mpi_integer, 0, neko_comm, ierr)
423
424 offset = 0
425 call mpi_exscan(this%n_local, offset, 1, mpi_integer, mpi_sum, &
426 neko_comm, ierr)
427 if (pe_rank .eq. 0) offset = 0
428
429 call mpi_gather(offset, 1, mpi_integer, this%displs, 1, mpi_integer, &
430 0, neko_comm, ierr)
431
432 allocate(this%recvcounts_c(pe_size))
433 allocate(this%displs_c(pe_size))
434 this%recvcounts_c = this%recvcounts * this%n_cols
435 this%displs_c = this%displs * this%n_cols
436
437 if (pe_rank .eq. 0) then
438 allocate(this%global_buffer(max(1, this%n_cols), max(1, this%n_global)))
439 else
440 allocate(this%global_buffer(1, 1))
441 end if
442 this%global_buffer = 0.0_rp
443
444 ! Output file
445 call filename_suffix(output_filename, suffix)
446 if (trim(suffix) .ne. "csv" .and. trim(suffix) .ne. "h5" .and. &
447 trim(suffix) .ne. "hdf5") then
448 call neko_error("boundary_data_writer: output_filename must end in " // &
449 "'.csv', '.h5' or '.hdf5'")
450 end if
451
452 call this%fout%init(this%case%output_directory // trim(output_filename))
453
454 n_geom = 3
455 if (this%output_normals) n_geom = n_geom + 3
456 if (this%output_area) n_geom = n_geom + 1
457
458 select type (ft => this%fout%file_type)
459 type is (csv_file_t)
460 call boundary_data_writer_setup_output_csv(this, output_filename, &
461 n_geom)
462 class is (hdf5_file_t)
463 call boundary_data_writer_setup_output_hdf5(this, ft, n_geom)
464 class default
465 call neko_error("boundary_data_writer: expected csv_file_t or " // &
466 "hdf5_file_t")
467 end select
468
469 ! Log
470 call neko_log%section("Boundary data writer")
471 write(log_buf, '(A,A)') "Name: ", trim(this%name)
472 call neko_log%message(log_buf)
473 write(log_buf, '(A,*(I0,:,", "))') "Zone indices: ", this%zone_indices
474 call neko_log%message(log_buf)
475 write(log_buf, '(A,I0)') "Global number of masked points: ", this%n_global
476 call neko_log%message(log_buf)
477 write(log_buf, '(A,A)') "Output file: ", trim(output_filename)
478 call neko_log%message(log_buf)
479 call neko_log%message("Columns:")
480 do i = 1, this%n_cols
481 write(log_buf, '(A,A)') " ", trim(this%col_names(i))
482 call neko_log%message(log_buf)
483 end do
484 write(log_buf, '(A,L1)') "Moving mesh: ", this%mesh_has_changed
485 call neko_log%message(log_buf)
486 call neko_log%end_section()
487
489
493 subroutine boundary_data_writer_setup_output_csv(this, output_filename, &
494 n_geom)
495 class(boundary_data_writer_t), intent(inout) :: this
496 character(len=*), intent(in) :: output_filename
497 integer, intent(in) :: n_geom
498 character(len=1024) :: header_line
499 character(len=NEKO_FNAME_LEN) :: mesh_filename
500 type(matrix_t) :: mat_geom
501 type(file_t) :: fmesh
502 integer :: i, suffix_pos
503
504 header_line = "time"
505 do i = 1, this%n_cols
506 header_line = trim(header_line) // "," // trim(this%col_names(i))
507 end do
508 call this%fout%set_header(trim(header_line))
509
510 if (pe_rank .eq. 0) then
511 call this%mat_out%init(this%n_global, this%n_cols)
512 else
513 call this%mat_out%init(1, 1)
514 end if
515
516 ! The geometry goes into a companion file. For a moving mesh the
517 ! geometry appears in every sample, but the initial
518 ! geometry is additionally written once as `_initial_mesh`.
519 call boundary_data_writer_gather_geometry(this, mat_geom, n_geom)
520
521 if (pe_rank .eq. 0) then
522 header_line = "x,y,z"
523 if (this%output_normals) header_line = &
524 trim(header_line) // ",n_x,n_y,n_z"
525 if (this%output_area) header_line = trim(header_line) // ",area"
526
527 mesh_filename = this%case%output_directory // &
528 trim(output_filename)
529 suffix_pos = filename_suffix_pos(mesh_filename)
530 if (this%geometry_in_data) then
531 mesh_filename = trim(mesh_filename(1:suffix_pos-1)) // &
532 "_initial_mesh" // trim(mesh_filename(suffix_pos:))
533 else
534 mesh_filename = trim(mesh_filename(1:suffix_pos-1)) // "_mesh" // &
535 trim(mesh_filename(suffix_pos:))
536 end if
537
538 call fmesh%init(trim(mesh_filename), &
539 header = trim(header_line), overwrite = .true.)
540 call fmesh%write(mat_geom)
541 call fmesh%free()
542 end if
543 call mat_geom%free()
544
546
550 subroutine boundary_data_writer_setup_output_hdf5(this, ft, n_geom)
551 class(boundary_data_writer_t), intent(inout) :: this
552 class(hdf5_file_t), intent(inout) :: ft
553 integer, intent(in) :: n_geom
554 type(matrix_t) :: mat_geom
555 integer :: out_int
556 logical :: attr_exist
557
558 call ft%set_overwrite(.not. this%append_out)
559
560 call this%vec_out%init(max(0, this%n_local), "value")
561
562 call ft%open("w")
563 call ft%set_active_group("boundary_data")
564
565 ! If the file already carries samples we are restarting into it, so the
566 ! geometry must not be appended a second time. Similar to probes.
567 call ft%read_attribute("NSteps", out_int, attr_exist)
568 if (attr_exist) then
569 this%output_controller%nexecutions = out_int
570 else
571 out_int = this%n_global
572 call ft%write_attribute("NPoints", out_int)
573 call boundary_data_writer_local_geometry(this, mat_geom, n_geom)
574 if (this%geometry_in_data) then
575 mat_geom%name = "initial_coordinates"
576 else
577 mat_geom%name = "coordinates"
578 end if
579 call ft%write_dataset(mat_geom)
580 call mat_geom%free()
581 end if
582 call ft%close()
583
585
588 class(boundary_data_writer_t), intent(inout) :: this
589
590 call this%bdata%free()
591
592 call this%work%free()
593 call this%vec_out%free()
594 call this%mat_out%free()
595
596 call this%fields%free()
597 call this%fout%free()
598
599 if (allocated(this%zone_indices)) deallocate(this%zone_indices)
600 if (allocated(this%col_names)) deallocate(this%col_names)
601 if (allocated(this%local_buffer)) deallocate(this%local_buffer)
602 if (allocated(this%local_buffer_t)) deallocate(this%local_buffer_t)
603 if (allocated(this%global_buffer)) deallocate(this%global_buffer)
604 if (allocated(this%recvcounts)) deallocate(this%recvcounts)
605 if (allocated(this%displs)) deallocate(this%displs)
606 if (allocated(this%recvcounts_c)) deallocate(this%recvcounts_c)
607 if (allocated(this%displs_c)) deallocate(this%displs_c)
608
609 nullify(this%coef)
610
611 call this%free_base()
612
613 end subroutine boundary_data_writer_free
614
615
619 subroutine boundary_data_writer_gather_column(this, f, col)
620 class(boundary_data_writer_t), intent(inout) :: this
621 type(field_t), intent(in) :: f
622 integer, intent(in) :: col
623
624 if (this%n_local .le. 0) return
625
626 call this%bdata%get(f, this%work)
627
628 if (neko_bcknd_device .eq. 1) then
629 call this%work%copy_from(device_to_host, .true.)
630 end if
631
632 call copy(this%local_buffer(:, col), this%work%x, this%n_local)
633
635
638 subroutine boundary_data_writer_compute(this, time)
639 class(boundary_data_writer_t), intent(inout) :: this
640 type(time_state_t), intent(in) :: time
641 integer :: i, col, n_rows, out_int
642 character(len=80) :: group_name
643 real(kind=rp) :: time_
644 type(vector_t) :: vec_time
645
646 if (time%t .lt. this%start_time) then
647 call this%output_controller%set_counter(time)
648 return
649 end if
650 if (.not. this%output_controller%check(time)) return
651
652 ! Re-gather the boundary geometry only when the mesh has moved.
653 if (this%mesh_has_changed) then
654 call this%bdata%update_geometry()
655
656 if (neko_bcknd_device .eq. 1) then
657 call this%bdata%x%copy_from(device_to_host, .false.)
658 call this%bdata%y%copy_from(device_to_host, .false.)
659 call this%bdata%z%copy_from(device_to_host, .false.)
660 call this%bdata%n_x%copy_from(device_to_host, .false.)
661 call this%bdata%n_y%copy_from(device_to_host, .false.)
662 call this%bdata%n_z%copy_from(device_to_host, .false.)
663 call this%bdata%area%copy_from(device_to_host, .true.)
664 end if
665 end if
666
667 ! Fill the local buffer
668 col = 0
669 if (this%geometry_in_data) then
670 call copy(this%local_buffer(:, col + 1), this%bdata%x%x, this%n_local)
671 call copy(this%local_buffer(:, col + 2), this%bdata%y%x, this%n_local)
672 call copy(this%local_buffer(:, col + 3), this%bdata%z%x, this%n_local)
673 col = col + 3
674
675 if (this%output_normals) then
676 call copy(this%local_buffer(:, col + 1), this%bdata%n_x%x, &
677 this%n_local)
678 call copy(this%local_buffer(:, col + 2), this%bdata%n_y%x, &
679 this%n_local)
680 call copy(this%local_buffer(:, col + 3), this%bdata%n_z%x, &
681 this%n_local)
682 col = col + 3
683 end if
684
685 if (this%output_area) then
686 call copy(this%local_buffer(:, col + 1), this%bdata%area%x, &
687 this%n_local)
688 col = col + 1
689 end if
690 end if
691
692 do i = 1, this%fields%size()
693 col = col + 1
694 call this%gather_column(this%fields%items(i)%ptr, col)
695 end do
696
697 ! Write
698 select type (ft => this%fout%file_type)
699 type is (csv_file_t)
701
702 n_rows = this%n_global
703
704 if (pe_rank .eq. 0) then
705 do i = 1, n_rows
706 this%mat_out%x(i, 1:this%n_cols) = &
707 this%global_buffer(1:this%n_cols, i)
708 end do
709 call this%fout%write(this%mat_out, time%t)
710 end if
711
712 class is (hdf5_file_t)
713 out_int = this%output_controller%nexecutions + 1
714
715 call ft%open("w")
716 call ft%set_active_group("boundary_data")
717 call ft%write_attribute("NSteps", out_int)
718
719 if (.not. this%append_out) then
720 write(group_name, '(A,I0)') "boundary_data/Step_", out_int
721 call ft%set_active_group(trim(group_name))
722 end if
723
724 do i = 1, this%n_cols
725 call copy(this%vec_out%x, this%local_buffer(:, i), &
726 this%vec_out%size())
727 this%vec_out%name = trim(this%col_names(i))
728 call ft%write_dataset(this%vec_out)
729 end do
730
731 if (this%append_out) then
732 ! Time is a growing dataset alongside the samples.
733 if (pe_rank .eq. 0) then
734 call vec_time%init(1, "time")
735 vec_time%x(1) = time%t
736 else
737 call vec_time%init(0, "time")
738 end if
739 call ft%write_dataset(vec_time)
740 call vec_time%free()
741 else
742 ! Time is an attribute on the Step_N subgroup.
743 time_ = time%t
744 call ft%write_attribute("time", time_)
745 end if
746
747 call ft%close()
748 end select
749
750 call this%output_controller%register_execution()
751
752 end subroutine boundary_data_writer_compute
753
756 class(boundary_data_writer_t), intent(inout) :: this
757 integer :: ierr
758
759 call trsp(this%local_buffer_t, this%n_cols, this%local_buffer, this%n_local)
760
761 call mpi_gatherv(this%local_buffer_t, this%n_local * this%n_cols, &
762 mpi_real_precision, this%global_buffer, this%recvcounts_c, &
763 this%displs_c, mpi_real_precision, 0, neko_comm, ierr)
764
766
770 subroutine boundary_data_writer_local_geometry(this, mat, n_geom)
771 class(boundary_data_writer_t), intent(inout) :: this
772 type(matrix_t), intent(inout) :: mat
773 integer, intent(in) :: n_geom
774 integer :: i, k
775
776 call mat%init(n_geom, max(0, this%n_local), "coordinates")
777
778 do i = 1, this%n_local
779 mat%x(1, i) = this%bdata%x%x(i)
780 mat%x(2, i) = this%bdata%y%x(i)
781 mat%x(3, i) = this%bdata%z%x(i)
782 k = 3
783 if (this%output_normals) then
784 mat%x(4, i) = this%bdata%n_x%x(i)
785 mat%x(5, i) = this%bdata%n_y%x(i)
786 mat%x(6, i) = this%bdata%n_z%x(i)
787 k = 6
788 end if
789 if (this%output_area) mat%x(k + 1, i) = this%bdata%area%x(i)
790 end do
791
793
797 subroutine boundary_data_writer_gather_geometry(this, mat, n_geom)
798 class(boundary_data_writer_t), intent(inout) :: this
799 type(matrix_t), intent(inout) :: mat
800 integer, intent(in) :: n_geom
801 real(kind=rp), allocatable :: sendbuf(:,:), recvbuf(:,:)
802 integer, allocatable :: counts(:), disp(:)
803 integer :: i, k, ierr
804
805 allocate(sendbuf(n_geom, this%n_local))
806 sendbuf = 0.0_rp
807
808 do i = 1, this%n_local
809 sendbuf(1, i) = this%bdata%x%x(i)
810 sendbuf(2, i) = this%bdata%y%x(i)
811 sendbuf(3, i) = this%bdata%z%x(i)
812 k = 3
813 if (this%output_normals) then
814 sendbuf(4, i) = this%bdata%n_x%x(i)
815 sendbuf(5, i) = this%bdata%n_y%x(i)
816 sendbuf(6, i) = this%bdata%n_z%x(i)
817 k = 6
818 end if
819 if (this%output_area) sendbuf(k + 1, i) = this%bdata%area%x(i)
820 end do
821
822 if (pe_rank .eq. 0) then
823 allocate(recvbuf(n_geom, this%n_global))
824 else
825 allocate(recvbuf(n_geom, 1))
826 end if
827 recvbuf = 0.0_rp
828
829 allocate(counts(pe_size), disp(pe_size))
830 counts = this%recvcounts * n_geom
831 disp = this%displs * n_geom
832
833 call mpi_gatherv(sendbuf, this%n_local * n_geom, mpi_real_precision, &
834 recvbuf, counts, disp, mpi_real_precision, 0, neko_comm, ierr)
835
836 if (pe_rank .eq. 0) then
837 call mat%init(this%n_global, n_geom)
838 do i = 1, this%n_global
839 mat%x(i, 1:n_geom) = recvbuf(1:n_geom, i)
840 end do
841 else
842 call mat%init(1, 1)
843 end if
844
845 deallocate(sendbuf, recvbuf, counts, disp)
846
Retrieves a parameter by name or assigns a provided default value. In the latter case also adds the m...
Retrieves a parameter by name or throws an error.
ALE Manager: Handles Mesh Motion.
type(ale_manager_t), pointer, public neko_ale
Implements the boundary_data_writer_t type.
subroutine boundary_data_writer_init_from_json(this, json, case)
Constructor from json.
subroutine boundary_data_writer_compute(this, time)
Sample the boundary and write.
subroutine boundary_data_writer_local_geometry(this, mat, n_geom)
Collect the reference geometry of the local points into a matrix.
subroutine boundary_data_writer_gather_column(this, f, col)
Gather a field at the masked points into a column of the local buffer.
subroutine boundary_data_writer_setup_output_hdf5(this, ft, n_geom)
Set up an HDF5 output.
subroutine boundary_data_writer_free(this)
Destructor.
subroutine boundary_data_writer_gather_to_root(this)
Gather the local sample buffer onto rank zero.
subroutine boundary_data_writer_init_from_controllers_properties(this, name, case, order, preprocess_control, preprocess_value, compute_control, compute_value, output_control, output_value, coef, zone_indices, fields, output_filename, output_normals, output_area, append_out, start_time)
Constructor from components, passing properties to the time_based_controller components in the base t...
subroutine boundary_data_writer_gather_geometry(this, mat, n_geom)
Gather the reference geometry of all points onto rank zero.
subroutine boundary_data_writer_init_common(this, name, coef, zone_indices, fields, output_filename, output_normals, output_area, append_out, start_time)
Common part of all constructors.
subroutine boundary_data_writer_setup_output_csv(this, output_filename, n_geom)
Set up a CSV output.
subroutine boundary_data_writer_init_from_controllers(this, name, case, order, preprocess_controller, compute_controller, output_controller, coef, zone_indices, fields, output_filename, output_normals, output_area, append_out, start_time)
Constructor from components, passing controllers.
Implements the boundary_data_t type.
Defines a simulation case.
Definition case.f90:34
Coefficients.
Definition coef.f90:34
Definition comm.F90:1
type(mpi_datatype), public mpi_real_precision
MPI type for working precision of REAL types.
Definition comm.F90:54
integer, public pe_size
MPI size of communicator.
Definition comm.F90:62
integer, public pe_rank
MPI rank.
Definition comm.F90:59
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
File format for .csv files, used for any read/write operations involving floating point data.
Definition csv_file.f90:35
Device abstraction, common interface for various accelerators.
Definition device.F90:34
integer, parameter, public device_to_host
Definition device.F90:48
Defines a field.
Definition field.f90:34
Module for file I/O operations.
Definition file.f90:34
HDF5 file format.
Definition hdf5_file.F90:34
Utilities for retrieving parameters from the case files.
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:80
integer, parameter, public log_size
Definition log.f90:46
Definition math.f90:60
subroutine, public copy(a, b, n)
Copy a vector .
Definition math.f90:291
Defines a matrix.
Definition matrix.f90:34
Build configurations.
integer, parameter neko_bcknd_device
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Implements output_controller_t
Defines a registry for storing solution fields.
Definition registry.f90:34
type(registry_t), target, public neko_registry
Global field registry.
Definition registry.f90:144
Simulation components are objects that encapsulate functionality that can be fit to a particular comp...
subroutine compute_(this, time)
Dummy compute function.
Tensor operations.
Definition tensor.f90:61
subroutine, public trsp(a, lda, b, ldb)
Transpose of a rectangular tensor .
Definition tensor.f90:124
Contains the time_based_controller_t type.
Module with things related to the simulation time.
Utilities.
Definition utils.f90:35
integer, parameter, public neko_fname_len
Definition utils.f90:42
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition utils.f90:398
subroutine, public filename_suffix(fname, suffix)
Extract a filename's suffix.
Definition utils.f90:124
integer, parameter, public neko_varname_len
Definition utils.f90:43
pure integer function, public filename_suffix_pos(fname)
Find position (in the string) of a filename's suffix.
Definition utils.f90:74
Defines a vector.
Definition vector.f90:34
Collects data on the boundary points of one or more labelled zones and perform some bounary operation...
A simulation component that writes registry fields sampled on labelled boundary zones....
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:63
field_list_t, To be able to group fields together
A wrapper around a polymorphic generic_file_t that handles its init. This is essentially a factory fo...
Definition file.f90:56
Interface for HDF5 files.
Definition hdf5_file.F90:60
Base abstract class for simulation components.
A utility type for determining whether an action should be executed based on the current time value....
A struct that contains all info about the time, expand as needed.
#define max(a, b)
Definition tensor.cu:40