Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
adjoint_case.f90
Go to the documentation of this file.
1
34
35! Implements the `adjoint_case_t` type.
36module adjoint_case
37 use num_types, only: rp, dp, sp
38 use case, only: case_t
39 use adjoint_fluid_scheme, only: adjoint_fluid_scheme_t
43 use fluid_pnpn, only: fluid_pnpn_t
45 use scalar_ic, only: set_scalar_ic
46 use checkpoint, only: chkp_t
47 use chkp_output, only: chkp_output_t
48 use flow_ic, only: set_flow_ic
49 use output_controller, only: output_controller_t
50 use time_based_controller, only: time_based_controller_t
51 use file, only: file_t
52 use json_module, only: json_file
53 use json_utils, only: json_get, json_get_or_default, json_extract_item, &
54 json_get_or_lookup
57 use logger, only : neko_log
58 use time_state, only : time_state_t
59 use utils, only: neko_error
62 use json_utils_ext, only: json_key_fallback
64 implicit none
65 private
66 public :: adjoint_case_t
67
73 class(adjoint_fluid_scheme_t), allocatable :: fluid_adj
75 type(adjoint_scalars_t), allocatable :: adjoint_scalars
78 adjoint_convection_term
79 type(case_t), pointer :: case
80 type(time_state_t) :: time
81 type(chkp_t) :: chkp
82 type(chkp_output_t) :: chkp_out
83
84 ! Fields
85 type(adjoint_output_t) :: f_out
86 type(output_controller_t) :: output_controller
87 type(time_based_controller_t) :: norm_output_ctrl
88 type(file_t) :: norm_output_file
89 logical :: norm_output_enabled = .false.
90
91 logical :: have_scalar = .false.
92
93 contains
94 procedure, pass(this) :: init => adjoint_init_from_json
95 procedure, pass(this) :: free => adjoint_free
96 end type adjoint_case_t
97
98contains
99
100 ! Constructor from json.
101 subroutine adjoint_init_from_json(this, neko_case)
102 class(adjoint_case_t), intent(inout) :: this
103 type(case_t), target, intent(inout) :: neko_case
104
105 this%case => neko_case
106 call adjoint_case_init_common(this, neko_case)
107
108 end subroutine adjoint_init_from_json
109
111 subroutine adjoint_case_init_common(this, neko_case)
112 class(adjoint_case_t), target, intent(inout) :: this
113 type(case_t), intent(inout) :: neko_case
114 integer :: lx = 0
115 real(kind=rp) :: real_val = 0.0_rp
116 character(len=:), allocatable :: string_val, file_format, name
117 character(len=:), allocatable :: norm_control, norm_file
118 integer :: precision, integer_val, layout
119 integer :: n_scalars_primal, n_scalars_adjoint, i
120 logical :: scalar = .false.
121 logical :: temperature_found = .false.
122 logical :: logical_val
123
124 ! extra things for json
125 type(json_file) :: ic_json, numerics_params
126 type(json_file) :: scalar_params_primal, scalar_params_adjoint, json_subdict
127 character(len=:), allocatable :: json_key
128 logical :: dealias_adjoint_scalar_convection
129
130 !
131 ! Setup adjoint fluid
132 !
133 call json_get(neko_case%params, 'case.fluid.scheme', string_val)
134 call adjoint_fluid_scheme_factory(this%fluid_adj, trim(string_val))
135
136 call json_get(neko_case%params, 'case.numerics.polynomial_order', lx)
137 lx = lx + 1 ! add 1 to get number of gll points
138
139 call this%chkp%init()
140 call this%chkp%add_time_state(this%time)
141
142 ! Checked before the adjoint fluid is initialised, which assumes a
143 ! three-dimensional mesh.
144 if (neko_case%msh%gdim .ne. 3) then
145 call neko_error("The adjoint requires a three-dimensional mesh: " // &
146 "the transpose of the curl-curl pressure term is exact in " // &
147 "three dimensions only.")
148 end if
149
150 select type (f => this%fluid_adj)
151 type is (adjoint_fluid_pnpn_t)
152 call f%init(neko_case%msh, lx, neko_case%params, &
153 neko_case%user, this%chkp)
154
155 ! Reject the cases for which the adjoint curl-curl load is not the
156 ! transpose of the primal's term. Both boundary condition lists are
157 ! complete only here.
158 select type (pf => neko_case%fluid)
159 type is (fluid_pnpn_t)
160 call adjoint_curl_curl_check(pf%bc_sym_surface, f%bc_sym_surface, &
161 pf%c_Xh%cyclic)
162 class default
163 call neko_error("The adjoint requires the Pn/Pn primal fluid " // &
164 "scheme (case.fluid.scheme = pnpn).")
165 end select
166 end select
167 !
168 ! Setup adjoint scalar
169 !
170 ! @todo no adjoint_scalars factory for now, probably not needed
171
172 ! check how many adjoint scalars
173 scalar = .false.
174 n_scalars_adjoint = 0
175 if (neko_case%params%valid_path('case.adjoint_scalar')) then
176 call json_get_or_default(neko_case%params, &
177 'case.adjoint_scalar.enabled', scalar, .true.)
178 n_scalars_adjoint = 1
179 n_scalars_primal = 1
180 else if (neko_case%params%valid_path('case.adjoint_scalars')) then
181 call neko_case%params%info('case.adjoint_scalars', &
182 n_children = n_scalars_adjoint)
183 call neko_case%params%info('case.scalars', n_children = n_scalars_primal)
184 if (n_scalars_adjoint > 0) then
185 scalar = .true.
186 end if
187 end if
188
189 this%have_scalar = scalar
190
191
192
193
194
195
196 if (this%have_scalar) then
197 allocate(this%adjoint_scalars)
198 call json_get(neko_case%params, 'case.numerics', &
199 numerics_params)
200 if (neko_case%params%valid_path('case.adjoint_scalar')) then
201 ! For backward compatibility
202 call json_get(neko_case%params, 'case.adjoint_scalar', &
203 scalar_params_adjoint)
204 call json_get(neko_case%params, 'case.scalar', &
205 scalar_params_primal)
206 call this%adjoint_scalars%init(neko_case%msh, neko_case%fluid%c_Xh, &
207 neko_case%fluid%gs_Xh, scalar_params_adjoint, &
208 scalar_params_primal, numerics_params, neko_case%user, &
209 this%chkp, neko_case%fluid%ulag, neko_case%fluid%vlag, &
210 neko_case%fluid%wlag, neko_case%fluid%ext_bdf, &
211 neko_case%fluid%rho)
212 ! allocate the coupling term
213 allocate(this%adjoint_convection_term)
214 ! initialize the coupling term
215 call json_get_or_default(neko_case%params, &
216 'case.adjoint_scalar.dealias_coupling_term', &
217 dealias_adjoint_scalar_convection, .true.)
218 call this%adjoint_convection_term%init_from_components( &
219 this%fluid_adj%f_adj_x, this%fluid_adj%f_adj_y, &
220 this%fluid_adj%f_adj_z, this%case%scalars%scalar_fields(1)%scalar%s, &
221 this%adjoint_scalars%adjoint_scalar_fields(1)%s_adj, &
222 this%fluid_adj%c_Xh, this%fluid_adj%c_Xh_GL, &
223 this%fluid_adj%GLL_to_GL, &
224 dealias_adjoint_scalar_convection, this%fluid_adj%scratch_GL)
225
226 select type (f => this%fluid_adj)
227 type is (adjoint_fluid_pnpn_t)
228 ! append the coupling term to the adjoint velocity equation
229 call f%source_term%add(this%adjoint_convection_term)
230 end select
231 else
232 ! Multiple scalars
233
234 call json_get(this%case%params, &
235 'case.adjoint_scalars', scalar_params_adjoint)
236 call json_get(this%case%params, &
237 'case.scalars', scalar_params_primal)
238 call this%adjoint_scalars%init(n_scalars_adjoint, n_scalars_primal, &
239 neko_case%msh, neko_case%fluid%c_Xh, neko_case%fluid%gs_Xh, &
240 scalar_params_adjoint, scalar_params_primal, numerics_params, &
241 neko_case%user, this%chkp, neko_case%fluid%ulag, &
242 neko_case%fluid%vlag, neko_case%fluid%wlag, &
243 neko_case%fluid%ext_bdf, neko_case%fluid%rho)
244 call neko_error('The adjoint scaling coupling term have not yet' // &
245 'been implemented for multiple scalars')
246 end if
247 end if
248
249 !
250 ! Time control
251 !
252 call json_get(this%case%params, 'case.time', json_subdict)
253 call this%time%init(json_subdict)
254
255 !
256 ! Setup user defined conditions
257 !
258 ! if (neko_case%params%valid_path('case.fluid.inflow_condition')) then
259 ! call json_get(neko_case%params, 'case.fluid.inflow_condition.type', &
260 ! string_val)
261 ! if (trim(string_val) .eq. 'user') then
262 ! call neko_case%fluid%set_usr_inflow(neko_case%user%fluid_user_if)
263 ! end if
264 ! end if
265
266 ! Setup user boundary conditions for the scalar.
267 ! if (adjoint_scalars) then
268 ! call neko_case%adjoint_scalars%set_user_bc(&
269 ! neko_case%user%scalar_user_bc)
270 ! end if
271
272 !
273 ! Setup initial conditions
274 !
275
276 call neko_log%section("Adjoint initial condition")
277 json_key = json_key_fallback(neko_case%params, &
278 'case.adjoint_fluid.initial_condition', 'case.fluid.initial_condition')
279
280 call json_get(neko_case%params, json_key, ic_json)
281 call json_get(ic_json, 'type', string_val)
282
283 if (trim(string_val) .ne. 'user') then
284 call set_flow_ic( &
285 this%fluid_adj%u_adj, this%fluid_adj%v_adj, this%fluid_adj%w_adj, &
286 this%fluid_adj%p_adj, this%fluid_adj%c_Xh, this%fluid_adj%gs_Xh, &
287 string_val, ic_json)
288 else
289 call set_flow_ic( &
290 this%fluid_adj%u_adj, this%fluid_adj%v_adj, this%fluid_adj%w_adj, &
291 this%fluid_adj%p_adj, this%fluid_adj%c_Xh, this%fluid_adj%gs_Xh, &
292 neko_case%user%initial_conditions, neko_case%fluid%name)
293 end if
294
295 call neko_log%end_section()
296
297 if (this%have_scalar) then
298
299 if (neko_case%params%valid_path('case.adjoint_scalar')) then
300 ! we shouldn't fallback to the primal here.
301 call json_get(neko_case%params, &
302 'case.adjoint_scalar.initial_condition.type', string_val)
303 call json_get(neko_case%params, &
304 'case.adjoint_scalar.initial_condition', ic_json)
305
306 !call neko_log%section("Adjoint scalar initial condition ")
307
308 if (trim(string_val) .ne. 'user') then
309 if (trim(neko_case%scalars%scalar_fields(1)%scalar%name) .eq. &
310 'temperature') then
311 call set_scalar_ic(&
312 this%adjoint_scalars%adjoint_scalar_fields(1)%s_adj, &
313 this%adjoint_scalars%adjoint_scalar_fields(1)%c_Xh, &
314 this%adjoint_scalars%adjoint_scalar_fields(1)%gs_Xh, &
315 string_val, ic_json, 0)
316 else
317 call set_scalar_ic(&
318 this%adjoint_scalars%adjoint_scalar_fields(1)%s_adj, &
319 this%adjoint_scalars%adjoint_scalar_fields(1)%c_Xh, &
320 this%adjoint_scalars%adjoint_scalar_fields(1)%gs_Xh, &
321 string_val, ic_json, 1)
322 end if
323 else
324 call neko_error("user ICs not implemented for adjoint scalar")
325 ! call set_scalar_ic(this%adjoint_scalars%s_adj, &
326 ! this%adjoint_scalars%c_Xh, this%adjoint_scalars%gs_Xh, &
327 ! this%usr%scalar_user_ic, neko_case%params)
328 end if
329
330 ! call neko_log%end_section()
331 else
332
333 ! Handle multiple scalars
334 do i = 1, n_scalars_adjoint
335 call json_extract_item(neko_case%params, 'case.adjoint_scalars', &
336 i, scalar_params_adjoint)
337 call json_get(scalar_params_adjoint, &
338 'initial_condition.type', string_val)
339 call json_get(scalar_params_adjoint, &
340 'initial_condition', json_subdict)
341
342 if (trim(string_val) .ne. 'user') then
343 if (trim(neko_case%scalars%scalar_fields(i)%scalar%name) .eq. &
344 'temperature') then
345 call set_scalar_ic(&
346 this%adjoint_scalars%adjoint_scalar_fields(i)%s_adj, &
347 this%adjoint_scalars%adjoint_scalar_fields(i)%c_Xh, &
348 this%adjoint_scalars%adjoint_scalar_fields(i)%gs_Xh, &
349 string_val, json_subdict, 0)
350 temperature_found = .true.
351 else
352 if (temperature_found) then
353 ! if temperature is found, scalars start from index 1
354 call set_scalar_ic(&
355 this%adjoint_scalars%adjoint_scalar_fields(i)%s_adj, &
356 this%adjoint_scalars%adjoint_scalar_fields(i)%c_Xh, &
357 this%adjoint_scalars%adjoint_scalar_fields(i)%gs_Xh, &
358 string_val, json_subdict, i - 1)
359 else
360 ! if temperature is not found, scalars start from index 0
361 call set_scalar_ic(&
362 this%adjoint_scalars%adjoint_scalar_fields(i)%s_adj, &
363 this%adjoint_scalars%adjoint_scalar_fields(i)%c_Xh, &
364 this%adjoint_scalars%adjoint_scalar_fields(i)%gs_Xh, &
365 string_val, json_subdict, i)
366 end if
367 end if
368 else
369 call neko_error("user ICs not implemented for adjoint scalar")
370 end if
371 end do
372 end if
373 end if
374
375 ! Add initial conditions to BDF fluid_adj (if present)
376 select type (f => this%fluid_adj)
377 type is (adjoint_fluid_pnpn_t)
378 call f%ulag%set(f%u_adj)
379 call f%vlag%set(f%v_adj)
380 call f%wlag%set(f%w_adj)
381 end select
382
383 !
384 ! Validate that the neko_case is properly setup for time-stepping
385 !
386 call this%fluid_adj%validate
387
388 if (this%have_scalar) then
389 call this%adjoint_scalars%validate()
390 end if
391
392 !
393 ! Setup output precision of the field files
394 !
395 call json_get_or_default(neko_case%params, 'case.output_precision', &
396 string_val, 'single')
397
398 if (trim(string_val) .eq. 'double') then
399 precision = dp
400 else
401 precision = sp
402 end if
403
404 !
405 ! Setup output_controller
406 !
407 call json_get_or_default(neko_case%params, &
408 'case.adjoint_fluid.output_filename', name, "adjoint")
409 call json_get_or_default(neko_case%params, &
410 'case.adjoint_fluid.output_format', file_format, 'fld')
411 call json_get_or_default(neko_case%params, &
412 'case.adjoint_fluid.output_mesh_in_all_files', &
413 logical_val, .false.)
414 call this%output_controller%init(this%time%end_time)
415 if (scalar) then
416 call this%f_out%init(precision, this%fluid_adj, &
417 this%adjoint_scalars, name = name, &
418 path = trim(neko_case%output_directory), &
419 fmt = trim(file_format), layout = layout, &
420 always_write_mesh = logical_val)
421 else
422 call this%f_out%init(precision, this%fluid_adj, name = name, &
423 path = trim(neko_case%output_directory), &
424 fmt = trim(file_format), layout = layout, &
425 always_write_mesh = logical_val)
426 end if
427
428 call json_get_or_default(neko_case%params, &
429 'case.adjoint_fluid.output_subdivide', logical_val, .false.)
430 call this%f_out%file_%set_subdivide(logical_val)
431
432 call json_get_or_default(neko_case%params, &
433 'case.adjoint_fluid.output_control', string_val, 'never')
434
435 if (trim(string_val) .eq. 'org') then
436 ! yes, it should be real_val below for type compatibility
437 call json_get_or_lookup(neko_case%params, 'case.nsamples', integer_val)
438 real_val = real(integer_val, kind=rp)
439 call this%output_controller%add(this%f_out, real_val, 'nsamples')
440 else if (trim(string_val) .eq. 'never') then
441 call this%output_controller%add(this%f_out, 0.0_rp, 'never')
442 else if (trim(string_val) .eq. 'tsteps' .or. &
443 trim(string_val) .eq. 'nsamples') then
444 call json_get_or_lookup(neko_case%params, &
445 'case.adjoint_fluid.output_value', integer_val)
446 real_val = real(integer_val, kind=rp)
447 call this%output_controller%add(this%f_out, real_val, string_val)
448 else if (trim(string_val) .eq. 'simulationtime') then
449 call json_get_or_lookup(neko_case%params, &
450 'case.adjoint_fluid.output_value', real_val)
451 call this%output_controller%add(this%f_out, real_val, string_val)
452 else
453 call neko_log%error('Unknown output control type for the fluid: ' // &
454 trim(string_val))
455 end if
456
457 !
458 ! Setup adjoint norm output
459 !
460 call json_get_or_default(neko_case%params, &
461 'case.adjoint_fluid.norm_output_control', norm_control, 'never')
462 if (trim(norm_control) .ne. 'never') then
463 call json_get_or_default(neko_case%params, &
464 'case.adjoint_fluid.norm_output_value', real_val, 1.0_rp)
465 call json_get_or_default(neko_case%params, &
466 'case.adjoint_fluid.norm_output_file', norm_file, &
467 'adjoint_norm.csv')
468 call this%norm_output_file%init(trim(neko_case%output_directory) // &
469 trim(norm_file))
470 call this%norm_output_file%set_header('Time, Norm')
471 call this%norm_output_file%set_overwrite(.true.)
472 call this%norm_output_ctrl%init(this%time%start_time, &
473 this%time%end_time, trim(norm_control), real_val)
474 this%norm_output_enabled = .true.
475 end if
476
477 ! !
478 ! ! Save checkpoints (if nothing specified, default to saving at end of sim)
479 ! !
480 ! call json_get_or_default(neko_case%params, 'case.output_checkpoints',&
481 ! logical_val, .true.)
482 ! if (logical_val) then
483 ! call json_get_or_default(neko_case%params, 'case.checkpoint_format', &
484 ! string_val, "chkp")
485 ! neko_case%f_chkp = chkp_output_t(this%fluid_adj%chkp, &
486 ! path = output_directory, &
487 ! ! fmt = trim(string_val))
488 ! call json_get_or_default(neko_case%params, 'case.checkpoint_control', &
489 ! string_val, "simulationtime")
490 ! call json_get_or_default(neko_case%params, 'case.checkpoint_value', &
491 ! real_val,&
492 ! 1e10_rp)
493 ! call this%output_controller%add(this%f_chkp, real_val, string_val)
494 ! end if
495
496 !
497 ! Initialize time and step
498 !
499 this%time%t = 0d0
500 this%time%tstep = 0
501
502 end subroutine adjoint_case_init_common
503
504 ! Destructor.
505 subroutine adjoint_free(this)
506 class(adjoint_case_t), intent(inout) :: this
507
508 if (allocated(this%fluid_adj)) then
509 call this%fluid_adj%free()
510 deallocate(this%fluid_adj)
511 end if
512
513 if (allocated(this%adjoint_scalars)) then
514 call this%adjoint_scalars%free()
515 deallocate(this%adjoint_scalars)
516 end if
517
518 if (allocated(this%adjoint_convection_term)) then
519 call this%adjoint_convection_term%free()
520 deallocate(this%adjoint_convection_term)
521 end if
522
523 nullify(this%case)
524
525 ! call this%time%free()
526 call this%chkp%free()
527 call this%chkp_out%free()
528
529 ! Fields
530 call this%f_out%free()
531 call this%output_controller%free()
532 call this%norm_output_ctrl%free()
533 call this%norm_output_file%free()
534
535 this%have_scalar = .false.
536
537 end subroutine adjoint_free
538
539end module adjoint_case
Adjoint of the curl-curl term in the Pn/Pn pressure residual.
subroutine, public adjoint_curl_curl_check(prim_sym, adj_sym, cyclic)
Stop with an error if the boundary conditions make the adjoint curl-curl load differ from the transpo...
Factory for all adjoint fluid schemes.
subroutine, public adjoint_fluid_scheme_factory(object, type_name)
Initialise a adjoint fluid scheme.
Adjoint Pn/Pn formulation.
Defines an output for a adjoint.
Implements the adjoint_scalar_convection_source_term type.
Contains the adjoint_scalar_pnpn_t type.
Contains the adjoint_scalar_scheme_t type.
Contains the adjoint_scalars_t type that manages multiple scalar fields.
Adjoint case type. Todo: This should Ideally be a subclass of case_t, however, that is not yet suppor...
Base type for a scalar advection-diffusion solver.
Type to manage multiple adjoint scalar transport equations.