Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
volume_constraint.f90
1! Copyright (c) 2023, 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!
39 use constraint, only: constraint_t
40
41 use design, only: design_t
42 use brinkman_design, only: brinkman_design_t
43 use simulation_m, only: simulation_t
45 use num_types, only: rp
46 use coefs, only: coef_t
47 use json_module, only: json_file
48 use json_utils, only: json_get, json_get_or_default
49 use field, only: field_t
50 use field_registry, only: neko_field_registry
51 use scratch_registry, only: neko_scratch_registry
52 use neko_config, only: neko_bcknd_device
54 use math, only: glsc2, copy, cmult
55 use device_math, only: device_glsc2, device_copy, device_cmult
56 use vector_math, only: vector_cmult
57 use math_ext, only: glsc2_mask
58 use field_math, only: field_rone, field_copy, field_cmult, field_cfill
59 use utils, only: neko_error
60 use vector, only: vector_t
61 use neko_ext, only: field_to_vector
62 implicit none
63 private
64
66 type, public, extends(constraint_t) :: volume_constraint_t
67 private
68
72 logical :: is_max
74 real(kind=rp) :: limit
76 real(kind=rp) :: volume_domain
78 class(coef_t), pointer :: c_xh => null()
82 logical :: if_mapping = .false.
83
84 contains
85
87 procedure, public, pass(this) :: init_json_sim => &
88 volume_constraint_init_json_sim
90 procedure, public, pass(this) :: init_from_components => &
91 volume_constraint_init_from_components
93 procedure, public, pass(this) :: free => volume_constraint_free
95 procedure, public, pass(this) :: update_value => &
96 volume_constraint_update_value
98 procedure, public, pass(this) :: update_sensitivity => &
99 volume_constraint_update_sensitivity
100
102 procedure, private, pass(this) :: compute_volume
103
104 end type volume_constraint_t
105
106contains
107
113 subroutine volume_constraint_init_json_sim(this, json, design, simulation)
114 class(volume_constraint_t), intent(inout) :: this
115 type(json_file), intent(inout) :: json
116 class(design_t), intent(in) :: design
117 type(simulation_t), target, intent(inout) :: simulation
118
119 character(len=:), allocatable :: mask_name
120 character(len=:), allocatable :: name
121 logical :: is_max
122 real(kind=rp) :: limit
123
124 call json_get_or_default(json, "name", name, "Volume constraint")
125 call json_get_or_default(json, "mask_name", mask_name, "")
126 call json_get_or_default(json, "is_max", is_max, .false.)
127 call json_get(json, "limit", limit)
128
129 call this%init_from_components(design, simulation, name, mask_name, &
130 is_max, limit)
131
132 ! check if we have a mapping
133 if ('mapping' .in. json) then
134 ! Initialize the mapper
135 call this%mapping%init_base(this%c_Xh)
136 call this%mapping%add(json, 'mapping')
137 this%if_mapping = .true.
138 ! recompute value after mapping
139 call this%update_value(design)
140 end if
141
142 end subroutine volume_constraint_init_json_sim
143
152 subroutine volume_constraint_init_from_components(this, design, simulation, &
153 name, mask_name, is_max, limit)
154 class(volume_constraint_t), intent(inout) :: this
155 class(design_t), intent(in) :: design
156 type(simulation_t), target, intent(inout) :: simulation
157 character(len=*), intent(in) :: mask_name
158 character(len=*), intent(in) :: name
159 logical, intent(in) :: is_max
160 real(kind=rp), intent(in) :: limit
161
162 type(field_t), pointer :: work
163 integer :: temp_indices(1)
164
165 ! Initialize the base class
166 call this%init_base(name, design%size(), mask_name)
167
168 ! Store the attributes
169 this%is_max = is_max
170 this%limit = limit
171 this%c_Xh => simulation%neko_case%fluid%c_Xh
172
173 ! Now we can extract the mask/has_mask from the design
174 if (this%has_mask) then
175
176 ! calculate the volume of the optimization domain
177 call neko_scratch_registry%request_field(work, temp_indices(1))
178 call field_rone(work)
179 call mask_exterior_const(work, this%mask, 0.0_rp)
180
181 if (neko_bcknd_device .eq. 1) then
182 this%volume_domain = device_glsc2(work%x_d, this%c_xh%B_d, &
183 work%size())
184 else
185 this%volume_domain = glsc2_mask(work%x, this%c_Xh%B, &
186 design%size(), this%mask%mask%get(), this%mask%size)
187 end if
188
189 call neko_scratch_registry%relinquish_field(temp_indices)
190 else
191 this%volume_domain = this%c_Xh%volume
192 end if
193
194 ! ------------------------------------------------------------------------ !
195 ! Initialize the value of constraint
196
197 call this%update_value(design)
198
199 ! ------------------------------------------------------------------------ !
200 ! Initialize the sensitivity value
201
202 if (neko_bcknd_device .eq. 1) then
203 call device_copy(this%sensitivity%x_d, this%c_xh%B_d, design%size())
204 call device_cmult(this%sensitivity%x_d, -1.0_rp / this%volume_domain, &
205 design%size())
206
207 if (this%is_max) then
208 call device_cmult(this%sensitivity%x_d, -1.0_rp, design%size())
209 end if
210 else
211 call copy(this%sensitivity%x, this%c_Xh%B, design%size())
212 call cmult(this%sensitivity%x, -1.0_rp / this%volume_domain, &
213 design%size())
214
215 if (this%is_max) then
216 call vector_cmult(this%sensitivity, -1.0_rp)
217 end if
218 end if
219
220 if (this%has_mask) then
221 call mask_exterior_const(this%sensitivity, this%mask, 0.0_rp)
222 end if
223
224 end subroutine volume_constraint_init_from_components
225
227 subroutine volume_constraint_free(this)
228 class(volume_constraint_t), intent(inout) :: this
229
230 call this%free_base()
231 end subroutine volume_constraint_free
232
236 subroutine volume_constraint_update_value(this, design)
237 class(volume_constraint_t), intent(inout) :: this
238 class(design_t), intent(in) :: design
239 real(kind=rp) :: volume
240
241 volume = this%compute_volume(design)
242
243 ! Compute the distance to the target volume
244 this%value = this%limit - volume / this%volume_domain
245
246 ! Invert the sign if it is a maximum constraint
247 if (this%is_max) this%value = -this%value
248
249 end subroutine volume_constraint_update_value
250
254 subroutine volume_constraint_update_sensitivity(this, design)
255 class(volume_constraint_t), intent(inout) :: this
256 class(design_t), intent(in) :: design
258 type(field_t), pointer :: unmapped, mapped
259 integer :: temp_indices(2)
260
261 if (this%if_mapping) then
262 ! Recompute and map backward
263 call neko_scratch_registry%request_field(unmapped, temp_indices(1))
264 call neko_scratch_registry%request_field(mapped, temp_indices(2))
265 ! The mapping will handle the mass matrix
266 call field_cfill(unmapped, -1.0_rp / this%volume_domain)
267 if (this%is_max) then
268 call field_cmult(unmapped, -1.0_rp)
269 end if
270 ! mask
271 if (this%has_mask) then
272 call mask_exterior_const(unmapped, this%mask, 0.0_rp)
273 end if
274 ! map backwards
275 call this%mapping%apply_backward(mapped, unmapped)
276 call field_to_vector(this%sensitivity, mapped)
277
278 call neko_scratch_registry%relinquish_field(temp_indices)
279
280 else
281 ! Sensitivity is just a constant so it should not be updated
282 end if
283
284 end subroutine volume_constraint_update_sensitivity
285
286 ! ========================================================================== !
287 ! The actual volume computations for different types of designs
288
294 function compute_volume(this, design) result(volume)
295 class(volume_constraint_t), intent(inout) :: this
296 class(design_t), intent(in) :: design
297 real(kind=rp) :: volume
298
299 volume = 0.0_rp
300 select type (design)
301 type is (brinkman_design_t)
302 volume = volume_brinkman_design(this, design)
303
304 class default
305 call neko_error('Volume constraint only works with brinkman_design')
306 end select
307
308 end function compute_volume
309
313 function volume_brinkman_design(this, design) result(volume)
314 class(volume_constraint_t), intent(inout) :: this
315 type(brinkman_design_t), intent(in) :: design
316 real(kind=rp) :: volume
317 type(field_t), pointer :: work
318 type(vector_t) :: values, unmapped_values
319 integer :: temp_indices(1)
320
321 volume = 0.0_rp
322 if (this%if_mapping) then
323 call design%get_values(unmapped_values)
324 call values%init(unmapped_values%size())
325 call this%mapping%apply_forward(values, unmapped_values)
326 call unmapped_values%free()
327 else
328 call design%get_values(values)
329 end if
330
331 if (this%has_mask) then
332
333 if (neko_bcknd_device .eq. 1) then
334 call neko_scratch_registry%request_field(work, temp_indices(1))
335 call device_copy(work%x_d, values%x_d, design%size())
336 call mask_exterior_const(work, this%mask, 0.0_rp)
337
338 volume = device_glsc2(work%x_d, this%c_xh%B_d, design%size())
339
340 call neko_scratch_registry%relinquish_field(temp_indices)
341 else
342 volume = glsc2_mask(values%x, this%c_Xh%B, design%size(), &
343 this%mask%mask%get(), this%mask%size)
344 end if
345
346 else
347
348 if (neko_bcknd_device .eq. 1) then
349 volume = device_glsc2(values%x_d, this%c_xh%B_d, design%size())
350 else
351 volume = glsc2(values%x, this%c_Xh%B, design%size())
352 end if
353
354 end if
355
356 call values%free()
357
358 end function volume_brinkman_design
359
360end module volume_constraint
Implements the constraint_t type.
Implements the design_t.
Definition design.f90:34
Implements the mapping_handler_t type.
Mappings to be applied to a scalar field.
Definition mapping.f90:35
Some common Masking operations we may need.
Definition mask_ops.f90:34
Contains extensions to the neko library required to run the topology optimization code.
Definition neko_ext.f90:9
subroutine, public field_to_vector(vector, field)
Field to vector.
Definition neko_ext.f90:373
Implements the steady_problem_t type.
Implements the volume_constraint_t type. Either .
A topology optimization design variable.
The abstract constraint type.
An abstract design type.
Definition design.f90:52
Abstract class for handling mapping_cascade.
A constraint on the volume of the design.