Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
boundary_operation.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!
35 use num_types, only : rp, dp
36 use json_module, only : json_file
38 use registry, only : neko_registry
39 use field, only : field_t
40 use case, only : case_t
42 use time_state, only : time_state_t
43 use coefs, only : coef_t
44 use neumann, only : neumann_t
45 use logger, only : neko_log, log_size
46 use utils, only : neko_error
47 use space, only : operator(.ne.)
48 use file, only : file_t
49 use vector, only : vector_t
54 use comm, only : neko_comm
55 use mpi_f08, only : mpi_allreduce, mpi_integer, mpi_sum
56 implicit none
57 private
58
62 type(field_t), pointer :: field => null()
64 type(coef_t), pointer :: coef => null()
66 type(neumann_t) :: bc
68 integer, allocatable :: zone_indices(:)
70 character(len=:), allocatable :: field_name
72 character(len=16), allocatable :: operations(:)
74 logical :: compute_integral = .false.
76 logical :: compute_average = .false.
78 logical :: compute_min = .false.
80 logical :: compute_max = .false.
82 logical :: log = .true.
84 logical :: csv_output_enabled = .false.
86 type(file_t) :: csv_output
88 type(vector_t) :: csv_row
90 type(vector_t) :: areas
92 type(vector_t) :: surface_values
94 real(kind=rp) :: integral = 0.0_rp
96 real(kind=rp) :: average = 0.0_rp
98 real(kind=rp) :: minimum = huge(0.0_rp)
100 real(kind=rp) :: maximum = -huge(0.0_rp)
101 contains
103 procedure, pass(this) :: init => boundary_operation_init_from_json
105 generic :: init_from_components => &
106 init_from_controllers, init_from_controllers_properties
108 procedure, pass(this) :: init_from_controllers => &
111 procedure, pass(this) :: init_from_controllers_properties => &
114 procedure, private, pass(this) :: init_common => &
117 procedure, pass(this) :: free => boundary_operation_free
119 procedure, pass(this) :: compute_ => boundary_operation_compute
120 end type boundary_operation_t
121
122contains
123
127 subroutine boundary_operation_init_from_json(this, json, case)
128 class(boundary_operation_t), intent(inout), target :: this
129 type(json_file), intent(inout) :: json
130 class(case_t), intent(inout), target :: case
131 character(len=:), allocatable :: name
132 integer, allocatable :: zone_indices(:)
133 character(len=:), allocatable :: field_name
134 character(len=16), allocatable :: operations(:)
135 logical :: log
136 character(len=:), allocatable :: output_filename
137
138 call this%free()
139
140 call json_get_or_default(json, "name", name, "boundary_operation")
141 call json_get_or_default(json, "log", log, .true.)
142 call this%init_base(json, case)
143
144 call json_get(json, "zone_indices", zone_indices)
145 call json_get(json, "field_name", field_name)
146 call json_get(json, "operations", operations)
147
148 if (json%valid_path("output_filename")) then
149 call json_get(json, "output_filename", output_filename)
150 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
151 operations, log, output_filename)
152 else
153 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
154 operations, log)
155 end if
157
166 subroutine boundary_operation_init_common(this, name, coef, zone_indices, &
167 field_name, operations, log, output_filename)
168 class(boundary_operation_t), intent(inout) :: this
169 character(len=*), intent(in) :: name
170 type(coef_t), intent(inout), target :: coef
171 integer, intent(in) :: zone_indices(:)
172 character(len=*), intent(in) :: field_name
173 character(len=*), intent(in) :: operations(:)
174 logical, intent(in) :: log
175 character(len=*), intent(in), optional :: output_filename
176 character(len=:), allocatable :: csv_header
177 character(len=LOG_SIZE) :: log_buf
178 integer :: i
179 integer :: n_pts, glb_n_pts, ierr
180
181 this%name = name
182 this%log = log
183 this%compute_integral = .false.
184 this%compute_average = .false.
185 this%compute_min = .false.
186 this%compute_max = .false.
187 this%csv_output_enabled = .false.
188 this%integral = 0.0_rp
189 this%average = 0.0_rp
190 this%minimum = huge(0.0_rp)
191 this%maximum = -huge(0.0_rp)
192
193 this%coef => coef
194 this%field => neko_registry%get_field_by_name(field_name)
195
196 if (size(zone_indices) .eq. 0) then
197 call neko_error("boundary_operation requires at least one zone index")
198 end if
199
200 if (size(operations) .eq. 0) then
201 call neko_error("boundary_operation requires at least one operation")
202 end if
203
204 allocate(this%zone_indices(size(zone_indices)))
205 this%zone_indices = zone_indices
206 this%field_name = field_name
207 allocate(character(len=16) :: this%operations(size(operations)))
208 this%operations = operations
209
210 do i = 1, size(this%operations)
211 select case (trim(this%operations(i)))
212 case ("integral")
213 this%compute_integral = .true.
214 case ("average")
215 this%compute_average = .true.
216 case ("min")
217 this%compute_min = .true.
218 case ("max")
219 this%compute_max = .true.
220 case default
221 call neko_error("boundary_operation supports only operations = " // &
222 "[integral, average, min, max]")
223 end select
224 end do
225
226 call this%bc%init_from_components(this%coef, 0.0_rp)
227 this%bc%zone_indices = this%zone_indices
228 do i = 1, size(this%zone_indices)
229 call this%bc%mark_zone(this%bc%msh%labeled_zones(this%zone_indices(i)))
230 end do
231 call this%bc%finalize()
232
233 n_pts = this%bc%facet_node_msk(0)
234 call mpi_allreduce(n_pts, glb_n_pts, 1, mpi_integer, mpi_sum, neko_comm, &
235 ierr)
236 if (n_pts .gt. 0) then
237 call this%areas%init(n_pts)
238 call this%surface_values%init(n_pts)
239 call vector_face_masked_gather_copy_0(this%areas, this%coef%area, &
240 this%bc%facet_node_msk, this%bc%facet, this%coef%Xh%lx, &
241 this%coef%Xh%ly, this%coef%Xh%lz, n_pts)
242 end if
243
244 if (present(output_filename)) then
245 csv_header = "tstep,time"
246 do i = 1, size(this%operations)
247 csv_header = trim(csv_header) // "," // trim(this%operations(i))
248 end do
249 call this%csv_output%init(trim(output_filename), header = &
250 trim(csv_header), overwrite = .true.)
251 call this%csv_row%init(2 + size(this%operations))
252 this%csv_output_enabled = .true.
253 end if
254
255 call neko_log%section("Boundary operation")
256 write(log_buf, '(A,A)') "Name: ", trim(this%name)
257 call neko_log%message(log_buf)
258 write(log_buf, '(A,A)') "Field: ", trim(this%field_name)
259 call neko_log%message(log_buf)
260 call neko_log%message("Operations:")
261 do i = 1, size(this%operations)
262 write(log_buf, '(A,A)') " ", trim(this%operations(i))
263 call neko_log%message(log_buf)
264 end do
265 write(log_buf, '(A,*(I0,:,", "))') "Zone indices: ", this%zone_indices
266 call neko_log%message(log_buf)
267 write(log_buf, '(A,I0)') "Marked boundary quadrature points: ", glb_n_pts
268 call neko_log%message(log_buf)
269 call neko_log%end_section()
270 end subroutine boundary_operation_init_common
271
284 subroutine boundary_operation_init_from_controllers(this, name, case, order, &
285 preprocess_controller, compute_controller, output_controller, &
286 zone_indices, field_name, operations, log, output_filename)
287 class(boundary_operation_t), intent(inout) :: this
288 character(len=*), intent(in) :: name
289 class(case_t), intent(inout), target :: case
290 integer, intent(in) :: order
291 type(time_based_controller_t), intent(in) :: preprocess_controller
292 type(time_based_controller_t), intent(in) :: compute_controller
293 type(time_based_controller_t), intent(in) :: output_controller
294 integer, intent(in) :: zone_indices(:)
295 character(len=*), intent(in) :: field_name
296 character(len=*), intent(in) :: operations(:)
297 logical, intent(in), optional :: log
298 character(len=*), intent(in), optional :: output_filename
299 logical :: log_enabled
300
301 call this%free()
302
303 log_enabled = .true.
304 if (present(log)) log_enabled = log
305
306 call this%init_base_from_components(case, order, preprocess_controller, &
307 compute_controller, output_controller)
308
309 if (present(output_filename)) then
310 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
311 operations, log_enabled, output_filename)
312 else
313 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
314 operations, log_enabled)
315 end if
317
334 case, order, preprocess_control, preprocess_value, compute_control, &
335 compute_value, output_control, output_value, zone_indices, field_name, &
336 operations, log, output_filename)
337 class(boundary_operation_t), intent(inout) :: this
338 character(len=*), intent(in) :: name
339 class(case_t), intent(inout), target :: case
340 integer, intent(in) :: order
341 character(len=*), intent(in) :: preprocess_control
342 real(kind=dp), intent(in) :: preprocess_value
343 character(len=*), intent(in) :: compute_control
344 real(kind=dp), intent(in) :: compute_value
345 character(len=*), intent(in) :: output_control
346 real(kind=dp), intent(in) :: output_value
347 integer, intent(in) :: zone_indices(:)
348 character(len=*), intent(in) :: field_name
349 character(len=*), intent(in) :: operations(:)
350 logical, intent(in), optional :: log
351 character(len=*), intent(in), optional :: output_filename
352 logical :: log_enabled
353
354 call this%free()
355
356 log_enabled = .true.
357 if (present(log)) log_enabled = log
358
359 call this%init_base_from_components(case, order, preprocess_control, &
360 preprocess_value, compute_control, compute_value, output_control, &
361 output_value)
362
363 if (present(output_filename)) then
364 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
365 operations, log_enabled, output_filename)
366 else
367 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
368 operations, log_enabled)
369 end if
371
373 subroutine boundary_operation_free(this)
374 class(boundary_operation_t), intent(inout) :: this
375
376 call this%bc%free()
377 call this%csv_output%free()
378 call this%csv_row%free()
379 call this%areas%free()
380 call this%surface_values%free()
381 if (allocated(this%zone_indices)) deallocate(this%zone_indices)
382 if (allocated(this%field_name)) deallocate(this%field_name)
383 if (allocated(this%operations)) deallocate(this%operations)
384
385 nullify(this%field)
386 nullify(this%coef)
387
388 call this%free_base()
389 end subroutine boundary_operation_free
390
393 subroutine boundary_operation_compute(this, time)
394 class(boundary_operation_t), intent(inout) :: this
395 type(time_state_t), intent(in) :: time
396 integer :: n_pts
397 integer :: i
398 real(kind=rp) :: area
399 character(len=18) :: value_buf
400 character(len=12) :: step_str
401 character(len=:), allocatable :: section_title, header_line, value_line
402 integer :: output_col
403
404 n_pts = this%bc%facet_node_msk(0)
405
406 this%integral = 0.0_rp
407 this%average = 0.0_rp
408 this%minimum = huge(0.0_rp)
409 this%maximum = -huge(0.0_rp)
410 area = 0.0_rp
411
412 call vector_masked_gather_copy_0(this%surface_values, &
413 this%field%x, this%bc%facet_node_msk, this%field%size(), n_pts)
414
415 if (this%compute_integral .or. this%compute_average) then
416 this%integral = vector_glsc2(this%surface_values, this%areas, n_pts)
417 end if
418
419 if (this%compute_average) then
420 area = vector_glsum(this%areas, n_pts)
421 if (area .gt. 0.0_rp) then
422 this%average = this%integral / area
423 else
424 this%average = 0.0_rp
425 end if
426 end if
427
428 if (this%compute_min) then
429 this%minimum = vector_glmin(this%surface_values, n_pts)
430 end if
431
432 if (this%compute_max) then
433 this%maximum = vector_glmax(this%surface_values, n_pts)
434 end if
435
436 if (this%log) then
437 section_title = trim(this%name)
438 call neko_log%section(section_title)
439
440 header_line = repeat(' ', 12) // ' |'
441 write(step_str, '(I12)') time%tstep
442 step_str = adjustl(step_str)
443 value_line = step_str // ' |'
444
445 do i = 1, size(this%operations)
446 select case (trim(this%operations(i)))
447 case ('integral')
448 header_line = header_line // left_pad('Integral:', 18)
449 write(value_buf, '(ES18.9)') this%integral
450 value_line = value_line // value_buf
451 case ('average')
452 header_line = header_line // left_pad('Average:', 18)
453 write(value_buf, '(ES18.9)') this%average
454 value_line = value_line // value_buf
455 case ('min')
456 header_line = header_line // left_pad('Min:', 18)
457 write(value_buf, '(ES18.9)') this%minimum
458 value_line = value_line // value_buf
459 case ('max')
460 header_line = header_line // left_pad('Max:', 18)
461 write(value_buf, '(ES18.9)') this%maximum
462 value_line = value_line // value_buf
463 end select
464 end do
465
466 call neko_log%message(header_line)
467 call neko_log%message(value_line)
468 call neko_log%end_section()
469 end if
470
471 if (this%csv_output_enabled) then
472 if (this%output_controller%check(time)) then
473 this%csv_row%x(1) = real(time%tstep, rp)
474 this%csv_row%x(2) = time%t
475 output_col = 3
476 do i = 1, size(this%operations)
477 select case (trim(this%operations(i)))
478 case ('integral')
479 this%csv_row%x(output_col) = this%integral
480 case ('average')
481 this%csv_row%x(output_col) = this%average
482 case ('min')
483 this%csv_row%x(output_col) = this%minimum
484 case ('max')
485 this%csv_row%x(output_col) = this%maximum
486 end select
487 output_col = output_col + 1
488 end do
489 call this%csv_output%write(this%csv_row)
490 call this%output_controller%register_execution(time)
491 end if
492 end if
493 end subroutine boundary_operation_compute
494
498 pure function left_pad(text, width) result(padded)
499 character(len=*), intent(in) :: text
500 integer, intent(in) :: width
501 character(len=:), allocatable :: padded
502 integer :: pad_width
503
504 pad_width = max(0, width - len_trim(text))
505 padded = repeat(' ', pad_width) // trim(text)
506 end function left_pad
507
508end module boundary_operation
double real
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.
Defines a boundary condition.
Definition bc.f90:34
Implements boundary_operation_t.
subroutine boundary_operation_free(this)
Free all resources owned by the component.
subroutine boundary_operation_init_from_controllers_properties(this, name, case, order, preprocess_control, preprocess_value, compute_control, compute_value, output_control, output_value, zone_indices, field_name, operations, log, output_filename)
Construct from time-based controller properties.
subroutine boundary_operation_init_from_controllers(this, name, case, order, preprocess_controller, compute_controller, output_controller, zone_indices, field_name, operations, log, output_filename)
Construct from explicit time-based controllers.
subroutine boundary_operation_init_common(this, name, coef, zone_indices, field_name, operations, log, output_filename)
Common constructor shared by all public constructors.
pure character(len=:) function, allocatable left_pad(text, width)
Left-pad a string to a fixed width.
subroutine boundary_operation_init_from_json(this, json, case)
Construct from JSON.
subroutine boundary_operation_compute(this, time)
Compute and optionally output the requested boundary operations.
Defines a simulation case.
Definition case.f90:34
Coefficients.
Definition coef.f90:34
Definition comm.F90:1
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
Defines a field.
Definition field.f90:34
Module for file I/O operations.
Definition 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:92
integer, parameter, public log_size
Definition log.f90:46
Defines a Neumann boundary condition.
Definition neumann.f90:34
integer, parameter, public dp
Definition num_types.f90:10
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
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:158
Simulation components are objects that encapsulate functionality that can be fit to a particular comp...
subroutine compute_(this, time)
Dummy compute function.
Defines a function space.
Definition space.f90:34
Contains the time_based_controller_t type.
Module with things related to the simulation time.
Utilities.
Definition utils.f90:35
subroutine, public vector_masked_gather_copy_0(a, b, mask, n, n_mask)
Gather a vector to reduced contigous array .
real(kind=rp) function, public vector_glmin(a, n)
Global minimum of all elements in a vector .
subroutine, public vector_face_masked_gather_copy_0(a, b, mask, facet, lx, ly, lz, n_mask)
Gather a face-local SEM field to a reduced contiguous vector.
real(kind=rp) function, public vector_glsum(a, n)
real(kind=rp) function, public vector_glsc2(a, b, n)
real(kind=rp) function, public vector_glmax(a, n)
Global maximum of all elements in a vector .
Defines a vector.
Definition vector.f90:34
A simulation component for boundary reductions on labelled zones.
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:136
A wrapper around a polymorphic generic_file_t that handles its init. This is essentially a factory fo...
Definition file.f90:56
A Neumann boundary condition. Sets the flux of the field to the chosen values.
Definition neumann.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