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