Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
adjoint_scalar_scheme.f90
1! Copyright (c) 2022-2024, 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!
34
36 use gather_scatter, only : gs_t
37 use checkpoint, only : chkp_t
38 use num_types, only: rp
39 use field, only : field_t
40 use field_list, only: field_list_t
41 use space, only : space_t
42 use dofmap, only : dofmap_t
43 use krylov, only : ksp_t, krylov_solver_factory, ksp_max_iter
44 use coefs, only : coef_t
45 use dirichlet, only : dirichlet_t
46 use neumann, only : neumann_t
47 use jacobi, only : jacobi_t
48 use device_jacobi, only : device_jacobi_t
49 use sx_jacobi, only : sx_jacobi_t
50 use hsmg, only : hsmg_t
51 use bc, only : bc_t
52 use bc_list, only : bc_list_t
53 use precon, only : pc_t, precon_factory, precon_destroy
54 use field_dirichlet, only: field_dirichlet_t, field_dirichlet_update
55 use mesh, only : mesh_t, neko_msh_max_zlbls, neko_msh_max_zlbl_len
56 use facet_zone, only : facet_zone_t
57 use time_scheme_controller, only : time_scheme_controller_t
58 use logger, only : neko_log, log_size, neko_log_verbose
59 use field_registry, only : neko_field_registry
60 use usr_scalar, only : usr_scalar_t, usr_scalar_bc_eval
61 use json_utils, only : json_get, json_get_or_default, json_extract_item, &
62 json_extract_object
63 use json_module, only : json_file
64 use user_intf, only : user_t, dummy_user_material_properties, &
65 user_material_properties
66 use utils, only : neko_error, neko_warning
67 use comm, only: neko_comm, mpi_integer, mpi_sum
68 use scalar_source_term, only : scalar_source_term_t
69 use field_series, only : field_series_t
70 use math, only : cfill, add2s2
71 use field_math, only : field_add2s2
72 use device_math, only : device_cfill, device_add2s2
73 use neko_config, only : neko_bcknd_device
74 use field_series, only : field_series_t
75 use time_step_controller, only : time_step_controller_t
76 use gradient_jump_penalty, only : gradient_jump_penalty_t
77 use json_utils_ext, only: json_key_fallback
78 use scalar_scheme, only: scalar_scheme_precon_factory, &
79 scalar_scheme_solver_factory
80 use scratch_registry, only : neko_scratch_registry
81 use time_state, only : time_state_t
82 use device, only : device_memcpy, device_to_host
83 use field_math, only : field_col3, field_cmult2, field_add2, field_cfill
84 implicit none
85
87 type, abstract :: adjoint_scalar_scheme_t
89 character(len=:), allocatable :: name
91 type(field_t), pointer :: u
93 type(field_t), pointer :: v
95 type(field_t), pointer :: w
97 type(field_t), pointer :: s
99 type(field_t), pointer :: s_adj
101 type(field_series_t) :: s_adj_lag
103 type(space_t), pointer :: xh
105 type(dofmap_t), pointer :: dm_xh
107 type(gs_t), pointer :: gs_xh
109 type(coef_t), pointer :: c_xh
111 type(field_t), pointer :: f_xh => null()
113 type(scalar_source_term_t) :: source_term
115 class(ksp_t), allocatable :: ksp
117 integer :: ksp_maxiter
119 integer :: projection_dim
120
121 integer :: projection_activ_step
123 class(pc_t), allocatable :: pc
125 type(bc_list_t) :: bcs
127 type(json_file), pointer :: params
129 type(mesh_t), pointer :: msh => null()
131 type(chkp_t), pointer :: chkp => null()
133 character(len=:), allocatable :: nut_field_name
135 type(field_t), pointer :: rho => null()
137 type(field_t) :: lambda
139 type(field_t) :: cp
141 real(kind=rp) :: pr_turb
143 type(field_list_t) :: material_properties
145 logical :: variable_material_properties = .false.
146 procedure(user_material_properties), nopass, pointer :: &
147 user_material_properties => null()
149 logical :: if_gradient_jump_penalty
150 type(gradient_jump_penalty_t) :: gradient_jump_penalty
151 contains
153 procedure, pass(this) :: scheme_init => adjoint_scalar_scheme_init
155 procedure, pass(this) :: scheme_free => adjoint_scalar_scheme_free
157 procedure, pass(this) :: validate => adjoint_scalar_scheme_validate
159 procedure, pass(this) :: set_material_properties => &
160 adjoint_scalar_scheme_set_material_properties
162 procedure, pass(this) :: update_material_properties => &
163 adjoint_scalar_scheme_update_material_properties
165 procedure(adjoint_scalar_scheme_init_intrf), pass(this), deferred :: init
167 procedure(adjoint_scalar_scheme_free_intrf), pass(this), deferred :: free
169 procedure(adjoint_scalar_scheme_step_intrf), pass(this), deferred :: step
171 procedure(adjoint_scalar_scheme_restart_intrf), pass(this), deferred :: &
172 restart
174
176 abstract interface
177 subroutine adjoint_scalar_scheme_init_intrf(this, msh, coef, gs, params, &
178 user, ulag, vlag, wlag, time_scheme, rho)
180 import json_file
181 import coef_t
182 import gs_t
183 import mesh_t
184 import user_t
185 import field_series_t, field_t
186 import time_scheme_controller_t
187 import rp
188 class(adjoint_scalar_scheme_t), target, intent(inout) :: this
189 type(mesh_t), target, intent(in) :: msh
190 type(coef_t), target, intent(in) :: coef
191 type(gs_t), target, intent(inout) :: gs
192 type(json_file), target, intent(inout) :: params
193 type(user_t), target, intent(in) :: user
194 type(field_series_t), target, intent(in) :: ulag, vlag, wlag
195 type(time_scheme_controller_t), target, intent(in) :: time_scheme
196 type(field_t), target, intent(in) :: rho
198 end interface
199
201 abstract interface
202 subroutine adjoint_scalar_scheme_restart_intrf(this, dtlag, tlag)
204 import chkp_t
205 import rp
206 class(adjoint_scalar_scheme_t), target, intent(inout) :: this
207 real(kind=rp) :: dtlag(10), tlag(10)
209 end interface
210
212 abstract interface
215 class(adjoint_scalar_scheme_t), intent(inout) :: this
217 end interface
218
220 abstract interface
221 subroutine adjoint_scalar_scheme_step_intrf(this, t, tstep, dt, ext_bdf, &
222 dt_controller)
224 import time_scheme_controller_t
225 import time_step_controller_t
226 import rp
227 class(adjoint_scalar_scheme_t), intent(inout) :: this
228 real(kind=rp), intent(in) :: t
229 integer, intent(in) :: tstep
230 real(kind=rp), intent(in) :: dt
231 type(time_scheme_controller_t), intent(in) :: ext_bdf
232 type(time_step_controller_t), intent(in) :: dt_controller
234 end interface
235
236contains
237
247 subroutine adjoint_scalar_scheme_init(this, msh, c_Xh, gs_Xh, params, &
248 scheme, user, rho)
249 class(adjoint_scalar_scheme_t), target, intent(inout) :: this
250 type(mesh_t), target, intent(in) :: msh
251 type(coef_t), target, intent(in) :: c_Xh
252 type(gs_t), target, intent(inout) :: gs_Xh
253 type(json_file), target, intent(inout) :: params
254 character(len=*), intent(in) :: scheme
255 type(user_t), target, intent(in) :: user
256 type(field_t), target, intent(in) :: rho
257 ! IO buffer for log output
258 character(len=LOG_SIZE) :: log_buf
259 ! Variables for retrieving json parameters
260 logical :: logical_val
261 real(kind=rp) :: solver_abstol
262 integer :: integer_val
263 character(len=:), allocatable :: solver_type, solver_precon
264 ! real(kind=rp) :: GJP_param_a, GJP_param_b
265 character(len=:), allocatable :: json_key
266 type(json_file) :: precon_params
267
268 this%u => neko_field_registry%get_field('u')
269 this%v => neko_field_registry%get_field('v')
270 this%w => neko_field_registry%get_field('w')
271 this%s => neko_field_registry%get_field('s')
272 this%rho => rho
273
274 call neko_log%section('Adjoint scalar')
275 ! In principle this could be different,
276 ! but I think we should use the same as the forward
277 call json_get(params, 'case.scalar.solver.type', solver_type)
278 json_key = json_key_fallback(params, &
279 'case.adjoint_scalar.solver.preconditioner.type', &
280 'case.scalar.solver.preconditioner.type')
281 call json_get(params, json_key, solver_precon)
282
283 json_key = json_key_fallback(params, &
284 'case.adjoint_scalar.solver.preconditioner', &
285 'case.scalar.solver.preconditioner')
286 call json_extract_object(params, json_key, precon_params)
287
288 json_key = json_key_fallback(params, &
289 'case.adjoint_scalar.solver.absolute_tolerance', &
290 'case.scalar.solver.absolute_tolerance')
291 call json_get(params, json_key, solver_abstol)
292
293 json_key = json_key_fallback(params, &
294 'case.adjoint_scalar.solver.projection_space_size', &
295 'case.scalar.solver.projection_space_size')
296 call json_get_or_default(params, json_key, this%projection_dim, 0)
297
298 json_key = json_key_fallback(params, &
299 'case.adjoint_scalar.solver.projection_hold_steps', &
300 'case.scalar.solver.projection_hold_steps')
301 call json_get_or_default(params, json_key, this%projection_activ_step, 5)
302
303
304 write(log_buf, '(A, A)') 'Type : ', trim(scheme)
305 call neko_log%message(log_buf)
306 call neko_log%message('Ksp adjoint scalar : ('// trim(solver_type) // &
307 ', ' // trim(solver_precon) // ')')
308 write(log_buf, '(A,ES13.6)') ' `-abs tol :', solver_abstol
309 call neko_log%message(log_buf)
310
311 this%Xh => this%u%Xh
312 this%dm_Xh => this%u%dof
313 this%params => params
314 this%msh => msh
315 if (.not. neko_field_registry%field_exists('s_adj')) then
316 call neko_field_registry%add_field(this%dm_Xh, 's_adj')
317 end if
318 this%s_adj => neko_field_registry%get_field('s_adj')
319
320 call this%s_adj_lag%init(this%s_adj, 2)
321
322 this%gs_Xh => gs_xh
323 this%c_Xh => c_xh
324
325 !
326 ! Material properties
327 !
328 call this%set_material_properties(params, user)
329 !
330 ! Turbulence modelling and variable material properties
331 !
332 if (params%valid_path('case.scalar.variable_material_properties')) then
333 call json_get(params, 'variable_material_properties', &
334 this%variable_material_properties)
335
336 ! Warn, no variable properties, but nut_field
337 if ((params%valid_path('case.scalar.nut_field')) .and. &
338 (this%variable_material_properties .eqv. .false.)) then
339 call neko_warning("You set variable_material properties to " // &
340 "false, the nut_field setting will have no effect.")
341 end if
342
343 ! Warn, no variable properties, but user routine associated
344 if ((.not. associated(user%material_properties, &
345 dummy_user_material_properties)) .and. &
346 (this%variable_material_properties .eqv. .false.)) then
347 call neko_warning("You set variable_material properties to " // &
348 "false, you can only vary rho and mu in time in the user file.")
349 end if
350 else if (params%valid_path('case.scalar.nut_field')) then
351 call json_get(params, 'case.scalar.Pr_t', this%pr_turb)
352 call json_get(params, 'case.scalar.nut_field', this%nut_field_name)
353 this%variable_material_properties = .true.
354 else if (.not. associated(user%material_properties, &
355 dummy_user_material_properties)) then
356 this%nut_field_name = ""
357 this%variable_material_properties = .true.
358 end if
359
360 write(log_buf, '(A,L1)') 'LES : ', this%variable_material_properties
361 call neko_log%message(log_buf)
362
363
364 !
365 ! Setup right-hand side field.
366 !
367 allocate(this%f_Xh)
368 call this%f_Xh%init(this%dm_Xh, fld_name = "adjoint_scalar_rhs")
369
370 ! Initialize the source term
371 call this%source_term%init(this%f_Xh, this%c_Xh, user)
372 ! We should ONLY read the adjoint
373 call this%source_term%add(params, 'case.adjoint_scalar.source_terms')
374
375 ! todo parameter file ksp tol should be added
376 json_key = json_key_fallback(params, &
377 'case.adjoint_fluid.velocity_solver.max_iterations', &
378 'case.fluid.velocity_solver.max_iterations')
379 call json_get_or_default(params, json_key, integer_val, ksp_max_iter)
380 json_key = json_key_fallback(params, &
381 'case.adjoint_fluid.velocity_solver.monitor', &
382 'case.fluid.velocity_solver.monitor')
383 call json_get_or_default(params, json_key, logical_val, .false.)
384 call scalar_scheme_solver_factory(this%ksp, this%dm_Xh%size(), &
385 solver_type, integer_val, solver_abstol, logical_val)
386 call scalar_scheme_precon_factory(this%pc, this%ksp, &
387 this%c_Xh, this%dm_Xh, this%gs_Xh, this%bcs, &
388 solver_precon, precon_params)
389
390 ! Initiate gradient jump penalty
391 json_key = json_key_fallback(params, &
392 'case.adjoint_scalar.gradient_jump_penalty.enabled', &
393 'case.scalar.gradient_jump_penalty.enabled')
394 call json_get_or_default(params, json_key, &
395 this%if_gradient_jump_penalty, .false.)
396
397 if (this%if_gradient_jump_penalty .eqv. .true.) then
398 call neko_error("gradient jump not implemented for adjoint scalar")
399 ! if ((this%dm_Xh%xh%lx - 1) .eq. 1) then
400 ! call json_get_or_default(params, &
401 ! 'case.scalar.gradient_jump_penalty.tau',&
402 ! GJP_param_a, 0.02_rp)
403 ! GJP_param_b = 0.0_rp
404 ! else
405 ! call json_get_or_default(params, &
406 ! 'case.scalar.gradient_jump_penalty.scaling_factor',&
407 ! GJP_param_a, 0.8_rp)
408 ! call json_get_or_default(params, &
409 ! 'case.scalar.gradient_jump_penalty.scaling_exponent',&
410 ! GJP_param_b, 4.0_rp)
411 ! end if
412 ! call this%gradient_jump_penalty%init(params, this%dm_Xh, this%c_Xh, &
413 ! GJP_param_a, GJP_param_b)
414 end if
415
416 call neko_log%end_section()
417
418 end subroutine adjoint_scalar_scheme_init
419
420
422 subroutine adjoint_scalar_scheme_free(this)
423 class(adjoint_scalar_scheme_t), intent(inout) :: this
424
425 nullify(this%Xh)
426 nullify(this%dm_Xh)
427 nullify(this%gs_Xh)
428 nullify(this%c_Xh)
429 nullify(this%params)
430
431 if (allocated(this%ksp)) then
432 call this%ksp%free()
433 deallocate(this%ksp)
434 end if
435
436 if (allocated(this%pc)) then
437 call precon_destroy(this%pc)
438 deallocate(this%pc)
439 end if
440
441 call this%source_term%free()
442
443 call this%bcs%free()
444
445 call this%cp%free()
446 call this%lambda%free()
447
448 ! Free gradient jump penalty
449 if (this%if_gradient_jump_penalty .eqv. .true.) then
450 call this%gradient_jump_penalty%free()
451 end if
452
453 end subroutine adjoint_scalar_scheme_free
454
457 subroutine adjoint_scalar_scheme_validate(this)
458 class(adjoint_scalar_scheme_t), target, intent(inout) :: this
459
460 if ( (.not. allocated(this%u%x)) .or. &
461 (.not. allocated(this%v%x)) .or. &
462 (.not. allocated(this%w%x)) .or. &
463 (.not. allocated(this%s%x)) .or. &
464 (.not. allocated(this%s_adj%x))) then
465 call neko_error('Fields are not allocated')
466 end if
467
468 if (.not. allocated(this%ksp)) then
469 call neko_error('No Krylov solver for velocity defined')
470 end if
471
472 if (.not. associated(this%Xh)) then
473 call neko_error('No function space defined')
474 end if
475
476 if (.not. associated(this%dm_Xh)) then
477 call neko_error('No dofmap defined')
478 end if
479
480 if (.not. associated(this%c_Xh)) then
481 call neko_error('No coefficients defined')
482 end if
483
484 if (.not. associated(this%f_Xh)) then
485 call neko_error('No rhs allocated')
486 end if
487
488 if (.not. associated(this%params)) then
489 call neko_error('No parameters defined')
490 end if
491
492 !
493 ! Setup checkpoint structure (if everything is fine)
494 !
495! @todo no io for now
496! call this%chkp%init(this%u, this%v, this%w, this%p)
497
498 end subroutine adjoint_scalar_scheme_validate
499
505 subroutine adjoint_scalar_scheme_update_material_properties(t, tstep, this)
506 class(adjoint_scalar_scheme_t), intent(inout) :: this
507 real(kind=rp),intent(in) :: t
508 integer, intent(in) :: tstep
509 type(field_t), pointer :: nut
510 integer :: index
511 ! Factor to transform nu_t to lambda_t
512 type(field_t), pointer :: lambda_factor
513
514 call this%user_material_properties(t, tstep, this%name, &
515 this%material_properties)
516
517 ! factor = rho * cp / pr_turb
518 if (this%variable_material_properties .and. &
519 len(trim(this%nut_field_name)) > 0) then
520 nut => neko_field_registry%get_field(this%nut_field_name)
521
522 ! lambda = lambda + rho * cp * nut / pr_turb
523 call neko_scratch_registry%request_field(lambda_factor, index)
524
525 call field_col3(lambda_factor, this%cp, this%rho)
526 call field_cmult2(lambda_factor, nut, 1.0_rp / this%pr_turb)
527 call field_add2(this%lambda, lambda_factor)
528 call neko_scratch_registry%relinquish_field(index)
529 end if
530
531 ! Since cp is a fields and we use the %x(1,1,1,1) of the
532 ! host array data to pass constant material properties
533 ! to some routines, we need to make sure that the host
534 ! values are also filled
535 if (neko_bcknd_device .eq. 1) then
536 call device_memcpy(this%cp%x, this%cp%x_d, this%cp%size(), &
537 device_to_host, sync=.false.)
538 end if
539
540 end subroutine adjoint_scalar_scheme_update_material_properties
541
546 subroutine adjoint_scalar_scheme_set_material_properties(this, params, user)
547 class(adjoint_scalar_scheme_t), intent(inout) :: this
548 type(json_file), intent(inout) :: params
549 type(user_t), target, intent(in) :: user
550 character(len=LOG_SIZE) :: log_buf
551 ! A local pointer that is needed to make Intel happy
552 procedure(user_material_properties), pointer :: dummy_mp_ptr
553 real(kind=rp) :: const_cp, const_lambda
554
555 ! TODO
556 ! I think everything should be read from .scalar. and we shouldnt
557 ! change anything, but this should be come back to.
558 dummy_mp_ptr => dummy_user_material_properties
559
560 ! Fill lambda field with the physical value
561 call this%lambda%init(this%dm_Xh, "lambda")
562 call this%cp%init(this%dm_Xh, "cp")
563
564 call this%material_properties%init(2)
565 call this%material_properties%assign_to_field(1, this%cp)
566 call this%material_properties%assign_to_field(2, this%lambda)
567
568 if (.not. associated(user%material_properties, dummy_mp_ptr)) then
569
570 write(log_buf, '(A)') "Material properties must be set in the user " // &
571 "file!"
572 call neko_log%message(log_buf)
573 this%user_material_properties => user%material_properties
574 call user%material_properties(0.0_rp, 0, this%name, &
575 this%material_properties)
576 else
577 this%user_material_properties => dummy_user_material_properties
578 if (params%valid_path('case.scalar.Pe') .and. &
579 (params%valid_path('case.scalar.lambda') .or. &
580 params%valid_path('case.scalar.cp'))) then
581 call neko_error("To set the material properties for the scalar, " // &
582 "either provide Pe OR lambda and cp in the case file.")
583 ! Non-dimensional case
584 else if (params%valid_path('case.scalar.Pe')) then
585 write(log_buf, '(A)') 'Non-dimensional scalar material properties' //&
586 ' input.'
587 call neko_log%message(log_buf, lvl = neko_log_verbose)
588 write(log_buf, '(A)') 'Specific heat capacity will be set to 1,'
589 call neko_log%message(log_buf, lvl = neko_log_verbose)
590 write(log_buf, '(A)') 'conductivity to 1/Pe. Assumes density is 1.'
591 call neko_log%message(log_buf, lvl = neko_log_verbose)
592
593 ! Read Pe into lambda for further manipulation.
594 call json_get(params, 'case.scalar.Pe', const_lambda)
595 write(log_buf, '(A,ES13.6)') 'Pe :', const_lambda
596 call neko_log%message(log_buf)
597
598 ! Set cp and rho to 1 since the setup is non-dimensional.
599 const_cp = 1.0_rp
600 ! Invert the Pe to get conductivity
601 const_lambda = 1.0_rp/const_lambda
602 ! Dimensional case
603 else
604 call json_get(params, 'case.scalar.lambda', const_lambda)
605 call json_get(params, 'case.scalar.cp', const_cp)
606 end if
607 end if
608 ! We need to fill the fields based on the parsed const values
609 ! if the user routine is not used.
610 if (associated(user%material_properties, dummy_mp_ptr)) then
611 ! Fill mu and rho field with the physical value
612 call field_cfill(this%lambda, const_lambda)
613 call field_cfill(this%cp, const_cp)
614
615 write(log_buf, '(A,ES13.6)') 'lambda :', const_lambda
616 call neko_log%message(log_buf)
617 write(log_buf, '(A,ES13.6)') 'cp :', const_cp
618 call neko_log%message(log_buf)
619 end if
620
621 ! Since cp is a field and we use the %x(1,1,1,1) of the
622 ! host array data to pass constant material properties
623 ! to some routines, we need to make sure that the host
624 ! values are also filled
625 if (neko_bcknd_device .eq. 1) then
626 call device_memcpy(this%cp%x, this%cp%x_d, this%cp%size(), &
627 device_to_host, sync=.false.)
628 end if
629 end subroutine adjoint_scalar_scheme_set_material_properties
630
631end module adjoint_scalar_scheme
Abstract interface to dealocate a scalar formulation.
Abstract interface to initialize a scalar formulation.
Abstract interface to restart a scalar formulation.
Contains the adjoint_scalar_scheme_t type.
Base type for a scalar advection-diffusion solver.