Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
design_brinkman.f90
Go to the documentation of this file.
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 `brinkman_design_t` type.
35 use num_types, only: rp, sp
36 use field, only: field_t
37 use json_module, only: json_file
38 use mapping, only: mapping_t
39 use pde_filter, only: pde_filter_t
41 use coefs, only: coef_t
42 use scratch_registry, only: neko_scratch_registry
43 use fld_file_output, only: fld_file_output_t
44 use point_zone_registry, only: neko_point_zone_registry
45 use point_zone, only: point_zone_t
47 use neko_config, only: neko_bcknd_device
48 use device, only: device_memcpy, host_to_device
49 use design, only: design_t
50 use math, only: rzero
51 use simulation, only: simulation_t
52 use json_module, only: json_file
54 use vector, only: vector_t
55 use math, only: copy
56 use field_registry, only: neko_field_registry
57 implicit none
58 private
59
61 type, extends(design_t), public :: brinkman_design_t
62 private
63
64 ! TODO
65 ! in the future make this a derived type of a `design_variable`
66 ! type, public, extends(design_variable_t) :: brinkman_design_t
68 type(field_t), pointer :: design_indicator
69
71 ! TODO
72 ! NOTE: Tim, right now we only have the brinkman term
73 ! in the future we may map to other coeeficients for other equations...
74 ! in which case this should be a field list
75 !
76 ! or as I describe below, we also have multiple constraints,
77 ! so a list-of-lists may be the correct way forward
78 type(field_t), pointer :: brinkman_amplitude
79
80 ! NOTE:
81 ! again, we have to be so clear with nomenclature.
82 ! If we have an objective function F.
83 ! I also like to use \rho to describe the design_indicator
84 !
85 ! and let's say, for completness, we're doing conjugate heat transfer,
86 ! so we have to map
87 ! to 3 coefficients, \chi, C and \kappa.
88 !
89 ! then we will perform 3 mapping,
90 ! \rho -> \chi
91 ! \rho -> C
92 ! \rho -> \kappa
93 !
94 ! Then during the forward/adjoint looping there will be an additional
95 ! object, the "objective_function" object that will be responsible for
96 ! computing the sensitivity of the objective function with respect to the
97 ! coefficients
98 ! ie,
99 ! dF/d\chi, dF/dC and dF/d\kappa
100 !
101 ! What I'm calling "sensitivity" here, is the sensitivity with respect to
102 ! the design indicator
103 ! so dF/d\rho
104 !
105 ! so the proceedure "map_backwards" will take in the field list
106 ! dF/d\chi, dF/dC and dF/d\kappa
107 !and chain rule it's way back to
108 ! dF/d\rho
109 ! and store it here v
110 type(field_t), pointer :: sensitivity
111 ! have a field list here
112 ! type(filed_list_t), public :: constraint_sensitivity
113 ! HOWEVER !
114 ! What is a bit confusing to me... is how we'll deal with constraints.
115 !
116 ! Because in principle we could have constraints C1, C2, C3 etc
117 ! Implying we also want dC1/d\rho, dC2/d\rho etc
118 ! So this "sensitivity" should also be a list...
119 !
120 ! So I bet you'll have a nice abstract way of constructing this in the
121 ! future but I think a sort of list-of-lists would be nice.
122 !
123 ! For F:
124 ! \rho -> \tild{\rho} -> \chi
125 ! \rho -> \tild{\rho} -> C
126 ! \rho -> \tild{\rho} -> \kappa
127 !
128 ! For C1:
129 ! \rho -> \tild{\rho} (eg for a volume constraint or so)
130 !
131 ! For C2:
132 ! \rho -> \tild{\rho} (for something else)
133 !
134 ! etc..
135 !
136 ! So now we have multiple instances of the "objective" type,
137 ! each for F, C1, C2 etc
138 ! each of them can pass their dF\d_coefficents to the design
139 ! and we continue from there.
140 !
141 ! perhaps even the "objective" type is defined as a list of objectives.
142
143 ! Let's say way have a chain of two mappings
144 type(pde_filter_t) :: filter
146
147 ! and we need to hold onto a field for the chain of mappings
148 type(field_t), pointer :: filtered_design
149
150
152 class(point_zone_t), pointer :: optimization_domain
154 logical :: if_mask
155
156 ! TODO
157 ! you also had logicals for convergence etc,
158 ! I feel they should live in "problem"
159
160 ! afield writer would be nice too
161 type(fld_file_output_t), private :: output
162
163 contains
164
165 ! ----------------------------------------------------------------------- !
166 ! Initializations
167
169 generic, public :: init => init_from_json_sim, init_from_components
171 procedure, pass(this), public :: init_from_json_sim => &
174 procedure, pass(this), public :: init_from_components => &
176
178 procedure, pass(this) :: get_design => brinkman_design_get_design
179
181 procedure, pass(this) :: update_design => brinkman_design_update_design
182
184 ! design_indicator -> filtering -> chi
185 ! and ultimately handle mapping different coeficients!
186 procedure, pass(this) :: map_forward => brinkman_design_map_forward
188 ! d_design_indicator <- d_filtering <- d_chi
189 ! and ultimately handle mapping different coeficients!
190 procedure, pass(this) :: map_backward => brinkman_design_map_backward
191 ! TODO
192 ! maybe it would have been smarter to have a "coeficient" type,
193 ! which is just a scalar field and set of mappings going from
194 ! design_indicator -> coeficient and their corresponding chain rules
195 ! maybe also some information about what equation they live in...
196
197 ! a writer being called from outside would be nice
198 procedure, pass(this) :: write => brinkman_design_write
199
201 procedure, pass(this) :: free => brinkman_design_free
202 ! TODO
203 ! I'm not sure who owns the optimizer...
204 ! but it would make sense to have it in here so you provide it
205 ! with dF/d_design_indicator and it updates itself.
206 ! procedure, pass(this) :: update => brinkman_design_update_design
207 end type brinkman_design_t
208
209
210contains
211
213 subroutine brinkman_design_init_from_json_sim(this, parameters, simulation)
214 class(brinkman_design_t), intent(inout) :: this
215 type(json_file), intent(inout) :: parameters
216 type(simulation_t), intent(inout) :: simulation
217
218 call this%init_from_components(simulation)
219
220 ! Todo: This need to be read from the parameters in the JSON
221 associate(coef => simulation%neko_case%fluid%c_Xh)
222 call this%filter%init(parameters, coef)
223 call this%mapping%init(parameters, coef)
224 end associate
225
226 ! and then we would map for the first one
227 call this%map_forward()
228
230
232 subroutine brinkman_design_free(this)
233 class(brinkman_design_t), intent(inout) :: this
234
235 call this%free_base()
236 call this%brinkman_amplitude%free()
237 call this%design_indicator%free()
238 call this%filtered_design%free()
239 call this%sensitivity%free()
240
241 end subroutine brinkman_design_free
242
243 subroutine brinkman_design_init_from_components(this, simulation)
244 class(brinkman_design_t), intent(inout) :: this
245 type(simulation_t), intent(inout) :: simulation
246 character(len=:), allocatable :: optimization_domain_zone_name
247 integer :: n, i
248 type(simple_brinkman_source_term_t) :: forward_brinkman, adjoint_brinkman
249
250 associate(dof => simulation%neko_case%fluid%dm_Xh)
251
252 call neko_field_registry%add_field(dof, "design_indicator", .true.)
253 call neko_field_registry%add_field(dof, "brinkman_amplitude", .true.)
254 call neko_field_registry%add_field(dof, "sensitivity", .true.)
255 call neko_field_registry%add_field(dof, "filtered_design", .true.)
256
257 end associate
258
259 this%design_indicator => &
260 neko_field_registry%get_field("design_indicator")
261 this%brinkman_amplitude => &
262 neko_field_registry%get_field("brinkman_amplitude")
263 this%sensitivity => &
264 neko_field_registry%get_field("sensitivity")
265 this%filtered_design => &
266 neko_field_registry%get_field("filtered_design")
267
268 ! TODO
269 ! this is where we steal basically everything in
270 ! brinkman_source_term regarding loading initial fields
271 ! for now, make it a cylinder by hand
272 this%design_indicator = 0.0_rp
273 this%brinkman_amplitude = 0.0_rp
274 this%design_indicator%x = 0.0_rp
275
276 n = this%design_indicator%dof%size()
277 do i = 1, n
278 if (sqrt((this%design_indicator%dof%x(i,1,1,1) - 0.5_rp)**2 + &
279 (this%design_indicator%dof%y(i,1,1,1) &
280 - 0.5_rp)**2) .lt. 0.25_rp) then
281 this%design_indicator%x(i,1,1,1) = 1.0_rp
282 end if
283 end do
284
285 ! again this will be handled better in the future...
286 if (neko_bcknd_device .eq. 1) then
287 call device_memcpy(this%design_indicator%x, &
288 this%design_indicator%x_d, n, &
289 host_to_device, sync = .false.)
290 end if
291
292 ! TODO, of course when we move all of Tim's stuff for initialization of
293 ! the initial design field we'll be reading things properly from the JSON.
294 ! call json_get(parameters, 'name', optimization_domain_zone_name)
295 ! Right now, I'm hardcoding the name of the point zone.
296 this%if_mask = .true.
297 optimization_domain_zone_name = "optimization_domain"
298
299 ! Initialize the mask
300 if (this%if_mask) then
301 this%optimization_domain => &
302 neko_point_zone_registry%get_point_zone(&
303 optimization_domain_zone_name)
304 end if
305
306 ! TODO
307 ! Regarding masks and filters,
308 ! I suppose there are two ways of thinking about it:
309 ! 1) Mask first, then filter
310 ! 2) filter first, then mask
311 !
312 ! Each one can be used to achieve different results, and when we do complex
313 ! non-linear filter cascades the choice here has implications for exactly
314 ! how we define "minimum size control".
315 !
316 ! On top of this, I've only ever used spatial convolution filters before,
317 ! not PDE based filters.
318 ! With these filters you need to think of how your domain is "padded" when
319 ! you move the kernel over the boundary. Again, you can achieve different
320 ! effects depending on the choice of padding.
321 ! I don't really know if there's a similar implication for PDE based
322 ! based filters, I bet there is. (We should ask Niels and Casper) I bet it
323 ! means we need to consider the boundary of the mask as the boundary we
324 ! enforce to be Nuemann, not the boundary of the computational domain.
325 ! That sounds REALLY hard to implement...I hope it doesn't come down to
326 ! that.
327 !
328 ! We can always change this decision later, but I'm going with (1)
329 ! mask first, then filter.
330 ! The reason being, one of the purposes of the filtering is to avoid sharp
331 ! interfaces, if we filter first then mask there's a chance we have a sharp
332 ! interface on the boundary of the optimization domain.
333 ! if we mask first then filter, at least all the boundaries will be smooth.
334 if (this%if_mask) then
335 call mask_exterior_const(this%design_indicator, &
336 this%optimization_domain, 0.0_rp)
337 end if
338
339 ! a field writer would be nice to output
340 ! - design indicator (\rho)
341 ! - mapped design (\chi)
342 ! - sensitivity (dF/d\chi)
343 ! TODO
344 ! do this properly with JSON
345 ! TODO
346 ! obviously when we do the mappings properly, to many coeficients,
347 ! we'll also have to modify this
348 call this%output%init(sp, 'design', 3)
349 call this%output%fields%assign_to_field(1, this%design_indicator)
350 call this%output%fields%assign_to_field(2, this%brinkman_amplitude)
351 call this%output%fields%assign_to_field(3, this%sensitivity)
352
353 call this%init_base(n)
354
355 ! init the simple brinkman term for the forward problem
356 call forward_brinkman%init_from_components( &
357 simulation%fluid_scheme%f_x, &
358 simulation%fluid_scheme%f_y, &
359 simulation%fluid_scheme%f_z, &
360 this%brinkman_amplitude, &
361 simulation%fluid_scheme%u, &
362 simulation%fluid_scheme%v, &
363 simulation%fluid_scheme%w, &
364 simulation%fluid_scheme%c_Xh)
365 ! append brinkman source term to the forward problem
366 call simulation%fluid_scheme%source_term%add(forward_brinkman)
367
368 ! init the simple brinkman term for the adjoint
369 call adjoint_brinkman%init_from_components( &
370 simulation%adjoint_case%scheme%f_adj_x, &
371 simulation%adjoint_case%scheme%f_adj_y, &
372 simulation%adjoint_case%scheme%f_adj_z, &
373 this%brinkman_amplitude, &
374 simulation%adjoint_case%scheme%u_adj, &
375 simulation%adjoint_case%scheme%v_adj, &
376 simulation%adjoint_case%scheme%w_adj, &
377 simulation%adjoint_case%scheme%c_Xh)
378 ! append brinkman source term based on design
379 call simulation%adjoint_case%scheme%source_term%add(adjoint_brinkman)
380
382
383
385 class(brinkman_design_t), intent(inout) :: this
386
387 ! TODO, see previous todo about mask first, then mapping
388 if (this%if_mask) then
389 call mask_exterior_const(this%design_indicator, &
390 this%optimization_domain, 0.0_rp)
391 end if
392
393 ! TODO
394 ! this should be somehow deffered so we can pick different mappings!!!
395 ! so this would be:
396 ! call mapper%forward(fld_out, fld_in)
397
398 call this%filter%apply_forward(this%filtered_design, &
399 this%design_indicator)
400
401 call this%mapping%apply_forward(this%brinkman_amplitude, &
402 this%filtered_design)
403
404 end subroutine brinkman_design_map_forward
405
406 function brinkman_design_get_design(this) result(x)
407 class(brinkman_design_t), intent(in) :: this
408 type(vector_t) :: x
409 integer :: n
410
411 n = this%size()
412 call x%init(n)
413 call copy(x%x, this%design_indicator%x, n)
414 if (neko_bcknd_device .eq. 1) then
415 call device_copy(x%x_d, this%design_indicator%x_d, n)
416 end if
417
418 end function brinkman_design_get_design
419
421 class(brinkman_design_t), intent(inout) :: this
422 type(vector_t), intent(inout) :: x
423 integer :: n
424
425 n = this%size()
426 call copy(this%design_indicator%x, x%x, n)
427 if (neko_bcknd_device .eq. 1) then
428 call device_copy(this%design_indicator%x_d, x%x_d, n)
429 end if
430
431 call this%map_forward()
432
433 call copy(x%x, this%design_indicator%x, n)
434 if (neko_bcknd_device .eq. 1) then
435 call device_copy(x%x_d, this%design_indicator%x_d, n)
436 end if
437
438 end subroutine brinkman_design_update_design
439
440 subroutine brinkman_design_map_backward(this, sensitivity)
441 class(brinkman_design_t), intent(inout) :: this
442 type(vector_t), intent(in) :: sensitivity
443 type(field_t), pointer :: df_dchi
444 type(field_t), pointer :: dF_dfiltered_design
445 integer :: temp_indices(2)
446
447 ! it would be nice to visualize this
448
449 call neko_scratch_registry%request_field(df_dchi, temp_indices(1))
450 call copy(df_dchi%x, sensitivity%x, this%size())
451
452 ! TODO
453 ! again..
454 ! so this would be:
455 ! call mapper%backward(fld_out, fld_in)
456 call neko_scratch_registry%request_field(df_dfiltered_design, &
457 temp_indices(2))
458
459 call this%mapping%apply_backward(df_dfiltered_design, df_dchi, &
460 this%filtered_design)
461
462 call this%filter%apply_backward(this%sensitivity, df_dfiltered_design, &
463 this%filtered_design)
464
465 ! TODO
466 ! DELETE THIS LATER
467 !
468 ! When Abbas writes the interface for the optimization
469 ! module this may be a moot point, because we would only really collect
470 ! the sensitivity of the design variables inside the mask.
471 !
472 ! Note for Abbas,
473 ! I'm NOT doing this because I'm too lazy and I just need masks so I can
474 ! test something in the passive scalar.
475 if (this%if_mask) then
476 call mask_exterior_const(this%sensitivity, this%optimization_domain, &
477 0.0_rp)
478 end if
479
480 call neko_scratch_registry%relinquish_field(temp_indices)
481
482 end subroutine brinkman_design_map_backward
483
484 subroutine brinkman_design_write(this, idx)
485 class(brinkman_design_t), intent(inout) :: this
486 integer, intent(in) :: idx
487
488 call this%output%sample(real(idx, kind=rp))
489
490 end subroutine brinkman_design_write
491
492end module brinkman_design
subroutine brinkman_design_init_from_json_sim(this, parameters, simulation)
Initialize the design from a JSON file.
subroutine brinkman_design_write(this, idx)
subroutine brinkman_design_map_forward(this)
subroutine brinkman_design_free(this)
Free the design.
type(vector_t) function brinkman_design_get_design(this)
subroutine brinkman_design_init_from_components(this, simulation)
subroutine brinkman_design_update_design(this, x)
subroutine brinkman_design_map_backward(this, sensitivity)
Implements the design_t.
Definition design.f90:34
Mappings to be applied to a scalar field.
Definition mapping.f90:35
Some common Masking operations we may need.
Definition mask_ops.f90:34
A PDE based filter.
A RAMP mapping of coefficients.
Sensitivity module. This module contains the sensitivity computation of the topology optimization.
Implements the simple_brinkman_source_term_t type.
Implements the steady_problem_t type.
A topology optimization design variable.
An abstract design type.
Definition design.f90:48
Base abstract class for mapping.
Definition mapping.f90:44
A PDE based filter mapping $\rho \mapsto \tilde{\rho}$, see Lazarov & O. Sigmund 2010,...
A RAMP mapping of coefficients This is the standard RAMP described in https://doi....