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