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