Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
adjoint_scalar_pnpn.f90
Go to the documentation of this file.
1
34!
36
38 use comm, only: neko_comm
39 use utils, only: neko_error
40 use num_types, only: rp, dp
41 use, intrinsic :: iso_fortran_env, only: error_unit
42 use rhs_maker, only : rhs_maker_bdf_t, rhs_maker_ext_t, rhs_maker_oifs_t, &
43 rhs_maker_ext_fctry, rhs_maker_bdf_fctry, rhs_maker_oifs_fctry
45 use checkpoint_payload, only : checkpoint_payload_t
46 use checkpoint, only : chkp_t
47 use field, only : field_t
48 use scalar_bc_projector, only : scalar_bc_projector_t
49 use mesh, only : mesh_t
50 use coefs, only : coef_t
51 use device, only : host_to_device, device_memcpy
52 use gather_scatter, only : gs_t, gs_op_add
53 use scalar_residual, only : scalar_residual_t, scalar_residual_factory
54 use ax_product, only : ax_t, ax_helm_allocator
55 use field_series, only: field_series_t
56 use facet_normal, only : facet_normal_t
57 use krylov, only : ksp_monitor_t
58 use device_math, only : device_add2s2, device_col2
59 use time_scheme_controller, only : time_scheme_controller_t
60 use projection, only : projection_t
61 use math, only : glsc2, col2, add2s2
62 use field_math, only : field_col3, field_col2
63 use logger, only : neko_log, log_size, neko_log_debug
64 use advection_adjoint, only : advection_adjoint_t, advection_adjoint_factory
65 use profiler, only : profiler_start_region, profiler_end_region
66 use json_utils, only : json_get, json_get_or_default, json_extract_item
67 use json_module, only : json_file, json_core, json_value
68 use user_intf, only : user_t
69 use neko_config, only : neko_bcknd_device
70 use time_step_controller, only : time_step_controller_t
71 use scratch_registry, only : neko_scratch_registry
72 use time_state, only : time_state_t
73 use bc, only : bc_t, bc_dirichlet
74 use mpi_f08, only: mpi_integer, mpi_sum, mpi_max
75 implicit none
76 private
77
78
80
82 type(field_t) :: s_adj_res
83
85 type(field_t) :: ds_adj
86
88 class(ax_t), allocatable :: ax
89
91 type(projection_t) :: proj_s
92
94 type(scalar_bc_projector_t) :: bc_projector
95
97 class(advection_adjoint_t), allocatable :: adv
98
99 ! Time interpolation scheme
100 logical :: oifs
101
102 ! Advection terms for the oifs method
103 type(field_t) :: advs
104
106 class(scalar_residual_t), allocatable :: res
107
109 class(rhs_maker_ext_t), allocatable :: makeext
110
112 class(rhs_maker_bdf_t), allocatable :: makebdf
113
115 class(rhs_maker_oifs_t), allocatable :: makeoifs
116
117 contains
119 procedure, pass(this) :: init => adjoint_scalar_pnpn_init
121 procedure, pass(this) :: register_checkpoint => &
122 adjoint_scalar_pnpn_register_checkpoint
124 procedure, pass(this) :: restart => adjoint_scalar_pnpn_restart
126 procedure, pass(this) :: free => adjoint_scalar_pnpn_free
128 procedure, pass(this) :: step => adjoint_scalar_pnpn_step
130 procedure, pass(this) :: setup_bcs_ => adjoint_scalar_pnpn_setup_bcs_
131 end type adjoint_scalar_pnpn_t
132
140 module subroutine adjoint_bc_factory(object, scheme, json, coef, user)
141 class(bc_t), pointer, intent(inout) :: object
142 type(adjoint_scalar_pnpn_t), intent(in) :: scheme
143 type(json_file), intent(inout) :: json
144 type(coef_t), target, intent(in) :: coef
145 type(user_t), target, intent(in) :: user
146 end subroutine adjoint_bc_factory
147 end interface adjoint_bc_factory
148
149contains
150
167 subroutine adjoint_scalar_pnpn_init(this, msh, coef, gs, params_adjoint, &
168 params_primal, numerics_params, user, chkp, ulag, vlag, wlag, &
169 time_scheme, rho)
170 class(adjoint_scalar_pnpn_t), target, intent(inout) :: this
171 type(mesh_t), target, intent(in) :: msh
172 type(coef_t), target, intent(in) :: coef
173 type(gs_t), target, intent(inout) :: gs
174 type(json_file), target, intent(inout) :: params_adjoint
175 type(json_file), target, intent(inout) :: params_primal
176 type(json_file), target, intent(inout) :: numerics_params
177 type(user_t), target, intent(in) :: user
178 type(chkp_t), target, intent(inout) :: chkp
179 type(field_series_t), target, intent(in) :: ulag, vlag, wlag
180 type(time_scheme_controller_t), target, intent(in) :: time_scheme
181 type(field_t), target, intent(in) :: rho
182 integer :: i
183 class(bc_t), pointer :: bc_i
184 character(len=15), parameter :: scheme = 'Modular (Pn/Pn)'
185 logical :: advection
186 real(kind=dp), pointer :: tlag(:), dtlag(:)
187
188 call this%free()
189
190 ! Initiliaze base type.
191 call this%scheme_init(msh, coef, gs, params_adjoint, params_primal, &
192 scheme, user, rho)
193
194 ! Setup backend dependent Ax routines
195 call ax_helm_allocator(this%ax, type_name = "standard")
196
197 ! Setup backend dependent scalar residual routines
198 call scalar_residual_factory(this%res)
199
200 ! Setup backend dependent summation of extrapolation scheme
201 call rhs_maker_ext_fctry(this%makeext)
202
203 ! Setup backend dependent contributions to F from lagged BD terms
204 call rhs_maker_bdf_fctry(this%makebdf)
205
206 ! Setup backend dependent contributions of the OIFS scheme
207 call rhs_maker_oifs_fctry(this%makeoifs)
208
209 ! Initialize variables specific to this plan
210 associate(xh_lx => this%Xh%lx, xh_ly => this%Xh%ly, xh_lz => this%Xh%lz, &
211 dm_xh => this%dm_Xh, nelv => this%msh%nelv)
212
213 call this%s_adj_res%init(dm_xh, "s_adj_res")
214
215 call this%abx1%init(dm_xh, "abx1")
216
217 call this%abx2%init(dm_xh, "abx2")
218
219 call this%advs%init(dm_xh, "advs")
220
221 call this%ds_adj%init(dm_xh, 'ds_adj')
222
223 end associate
224
225 ! Set up boundary conditions
226 call this%setup_bcs_(user)
227
228 ! Collect the Dirichlet dofs of all bcs into the increment projector.
229 do i = 1, this%bcs%size()
230 if (this%bcs%bc_type(i) .eq. bc_dirichlet) then
231 bc_i => this%bcs%get(i)
232 call this%bc_projector%mark(bc_i)
233 end if
234 end do
235
236
237 ! Initialize projection space
238 call this%proj_s%init(this%dm_Xh%size(), this%projection_dim, &
239 this%projection_activ_step)
240
241 ! Determine the time-interpolation scheme
242 call json_get_or_default(numerics_params, 'oifs', this%oifs, .false.)
243
244 ! Point to case checkpoint
245 this%chkp => chkp
246
247 ! Initialize advection factory
248 call json_get_or_default(params_adjoint, 'advection', advection, .true.)
249
250 call chkp%get_time_history(tlag, dtlag)
251 call advection_adjoint_factory(this%adv, numerics_params, this%c_Xh, &
252 ulag, vlag, wlag, dtlag, &
253 tlag, time_scheme, .not. advection, &
254 this%s_adj_lag)
255 end subroutine adjoint_scalar_pnpn_init
256
258 subroutine adjoint_scalar_pnpn_register_checkpoint(this, chkp)
259 class(adjoint_scalar_pnpn_t), target, intent(inout) :: this
260 type(chkp_t), intent(inout) :: chkp
261 type(checkpoint_payload_t), pointer :: payload
262
263 payload => chkp%add_payload("adjoint_scalars/" // trim(this%name))
264 call payload%add_field(this%s_adj)
265 call payload%add_series(this%s_adj_lag)
266 call payload%add_field(this%abx1)
267 call payload%add_field(this%abx2)
268
269 end subroutine adjoint_scalar_pnpn_register_checkpoint
270
272 subroutine adjoint_scalar_pnpn_restart(this, chkp)
273 class(adjoint_scalar_pnpn_t), target, intent(inout) :: this
274 type(chkp_t), intent(inout) :: chkp
275 logical :: interpolated
276 integer :: n
277
278 n = this%s_adj%dof%size()
279
280 ! Lag fields are restored through the checkpoint's fsp mechanism
281
282 ! The restored fields are continuous unless the checkpoint was written
283 ! on another mesh or at another polynomial order and was interpolated
284 ! on the way in. Only then do the copies of a node shared between
285 ! elements need averaging: scale by the inverse multiplicity, then sum
286 ! the copies with a gather-scatter. On a plain restart that is the
287 ! identity in exact arithmetic but not in floating point, and would put
288 ! about one ulp of error on every shared node. Same guard as the fluid.
289 interpolated = allocated(chkp%previous_mesh%elements) .or. &
290 chkp%previous_Xh%lx .ne. this%Xh%lx
291
292 if (interpolated) then
293 call col2(this%s_adj%x, this%c_Xh%mult, n)
294 call col2(this%s_adj_lag%lf(1)%x, this%c_Xh%mult, n)
295 call col2(this%s_adj_lag%lf(2)%x, this%c_Xh%mult, n)
296 end if
297 if (neko_bcknd_device .eq. 1) then
298 call device_memcpy(this%s_adj%x, this%s_adj%x_d, &
299 n, host_to_device, sync = .false.)
300 call device_memcpy(this%s_adj_lag%lf(1)%x, this%s_adj_lag%lf(1)%x_d, &
301 n, host_to_device, sync = .false.)
302 call device_memcpy(this%s_adj_lag%lf(2)%x, this%s_adj_lag%lf(2)%x_d, &
303 n, host_to_device, sync = .false.)
304 call device_memcpy(this%abx1%x, this%abx1%x_d, &
305 n, host_to_device, sync = .false.)
306 call device_memcpy(this%abx2%x, this%abx2%x_d, &
307 n, host_to_device, sync = .false.)
308 call device_memcpy(this%advs%x, this%advs%x_d, &
309 n, host_to_device, sync = .false.)
310 end if
311
312 if (interpolated) then
313 call this%gs_Xh%op(this%s_adj, gs_op_add)
314 call this%gs_Xh%op(this%s_adj_lag%lf(1), gs_op_add)
315 call this%gs_Xh%op(this%s_adj_lag%lf(2), gs_op_add)
316 end if
317
318 end subroutine adjoint_scalar_pnpn_restart
319
320 subroutine adjoint_scalar_pnpn_free(this)
321 class(adjoint_scalar_pnpn_t), intent(inout) :: this
322
323 !Deallocate scalar field
324 call this%scheme_free()
325
326 call this%bc_projector%free()
327 call this%proj_s%free()
328
329 call this%s_adj_res%free()
330
331 call this%ds_adj%free()
332
333 call this%abx1%free()
334 call this%abx2%free()
335
336 call this%advs%free()
337
338 if (allocated(this%Ax)) then
339 deallocate(this%Ax)
340 end if
341
342 if (allocated(this%res)) then
343 deallocate(this%res)
344 end if
345
346 if (allocated(this%makeext)) then
347 deallocate(this%makeext)
348 end if
349
350 if (allocated(this%makebdf)) then
351 deallocate(this%makebdf)
352 end if
353
354 if (allocated(this%makeoifs)) then
355 deallocate(this%makeoifs)
356 end if
357
358 end subroutine adjoint_scalar_pnpn_free
359
360 subroutine adjoint_scalar_pnpn_step(this, time, ext_bdf, dt_controller, &
361 ksp_results)
362 class(adjoint_scalar_pnpn_t), intent(inout) :: this
363 type(time_state_t), intent(in) :: time
364 type(time_scheme_controller_t), intent(in) :: ext_bdf
365 type(time_step_controller_t), intent(in) :: dt_controller
366 type(ksp_monitor_t), intent(inout) :: ksp_results
367 type(field_t), pointer :: rho_cp
368 integer :: rho_cp_index
369 ! Number of degrees of freedom
370 integer :: n
371
372 if (this%freeze) return
373
374 n = this%dm_Xh%size()
375 call neko_scratch_registry%request_field(rho_cp, rho_cp_index, .false.)
376
377 call profiler_start_region('Adjoint Scalar')
378 associate(u => this%u, v => this%v, w => this%w, s_adj => this%s_adj, &
379 cp => this%cp, rho => this%rho, lambda => this%lambda, &
380 ds_adj => this%ds_adj, &
381 s_adj_res => this%s_adj_res, &
382 ax => this%Ax, f_xh => this%f_Xh, xh => this%Xh, &
383 c_xh => this%c_Xh, dm_xh => this%dm_Xh, gs_xh => this%gs_Xh, &
384 s_adj_lag => this%s_adj_lag, oifs => this%oifs, &
385 projection_dim => this%projection_dim, &
386 msh => this%msh, res => this%res, makeoifs => this%makeoifs, &
387 makeext => this%makeext, makebdf => this%makebdf, &
388 t => time%t, tstep => time%tstep, dt => time%dt)
389
390 ! Logs extra information the log level is NEKO_LOG_DEBUG or above.
391 call print_debug(this)
392
393 ! Update material properties and their pointwise product.
394 ! This MUST happen before rho_cp is used below (by makebdf and by the
395 ! source/advection scaling). It used to sit after those uses, which left
396 ! makebdf reading an uninitialised scratch field while res%compute used
397 ! the correct value -- so the BDF mass terms could not cancel at steady
398 ! state and the converged adjoint scalar came out proportional to dt.
399 ! Mirrors the ordering in Neko's forward scalar_pnpn.
400 call this%update_material_properties(time)
401 call field_col3(rho_cp, rho, cp)
402
403 ! Compute the source terms that are extrapolated in time below
404 call this%source_term%compute(time, extrapolate = .true.)
405
406 ! if (oifs) then
407 ! call neko_error("oifs not implemented for adjoint scalar")
408 ! ! Add the advection operators to the right-hans-side.
409 ! call this%adv%compute_scalar(u, v, w, s_adj, this%advs, &
410 ! Xh, this%c_Xh, dm_Xh%size())
411
412 ! call makeext%compute_scalar(this%abx1, this%abx2, f_Xh%x, rho, &
413 ! ext_bdf%advection_coeffs, n)
414
415 ! call makeoifs%compute_scalar(this%advs%x, f_Xh%x, rho, dt, n)
416 ! else
417 ! Add the advection operators to the right-hans-side.
418 call this%adv%compute_adjoint_scalar(u, v, w, s_adj, f_xh, &
419 xh, this%c_Xh, dm_xh%size())
420
421 ! Scale the volumetric source and advection terms by rho * cp, then add
422 ! the weak boundary fluxes without that scaling -- same split as the
423 ! forward scalar. (The weak BC application was previously done before
424 ! the advection term and never scaled, so it could not follow this
425 ! convention.)
426 call field_col2(f_xh, rho_cp)
427
428 ! Apply weak boundary conditions, that contribute to the source terms.
429 call this%bcs%apply_scalar(this%f_Xh%x, dm_xh%size(), time, .false.)
430
431 ! At this point the RHS contains the sum of the advection operator,
432 ! Neumann boundary sources and additional source terms, evaluated using
433 ! the scalar field from the previous time-step.
434 ! Now, this value is used in the explicit time scheme to advance these
435 ! terms in time.
436 call makeext%compute_scalar(this%abx1, this%abx2, f_xh%x, &
437 ext_bdf%advection_coeffs%x, n)
438
439 ! Add the RHS contributions coming from the BDF scheme.
440 call makebdf%compute_scalar(s_adj_lag, f_xh%x, s_adj, c_xh%B, &
441 rho_cp, dt, ext_bdf%diffusion_coeffs%x, ext_bdf%ndiff, n)
442 ! end if
443
444 ! Source terms that must not be extrapolated in time are added as
445 ! computed, weighted by the mass matrix and rho * cp like the
446 ! extrapolated terms above.
447 call this%source_term%compute(time, extrapolate = .false., &
448 scale = rho_cp)
449
450 call s_adj_lag%update()
451
453 call this%bcs%apply_scalar(this%s_adj%x, this%dm_Xh%size(), time, &
454 .true.)
455
456 ! Compute scalar residual.
457 call profiler_start_region('Adjoint_scalar_residual')
458 call res%compute(ax, s_adj, s_adj_res, f_xh, c_xh, msh, xh, &
459 lambda, rho_cp, ext_bdf%diffusion_coeffs%x(1), &
460 dt, dm_xh%size())
461
462 call gs_xh%op(s_adj_res, gs_op_add)
463
464
465 ! Zero-out residual at Dirichlet nodes before solving.
466 call this%bc_projector%apply(s_adj_res%x, dm_xh%size())
467
468 call profiler_end_region('Adjoint_scalar_residual')
469
470 call this%proj_s%pre_solving(s_adj_res%x, tstep, c_xh, n, dt_controller)
471
472 call this%pc%update()
473 call profiler_start_region('Adjoint_scalar_solve')
474 ksp_results = this%ksp%solve(ax, ds_adj, s_adj_res%x, n, &
475 c_xh, this%bc_projector, gs_xh)
476 call profiler_end_region('Adjoint_scalar_solve')
477
478 ksp_results%name = 'Adjoint Scalar'
479
480 call this%proj_s%post_solving(ds_adj%x, ax, c_xh, this%bc_projector, &
481 gs_xh, n, tstep, dt_controller)
482
483 ! Update the solution
484 if (neko_bcknd_device .eq. 1) then
485 call device_add2s2(s_adj%x_d, ds_adj%x_d, 1.0_rp, n)
486 else
487 call add2s2(s_adj%x, ds_adj%x, 1.0_rp, n)
488 end if
489
490 end associate
491 call neko_scratch_registry%relinquish_field(rho_cp_index)
492 call profiler_end_region('Adjoint Scalar')
493 end subroutine adjoint_scalar_pnpn_step
494
495 subroutine print_debug(this)
496 class(adjoint_scalar_pnpn_t), intent(inout) :: this
497 ! character(len=LOG_SIZE) :: log_buf
498 integer :: n
499
500 n = this%dm_Xh%size()
501 ! TODO come back to this
502 !write(log_buf,'(A,A,E15.7,A,E15.7,A,E15.7)') 'Adjoint scalar debug', &
503 ! ' l2norm s_adj', glsc2(this%s_adj%x, this%s_adj%x, n), &
504 ! ' slag1', glsc2(this%s_adj_lag%lf(1)%x, this%s_adj_lag%lf(1)%x, n), &
505 ! ' slag2', glsc2(this%s_adj_lag%lf(2)%x, this%s_adj_lag%lf(2)%x, n)
506 !call neko_log%message(log_buf, lvl=NEKO_LOG_DEBUG)
507 !write(log_buf,'(A,A,E15.7,A,E15.7)') 'Adjoint scalar debug2', &
508 ! ' l2norm abx1', glsc2(this%abx1%x, this%abx1%x, n), &
509 ! ' abx2', glsc2(this%abx2%x, this%abx2%x, n)
510 !call neko_log%message(log_buf, lvl=NEKO_LOG_DEBUG)
511 end subroutine print_debug
512
516 subroutine adjoint_scalar_pnpn_setup_bcs_(this, user)
517 class(adjoint_scalar_pnpn_t), target, intent(inout) :: this
518 type(user_t), target, intent(in) :: user
519 integer :: i, j, n_bcs, zone_size, global_zone_size, ierr
520 type(json_core) :: core
521 type(json_value), pointer :: bc_object
522 type(json_file) :: bc_subdict
523 class(bc_t), pointer :: bc_i
524 logical :: found
525 ! Monitor which boundary zones have been marked
526 logical, allocatable :: marked_zones(:)
527 integer, allocatable :: zone_indices(:)
528
529 if (this%params%valid_path('boundary_conditions')) then
530 call this%params%info('boundary_conditions', &
531 n_children = n_bcs)
532 call this%params%get_core(core)
533 call this%params%get('boundary_conditions', bc_object, found)
534
535 call this%bcs%init(n_bcs)
536
537 allocate(marked_zones(size(this%msh%labeled_zones)))
538 marked_zones = .false.
539
540 do i = 1, n_bcs
541 ! Create a new json containing just the subdict for this bc
542 call json_extract_item(core, bc_object, i, bc_subdict)
543
544 ! Check that we are not trying to assing a bc to zone, for which one
545 ! has already been assigned and that the zone has more than 0 size
546 ! in the mesh.
547 call json_get(bc_subdict, "zone_indices", zone_indices)
548
549 do j = 1, size(zone_indices)
550 zone_size = this%msh%labeled_zones(zone_indices(j))%size
551 call mpi_allreduce(zone_size, global_zone_size, 1, &
552 mpi_integer, mpi_max, neko_comm, ierr)
553
554 if (global_zone_size .eq. 0) then
555 write(error_unit, '(A, A, I0, A, A, I0, A)') "*** ERROR ***: ",&
556 "Zone index ", zone_indices(j), &
557 " is invalid as this zone has 0 size, meaning it ", &
558 "does not exist in the mesh. Check adjoint scalar BC ", &
559 i, "."
560 error stop
561 end if
562
563 if (marked_zones(zone_indices(j)) .eqv. .true.) then
564 write(error_unit, '(A, A, I0, A, A, A, A)') "*** ERROR ***: ", &
565 "Zone with index ", zone_indices(j), &
566 " has already been assigned a boundary condition. ", &
567 "Please check your boundary_conditions entry for the ", &
568 "adjoint scalar and make sure that each zone index ", &
569 "appears only in a single boundary condition."
570 error stop
571 else
572 marked_zones(zone_indices(j)) = .true.
573 end if
574 end do
575
576 bc_i => null()
577
578 call adjoint_bc_factory(bc_i, this, bc_subdict, this%c_Xh, user)
579 call this%bcs%append(bc_i)
580 end do
581
582 ! Make sure all labeled zones with non-zero size have been marked
583 do i = 1, size(this%msh%labeled_zones)
584 if ((this%msh%labeled_zones(i)%size .gt. 0) .and. &
585 (marked_zones(i) .eqv. .false.)) then
586 write(error_unit, '(A, A, I0)') "*** ERROR ***: ", &
587 "No adjoint scalar boundary condition assigned to zone ", i
588 error stop
589 end if
590 end do
591 else
592 ! Check that there are no labeled zones, i.e. all are periodic.
593 do i = 1, size(this%msh%labeled_zones)
594 if (this%msh%labeled_zones(i)%size .gt. 0) then
595 write(error_unit, '(A, A, A)') "*** ERROR ***: ", &
596 "No boundary_conditions entry in the case file for " // &
597 " adjoint scalar ", &
598 this%s%name
599 error stop
600 end if
601 end do
602 end if
603 end subroutine adjoint_scalar_pnpn_setup_bcs_
604
605end module adjoint_scalar_pnpn
Boundary condition factory. Both constructs and initializes the object.
Contains the adjoint_scalar_pnpn_t type.
Contains the adjoint_scalar_scheme_t type.
Subroutines to add advection terms to the RHS of a transport equation.
Base type for a scalar advection-diffusion solver.
Base abstract type for computing the advection operator.