Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
mma_optimizer.f90
1module mma_optimizer
2 use optimizer, only: optimizer_t
3 use problem, only : problem_t
4 use mma, only: mma_t
5 use problem, only: problem_t
6 use num_types, only: rp
7 use utils, only: neko_error
8 use json_module, only: json_file
9 use json_utils, only: json_get, json_get_or_default
10 use simulation_m, only: simulation_t
11 use design, only: design_t
12 use brinkman_design, only: brinkman_design_t
13 use field, only: field_t
14 use field_registry, only: neko_field_registry
15 use profiler, only: profiler_start_region, profiler_end_region
16
17 use vector, only: vector_t
18 use matrix, only: matrix_t
19
20 !only to print nglobal when running in parallel
21 use comm, only: neko_comm, pe_rank
22 use mpi_f08, only: mpi_integer, mpi_sum, mpi_allreduce
23
24 use neko_config, only: neko_bcknd_device
25 ! Inclusions from external dependencies and standard libraries
26 use, intrinsic :: iso_fortran_env, only: stderr => error_unit
27
28 use math, only: copy, cmult
29 use device_math, only: device_copy, device_cmult
30 use field_math, only: field_rzero
31 use vector_math, only: vector_cmult
32 use neko_ext, only: reset
34 use device, only: device_memcpy, host_to_device, device_to_host
35
36 use constraint, only: constraint_t
38 implicit none
39 private
40 public :: mma_optimizer_t
41
42 ! Concrete type for MMA optimizer
43 type, extends(optimizer_t) :: mma_optimizer_t
44
45 type(mma_t) :: mma
46
53 real(kind=rp) :: scale
54 logical :: auto_scale
55
56 ! Set to flase to remove logging for optimal performance
57 logical :: enable_output
58
59 contains
60
61 ! Override the deferred methods
62 generic :: init => init_from_json, init_from_components
63 procedure, pass(this) :: init_from_json => mma_optimizer_init_from_json
64 procedure, pass(this) :: init_from_components => &
65 mma_optimizer_init_from_components
66
67 procedure, pass(this) :: run => mma_optimizer_run
68 procedure, pass(this) :: validate => mma_optimizer_validate
69 procedure, pass(this) :: free => mma_optimizer_free
70
71 end type mma_optimizer_t
72
73contains
74
76 subroutine mma_optimizer_init_from_json(this, parameters, problem, design, &
77 simulation)
78 class(mma_optimizer_t), intent(inout) :: this
79 type(json_file), intent(inout) :: parameters
80 class(problem_t), intent(in) :: problem
81 class(design_t), intent(in) :: design
82 type(simulation_t), optional, intent(in) :: simulation
83 logical :: enable_output
84 integer :: max_iterations
85 real(kind=rp) :: tolerance
86
87 character(len=1024) :: optimization_header
88 character(len=1024) :: problem_header
89
90 type(vector_t) :: x
91 type(json_file) :: solver_parameters
92 logical :: unconstrained_problem
93
94 ! Initialize the logger
95 call this%logger%init('optimization_data.csv')
96
97 ! Write the header
98 problem_header = problem%get_log_header()
99 optimization_header = 'iter, ' // trim(problem_header) // &
100 ', KKTmax, KKTnorm2, scaling factor'
101
102 call design%get_values(x)
103
104 if (pe_rank .eq. 0) then
105 print *, "Initializing mma_optimizer with steady_state_problem_t."
106 end if
107
108 call json_get(parameters, "optimization.solver", &
109 solver_parameters)
110
111 ! Initialize mma_t, handling the dummy_constraint added for unconstrained
112 ! problems in mma_optimizer_run()
113 unconstrained_problem = (problem%get_n_constraints() == 0)
114 if (unconstrained_problem) then
115 call this%mma%init(x, design%size(), 1, &
116 solver_parameters, this%scale, this%auto_scale)
117 else
118 call this%mma%init(x, design%size(), problem%get_n_constraints(), &
119 solver_parameters, this%scale, this%auto_scale)
120 end if
121
122 call json_get_or_default(parameters, "optimization.solver.max_iterations", &
123 max_iterations, 100)
124 call json_get_or_default(parameters, "optimization.solver.tolerance", &
125 tolerance, 1.0e-3_rp)
126 call json_get_or_default(parameters, "optimization.solver.enable_output", &
127 enable_output, .true.)
128
129 call this%init_from_components(problem, design, &
130 max_iterations, tolerance, enable_output, simulation)
131
132 call this%logger%set_header(trim(optimization_header) // &
133 this%mma%get_backend_and_subsolver())
134 call x%free()
135 end subroutine mma_optimizer_init_from_json
136
138 subroutine mma_optimizer_init_from_components(this, problem, design, &
139 max_iterations, tolerance, enable_output, simulation)
140 class(mma_optimizer_t), intent(inout) :: this
141 class(problem_t), intent(in) :: problem
142 class(design_t), intent(in) :: design
143 integer, intent(in) :: max_iterations
144 real(kind=rp), intent(in) :: tolerance
145 type(simulation_t), intent(in), optional :: simulation
146 logical, intent(in) :: enable_output
147
148 !set the enable_output flag
149 this%enable_output = enable_output
150
151 call this%init_base(max_iterations, tolerance)
152
153 end subroutine mma_optimizer_init_from_components
154
156 subroutine mma_optimizer_run(this, problem, design, simulation)
157 class(mma_optimizer_t), intent(inout) :: this
158 class(problem_t), intent(inout) :: problem
159 class(design_t), intent(inout) :: design
160 type(simulation_t), optional, intent(inout) :: simulation
161
162 type(vector_t) :: x
163
164 integer :: iter, ierr, nglobal, n
165 real(kind=rp) :: scaling_factor
166
167 real(kind=rp) :: objective_value
168 type(vector_t) :: all_objectives
169 type(vector_t) :: constraint_value
170 type(vector_t) :: objective_sensitivities
171 type(matrix_t) :: constraint_sensitivities
172
173 type(vector_t) :: log_data
174 logical :: unconstrained_problem = .false.
175 class(constraint_t), allocatable :: dummy_con
176 type(json_file) :: parameters
177
178 n = design%size()
179 call mpi_allreduce(n, nglobal, 1, mpi_integer, mpi_sum, neko_comm, ierr)
180
181 unconstrained_problem = (problem%get_n_constraints() == 0)
182 if (unconstrained_problem) then
183 allocate(dummy_constraint_t::dummy_con)
184 call dummy_con%init(parameters, design)
185 call problem%add_constraint(dummy_con)
186 end if
187
188 ! Initialize the vectors
189 call x%init(n)
190 call all_objectives%init(problem%get_n_objectives())
191 call constraint_value%init(problem%get_n_constraints())
192 call objective_sensitivities%init(n)
193 call constraint_sensitivities%init(problem%get_n_constraints(), n)
194
196 scaling_factor = 1.0_rp
197 if (pe_rank .eq. 0) then
198 print *, "max_iterations for the optimization loop = ", &
199 this%max_iterations
200 end if
201 call profiler_start_region("Optimizer iteration")
202
203 call problem%compute(design, simulation)
204 call problem%compute_sensitivity(design, simulation)
205
206 call problem%get_objective_value(objective_value)
207 call problem%get_constraint_values(constraint_value)
208 select type (des => design)
209 type is (brinkman_design_t)
210 call des%get_sensitivity(objective_sensitivities)
211 class default
212 call problem%get_objective_sensitivities(objective_sensitivities)
213 end select
214 call problem%get_constraint_sensitivities(constraint_sensitivities)
215
216 call profiler_end_region("Optimizer iteration")
217
218 if (this%enable_output) then
219 call profiler_start_region("Optimizer logging")
220 ! Stamp the initial condition
221 call problem%get_all_objective_values(all_objectives)
222 call mma_logger_assemble_data(log_data, 0, objective_value, &
223 all_objectives, constraint_value, 0.0_rp, 0.0_rp, scaling_factor, &
224 problem%get_n_objectives(), problem%get_n_constraints(), &
225 unconstrained_problem)
226 call this%logger%write(log_data)
227
228 if (present(simulation)) then
229 call simulation%write(0)
230 end if
231
232 call profiler_end_region("Optimizer logging")
233 end if
234 call design%write(0)
235
236 do iter = 1, this%max_iterations
237 if (this%mma%get_residumax() .lt. this%tolerance) exit
238
239 call design%get_values(x)
240
241 call profiler_start_region("Optimizer iteration")
242
243 ! Scaling
244 if (this%auto_scale .eqv. .true.) then
245 scaling_factor = abs(this%scale/constraint_value%x(1))
246 else
247 scaling_factor = abs(this%scale)
248 end if
249
250 call vector_cmult(constraint_value, scaling_factor)
251
252 if (neko_bcknd_device .eq. 1) then
253 call device_cmult(constraint_sensitivities%x_d, scaling_factor, &
254 constraint_sensitivities%size())
255 else
256 call cmult(constraint_sensitivities%x, scaling_factor, &
257 constraint_sensitivities%size())
258 end if
259
260 ! Use scaled sensitivities to update the design variable
261 call profiler_start_region("MMA update")
262 call this%mma%update(iter, x, objective_sensitivities, &
263 constraint_value, constraint_sensitivities)
264 call profiler_end_region("MMA update")
265
266 call design%update_design(x)
267
268 call problem%compute(design, simulation)
269 call problem%compute_sensitivity(design, simulation)
270
271 call problem%get_objective_value(objective_value)
272 call problem%get_constraint_values(constraint_value)
273 select type (des => design)
274 type is (brinkman_design_t)
275 call des%get_sensitivity(objective_sensitivities)
276 class default
277 call problem%get_objective_sensitivities(objective_sensitivities)
278 end select
279 call problem%get_constraint_sensitivities(constraint_sensitivities)
280
281 call profiler_start_region("MMA KKT computation")
282 call this%mma%KKT(x, objective_sensitivities, &
283 constraint_value, constraint_sensitivities)
284 call profiler_end_region("MMA KKT computation")
285
286 call profiler_end_region("Optimizer iteration")
287
288 if (this%enable_output) then
289 call profiler_start_region("Optimizer logging")
290 ! Stamp the i^th iteration
291 call problem%get_all_objective_values(all_objectives)
292 call mma_logger_assemble_data(log_data, iter, objective_value, &
293 all_objectives, constraint_value, this%mma%get_residumax(), &
294 this%mma%get_residunorm(), scaling_factor, &
295 problem%get_n_objectives(), problem%get_n_constraints(), &
296 unconstrained_problem)
297 call this%logger%write(log_data)
298
299 if (present(simulation)) then
300 call simulation%write(iter)
301 end if
302 call design%write(iter)
303 call profiler_end_region("Optimizer logging")
304 end if
305
306 end do
307
308 call this%validate(problem, design)
309
310 ! Final state after optimization
311 if (pe_rank .eq. 0) then
312 print *, "MMA Optimization completed after", iter-1, "iterations."
313 end if
314
315 ! Free local resources
316 call x%free()
317 call log_data%free()
318 call all_objectives%free()
319 call constraint_value%free()
320 call objective_sensitivities%free()
321 call constraint_sensitivities%free()
322
323 end subroutine mma_optimizer_run
324
326 subroutine mma_optimizer_validate(this, problem, design)
327 class(mma_optimizer_t), intent(inout) :: this
328 class(problem_t), intent(in) :: problem
329 class(design_t), intent(in) :: design
330
331 type(vector_t) :: constraint_values
332
333 call constraint_values%init(problem%get_n_constraints())
334 call problem%get_constraint_values(constraint_values)
335 if (neko_bcknd_device .eq. 1) then
336 call device_memcpy(constraint_values%x, constraint_values%x_d, &
337 constraint_values%size(), host_to_device, .true.)
338 end if
339
340 if (any(constraint_values%x .gt. 0.0_rp)) then
341 call neko_error("MMA optimizer validation failed: " // &
342 "Constraints are not satisfied.")
343 end if
344
345 ! Free local resources
346 call constraint_values%free()
347
348 end subroutine mma_optimizer_validate
349
350 ! Free resources associated with the MMA optimizer
351 subroutine mma_optimizer_free(this)
352 class(mma_optimizer_t), intent(inout) :: this
353
354 ! Free MMA-specific data
355 call this%mma%free()
356 end subroutine mma_optimizer_free
357
358 ! package up the log data
359 subroutine mma_logger_assemble_data(log_data, iter, objective_value, &
360 all_objectives, constraint_value, residumax, residunorm, &
361 scaling_factor, n, m, unconstrained_problem)
362 type(vector_t), intent(out) :: log_data
363 integer, intent(in) :: iter
364 real(kind=rp), intent(in) ::objective_value
365 type(vector_t), intent(in) :: all_objectives
366 type(vector_t), intent(in) :: constraint_value
367 real(kind=rp), intent(in) :: residumax, residunorm, scaling_factor
368 logical, intent(in) :: unconstrained_problem
369 integer, intent(in) :: n, m
370 integer :: i_tmp1, i_tmp2
371
372
373
374 ! initialize the logger data
375 ! iter | tot F | F_1 | .. |F_n | C_1 | ... | C_n | KKT | KKT2 | scale |
376 if (unconstrained_problem) then
377 call log_data%init(5 + n)
378 else
379 call log_data%init(5 + n + m)
380 endif
381
382 ! iteration
383 log_data%x(1) = real(iter, kind=rp)
384
385 ! total objective
386 log_data%x(2) = objective_value
387
388 ! individual objectives
389 i_tmp1 = 3
390 i_tmp2 = i_tmp1 + n - 1
391 log_data%x(i_tmp1 : i_tmp2) = all_objectives%x
392
393 ! constraints
394 if (.not. unconstrained_problem) then
395 i_tmp1 = i_tmp2 + 1
396 i_tmp2 = i_tmp1 + m - 1
397 log_data%x(i_tmp1 : i_tmp2) = constraint_value%x
398 end if
399
400
401 ! convergence stuff
402 log_data%x(i_tmp2 + 1) = residumax
403 log_data%x(i_tmp2 + 2) = residunorm
404 log_data%x(i_tmp2 + 3) = scaling_factor
405
406 end subroutine mma_logger_assemble_data
407end module mma_optimizer
408
Implements the constraint_t type.
Implements the design_t.
Definition design.f90:34
Implements the dummy_constraint_t type.
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 reset(neko_case)
Reset the case data structure.
Definition neko_ext.f90:58
Module for handling the optimization problem.
Definition problem.f90:35
Implements the steady_problem_t type.
A topology optimization design variable.
The abstract constraint type.
An abstract design type.
Definition design.f90:52
Abstract optimizer class.
Definition optimizer.f90:18
The abstract problem type.
Definition problem.f90:62