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 csv_file, only: csv_file_t
56 use vector_math, only: vector_cmult
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 type(csv_file_t), private :: csv_log
90 contains
91
92 ! Override the deferred methods
93 generic :: init => init_from_json, init_from_components
94 procedure, pass(this) :: init_from_json => mma_optimizer_init_from_json
95 procedure, pass(this) :: init_from_components => &
96 mma_optimizer_init_from_components
97
98 procedure, pass(this) :: initialize => mma_optimizer_initialize
99 procedure, pass(this) :: step => mma_optimizer_step
100 procedure, pass(this) :: validate => mma_optimizer_validate
101 procedure, pass(this) :: write => mma_optimizer_write
102 procedure, pass(this) :: free => mma_optimizer_free
103
104 procedure, pass(this) :: save_checkpoint_components => &
105 mma_optimizer_save_checkpoint_components
106 procedure, pass(this) :: load_checkpoint_components => &
107 mma_optimizer_load_checkpoint_components
108
109 end type mma_optimizer_t
110
111contains
112
113 ! -------------------------------------------------------------------------- !
114 ! Allocator and deallocator methods for the MMA optimizer
115
117 subroutine mma_optimizer_init_from_json(this, parameters, problem, design, &
118 simulation)
119 class(mma_optimizer_t), intent(inout) :: this
120 type(json_file), intent(inout) :: parameters
121 class(problem_t), intent(inout) :: problem
122 class(design_t), intent(in) :: design
123 type(simulation_t), optional, intent(in) :: simulation
124
125 ! Variables for settings
126 type(json_file) :: solver_parameters
127 logical :: enable_output
128 integer :: max_iterations
129 real(kind=rp) :: tolerance, max_runtime
130
131 ! Read the solver properties from the JSON file
132 call json_get(parameters, 'optimization.solver', solver_parameters)
133 call json_get_or_default(solver_parameters, 'max_iterations', &
134 max_iterations, 100)
135 call json_get_or_default(solver_parameters, 'tolerance', &
136 tolerance, 1.0e-3_rp)
137 call json_get_or_default(solver_parameters, 'enable_output', &
138 enable_output, .true.)
139 call json_get_or_default(solver_parameters, 'max_runtime', &
140 max_runtime, -1.0_rp)
141
142 call this%init_from_components(problem, design, max_iterations, tolerance, &
143 enable_output, solver_parameters, simulation, max_runtime)
144
145 end subroutine mma_optimizer_init_from_json
146
148 subroutine mma_optimizer_init_from_components(this, problem, design, &
149 max_iterations, tolerance, enable_output, &
150 solver_parameters, simulation, max_runtime)
151 class(mma_optimizer_t), intent(inout) :: this
152 class(problem_t), intent(inout) :: problem
153 class(design_t), intent(in) :: design
154 integer, intent(in) :: max_iterations
155 real(kind=rp), intent(in) :: tolerance
156 logical, intent(in) :: enable_output
157 type(json_file), intent(inout), optional :: solver_parameters
158 type(simulation_t), intent(in), optional :: simulation
159 real(kind=rp), intent(in), optional :: max_runtime
160
161 ! Local variables
162 type(vector_t), pointer :: x
163 integer :: ind
164 character(len=1024) :: header
165 class(constraint_t), allocatable :: dummy_con
166
167 call neko_log%section('Optimizer Initialization')
168
169 ! Check if the problem is unconstrained
170 this%unconstrained_problem = problem%get_n_constraints() .eq. 0
171 if (this%unconstrained_problem) then
172 call neko_log%message('Unconstrained problem detected. ' // &
173 'Adding a dummy constraint to enable MMA optimization.')
174
175 allocate(dummy_constraint_t::dummy_con)
176 select type (con => dummy_con)
177 type is (dummy_constraint_t)
178 call con%init_from_attributes(design)
179 end select
180
181 call problem%add_constraint(dummy_con)
182 if (allocated(dummy_con)) deallocate(dummy_con)
183 end if
184
185 ! Initialize mma_t, handling the dummy_constraint added for unconstrained
186 ! problems in mma_optimizer_run()
187 call neko_scratch_registry%request(x, ind, design%size(), .false.)
188
189 call design%get_values(x)
190 call this%mma%init(x, design%size(), problem%get_n_constraints(), &
191 solver_parameters, this%scale, this%auto_scale)
192
193 call neko_scratch_registry%relinquish(ind)
194
195 !set the enable_output flag
196 this%enable_output = enable_output
197 this%scaling_factor = this%scale
198 this%tolerance = tolerance
199
200 ! Initialize the logger
201 if (this%enable_output) then
202 call this%csv_log%init('optimization_data.csv')
203 header = 'iter, ' // trim(problem%get_log_header()) // &
204 ', KKTmax, KKTnorm2, scaling factor, ' // &
205 trim(this%mma%get_backend_and_subsolver())
206
207 call this%csv_log%set_header(trim(header))
208 end if
209
210 call this%init_base('MMA', max_iterations, max_runtime)
211
212 call neko_log%end_section()
213
214 end subroutine mma_optimizer_init_from_components
215
216 ! Free resources associated with the MMA optimizer
217 subroutine mma_optimizer_free(this)
218 class(mma_optimizer_t), intent(inout) :: this
219
220 ! Free MMA-specific data
221 call this%free_base()
222 call this%mma%free()
223 end subroutine mma_optimizer_free
224
225 ! -------------------------------------------------------------------------- !
226 ! Implementation of the deferred methods for the MMA optimizer
227
229 subroutine mma_optimizer_initialize(this, problem, design, simulation)
230 class(mma_optimizer_t), intent(inout) :: this
231 class(problem_t), intent(inout) :: problem
232 class(design_t), intent(inout) :: design
233 type(simulation_t), optional, intent(inout) :: simulation
234
235 type(vector_t), pointer :: x
236 type(vector_t), pointer :: constraint_value
237 type(vector_t), pointer :: objective_sensitivities
238 type(matrix_t), pointer :: constraint_sensitivities
239 integer :: n_design, n_constraint, indices(4)
240
241 n_design = design%size()
242 n_constraint = problem%get_n_constraints()
243
244 ! Grab some local pointers
245 call neko_scratch_registry%request(x, indices(1), n_design, .false.)
246 call neko_scratch_registry%request(constraint_value, indices(2), &
247 n_constraint, .false.)
248 call neko_scratch_registry%request(objective_sensitivities, indices(3), &
249 n_design, .false.)
250 call neko_scratch_registry%request(constraint_sensitivities, indices(4), &
251 n_constraint, n_design, .false.)
252
253 ! Evaluate the problem based on the updated design
254 call problem%compute(design, simulation)
255 call problem%compute_sensitivity(design, simulation)
256
257 ! Retrieve the updated objective and constraint values and sensitivities
258 call design%get_values(x)
259 call problem%get_constraint_values(constraint_value)
260
261 select type (des => design)
262 type is (brinkman_design_t)
263 call des%get_sensitivity(objective_sensitivities)
264 class default
265 call problem%get_objective_sensitivities(objective_sensitivities)
266 end select
267
268 call problem%get_constraint_sensitivities(constraint_sensitivities)
269
270 ! Check the KKT conditions and check for convergence
271 call this%mma%KKT(x, objective_sensitivities, &
272 constraint_value, constraint_sensitivities)
273
274 call neko_scratch_registry%relinquish(indices)
275 end subroutine mma_optimizer_initialize
276
278 function mma_optimizer_step(this, iter, problem, design, simulation) &
279 result(converged)
280 class(mma_optimizer_t), intent(inout) :: this
281 integer, intent(in) :: iter
282 class(problem_t), intent(inout) :: problem
283 class(design_t), intent(inout) :: design
284 type(simulation_t), optional, intent(inout) :: simulation
285
286 type(vector_t), pointer :: x
287 type(vector_t), pointer :: constraint_value
288 type(vector_t), pointer :: objective_sensitivities
289 type(matrix_t), pointer :: constraint_sensitivities
290 integer :: n_design, n_constraint, indices(4)
291
292 logical :: converged
293
294 n_design = design%size()
295 n_constraint = problem%get_n_constraints()
296
297 ! Grab some local pointers
298 call neko_scratch_registry%request(x, indices(1), n_design, .false.)
299 call neko_scratch_registry%request(constraint_value, indices(2), &
300 n_constraint, .false.)
301 call neko_scratch_registry%request(objective_sensitivities, indices(3), &
302 n_design, .false.)
303 call neko_scratch_registry%request(constraint_sensitivities, indices(4), &
304 n_constraint, n_design, .false.)
305
306 ! Retrieve the current objective and constraint values and sensitivities
307 call design%get_values(x)
308 call problem%get_constraint_values(constraint_value)
309
310 select type (des => design)
311 type is (brinkman_design_t)
312 call des%get_sensitivity(objective_sensitivities)
313 class default
314 call problem%get_objective_sensitivities(objective_sensitivities)
315 end select
316
317 call problem%get_constraint_sensitivities(constraint_sensitivities)
318
319 ! Execute the scaling
320 if (this%auto_scale) then
321 call constraint_value%copy_from(device_to_host, sync = .true.)
322 this%scaling_factor = abs(this%scale / constraint_value%x(1))
323 end if
324
325 if (.not. abscmp(this%scaling_factor, 1.0_rp)) then
326 call vector_cmult(constraint_value, this%scaling_factor)
327 call matrix_cmult(constraint_sensitivities, this%scaling_factor)
328 end if
329
330 ! Update the design variable
331 call this%mma%update(iter, x, objective_sensitivities, &
332 constraint_value, constraint_sensitivities)
333 call design%update_design(x)
334
335 ! Evaluate the problem based on the updated design
336 call problem%compute(design, simulation)
337 if (present(simulation) .and. this%enable_output) then
338 call simulation%write_forward(iter)
339 end if
340 call problem%compute_sensitivity(design, simulation)
341 if (present(simulation) .and. this%enable_output) then
342 call simulation%write_adjoint(iter)
343 end if
344
345 ! Retrieve the updated objective and constraint values and sensitivities
346 call problem%get_constraint_values(constraint_value)
347
348 select type (des => design)
349 type is (brinkman_design_t)
350 call des%get_sensitivity(objective_sensitivities)
351 class default
352 call problem%get_objective_sensitivities(objective_sensitivities)
353 end select
354
355 call problem%get_constraint_sensitivities(constraint_sensitivities)
356
357 ! Check the KKT conditions and check for convergence
358 call this%mma%KKT(x, objective_sensitivities, &
359 constraint_value, constraint_sensitivities)
360 converged = this%mma%get_residumax() .lt. this%tolerance
361
362 ! Free local resources
363 call neko_scratch_registry%relinquish(indices)
364
365 end function mma_optimizer_step
366
368 subroutine mma_optimizer_validate(this, problem, design)
369 class(mma_optimizer_t), intent(inout) :: this
370 class(problem_t), intent(in) :: problem
371 class(design_t), intent(in) :: design
372
373 type(vector_t), pointer :: constraint_values
374 integer :: ind
375
376 call neko_scratch_registry%request(constraint_values, ind, &
377 problem%get_n_constraints(), .false.)
378
379 call problem%get_constraint_values(constraint_values)
380 call constraint_values%copy_from(device_to_host, sync = .true.)
381
382 if (any(constraint_values%x .gt. 0.0_rp)) then
383 call neko_error('MMA optimizer validation failed: ' // &
384 'Constraints are not satisfied.')
385 end if
386
387 ! Free local resources
388 call neko_scratch_registry%relinquish(ind)
389
390 end subroutine mma_optimizer_validate
391
392 ! -------------------------------------------------------------------------- !
393 ! Logging and IO methods for the MMA optimizer
394
401 subroutine mma_optimizer_write(this, iter, problem)
402 class(mma_optimizer_t), intent(inout) :: this
403 integer, intent(in) :: iter
404 class(problem_t), intent(in) :: problem
405
406 type(vector_t), pointer :: log_data
407 type(vector_t), pointer :: all_objectives
408 type(vector_t), pointer :: constraint_value
409 real(kind=rp) :: objective_value
410
411 integer :: log_size, ind(3), n, m, i_tmp1, i_tmp2
412
413 if (.not. this%enable_output) return
414 call profiler_start_region('Optimizer logging')
415
416 n = problem%get_n_objectives()
417 m = problem%get_n_constraints()
418 if (this%unconstrained_problem) then
419 log_size = 5 + n
420 else
421 log_size = 5 + n + m
422 endif
423
424 call neko_scratch_registry%request(log_data, ind(1), log_size, .false.)
425 call neko_scratch_registry%request(all_objectives, ind(2), n, .false.)
426 call neko_scratch_registry%request(constraint_value, ind(3), m, .false.)
427
428 ! Prepare data for logging
429 call problem%get_objective_value(objective_value)
430 call problem%get_all_objective_values(all_objectives)
431 call problem%get_constraint_values(constraint_value)
432
433 call all_objectives%copy_from(device_to_host, sync = .true.)
434 call constraint_value%copy_from(device_to_host, sync = .true.)
435
436 ! Assemble the log data
437 log_data%x(1) = real(iter, kind=rp)
438
439 ! total objective
440 log_data%x(2) = objective_value
441
442 ! individual objectives
443 i_tmp1 = 3
444 i_tmp2 = i_tmp1 + n - 1
445 log_data%x(i_tmp1 : i_tmp2) = all_objectives%x
446
447 ! constraints
448 if (.not. this%unconstrained_problem) then
449 i_tmp1 = i_tmp2 + 1
450 i_tmp2 = i_tmp1 + m - 1
451 log_data%x(i_tmp1 : i_tmp2) = constraint_value%x
452 end if
453
454 ! convergence stuff
455 if (iter .eq. 0) then
456 log_data%x(i_tmp2 + 1) = 0.0_rp
457 log_data%x(i_tmp2 + 2) = 0.0_rp
458 else
459 log_data%x(i_tmp2 + 1) = this%mma%get_residumax()
460 log_data%x(i_tmp2 + 2) = this%mma%get_residunorm()
461 end if
462 log_data%x(i_tmp2 + 3) = this%scaling_factor
463
464 call this%csv_log%write(log_data)
465
466 ! Free local resources
467 call neko_scratch_registry%relinquish(ind)
468
469 call profiler_end_region('Optimizer logging')
470 end subroutine mma_optimizer_write
471
472 ! -------------------------------------------------------------------------- !
473 ! Checkpointing methods for the MMA optimizer
474
476 subroutine mma_optimizer_save_checkpoint_components(this, filename, overwrite)
477 class(mma_optimizer_t), intent(inout) :: this
478 character(len=*), intent(in) :: filename
479 logical, intent(in), optional :: overwrite
480
481 call this%mma%save_checkpoint(filename, overwrite)
482 end subroutine mma_optimizer_save_checkpoint_components
483
485 subroutine mma_optimizer_load_checkpoint_components(this, filename)
486 class(mma_optimizer_t), intent(inout) :: this
487 character(len=*), intent(in) :: filename
488
489 call this%mma%load_checkpoint(filename)
490 end subroutine mma_optimizer_load_checkpoint_components
491
492end module mma_optimizer
493
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 The optimizer type is defined to provide a generic interface to u...
Definition optimizer.f90:39
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:54
MMA type.
Definition mma.f90:89
Abstract optimizer class.
Definition optimizer.f90:54
The abstract problem type.
Definition problem.f90:67