Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
mma_optimizer.f90
Go to the documentation of this file.
1
34
35module mma_optimizer
36 use optimizer, only: optimizer_t
37 use mma, only: mma_t
38 use problem, only: problem_t
39 use num_types, only: rp
40 use utils, only: neko_error
41 use json_utils, only: json_get, json_get_or_default
42 use simulation_m, only: simulation_t
43 use design, only: design_t
44 use brinkman_design, only: brinkman_design_t
45 use constraint, only: constraint_t
47
48 ! External modules
49 use json_module, only: json_file
50 use vector, only: vector_t
51 use matrix, only: matrix_t
52 use math, only: abscmp
53 use profiler, only: profiler_start_region, profiler_end_region
54 use logger, only: neko_log
55 use vector_math, only: vector_cmult, vector_absval, vector_sub2, &
56 vector_glmax, vector_glsubnorm
57 use matrix_math, only: matrix_cmult
58 use device, only: device_memcpy, device_to_host
59 use scratch_registry, only: neko_scratch_registry
60 use comm, only: pe_rank, neko_comm
61 use mpi_f08, only: mpi_barrier
62
63 implicit none
64 private
65
66 public :: mma_optimizer_t
67
68 ! Concrete type for MMA optimizer
69 type, extends(optimizer_t) :: mma_optimizer_t
70
71 type(mma_t), private :: mma
72
79 real(kind=rp), private :: scale = 1.0_rp
80 real(kind=rp), private :: scaling_factor = 1.0_rp
81 logical, private :: auto_scale = .false.
82 real(kind=rp) :: tolerance = 0.0_rp
83
84 ! Set to flags to remove logging for optimal performance
85 logical, private :: unconstrained_problem = .false.
86
88 logical, private :: enable_output = .true.
89 contains
90
91 ! Override the deferred methods
92 generic :: init => init_from_json, init_from_components
93 procedure, pass(this) :: init_from_json => mma_optimizer_init_from_json
94 procedure, pass(this) :: init_from_components => &
95 mma_optimizer_init_from_components
96
97 procedure, pass(this) :: initialize => mma_optimizer_initialize
98 procedure, pass(this) :: step => mma_optimizer_step
99 procedure, pass(this) :: validate => mma_optimizer_validate
100 procedure, pass(this) :: write => mma_optimizer_write
101 procedure, pass(this) :: free => mma_optimizer_free
102
103 procedure, pass(this) :: save_checkpoint_components => &
104 mma_optimizer_save_checkpoint_components
105 procedure, pass(this) :: load_checkpoint_components => &
106 mma_optimizer_load_checkpoint_components
107
108 end type mma_optimizer_t
109
110contains
111
112 ! -------------------------------------------------------------------------- !
113 ! Allocator and deallocator methods for the MMA optimizer
114
116 subroutine mma_optimizer_init_from_json(this, parameters, problem, design, &
117 simulation)
118 class(mma_optimizer_t), intent(inout) :: this
119 type(json_file), intent(inout) :: parameters
120 class(problem_t), intent(inout) :: problem
121 class(design_t), intent(in) :: design
122 type(simulation_t), optional, intent(in) :: simulation
123
124 ! Variables for settings
125 type(json_file) :: solver_parameters
126 logical :: enable_output
127 integer :: max_iterations
128 real(kind=rp) :: tolerance
129
130 ! Read the solver properties from the JSON file
131 call json_get(parameters, 'optimization.solver', solver_parameters)
132 call json_get_or_default(solver_parameters, 'max_iterations', &
133 max_iterations, 100)
134 call json_get_or_default(solver_parameters, 'tolerance', &
135 tolerance, 1.0e-3_rp)
136 call json_get_or_default(solver_parameters, 'enable_output', &
137 enable_output, .true.)
138 call this%read_base_settings(solver_parameters)
139
140 call this%init_from_components(problem, design, max_iterations, tolerance, &
141 enable_output, solver_parameters, simulation)
142
143 end subroutine mma_optimizer_init_from_json
144
146 subroutine mma_optimizer_init_from_components(this, problem, design, &
147 max_iterations, tolerance, enable_output, &
148 solver_parameters, simulation)
149 class(mma_optimizer_t), intent(inout) :: this
150 class(problem_t), intent(inout) :: problem
151 class(design_t), intent(in) :: design
152 integer, intent(in) :: max_iterations
153 real(kind=rp), intent(in) :: tolerance
154 logical, intent(in) :: enable_output
155 type(json_file), intent(inout), optional :: solver_parameters
156 type(simulation_t), intent(in), optional :: simulation
157
158 ! Local variables
159 type(vector_t), pointer :: x
160 integer :: ind
161 character(len=32) :: extra_headers(3)
162 class(constraint_t), allocatable :: dummy_con
163
164 call neko_log%section('Optimizer Initialization')
165
166 ! Check if the problem is unconstrained
167 this%unconstrained_problem = problem%get_n_constraints() .eq. 0
168 if (this%unconstrained_problem) then
169 call neko_log%message('Unconstrained problem detected. ' // &
170 'Adding a dummy constraint to enable MMA optimization.')
171
172 allocate(dummy_constraint_t::dummy_con)
173 select type (con => dummy_con)
174 type is (dummy_constraint_t)
175 call con%init_from_attributes(design)
176 end select
177
178 call problem%add_constraint(dummy_con)
179 if (allocated(dummy_con)) deallocate(dummy_con)
180 end if
181
182 ! Initialize mma_t, handling the dummy_constraint added for unconstrained
183 ! problems in mma_optimizer_run()
184 call neko_scratch_registry%request(x, ind, design%size(), .false.)
185
186 call design%get_values(x)
187 call this%mma%init(x, design%size(), problem%get_n_constraints(), &
188 solver_parameters, this%scale, this%auto_scale)
189
190 call neko_scratch_registry%relinquish(ind)
191
192 !set the enable_output flag
193 this%enable_output = enable_output
194 this%scaling_factor = this%scale
195 this%tolerance = tolerance
196
197 ! Initialize the logger
198 if (this%enable_output) then
199 extra_headers(1) = 'KKTmax'
200 extra_headers(2) = 'KKTnorm2'
201 extra_headers(3) = 'scaling factor'
202 call this%init_log(problem, extra_headers = extra_headers, &
203 include_constraints = .not. this%unconstrained_problem, &
204 filename = 'optimization_data.csv')
205 end if
206
207 call this%init_base('MMA', max_iterations)
208
209 call neko_log%end_section()
210
211 end subroutine mma_optimizer_init_from_components
212
213 ! Free resources associated with the MMA optimizer
214 subroutine mma_optimizer_free(this)
215 class(mma_optimizer_t), intent(inout) :: this
216
217 ! Free MMA-specific data
218 call this%free_base()
219 call this%mma%free()
220 end subroutine mma_optimizer_free
221
222 ! -------------------------------------------------------------------------- !
223 ! Implementation of the deferred methods for the MMA optimizer
224
226 subroutine mma_optimizer_initialize(this, problem, design, simulation)
227 class(mma_optimizer_t), intent(inout) :: this
228 class(problem_t), intent(inout) :: problem
229 class(design_t), intent(inout) :: design
230 type(simulation_t), optional, intent(inout) :: simulation
231
232 type(vector_t), pointer :: x
233 type(vector_t), pointer :: constraint_value
234 type(vector_t), pointer :: objective_sensitivities
235 type(matrix_t), pointer :: constraint_sensitivities
236 integer :: n_design, n_constraint, indices(4)
237
238 n_design = design%size()
239 n_constraint = problem%get_n_constraints()
240
241 ! Grab some local pointers
242 call neko_scratch_registry%request(x, indices(1), n_design, .false.)
243 call neko_scratch_registry%request(constraint_value, indices(2), &
244 n_constraint, .false.)
245 call neko_scratch_registry%request(objective_sensitivities, indices(3), &
246 n_design, .false.)
247 call neko_scratch_registry%request(constraint_sensitivities, indices(4), &
248 n_constraint, n_design, .false.)
249
250 ! Evaluate the problem based on the updated design
251 call problem%compute(design, simulation)
252 if (present(simulation) .and. this%enable_output) then
253 call simulation%write_forward(0)
254 end if
255 call problem%compute_sensitivity(design, simulation)
256 if (present(simulation) .and. this%enable_output) then
257 call simulation%write_adjoint(0)
258 end if
259
260 ! Retrieve the updated objective and constraint values and sensitivities
261 call design%get_values(x)
262 call problem%get_constraint_values(constraint_value)
263 call problem%get_constraint_sensitivities(constraint_sensitivities)
264
265 select type (des => design)
266 type is (brinkman_design_t)
267 call des%get_sensitivity(objective_sensitivities)
268 ! Convert gradient to directional derivative
269 call des%project_sensitivity(objective_sensitivities)
270 call des%project_sensitivity(constraint_sensitivities)
271 class default
272 call problem%get_objective_sensitivities(objective_sensitivities)
273 end select
274
275 ! Check the KKT conditions and check for convergence
276 call this%mma%KKT(x, objective_sensitivities, &
277 constraint_value, constraint_sensitivities)
278
279 call neko_scratch_registry%relinquish(indices)
280 end subroutine mma_optimizer_initialize
281
283 function mma_optimizer_step(this, iter, problem, design, simulation) &
284 result(converged)
285 class(mma_optimizer_t), intent(inout) :: this
286 integer, intent(in) :: iter
287 class(problem_t), intent(inout) :: problem
288 class(design_t), intent(inout) :: design
289 type(simulation_t), optional, intent(inout) :: simulation
290
291 type(vector_t), pointer :: x, x_old
292 type(vector_t), pointer :: constraint_value
293 type(vector_t), pointer :: objective_sensitivities
294 type(matrix_t), pointer :: constraint_sensitivities
295 integer :: n_design, n_constraint, indices(5)
296
297 logical :: converged
298
299 n_design = design%size()
300 n_constraint = problem%get_n_constraints()
301
302 ! Grab some local pointers
303 call neko_scratch_registry%request(x, indices(1), n_design, .false.)
304 call neko_scratch_registry%request(x_old, indices(2), n_design, .false.)
305 call neko_scratch_registry%request(constraint_value, indices(3), &
306 n_constraint, .false.)
307 call neko_scratch_registry%request(objective_sensitivities, indices(4), &
308 n_design, .false.)
309 call neko_scratch_registry%request(constraint_sensitivities, indices(5), &
310 n_constraint, n_design, .false.)
311
312 ! Retrieve the current objective and constraint values and sensitivities
313 call design%get_values(x)
314 call problem%get_constraint_values(constraint_value)
315 call problem%get_constraint_sensitivities(constraint_sensitivities)
316
317 select type (des => design)
318 type is (brinkman_design_t)
319 call des%get_sensitivity(objective_sensitivities)
320 ! Convert gradient to directional derivative
321 call des%project_sensitivity(objective_sensitivities)
322 call des%project_sensitivity(constraint_sensitivities)
323 class default
324 call problem%get_objective_sensitivities(objective_sensitivities)
325 end select
326
327 ! Execute the scaling
328 if (this%auto_scale) then
329 call constraint_value%copy_from(device_to_host, sync = .true.)
330 this%scaling_factor = abs(this%scale / constraint_value%x(1))
331 end if
332
333 if (.not. abscmp(this%scaling_factor, 1.0_rp)) then
334 call vector_cmult(constraint_value, this%scaling_factor)
335 call matrix_cmult(constraint_sensitivities, this%scaling_factor)
336 end if
337
338 ! Update the design variable
339 x_old = x
340 call this%mma%update(iter, x, objective_sensitivities, &
341 constraint_value, constraint_sensitivities)
342 call design%update_design(x)
343
344 ! Evaluate the problem based on the updated design
345 call problem%compute(design, simulation)
346 if (present(simulation) .and. this%enable_output) then
347 call simulation%write_forward(iter)
348 end if
349 call problem%compute_sensitivity(design, simulation)
350 if (present(simulation) .and. this%enable_output) then
351 call simulation%write_adjoint(iter)
352 end if
353
354 ! Retrieve the updated objective and constraint values and sensitivities
355 call problem%get_constraint_values(constraint_value)
356 call problem%get_constraint_sensitivities(constraint_sensitivities)
357
358 select type (des => design)
359 type is (brinkman_design_t)
360 call des%get_sensitivity(objective_sensitivities)
361 ! Convert gradient to directional derivative
362 call des%project_sensitivity(objective_sensitivities)
363 call des%project_sensitivity(constraint_sensitivities)
364 class default
365 call problem%get_objective_sensitivities(objective_sensitivities)
366 end select
367
368 ! Check the KKT conditions and check for convergence
369 call this%mma%KKT(x, objective_sensitivities, &
370 constraint_value, constraint_sensitivities)
371
372 converged = this%mma%get_residumax() .lt. this%tolerance
373
374 ! Compute L2 norm of design change
375 this%norm2_design_change = vector_glsubnorm(x, x_old)
376
377 ! Compute maximum absolute change
378 call vector_sub2(x_old, x)
379 call vector_absval(x_old)
380 this%max_design_change = vector_glmax(x_old)
381
382 ! Free local resources
383 nullify(x, x_old, constraint_value, objective_sensitivities, &
384 constraint_sensitivities)
385 call neko_scratch_registry%relinquish(indices)
386
387 end function mma_optimizer_step
388
390 subroutine mma_optimizer_validate(this, problem, design)
391 class(mma_optimizer_t), intent(inout) :: this
392 class(problem_t), intent(in) :: problem
393 class(design_t), intent(in) :: design
394
395 type(vector_t), pointer :: constraint_values
396 integer :: ind
397
398 call neko_scratch_registry%request(constraint_values, ind, &
399 problem%get_n_constraints(), .false.)
400
401 call problem%get_constraint_values(constraint_values)
402 call constraint_values%copy_from(device_to_host, sync = .true.)
403
404 if (any(constraint_values%x .gt. 0.0_rp)) then
405 call neko_error('MMA optimizer validation failed: ' // &
406 'Constraints are not satisfied.')
407 end if
408
409 ! Free local resources
410 call neko_scratch_registry%relinquish(ind)
411
412 end subroutine mma_optimizer_validate
413
414 ! -------------------------------------------------------------------------- !
415 ! Logging and IO methods for the MMA optimizer
416
423 subroutine mma_optimizer_write(this, iter, problem)
424 class(mma_optimizer_t), intent(inout) :: this
425 integer, intent(in) :: iter
426 class(problem_t), intent(inout) :: problem
427 real(kind=rp) :: extras(3)
428
429 if (.not. this%enable_output) return
430 call profiler_start_region('Optimizer logging')
431
432 if (iter .eq. 0) then
433 extras(1) = 0.0_rp
434 extras(2) = 0.0_rp
435 else
436 extras(1) = this%mma%get_residumax()
437 extras(2) = this%mma%get_residunorm()
438 end if
439 extras(3) = this%scaling_factor
440
441 call this%write_log(iter, problem, extras)
442
443 call profiler_end_region('Optimizer logging')
444 end subroutine mma_optimizer_write
445
446 ! -------------------------------------------------------------------------- !
447 ! Checkpointing methods for the MMA optimizer
448
450 subroutine mma_optimizer_save_checkpoint_components(this, filename, overwrite)
451 class(mma_optimizer_t), intent(inout) :: this
452 character(len=*), intent(in) :: filename
453 logical, intent(in), optional :: overwrite
454
455 call this%mma%save_checkpoint(filename, overwrite)
456 end subroutine mma_optimizer_save_checkpoint_components
457
459 subroutine mma_optimizer_load_checkpoint_components(this, filename)
460 class(mma_optimizer_t), intent(inout) :: this
461 character(len=*), intent(in) :: filename
462
463 call this%mma%load_checkpoint(filename)
464 end subroutine mma_optimizer_load_checkpoint_components
465
466end module mma_optimizer
Implements the constraint_t type.
Implements the design_t.
Definition design.f90:36
Implements the dummy_constraint_t type.
MMA module.
Definition mma.f90:69
Defines the abstract type optimizer.
Definition optimizer.f90:40
Module for handling the optimization problem.
Definition problem.f90:41
Implements the steady_problem_t type.
A topology optimization design variable.
The abstract constraint type.
An abstract design type.
Definition design.f90:53
MMA type.
Definition mma.f90:90
Abstract optimizer class.
Definition optimizer.f90:61
The abstract problem type.
Definition problem.f90:67