Neko 1.99.2
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
brinkman_source_term.f90
Go to the documentation of this file.
1! Copyright (c) 2023, 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 aabb, only : aabb_t, get_aabb
36 use coefs, only : coef_t
38 use field, only : field_t
39 use field_list, only : field_list_t
40 use math, only : cfill_mask, pwmax2
43 use registry, only : neko_registry
46 use file, only : file_t
47 use json_module, only : json_file, json_core, json_value
51 use tri_mesh, only : tri_mesh_t
53 use num_types, only : rp, dp
54 use point_zone, only : point_zone_t
58 use source_term, only : source_term_t
59 use utils, only : neko_error
60 use filter, only : filter_t
61 use pde_filter, only : pde_filter_t
64 use num_types, only : sp, dp
65 use time_state, only : time_state_t
66
69 use space, only: space_t, gll
70 implicit none
71 private
72
76 type, public, extends(source_term_t) :: brinkman_source_term_t
77 private
78
80 type(field_t), pointer :: indicator_unfiltered
82 type(field_t), pointer :: indicator
84 type(field_t), pointer :: brinkman
86 class(filter_t), allocatable :: filter
87 contains
89 procedure, public, pass(this) :: init => &
92 procedure, public, pass(this) :: free => brinkman_source_term_free
94 procedure, public, pass(this) :: compute_ => brinkman_source_term_compute
95
96 ! ----------------------------------------------------------------------- !
97 ! Private methods
98 procedure, pass(this) :: init_boundary_mesh
99 procedure, pass(this) :: init_point_zone
100
102
103contains
104
105 ! ========================================================================== !
106 ! Public methods
107
113 subroutine brinkman_source_term_init_from_json(this, json, fields, coef, &
114 variable_name)
115 class(brinkman_source_term_t), intent(inout) :: this
116 type(json_file), intent(inout) :: json
117 type(field_list_t), intent(in), target :: fields
118 type(coef_t), intent(in), target :: coef
119 character(len=*), intent(in) :: variable_name
120 real(kind=rp) :: start_time, end_time
121
122 character(len=:), allocatable :: filter_type
123 real(kind=rp) :: filter_radius
124 real(kind=rp), dimension(:), allocatable :: brinkman_limits
125 real(kind=rp) :: brinkman_penalty
126
127 type(json_value), pointer :: json_object_list
128 type(json_core) :: core
129
130 character(len=:), allocatable :: object_type
131 type(json_file) :: object_settings
132 integer :: n_regions
133 integer :: i
134 type(fld_file_output_t) :: output
135
136
137 ! Mandatory fields for the general source term
138 call json_get_or_lookup_or_default(json, "start_time", start_time, 0.0_rp)
139 call json_get_or_lookup_or_default(json, "end_time", end_time, huge(0.0_rp))
140
141 ! Read the options for the permeability field
142 call json_get_or_lookup(json, 'brinkman.limits', brinkman_limits)
143 call json_get_or_lookup(json, 'brinkman.penalty', brinkman_penalty)
144
145 if (size(brinkman_limits) .ne. 2) then
146 call neko_error('brinkman_limits must be a 2 element array of reals')
147 end if
148
149 call this%free()
150 call this%init_base(fields, coef, start_time, end_time)
151
152 ! ------------------------------------------------------------------------ !
153 ! Allocate the permeability and indicator field
154
155 call neko_registry%add_field(coef%dof, 'brinkman_indicator', .true.)
156 call neko_registry%add_field(coef%dof, 'brinkman_indicator_unfiltered', &
157 .true.)
158 call neko_registry%add_field(coef%dof, 'brinkman_permeability', &
159 .true.)
160
161 this%indicator => neko_registry%get_field('brinkman_indicator')
162 this%indicator_unfiltered => &
163 neko_registry%get_field('brinkman_indicator_unfiltered')
164 this%brinkman => neko_registry%get_field('brinkman_permeability')
165
166 ! ------------------------------------------------------------------------ !
167 ! Select which constructor should be called
168
169 call json%info('objects', n_children = n_regions)
170
171 do i = 1, n_regions
172 call json_extract_item(json, "objects", i, object_settings)
173 call json_get_or_default(object_settings, 'type', object_type, 'none')
174
175 select case (object_type)
176 case ('boundary_mesh')
177 call this%init_boundary_mesh(object_settings)
178 case ('point_zone')
179 call this%init_point_zone(object_settings)
180
181 case ('none')
182 call object_settings%print()
183 call neko_error('Brinkman source term objects require a region type')
184 case default
185 call neko_error('Brinkman source term unknown region type')
186 end select
187
188 end do
189
190 ! ------------------------------------------------------------------------ !
191 ! Filter the indicator field
192
193 call json_get_or_default(json, 'filter.type', filter_type, 'none')
194 select case (filter_type)
195 case ('PDE')
196 ! Initialize the unfiltered design field
197 call this%indicator_unfiltered%init(coef%dof)
198
199 ! Allocate a PDE filter
200 allocate(pde_filter_t::this%filter)
201
202 ! Initialize the filter
203 call this%filter%init(json, coef)
204
205 ! Copy the current indicator to unfiltered (essentially a rename)
206 call field_copy(this%indicator_unfiltered, this%indicator)
207
208 ! Apply the filter
209 call this%filter%apply(this%indicator, this%indicator_unfiltered)
210
211 ! Set up sampler to include the unfiltered and filtered fields
212 call output%init(sp, 'brinkman', 3)
213 call output%fields%assign_to_field(1, this%indicator_unfiltered)
214 call output%fields%assign_to_field(2, this%indicator)
215 call output%fields%assign_to_field(3, this%brinkman)
216
217 case ('none')
218 ! Set up sampler to include the unfiltered field
219 call output%init(sp, 'brinkman', 2)
220 call output%fields%assign_to_field(1, this%indicator)
221 call output%fields%assign_to_field(2, this%brinkman)
222
223 case default
224 call neko_error('Brinkman source term unknown filter type')
225 end select
226
227 ! ------------------------------------------------------------------------ !
228 ! Compute the permeability field
229
230 this%brinkman = this%indicator
231 call permeability_field(this%brinkman, &
232 brinkman_limits(1), brinkman_limits(2), brinkman_penalty)
233
234 ! Sample the Brinkman field
235 call output%sample(0.0_rp)
236
238
241 class(brinkman_source_term_t), intent(inout) :: this
242
243 nullify(this%indicator)
244 nullify(this%indicator_unfiltered)
245 nullify(this%brinkman)
246
247 if (allocated(this%filter)) then
248 call this%filter%free()
249 deallocate(this%filter)
250 end if
251 call this%free_base()
252
253 end subroutine brinkman_source_term_free
254
257 subroutine brinkman_source_term_compute(this, time)
258 class(brinkman_source_term_t), intent(inout) :: this
259 type(time_state_t), intent(in) :: time
260 type(field_t), pointer :: u, v, w, fu, fv, fw
261 integer :: n
262
263 n = this%fields%item_size(1)
264
265 u => neko_registry%get_field('u')
266 v => neko_registry%get_field('v')
267 w => neko_registry%get_field('w')
268
269 fu => this%fields%get(1)
270 fv => this%fields%get(2)
271 fw => this%fields%get(3)
272
273 call field_subcol3(fu, u, this%brinkman, n)
274 call field_subcol3(fv, v, this%brinkman, n)
275 call field_subcol3(fw, w, this%brinkman, n)
276
277 end subroutine brinkman_source_term_compute
278
279 ! ========================================================================== !
280 ! Private methods
281
283 subroutine init_boundary_mesh(this, json)
284 class(brinkman_source_term_t), intent(inout) :: this
285 type(json_file), intent(inout) :: json
286
287 ! Options
288 character(len=:), allocatable :: mesh_file_name
289 character(len=:), allocatable :: distance_transform
290 character(len=:), allocatable :: filter_type
291 character(len=:), allocatable :: mesh_transform
292
293 ! Read the options for the boundary mesh
294 type(file_t) :: mesh_file
295 type(tri_mesh_t) :: boundary_mesh
296 real(kind=rp) :: scalar_r
297 real(kind=dp) :: scalar_d
298 logical :: cache, cache_exist
299 character(len=:), allocatable :: cache_filename
300 type(file_t) :: cache_file
301 type(fld_file_output_t) :: cache_output
302 type(fld_file_data_t) :: cache_data
303 type(global_interpolation_t) :: global_interp
304 type(space_t) :: prev_Xh
305 type(interpolator_t) :: space_interp
306
307 ! Mesh transform options variables
308 real(kind=dp), dimension(:), allocatable :: box_min, box_max
309 logical :: keep_aspect_ratio
310 real(kind=dp), dimension(3) :: scaling
311 real(kind=dp), dimension(3) :: translation
312 type(field_t) :: temp_field
313 type(aabb_t) :: mesh_box, target_box
314 integer :: idx_p
315 character(len=LOG_SIZE) :: log_msg
316
317 ! ------------------------------------------------------------------------ !
318 ! Read the options for the boundary mesh
319
320 call json_get(json, 'name', mesh_file_name)
321 call json_get_or_default(json, 'cache', cache, .false.)
322
323 ! Settings on how to filter the design field
324 call json_get(json, 'distance_transform.type', distance_transform)
325
326 ! ------------------------------------------------------------------------ !
327 ! Check if we can load from cache
328 if (cache) then
329 call json_get(json, 'cache_file', cache_filename)
330
331 inquire(file=trim(cache_filename) // "0.nek5000", exist=cache_exist)
332 write(log_msg, '(A)') "Checking for Brinkman source term cache."
333 call neko_log%message(log_msg, neko_log_debug)
334
335 if (cache_exist) then
336 write(log_msg, '(A)') "Loading Brinkman source term from cache."
337 call neko_log%message(log_msg, neko_log_debug)
338
339 call cache_data%init()
340 call temp_field%init(this%coef%dof)
341
342 call cache_file%init(cache_filename // "0.fld")
343 call cache_file%set_counter(0)
344 call cache_file%read(cache_data)
345
346 !
347 ! Check that the data in the fld file matches the current case.
348 ! Note that this is a safeguard and there are corner cases where
349 ! two different meshes have the same dimension and same # of elements
350 ! but this should be enough to cover obvious cases.
351 !
352 if (cache_data%glb_nelv .ne. temp_field%msh%glb_nelv .or. &
353 cache_data%gdim .ne. temp_field%msh%gdim) then
354 call neko_error("The fld file must match the current mesh! " // &
355 "Use 'interpolate': 'true' to enable interpolation.")
356 end if
357
358 ! Do the space-to-space interpolation
359 call prev_xh%init(gll, cache_data%lx, cache_data%ly, cache_data%lz)
360 call space_interp%init(temp_field%Xh, prev_xh)
361 call space_interp%map_host(temp_field%x, cache_data%p%x, &
362 cache_data%nelv, temp_field%Xh)
363 call space_interp%free()
364 call prev_xh%free()
365
366 ! Synchronize to device if needed
367 if (neko_bcknd_device .eq. 1) then
368 call device_memcpy(temp_field%x, temp_field%x_d, &
369 temp_field%size(), host_to_device, sync = .true.)
370 end if
371
372 ! Update the global indicator field by max operator
373 call field_pwmax2(this%indicator, temp_field)
374
375 ! Clean up
376 call cache_data%free()
377 call temp_field%free()
378 call cache_file%free()
379 return
380 end if
381 end if
382
383 ! ------------------------------------------------------------------------ !
384 ! Load the immersed boundary mesh
385
386 call mesh_file%init(mesh_file_name)
387 call mesh_file%read(boundary_mesh)
388
389 if (boundary_mesh%nelv .eq. 0) then
390 call neko_error('No elements in the boundary mesh')
391 end if
392
393 ! ------------------------------------------------------------------------ !
394 ! Transform the mesh if specified.
395
396 call json_get_or_default(json, 'mesh_transform.type', &
397 mesh_transform, 'none')
398
399 select case (mesh_transform)
400 case ('none')
401 ! Do nothing
402 case ('bounding_box')
403 call json_get_or_lookup(json, 'mesh_transform.box_min', box_min)
404 call json_get_or_lookup(json, 'mesh_transform.box_max', box_max)
405 call json_get_or_default(json, 'mesh_transform.keep_aspect_ratio', &
406 keep_aspect_ratio, .true.)
407
408 if (size(box_min) .ne. 3 .or. size(box_max) .ne. 3) then
409 call neko_error('Case file: mesh_transform. &
410 &box_min and box_max must be 3 element arrays of reals')
411 end if
412
413 call target_box%init(box_min, box_max)
414
415 mesh_box = get_aabb(boundary_mesh)
416
417 scaling = target_box%get_diagonal() / mesh_box%get_diagonal()
418 if (keep_aspect_ratio) then
419 scaling = minval(scaling)
420 end if
421
422 translation = - scaling * mesh_box%get_min() + target_box%get_min()
423
424 do idx_p = 1, boundary_mesh%mpts
425 boundary_mesh%points(idx_p)%x = &
426 scaling * boundary_mesh%points(idx_p)%x + translation
427 end do
428
429 ! Report the transformation applied
430 write(log_msg, '(A)') "The following transformation was applied:"
431 call neko_log%message(log_msg)
432 write(log_msg, '(A, 3F12.6)') "Scaling: ", scaling
433 call neko_log%message(log_msg)
434 write(log_msg, '(A, 3F12.6)') "Translation: ", translation
435 call neko_log%message(log_msg)
436
437 case default
438 call neko_error('Unknown mesh transform')
439 end select
440
441 ! ------------------------------------------------------------------------ !
442 ! Compute the permeability field
443
444 ! Assign the signed distance field to all GLL points in the permeability
445 ! field. Initially we just run a brute force loop over all GLL points and
446 ! compute the signed distance function. This should be replaced with a
447 ! more efficient method, such as a tree search.
448
449 call temp_field%init(this%coef%dof)
450
451 ! Select how to transform the distance field to a design field
452 select case (distance_transform)
453 case ('smooth_step')
454 call json_get_or_lookup(json, 'distance_transform.value', scalar_d)
455 scalar_r = real(scalar_d, kind=rp)
456
457 call signed_distance_field(temp_field, boundary_mesh, scalar_d)
458 call smooth_step_field(temp_field, scalar_r, 0.0_rp)
459
460 case ('step')
461
462 call json_get_or_lookup(json, 'distance_transform.value', scalar_d)
463 scalar_r = real(scalar_d, kind=rp)
464
465 call signed_distance_field(temp_field, boundary_mesh, scalar_d)
466 call step_function_field(temp_field, scalar_r, 1.0_rp, 0.0_rp)
467
468 case default
469 call neko_error('Unknown distance transform')
470 end select
471
472 ! Write the field to cache
473 if (cache) then
474 write(log_msg, '(A)') "Writing Brinkman source term to cache."
475 call neko_log%message(log_msg, neko_log_debug)
476 call cache_output%init(dp, cache_filename, 1)
477 call cache_output%fields%assign_to_field(1, temp_field)
478 call cache_output%sample(0.0_rp)
479 end if
480
481 ! Update the global indicator field by max operator
482 call field_pwmax2(this%indicator, temp_field)
483
484 call temp_field%free()
485
486 end subroutine init_boundary_mesh
487
489 subroutine init_point_zone(this, json)
490 class(brinkman_source_term_t), intent(inout) :: this
491 type(json_file), intent(inout) :: json
492
493 ! Options
494 character(len=:), allocatable :: zone_name
495
496 type(field_t) :: temp_field
497 class(point_zone_t), pointer :: zone
498 integer :: i
499
500 ! ------------------------------------------------------------------------ !
501 ! Read the options for the point zone
502
503 call json_get(json, 'name', zone_name)
504
505 ! Compute the indicator field
506 call temp_field%init(this%coef%dof)
507
508 zone => neko_point_zone_registry%get_point_zone(zone_name)
509
510 if (neko_bcknd_device .eq. 1) then
511 call device_cfill_mask(temp_field%x_d, 1.0_rp, temp_field%size(), &
512 zone%mask%get_d(), zone%size)
513 else
514 call cfill_mask(temp_field%x, 1.0_rp, temp_field%size(), &
515 zone%mask%get(), zone%size)
516 end if
517
518 ! Update the global indicator field by max operator
519 call field_pwmax2(this%indicator, temp_field)
520
521 end subroutine init_point_zone
522
523end module brinkman_source_term
double real
Copy data between host and device (or device and device)
Definition device.F90:71
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.
Axis Aligned Bounding Box (aabb) implementation in Fortran.
Definition aabb.f90:71
type(aabb_t) function, public get_aabb(object, padding)
Construct the aabb of a predefined object.
Definition aabb.f90:181
Implements the brinkman_source_term_t type.
subroutine init_point_zone(this, json)
Initializes the source term from a point zone.
subroutine init_boundary_mesh(this, json)
Initializes the source term from a boundary mesh.
subroutine brinkman_source_term_init_from_json(this, json, fields, coef, variable_name)
The common constructor using a JSON object.
subroutine brinkman_source_term_free(this)
Destructor.
subroutine brinkman_source_term_compute(this, time)
Computes the source term and adds the result to fields.
Coefficients.
Definition coef.f90:34
subroutine, public device_cfill_mask(a_d, c, n, mask_d, n_mask, strm)
Fill a constant to a masked vector. .
subroutine, public device_pwmax2(a_d, b_d, n, strm)
Compute the point-wise maximum of two vectors .
Device abstraction, common interface for various accelerators.
Definition device.F90:34
integer, parameter, public host_to_device
Definition device.F90:47
subroutine, public field_pwmax2(a, b, n)
Point-wise max operation .
subroutine, public field_copy(a, b, n)
Copy a vector .
subroutine, public field_subcol3(a, b, c, n)
Returns .
Defines a field.
Definition field.f90:34
Module for file I/O operations.
Definition file.f90:34
Filter to be applied to a scalar field.
Definition filter.f90:38
Simple module to handle fld file series. Provides an interface to the different fields sotred in a fl...
Implements fld_file_output_t.
Implements global_interpolation given a dofmap.
Routines to interpolate between different spaces.
Utilities for retrieving parameters from the case files.
Logging routines.
Definition log.f90:34
integer, parameter, public neko_log_debug
Debug log level.
Definition log.f90:87
type(log_t), public neko_log
Global log stream.
Definition log.f90:77
integer, parameter, public log_size
Definition log.f90:46
A module containing mapping functions and subroutines. These functions are used to modify fields in a...
Definition mappings.f90:36
subroutine, public permeability_field(f, k_0, k_1, q)
Apply a permeability function to a field.
Definition mappings.f90:90
subroutine, public smooth_step_field(f, edge0, edge1)
Apply a smooth step function to a field.
Definition mappings.f90:70
subroutine, public step_function_field(f, x0, value0, value1)
Apply a step function to a field.
Definition mappings.f90:109
Definition math.f90:60
subroutine, public pwmax2(a, b, n)
Point-wise maximum of two vectors .
Definition math.f90:1385
subroutine, public cfill_mask(a, c, n, mask, n_mask)
Fill a constant to a masked vector. .
Definition math.f90:397
Build configurations.
integer, parameter neko_bcknd_device
integer, parameter, public dp
Definition num_types.f90:9
integer, parameter, public sp
Definition num_types.f90:8
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Defines an output.
Definition output.f90:34
A PDE based filter.
type(point_zone_registry_t), target, public neko_point_zone_registry
Global point_zone registry.
Profiling interface.
Definition profiler.F90:34
subroutine, public profiler_start_region(name, region_id)
Started a named (name) profiler region.
Definition profiler.F90:78
subroutine, public profiler_end_region(name, region_id)
End the most recently started profiler region.
Definition profiler.F90:107
Defines a registry for storing solution fields.
Definition registry.f90:34
type(registry_t), target, public neko_registry
Global field registry.
Definition registry.f90:149
Module containing Signed Distance Functions.
subroutine, public signed_distance_field(field_data, object, max_distance)
Signed distance field.
Implements the source_term_t type and a wrapper source_term_wrapper_t.
Defines a function space.
Definition space.f90:34
integer, parameter, public gll
Definition space.f90:49
Module with things related to the simulation time.
Defines a triangular surface mesh.
Definition tri_mesh.f90:35
Utilities.
Definition utils.f90:35
Axis Aligned Bounding Box (aabb) data structure.
Definition aabb.f90:107
A Brinkman source term. The region and strength are controlled by assigning regions types and brinkma...
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:56
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:55
Base abstract class for filter.
Definition filter.f90:48
A simple output saving a list of fields to a .fld file.
Implements global interpolation for arbitrary points in the domain.
Interpolation between two space::space_t.
A PDE based filter mapping $\rho \mapsto \tilde{\rho}$, see Lazarov & O. Sigmund 2010,...
Base abstract type for point zones.
Base abstract type for source terms.
The function space for the SEM solution fields.
Definition space.f90:63
A struct that contains all info about the time, expand as needed.