Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
adjoint_case.f90
1! Copyright (c) 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
33! Implements the `adjoint_case_t` type.
34module adjoint_case
35 use num_types, only: rp, dp, sp
36 use case, only: case_t
38 use adjoint_fluid_fctry, only: adjoint_fluid_scheme_factory
41 use scalar_ic, only: set_scalar_ic
42 use flow_ic, only: set_flow_ic
43 use output_controller, only: output_controller_t
44 use file, only: file_t
45 use json_module, only: json_file
46 use json_utils, only: json_get, json_get_or_default, json_extract_object
49 use logger, only : neko_log
50 use utils, only: neko_error
53 use json_utils_ext, only: json_key_fallback
54 implicit none
55 private
56 public :: adjoint_case_t, adjoint_init, adjoint_free
57
63 class(adjoint_fluid_scheme_t), allocatable :: fluid_adj
65 type(adjoint_scalar_pnpn_t), allocatable :: scalar_adj
68 adjoint_convection_term
69 type(case_t), pointer :: case
70
71 ! Fields
72 real(kind=rp) :: tol
73 type(adjoint_output_t) :: f_out
74 type(output_controller_t) :: output_controller
75
76 logical :: have_scalar = .false.
77
78 end type adjoint_case_t
79
80 interface adjoint_init
81 module procedure adjoint_init_from_json, adjoint_init_from_attributes
82 end interface adjoint_init
83
84contains
85
86 ! Constructor from json.
87 subroutine adjoint_init_from_json(this, neko_case)
88 class(adjoint_case_t), intent(inout) :: this
89 type(case_t), target, intent(inout) :: neko_case
90 real(kind=rp) :: tol
91 logical :: have_scalar
92
93 ! Read the tolerance
94 call json_get_or_default(neko_case%params, "tol", tol, 1.0e-6_rp)
95
96 ! I think this is correct.
97 ! Maybe there would be a case where we would want a scalar but
98 ! no adjoint scalar. So this forces us to prescribe an adjoint scalar.
99 call json_get_or_default(neko_case%params, &
100 'case.adjoint_scalar.enabled', have_scalar, .false.)
101
102 call adjoint_init_from_attributes(this, neko_case, tol, have_scalar)
103
104 end subroutine adjoint_init_from_json
105
106 ! Constructor from attributes
107 subroutine adjoint_init_from_attributes(this, neko_case, tol, have_scalar)
108 class(adjoint_case_t), intent(inout) :: this
109 class(case_t), intent(inout), target :: neko_case
110 real(kind=rp), intent(in) :: tol
111 logical :: have_scalar
112
113 this%case => neko_case
114 this%tol = tol
115 this%have_scalar = have_scalar
116
117
118 call adjoint_case_init_common(this, neko_case)
119
120 end subroutine adjoint_init_from_attributes
121
123 subroutine adjoint_case_init_common(this, neko_case)
124 class(adjoint_case_t), intent(inout) :: this
125 type(case_t), intent(inout) :: neko_case
126 integer :: lx = 0
127 real(kind=rp) :: real_val = 0.0_rp
128 character(len=:), allocatable :: string_val
129 integer :: precision
130
131 ! extra things for json
132 type(json_file) :: ic_json
133 character(len=:), allocatable :: json_key
134
135 !
136 ! Setup adjoint fluid
137 !
138 call json_get(neko_case%params, 'case.fluid.scheme', string_val)
139 call adjoint_fluid_scheme_factory(this%fluid_adj, trim(string_val))
140
141 call json_get(neko_case%params, 'case.numerics.polynomial_order', lx)
142 lx = lx + 1 ! add 1 to get number of gll points
143 call this%fluid_adj%init(neko_case%msh, lx, neko_case%params, &
144 neko_case%usr, neko_case%fluid%ext_bdf)
145 !
146 ! Setup adjoint scalar
147 !
148 ! @todo no scalar_adj factory for now, probably not needed
149
150 ! hmmm should we check for scalar or adjoint scalar?
151 ! I'm going to check for adjoint scalar because maybe there would be
152 ! a corner case where someone would want the scalar but not the
153 ! adjoint scalar?
154
155
156 if (this%have_scalar) then
157 allocate(this%scalar_adj)
158 ! @todo
159 ! these tlag and dtlag are new, we likely need to update the standard
160 ! fluid in a different PR.
161 ! For now I'm commenting them out in the scalar.
162 ! this%scalar_adj%chkp%tlag => neko_case%tlag
163 ! this%scalar_adj%chkp%dtlag => neko_case%dtlag
164 call this%scalar_adj%init(neko_case%msh, neko_case%fluid%c_Xh, &
165 neko_case%fluid%gs_Xh, neko_case%params, neko_case%usr, &
166 neko_case%fluid%ulag, neko_case%fluid%vlag, &
167 neko_case%fluid%wlag, neko_case%fluid%ext_bdf, neko_case%fluid%rho)
168
169 ! call neko_case%fluid%chkp%add_scalar(this%scalar_adj%s_adj)
170
171 ! ----------------------------------------------------------------------
172 ! @todo
173 ! I don't really understand checkpoints or why the fluid would need to
174 ! know about the scalar's lag and time integration terms.
175 !
176 ! Since we won't be using checkpoints, I'm commenting this out, but
177 ! leaving a rather large TODO here for when we come back to unsteady.
178 ! ----------------------------------------------------------------------
179 ! neko_case%fluid%chkp%abs1 => this%scalar_adj%abx1
180 ! neko_case%fluid%chkp%abs2 => this%scalar_adj%abx2
181 ! neko_case%fluid%chkp%slag => this%scalar_adj%slag
182
183 ! So if we have a passive scalar we also get a source term entering
184 ! the adjoint velocity equation which arises when you linearize the
185 ! the convective term in passive scalar equation.
186 !
187 ! $\phi^\dagger \nabla \phi$
188 !
189 ! I'm SOOOOO worried I have the sign the wrong way around.
190 ! We really have to write the adjoint derivation nicely.
191 !
192 ! for now I'm assuming in our adjoint derivation we ADD all the
193 ! equations together.
194 ! - So it starts as being positive on the LHS
195 ! - if we treat this term as a source term it goes on the RHS, so now
196 ! it's negative on the RHS.
197 !
198 ! I checked through Casper's adjoint equations and the first term
199 ! after the = sign of eq (14) looks like the term I'm talking about.
200 ! And his is negative too.
201 ! So I THINK this is correct, but we need to double check.
202
203 ! allocate the coupling term
204 allocate(this%adjoint_convection_term)
205 ! initialize the coupling term
206 call this%adjoint_convection_term%init_from_components( &
207 this%fluid_adj%f_adj_x, this%fluid_adj%f_adj_y, &
208 this%fluid_adj%f_adj_z, this%case%scalar%s, &
209 this%scalar_adj%s_adj, this%fluid_adj%c_Xh)
210 ! append the coupling term to the adjoint velocity equation
211 call this%fluid_adj%source_term%add(this%adjoint_convection_term)
212 end if
213
214 !
215 ! Setup user defined conditions
216 !
217 ! if (neko_case%params%valid_path('case.fluid.inflow_condition')) then
218 ! call json_get(neko_case%params, 'case.fluid.inflow_condition.type', &
219 ! string_val)
220 ! if (trim(string_val) .eq. 'user') then
221 ! call neko_case%fluid%set_usr_inflow(neko_case%usr%fluid_user_if)
222 ! end if
223 ! end if
224
225 ! Setup user boundary conditions for the scalar.
226 ! if (scalar_adj) then
227 ! call neko_case%scalar_adj%set_user_bc(neko_case%usr%scalar_user_bc)
228 ! end if
229
230 !
231 ! Setup initial conditions
232 !
233 json_key = json_key_fallback(neko_case%params, &
234 'case.adjoint_fluid.initial_condition', 'case.fluid.initial_condition')
235
236 call json_extract_object(neko_case%params, json_key, ic_json)
237 call json_get(ic_json, 'type', string_val)
238
239 if (trim(string_val) .ne. 'user') then
240 call set_flow_ic( &
241 this%fluid_adj%u_adj, this%fluid_adj%v_adj, this%fluid_adj%w_adj, &
242 this%fluid_adj%p_adj, this%fluid_adj%c_Xh, this%fluid_adj%gs_Xh, &
243 string_val, ic_json)
244 else
245 call set_flow_ic( &
246 this%fluid_adj%u_adj, this%fluid_adj%v_adj, this%fluid_adj%w_adj, &
247 this%fluid_adj%p_adj, this%fluid_adj%c_Xh, this%fluid_adj%gs_Xh, &
248 neko_case%usr%fluid_user_ic, neko_case%params)
249 end if
250
251 call neko_log%end_section()
252
253 if (this%have_scalar) then
254
255 ! we shouldn't fallback to the primal here.
256 call json_get(neko_case%params, &
257 'case.adjoint_scalar.initial_condition.type', string_val)
258 call json_extract_object(neko_case%params, &
259 'case.adjoint_scalar.initial_condition', ic_json)
260
261 !call neko_log%section("Adjoint scalar initial condition ")
262
263 if (trim(string_val) .ne. 'user') then
264 call set_scalar_ic(this%scalar_adj%s_adj, this%scalar_adj%c_Xh, &
265 this%scalar_adj%gs_Xh, string_val, ic_json)
266 else
267 call neko_error("user defined ICs not implemented for adjoint scalar")
268 ! call set_scalar_ic(this%scalar_adj%s_adj, &
269 ! this%scalar_adj%c_Xh, this%scalar_adj%gs_Xh, &
270 ! this%usr%scalar_user_ic, neko_case%params)
271 end if
272
273 ! call neko_log%end_section()
274
275 end if
276
277 ! Add initial conditions to BDF fluid_adj (if present)
278 select type (f => this%fluid_adj)
279 type is (adjoint_fluid_pnpn_t)
280 call f%ulag%set(f%u_adj)
281 call f%vlag%set(f%v_adj)
282 call f%wlag%set(f%w_adj)
283 end select
284
285 !
286 ! Validate that the neko_case is properly setup for time-stepping
287 !
288 call this%fluid_adj%validate
289
290 if (this%have_scalar) then
291 call this%scalar_adj%s_adj_lag%set(this%scalar_adj%s_adj)
292 call this%scalar_adj%validate
293 end if
294
295 !
296 ! Setup output precision of the field files
297 !
298 call json_get_or_default(neko_case%params, 'case.output_precision', &
299 string_val, 'single')
300
301 if (trim(string_val) .eq. 'double') then
302 precision = dp
303 else
304 precision = sp
305 end if
306
307 !
308 ! Setup output_controller
309 !
310 call this%output_controller%init(neko_case%time%end_time)
311 if (this%have_scalar) then
312 this%f_out = adjoint_output_t(precision, this%fluid_adj, &
313 this%scalar_adj, path = trim(neko_case%output_directory))
314 else
315 this%f_out = adjoint_output_t(precision, this%fluid_adj, &
316 path = trim(neko_case%output_directory))
317 end if
318
319 call json_get_or_default(neko_case%params, 'case.fluid.output_control',&
320 string_val, 'org')
321
322 if (trim(string_val) .eq. 'org') then
323 ! yes, it should be real_val below for type compatibility
324 call json_get(neko_case%params, 'case.nsamples', real_val)
325 call this%output_controller%add(this%f_out, real_val, 'nsamples')
326 else if (trim(string_val) .eq. 'never') then
327 ! Fix a dummy 0.0 output_value
328 call json_get_or_default(neko_case%params, 'case.fluid.output_value', &
329 real_val, 0.0_rp)
330 call this%output_controller%add(this%f_out, 0.0_rp, string_val)
331 else
332 call json_get(neko_case%params, 'case.fluid.output_value', real_val)
333 call this%output_controller%add(this%f_out, real_val, string_val)
334 end if
335
336 ! !
337 ! ! Save checkpoints (if nothing specified, default to saving at end of sim)
338 ! !
339 ! call json_get_or_default(neko_case%params, 'case.output_checkpoints',&
340 ! logical_val, .true.)
341 ! if (logical_val) then
342 ! call json_get_or_default(neko_case%params, 'case.checkpoint_format', &
343 ! string_val, "chkp")
344 ! neko_case%f_chkp = chkp_output_t(this%fluid_adj%chkp, &
345 ! path = output_directory, &
346 ! ! fmt = trim(string_val))
347 ! call json_get_or_default(neko_case%params, 'case.checkpoint_control', &
348 ! string_val, "simulationtime")
349 ! call json_get_or_default(neko_case%params, 'case.checkpoint_value', &
350 ! real_val,&
351 ! 1e10_rp)
352 ! call this%output_controller%add(neko_case%f_chkp, real_val, string_val)
353 ! end if
354
355 end subroutine adjoint_case_init_common
356
357 ! Destructor.
358 subroutine adjoint_free(this)
359 class(adjoint_case_t), intent(inout) :: this
360
361 nullify(this%case)
362 if (allocated(this%scalar_adj)) then
363 call this%scalar_adj%free()
364 end if
365
366 if (allocated(this%fluid_adj)) then
367 call this%fluid_adj%free()
368 end if
369 call this%output_controller%free()
370
371 end subroutine adjoint_free
372
373end module adjoint_case
374
Factory for all adjoint fluid schemes.
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.
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.