Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
PDE_filter_mapping.f90
Go to the documentation of this file.
1
34!
37 use num_types, only: rp
38 use json_module, only: json_file
39 use registry, only: neko_registry
40 use field, only: field_t
41 use coefs, only: coef_t
42 use ax_product, only: ax_t, ax_helm_allocator
43 use krylov, only: ksp_t, ksp_monitor_t, krylov_solver_factory
44 use precon, only: pc_t, precon_allocator, precon_destroy
45 use scalar_bc_projector, only: scalar_bc_projector_t
46 use neumann, only: neumann_t
47 use profiler, only: profiler_start_region, profiler_end_region
48 use gather_scatter, only: gs_t, gs_op_add
49 use pnpn_residual, only: pnpn_prs_res_t
50 use mesh, only: mesh_t, neko_msh_max_zlbls, neko_msh_max_zlbl_len
51 use registry, only: neko_registry
52 use mapping, only: mapping_t
53 use scratch_registry, only: neko_scratch_registry
54 use field_math, only: field_copy, field_add3
55 use coefs, only: coef_t
56 use logger, only: neko_log, log_size
57 use neko_config, only: neko_bcknd_device
58 use dofmap, only: dofmap_t
59 use jacobi, only: jacobi_t
60 use device_jacobi, only: device_jacobi_t
61 use sx_jacobi, only: sx_jacobi_t
62 use utils, only: neko_error
63 use device_math, only: device_cfill, device_subcol3, device_cmult
64 use json_utils, only: json_get, json_get_or_default
65 use continuation_scheduler, only: nekotop_continuation
66 implicit none
67 private
68
73 type, public, extends(mapping_t) :: pde_filter_t
75 class(ax_t), allocatable :: ax
77 type(ksp_monitor_t) :: ksp_results(1)
79 class(ksp_t), allocatable :: ksp_filt
81 class(pc_t), allocatable :: pc_filt
83 type(scalar_bc_projector_t) :: bc_projector_filt
84
85 ! Inputs from the user
87 real(kind=rp) :: r
89 real(kind=rp) :: abstol_filt
91 integer :: ksp_max_iter
93 character(len=:), allocatable :: ksp_solver
94 ! > preconditioner type
95 character(len=:), allocatable :: precon_type_filt
96 integer :: ksp_n, n, i
97
98
99
100 contains
102 procedure, pass(this) :: init => pde_filter_init_from_json
104 procedure, pass(this) :: init_from_attributes => &
105 pde_filter_init_from_attributes
107 procedure, pass(this) :: free => pde_filter_free
109 procedure, pass(this) :: forward_mapping => pde_filter_forward_mapping
111 ! TODO
112 ! TALK TO NIELS, I think this is correct...
113 ! but it's not exactly "chain ruling back"
114 ! it's filtering the sensitivity
115
116 ! UPDATE:
117 ! After an email with Niels, we should be using the chain rule,
118 ! not a sensitivity filter
119 procedure, pass(this) :: backward_mapping => pde_filter_backward_mapping
120 end type pde_filter_t
121
122contains
123
125 subroutine pde_filter_init_from_json(this, json, coef)
126 class(pde_filter_t), intent(inout) :: this
127 type(json_file), intent(inout) :: json
128 type(coef_t), intent(inout) :: coef
129 real(kind=rp) :: r, tol
130 integer :: max_iter
131 character(len=:), allocatable :: ksp_solver, precon_type
132
133 call nekotop_continuation%json_get_or_register(json, 'r', this%r, r)
134 call json_get_or_default(json, 'tol', tol, 0.0000000001_rp)
135 call json_get_or_default(json, 'max_iter', max_iter, 200)
136 call json_get_or_default(json, 'solver', ksp_solver, "cg")
137 call json_get_or_default(json, 'preconditioner', precon_type, "jacobi")
138
139 call this%init_base(json, coef, "PDE_filter_mapping")
140 call this%init_from_attributes(coef, r, tol, max_iter, &
141 ksp_solver, precon_type)
142
143 end subroutine pde_filter_init_from_json
144
146 subroutine pde_filter_init_from_attributes(this, coef, r, tol, max_iter, &
147 ksp_solver, precon_type)
148 class(pde_filter_t), intent(inout) :: this
149 type(coef_t), intent(inout) :: coef
150 real(kind=rp), intent(in) :: r, tol
151 integer, intent(in) :: max_iter
152 character(len=*), intent(in) :: ksp_solver, precon_type
153 integer :: n
154
155 this%r = r
156 this%abstol_filt = tol
157 this%ksp_max_iter = max_iter
158 this%ksp_solver = ksp_solver
159 this%precon_type_filt = precon_type
160
161 ! set the number of dofs
162 n = this%coef%dof%size()
163
164 ! Setup backend dependent Ax routines
165 call ax_helm_allocator(this%Ax, type_name = "standard")
166
167 ! set up krylov solver
168 call krylov_solver_factory(this%ksp_filt, n, this%ksp_solver, &
169 this%ksp_max_iter, this%abstol_filt)
170
171 ! set up preconditioner
172 call filter_precon_factory(this%pc_filt, this%ksp_filt, &
173 this%coef, this%coef%dof, this%coef%gs_h, &
174 this%bc_projector_filt, this%precon_type_filt)
175
176 end subroutine pde_filter_init_from_attributes
177
179 subroutine pde_filter_free(this)
180 class(pde_filter_t), intent(inout) :: this
181
182 if (allocated(this%Ax)) then
183 deallocate(this%Ax)
184 end if
185
186 if (allocated(this%ksp_filt)) then
187 call this%ksp_filt%free()
188 deallocate(this%ksp_filt)
189 end if
190
191 if (allocated(this%pc_filt)) then
192 call precon_destroy(this%pc_filt)
193 deallocate(this%pc_filt)
194 end if
195
196 call this%bc_projector_filt%free()
197
198 call this%free_base()
199
200 end subroutine pde_filter_free
201
206 subroutine pde_filter_forward_mapping(this, X_out, X_in)
207 class(pde_filter_t), intent(inout) :: this
208 type(field_t), intent(in) :: X_in
209 type(field_t), intent(inout) :: X_out
210 integer :: n, i
211 type(field_t), pointer :: RHS, d_X_out
212 character(len=LOG_SIZE) :: log_buf
213 integer :: temp_indices(2)
214
215 n = this%coef%dof%size()
216 call neko_scratch_registry%request_field(rhs, temp_indices(1), .false.)
217 call neko_scratch_registry%request_field(d_x_out, temp_indices(2), .false.)
218 ! in a similar fasion to pressure/velocity, we will solve for d_X_out.
219
220 ! to improve convergence, we use X_in as an initial guess for X_out.
221 ! so X_out = X_in + d_X_in.
222
223 ! Defining the operator A = -r^2 \nabla^2 + I
224 ! the system changes from:
225 ! A (X_out) = X_in
226 ! to
227 ! A (d_X_out) = X_in - A(X_in)
228
229 ! set up Helmholtz operators and RHS
230 if (neko_bcknd_device .eq. 1) then
231 call device_cfill(this%coef%h1_d, (this%r / (2.0_rp * sqrt(3.0_rp)))**2, n)
232 call device_cfill(this%coef%h2_d, 1.0_rp, n)
233 else
234 ! h1 is already negative in its definition
235 this%coef%h1 = (this%r / (2.0_rp * sqrt(3.0_rp)))**2
236 ! ax_helm includes the mass matrix in h2
237 this%coef%h2 = 1.0_rp
238 end if
239 this%coef%ifh2 = .true.
240
241 ! The Helmholtz coefficients above may have changed since the previous
242 ! application (or may not have been set when the preconditioner was
243 ! initialized). Update the preconditioner for the operator used below.
244 call this%pc_filt%update()
245
246 ! compute the A(X_in) component of the RHS
247 ! (note, to be safe with the inout intent we first copy X_in to the
248 ! temporary d_X_out)
249 call field_copy(d_x_out, x_in)
250 call this%Ax%compute(rhs%x, d_x_out%x, this%coef, this%coef%msh, &
251 this%coef%Xh)
252
253 if (neko_bcknd_device .eq. 1) then
254 call device_subcol3(rhs%x_d, x_in%x_d, this%coef%B_d, n)
255 call device_cmult(rhs%x_d, -1.0_rp, n)
256 else
257 do i = 1, n
258 ! mass matrix should be included here
259 rhs%x(i,1,1,1) = x_in%x(i,1,1,1) * this%coef%B(i,1,1,1) &
260 - rhs%x(i,1,1,1)
261 end do
262 end if
263
264 ! gather scatter
265 call this%coef%gs_h%op(rhs, gs_op_add)
266
267 ! set BCs
268 call this%bc_projector_filt%apply(rhs%x, n)
269
270 ! Solve Helmholtz equation
271 call profiler_start_region('filter solve')
272 this%ksp_results(1) = &
273 this%ksp_filt%solve(this%Ax, d_x_out, rhs%x, n, this%coef, &
274 this%bc_projector_filt, this%coef%gs_h)
275
276 call profiler_end_region
277
278 ! add result
279 call field_add3(x_out, x_in, d_x_out)
280
281 ! write it all out
282 call neko_log%message('Filter')
283
284 write(log_buf, '(A,A,A)') 'Iterations: ',&
285 'Start residual: ', 'Final residual:'
286 call neko_log%message(log_buf)
287 write(log_buf, '(I11,3x, E15.7,5x, E15.7)') this%ksp_results%iter, &
288 this%ksp_results%res_start, this%ksp_results%res_final
289 call neko_log%message(log_buf)
290
291 call neko_scratch_registry%relinquish_field(temp_indices)
292
293
294
295 end subroutine pde_filter_forward_mapping
296
302 ! TODO
303 ! this really confuses me!
304 ! it's not really a chain rule back, it's just a filtering of the sensitivity
305 ! ?
306 !
307 ! Update:
308 ! After an email exchange with Niels:
309 ! We DON'T want to be filtering the sensitivity, this IS just the chain rule.
310 ! So to the best of my knowledge, we're just applying the same filter
311 ! on the sensitivity field.
312 !
313 ! Niels did mention the order of the RHS assembly should be reversed however.
314 ! I'm not exactly sure how this applies to us, but it should be brought up
315 ! in the next group meeting.
316 subroutine pde_filter_backward_mapping(this, sens_out, sens_in, X_in)
317 class(pde_filter_t), intent(inout) :: this
318 type(field_t), intent(in) :: X_in
319 type(field_t), intent(in) :: sens_in
320 type(field_t), intent(inout) :: sens_out
321 integer :: n, i
322 type(field_t), pointer :: RHS, delta ! I'm so sorry for this notation..
323 integer :: temp_indices(2)
324 character(len=LOG_SIZE) :: log_buf
325
326 n = this%coef%dof%size()
327
328 call neko_scratch_registry%request_field(rhs, temp_indices(1), .false.)
329 call neko_scratch_registry%request_field(delta, temp_indices(2), .false.)
330
331 ! set up Helmholtz operators and RHS
332 if (neko_bcknd_device .eq. 1) then
333 call device_cfill(this%coef%h1_d, (this%r / (2.0_rp * sqrt(3.0_rp)))**2, n)
334 call device_cfill(this%coef%h2_d, 1.0_rp, n)
335 else
336 ! h1 is already negative in its definition
337 this%coef%h1 = (this%r / (2.0_rp * sqrt(3.0_rp)))**2
338 ! ax_helm includes the mass matrix in h2
339 this%coef%h2 = 1.0_rp
340 end if
341 this%coef%ifh2 = .true.
342
343 ! Keep the preconditioner synchronized with the Helmholtz operator used
344 ! for this application.
345 call this%pc_filt%update()
346
347 ! compute the A(sens_in) component of the RHS
348 ! (note, to be safe with the inout intent we first copy sens_in to the
349 ! temporary delta)
350 call field_copy(delta, sens_in)
351 call this%Ax%compute(rhs%x, delta%x, this%coef, this%coef%msh, &
352 this%coef%Xh)
353
354 if (neko_bcknd_device .eq. 1) then
355 call device_subcol3(rhs%x_d, sens_in%x_d, this%coef%B_d, n)
356 call device_cmult(rhs%x_d, -1.0_rp, n)
357 else
358 do i = 1, n
359 ! mass matrix should be included here
360 rhs%x(i,1,1,1) = sens_in%x(i,1,1,1) * this%coef%B(i,1,1,1) &
361 - rhs%x(i,1,1,1)
362 end do
363 end if
364
365 ! gather scatter
366 call this%coef%gs_h%op(rhs, gs_op_add)
367
368 ! set BCs
369 call this%bc_projector_filt%apply(rhs%x, n)
370
371 ! Solve Helmholtz equation
372 call profiler_start_region('filter solve')
373 this%ksp_results(1) = &
374 this%ksp_filt%solve(this%Ax, delta, rhs%x, n, this%coef, &
375 this%bc_projector_filt, this%coef%gs_h)
376
377 ! add result
378 call field_add3(sens_out, sens_in, delta)
379
380 call profiler_end_region
381
382 ! write it all out
383 call neko_log%message('Filter')
384
385 write(log_buf, '(A,A,A)') 'Iterations: ',&
386 'Start residual: ', 'Final residual:'
387 call neko_log%message(log_buf)
388 write(log_buf, '(I11,3x, E15.7,5x, E15.7)') this%ksp_results%iter, &
389 this%ksp_results%res_start, this%ksp_results%res_final
390 call neko_log%message(log_buf)
391
392 call neko_scratch_registry%relinquish_field(temp_indices)
393
394 end subroutine pde_filter_backward_mapping
395
396 subroutine filter_precon_factory(pc, ksp, coef, dof, gs, bc_projector, &
397 pctype)
398
399 implicit none
400 class(pc_t), allocatable, target, intent(inout) :: pc
401 class(ksp_t), target, intent(inout) :: ksp
402 type(coef_t), target, intent(in) :: coef
403 type(dofmap_t), target, intent(in) :: dof
404 type(gs_t), target, intent(inout) :: gs
405 type(scalar_bc_projector_t), target, intent(inout) :: bc_projector
406 character(len=*) :: pctype
407
408 call precon_allocator(pc, pctype)
409
410 select type (pcp => pc)
411 type is (jacobi_t)
412 call pcp%init(coef, dof, gs)
413 type is (sx_jacobi_t)
414 call pcp%init(coef, dof, gs)
415 type is (device_jacobi_t)
416 call pcp%init(coef, dof, gs)
417 end select
418
419 call ksp%set_pc(pc)
420
421 end subroutine filter_precon_factory
422
423end module pde_filter_mapping
Continuation scheduler for the optimization loop.
Mappings to be applied to a scalar field.
Definition mapping.f90:36
A PDE based filter.
subroutine pde_filter_init_from_json(this, json, coef)
Constructor from json.
Base abstract class for mapping.
Definition mapping.f90:47
A PDE based filter mapping , see Lazarov & O. Sigmund 2010, by solving an equation of the form .