Neko 1.99.3
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
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 implicit none
55 private
56
60 type(field_t), pointer :: field => null()
62 type(coef_t), pointer :: coef => null()
64 type(neumann_t) :: bc
66 integer, allocatable :: zone_indices(:)
68 character(len=:), allocatable :: field_name
70 character(len=16), allocatable :: operations(:)
72 logical :: compute_integral = .false.
74 logical :: compute_average = .false.
76 logical :: compute_min = .false.
78 logical :: compute_max = .false.
80 logical :: log = .true.
82 logical :: csv_output_enabled = .false.
84 type(file_t) :: csv_output
86 type(vector_t) :: csv_row
88 type(vector_t) :: areas
90 type(vector_t) :: surface_values
92 real(kind=rp) :: integral = 0.0_rp
94 real(kind=rp) :: average = 0.0_rp
96 real(kind=rp) :: minimum = huge(0.0_rp)
98 real(kind=rp) :: maximum = -huge(0.0_rp)
99 contains
101 procedure, pass(this) :: init => boundary_operation_init_from_json
103 generic :: init_from_components => &
104 init_from_controllers, init_from_controllers_properties
106 procedure, pass(this) :: init_from_controllers => &
109 procedure, pass(this) :: init_from_controllers_properties => &
112 procedure, private, pass(this) :: init_common => &
115 procedure, pass(this) :: free => boundary_operation_free
117 procedure, pass(this) :: compute_ => boundary_operation_compute
118 end type boundary_operation_t
119
120contains
121
125 subroutine boundary_operation_init_from_json(this, json, case)
126 class(boundary_operation_t), intent(inout), target :: this
127 type(json_file), intent(inout) :: json
128 class(case_t), intent(inout), target :: case
129 character(len=:), allocatable :: name
130 integer, allocatable :: zone_indices(:)
131 character(len=:), allocatable :: field_name
132 character(len=16), allocatable :: operations(:)
133 logical :: log
134 character(len=:), allocatable :: output_filename
135
136 call this%free()
137
138 call json_get_or_default(json, "name", name, "boundary_operation")
139 call json_get_or_default(json, "log", log, .true.)
140 call this%init_base(json, case)
141
142 call json_get(json, "zone_indices", zone_indices)
143 call json_get(json, "field_name", field_name)
144 call json_get(json, "operations", operations)
145
146 if (json%valid_path("output_filename")) then
147 call json_get(json, "output_filename", output_filename)
148 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
149 operations, log, output_filename)
150 else
151 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
152 operations, log)
153 end if
155
164 subroutine boundary_operation_init_common(this, name, coef, zone_indices, &
165 field_name, operations, log, output_filename)
166 class(boundary_operation_t), intent(inout) :: this
167 character(len=*), intent(in) :: name
168 type(coef_t), intent(inout), target :: coef
169 integer, intent(in) :: zone_indices(:)
170 character(len=*), intent(in) :: field_name
171 character(len=*), intent(in) :: operations(:)
172 logical, intent(in) :: log
173 character(len=*), intent(in), optional :: output_filename
174 character(len=:), allocatable :: csv_header
175 character(len=LOG_SIZE) :: log_buf
176 integer :: i
177 integer :: n_pts
178
179 this%name = name
180 this%log = log
181 this%compute_integral = .false.
182 this%compute_average = .false.
183 this%compute_min = .false.
184 this%compute_max = .false.
185 this%csv_output_enabled = .false.
186 this%integral = 0.0_rp
187 this%average = 0.0_rp
188 this%minimum = huge(0.0_rp)
189 this%maximum = -huge(0.0_rp)
190
191 this%coef => coef
192 this%field => neko_registry%get_field_by_name(field_name)
193
194 if (size(zone_indices) .eq. 0) then
195 call neko_error("boundary_operation requires at least one zone index")
196 end if
197
198 if (size(operations) .eq. 0) then
199 call neko_error("boundary_operation requires at least one operation")
200 end if
201
202 allocate(this%zone_indices(size(zone_indices)))
203 this%zone_indices = zone_indices
204 this%field_name = field_name
205 allocate(character(len=16) :: this%operations(size(operations)))
206 this%operations = operations
207
208 do i = 1, size(this%operations)
209 select case (trim(this%operations(i)))
210 case ("integral")
211 this%compute_integral = .true.
212 case ("average")
213 this%compute_average = .true.
214 case ("min")
215 this%compute_min = .true.
216 case ("max")
217 this%compute_max = .true.
218 case default
219 call neko_error("boundary_operation supports only operations = " // &
220 "[integral, average, min, max]")
221 end select
222 end do
223
224 call this%bc%init_from_components(this%coef, 0.0_rp)
225 this%bc%zone_indices = this%zone_indices
226 do i = 1, size(this%zone_indices)
227 call this%bc%mark_zone(this%bc%msh%labeled_zones(this%zone_indices(i)))
228 end do
229 call this%bc%finalize()
230
231 n_pts = this%bc%msk(0)
232 if (n_pts .gt. 0) then
233 call this%areas%init(n_pts)
234 call this%surface_values%init(n_pts)
235 call vector_face_masked_gather_copy_0(this%areas, this%coef%area, &
236 this%bc%msk, this%bc%facet, this%coef%Xh%lx, this%coef%Xh%ly, &
237 this%coef%Xh%lz, n_pts)
238 end if
239
240 if (present(output_filename)) then
241 csv_header = "tstep,time"
242 do i = 1, size(this%operations)
243 csv_header = trim(csv_header) // "," // trim(this%operations(i))
244 end do
245 call this%csv_output%init(trim(output_filename), header = &
246 trim(csv_header), overwrite = .true.)
247 call this%csv_row%init(2 + size(this%operations))
248 this%csv_output_enabled = .true.
249 end if
250
251 call neko_log%section("Boundary operation")
252 write(log_buf, '(A,A)') "Name: ", trim(this%name)
253 call neko_log%message(log_buf)
254 write(log_buf, '(A,A)') "Field: ", trim(this%field_name)
255 call neko_log%message(log_buf)
256 call neko_log%message("Operations:")
257 do i = 1, size(this%operations)
258 write(log_buf, '(A,A)') " ", trim(this%operations(i))
259 call neko_log%message(log_buf)
260 end do
261 write(log_buf, '(A,*(I0,:,", "))') "Zone indices: ", this%zone_indices
262 call neko_log%message(log_buf)
263 write(log_buf, '(A,I0)') "Marked boundary quadrature points: ", &
264 this%bc%msk(0)
265 call neko_log%message(log_buf)
266 call neko_log%end_section()
267 end subroutine boundary_operation_init_common
268
281 subroutine boundary_operation_init_from_controllers(this, name, case, order, &
282 preprocess_controller, compute_controller, output_controller, &
283 zone_indices, field_name, operations, log, output_filename)
284 class(boundary_operation_t), intent(inout) :: this
285 character(len=*), intent(in) :: name
286 class(case_t), intent(inout), target :: case
287 integer, intent(in) :: order
288 type(time_based_controller_t), intent(in) :: preprocess_controller
289 type(time_based_controller_t), intent(in) :: compute_controller
290 type(time_based_controller_t), intent(in) :: output_controller
291 integer, intent(in) :: zone_indices(:)
292 character(len=*), intent(in) :: field_name
293 character(len=*), intent(in) :: operations(:)
294 logical, intent(in), optional :: log
295 character(len=*), intent(in), optional :: output_filename
296 logical :: log_enabled
297
298 call this%free()
299
300 log_enabled = .true.
301 if (present(log)) log_enabled = log
302
303 call this%init_base_from_components(case, order, preprocess_controller, &
304 compute_controller, output_controller)
305
306 if (present(output_filename)) then
307 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
308 operations, log_enabled, output_filename)
309 else
310 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
311 operations, log_enabled)
312 end if
314
331 case, order, preprocess_control, preprocess_value, compute_control, &
332 compute_value, output_control, output_value, zone_indices, field_name, &
333 operations, log, output_filename)
334 class(boundary_operation_t), intent(inout) :: this
335 character(len=*), intent(in) :: name
336 class(case_t), intent(inout), target :: case
337 integer, intent(in) :: order
338 character(len=*), intent(in) :: preprocess_control
339 real(kind=rp), intent(in) :: preprocess_value
340 character(len=*), intent(in) :: compute_control
341 real(kind=rp), intent(in) :: compute_value
342 character(len=*), intent(in) :: output_control
343 real(kind=rp), intent(in) :: output_value
344 integer, intent(in) :: zone_indices(:)
345 character(len=*), intent(in) :: field_name
346 character(len=*), intent(in) :: operations(:)
347 logical, intent(in), optional :: log
348 character(len=*), intent(in), optional :: output_filename
349 logical :: log_enabled
350
351 call this%free()
352
353 log_enabled = .true.
354 if (present(log)) log_enabled = log
355
356 call this%init_base_from_components(case, order, preprocess_control, &
357 preprocess_value, compute_control, compute_value, output_control, &
358 output_value)
359
360 if (present(output_filename)) then
361 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
362 operations, log_enabled, output_filename)
363 else
364 call this%init_common(name, case%fluid%c_Xh, zone_indices, field_name, &
365 operations, log_enabled)
366 end if
368
370 subroutine boundary_operation_free(this)
371 class(boundary_operation_t), intent(inout) :: this
372
373 call this%bc%free()
374 call this%csv_output%free()
375 call this%csv_row%free()
376 call this%areas%free()
377 call this%surface_values%free()
378 if (allocated(this%zone_indices)) deallocate(this%zone_indices)
379 if (allocated(this%field_name)) deallocate(this%field_name)
380 if (allocated(this%operations)) deallocate(this%operations)
381
382 nullify(this%field)
383 nullify(this%coef)
384
385 call this%free_base()
386 end subroutine boundary_operation_free
387
390 subroutine boundary_operation_compute(this, time)
391 class(boundary_operation_t), intent(inout) :: this
392 type(time_state_t), intent(in) :: time
393 integer :: n_pts
394 integer :: i
395 real(kind=rp) :: area
396 character(len=18) :: value_buf
397 character(len=12) :: step_str
398 character(len=:), allocatable :: section_title, header_line, value_line
399 integer :: output_col
400
401 n_pts = this%bc%msk(0)
402
403 this%integral = 0.0_rp
404 this%average = 0.0_rp
405 this%minimum = huge(0.0_rp)
406 this%maximum = -huge(0.0_rp)
407 area = 0.0_rp
408
409 call vector_masked_gather_copy_0(this%surface_values, &
410 this%field%x, this%bc%msk, this%field%size(), n_pts)
411
412 if (this%compute_integral .or. this%compute_average) then
413 this%integral = vector_glsc2(this%surface_values, this%areas, n_pts)
414 end if
415
416 if (this%compute_average) then
417 area = vector_glsum(this%areas, n_pts)
418 if (area .gt. 0.0_rp) then
419 this%average = this%integral / area
420 else
421 this%average = 0.0_rp
422 end if
423 end if
424
425 if (this%compute_min) then
426 this%minimum = vector_glmin(this%surface_values, n_pts)
427 end if
428
429 if (this%compute_max) then
430 this%maximum = vector_glmax(this%surface_values, n_pts)
431 end if
432
433 if (this%log) then
434 section_title = trim(this%name)
435 call neko_log%section(section_title)
436
437 header_line = repeat(' ', 12) // ' |'
438 write(step_str, '(I12)') time%tstep
439 step_str = adjustl(step_str)
440 value_line = step_str // ' |'
441
442 do i = 1, size(this%operations)
443 select case (trim(this%operations(i)))
444 case ('integral')
445 header_line = header_line // left_pad('Integral:', 18)
446 write(value_buf, '(ES18.9)') this%integral
447 value_line = value_line // value_buf
448 case ('average')
449 header_line = header_line // left_pad('Average:', 18)
450 write(value_buf, '(ES18.9)') this%average
451 value_line = value_line // value_buf
452 case ('min')
453 header_line = header_line // left_pad('Min:', 18)
454 write(value_buf, '(ES18.9)') this%minimum
455 value_line = value_line // value_buf
456 case ('max')
457 header_line = header_line // left_pad('Max:', 18)
458 write(value_buf, '(ES18.9)') this%maximum
459 value_line = value_line // value_buf
460 end select
461 end do
462
463 call neko_log%message(header_line)
464 call neko_log%message(value_line)
465 call neko_log%end_section()
466 end if
467
468 if (this%csv_output_enabled) then
469 if (this%output_controller%check(time)) then
470 this%csv_row%x(1) = real(time%tstep, rp)
471 this%csv_row%x(2) = time%t
472 output_col = 3
473 do i = 1, size(this%operations)
474 select case (trim(this%operations(i)))
475 case ('integral')
476 this%csv_row%x(output_col) = this%integral
477 case ('average')
478 this%csv_row%x(output_col) = this%average
479 case ('min')
480 this%csv_row%x(output_col) = this%minimum
481 case ('max')
482 this%csv_row%x(output_col) = this%maximum
483 end select
484 output_col = output_col + 1
485 end do
486 call this%csv_output%write(this%csv_row)
487 call this%output_controller%register_execution()
488 end if
489 end if
490 end subroutine boundary_operation_compute
491
495 pure function left_pad(text, width) result(padded)
496 character(len=*), intent(in) :: text
497 integer, intent(in) :: width
498 character(len=:), allocatable :: padded
499 integer :: pad_width
500
501 pad_width = max(0, width - len_trim(text))
502 padded = repeat(' ', pad_width) // trim(text)
503 end function left_pad
504
505end 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
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:79
integer, parameter, public log_size
Definition log.f90:45
Defines a Neumann boundary condition.
Definition neumann.f90:34
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.
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:63
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