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 mma, only: mma_t
4 use problem, only: problem_t
5 use num_types, only: rp
6 use utils, only: neko_error
7 use json_utils, only: json_get, json_get_or_default
9 use design, only: design_t
10 use brinkman_design, only: brinkman_design_t
11 use constraint, only: constraint_t
13
14 ! External modules
15 use json_module, only: json_file
16 use vector, only: vector_t
17 use matrix, only: matrix_t
18 use comm, only: pe_rank
19 use neko_config, only: neko_bcknd_device
20 use scratch_registry, only: neko_scratch_registry
21 use profiler, only: profiler_start_region, profiler_end_region
22 use logger, only: neko_log
23 use csv_file, only: csv_file_t
24 use vector_math, only: vector_cmult
25 use matrix_math, only: matrix_cmult
26 use device, only: device_memcpy, host_to_device
27 implicit none
28 private
29
30 public :: mma_optimizer_t
31
32 ! Concrete type for MMA optimizer
33 type, extends(optimizer_t) :: mma_optimizer_t
34
35 type(mma_t) :: mma
36
43 real(kind=rp) :: scale = 1.0_rp
44 real(kind=rp) :: scaling_factor = 1.0_rp
45 logical :: auto_scale = .false.
46
47 ! Set to flase to remove logging for optimal performance
48 logical :: unconstrained_problem = .false.
49
51 logical :: enable_output = .true.
52 type(csv_file_t) :: csv_log
53 contains
54
55 ! Override the deferred methods
56 generic :: init => init_from_json, init_from_components
57 procedure, pass(this) :: init_from_json => mma_optimizer_init_from_json
58 procedure, pass(this) :: init_from_components => &
59 mma_optimizer_init_from_components
60
61 procedure, pass(this) :: run => mma_optimizer_run
62 procedure, pass(this) :: validate => mma_optimizer_validate
63 procedure, pass(this) :: write => mma_optimizer_write
64 procedure, pass(this) :: free => mma_optimizer_free
65
66 end type mma_optimizer_t
67
68contains
69
71 subroutine mma_optimizer_init_from_json(this, parameters, problem, design, &
72 simulation)
73 class(mma_optimizer_t), intent(inout) :: this
74 type(json_file), intent(inout) :: parameters
75 class(problem_t), intent(inout) :: problem
76 class(design_t), intent(in) :: design
77 type(simulation_t), optional, intent(in) :: simulation
78
79 ! Variables for settings
80 type(json_file) :: solver_parameters
81 logical :: enable_output
82 integer :: max_iterations
83 real(kind=rp) :: tolerance
84
85 ! Read the solver properties from the JSON file
86 call json_get(parameters, 'optimization.solver', solver_parameters)
87 call json_get_or_default(solver_parameters, 'max_iterations', &
88 max_iterations, 100)
89 call json_get_or_default(solver_parameters, 'tolerance', &
90 tolerance, 1.0e-3_rp)
91 call json_get_or_default(solver_parameters, 'enable_output', &
92 enable_output, .true.)
93
94 call this%init_from_components(problem, design, max_iterations, tolerance, &
95 enable_output, solver_parameters, simulation)
96
97 end subroutine mma_optimizer_init_from_json
98
100 subroutine mma_optimizer_init_from_components(this, problem, design, &
101 max_iterations, tolerance, enable_output, solver_parameters, simulation)
102 class(mma_optimizer_t), intent(inout) :: this
103 class(problem_t), intent(inout) :: problem
104 class(design_t), intent(in) :: design
105 integer, intent(in) :: max_iterations
106 real(kind=rp), intent(in) :: tolerance
107 logical, intent(in) :: enable_output
108 type(json_file), intent(inout), optional :: solver_parameters
109 type(simulation_t), intent(in), optional :: simulation
110
111 ! Local variables
112 type(vector_t), pointer :: x
113 integer :: ind
114
115 ! Local variables
116 class(constraint_t), allocatable :: dummy_con
117
118 call neko_log%section('Optimizer Initialization')
119
120 ! Check if the problem is unconstrained
121 this%unconstrained_problem = (problem%get_n_constraints() .eq. 0)
122 if (this%unconstrained_problem) then
123 call neko_log%message('Unconstrained problem detected. ' // &
124 'Adding a dummy constraint to enable MMA optimization.')
125
126 allocate(dummy_constraint_t::dummy_con)
127 select type (con => dummy_con)
128 type is (dummy_constraint_t)
129 call con%init_from_attributes(design)
130 end select
131
132 call problem%add_constraint(dummy_con)
133 end if
134
135 ! Initialize mma_t, handling the dummy_constraint added for unconstrained
136 ! problems in mma_optimizer_run()
137 call neko_scratch_registry%request(x, ind, design%size(), .false.)
138
139 call design%get_values(x)
140 call this%mma%init(x, design%size(), problem%get_n_constraints(), &
141 solver_parameters, this%scale, this%auto_scale)
142
143 call neko_scratch_registry%relinquish_vector(ind)
144
145 !set the enable_output flag
146 this%enable_output = enable_output
147 this%scaling_factor = this%scale
148
149 ! Initialize the logger
150 if (this%enable_output) then
151 call this%csv_log%init('optimization_data.csv')
152 end if
153
154 call this%init_base(max_iterations, tolerance)
155
156 call neko_log%end_section()
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, n
170
171 real(kind=rp) :: objective_value
172 type(vector_t), pointer :: constraint_value
173 type(vector_t), pointer :: objective_sensitivities
174 type(matrix_t), pointer :: constraint_sensitivities
175 integer :: ind(4)
176
177 n = design%size()
178
179 ! Initialize the vectors
180 call neko_scratch_registry%request(x, ind(1), n, .false.)
181 call neko_scratch_registry%request(constraint_value, ind(2), &
182 problem%get_n_constraints(), .false.)
183 call neko_scratch_registry%request(objective_sensitivities, ind(3), &
184 n, .false.)
185 call neko_scratch_registry%request(constraint_sensitivities, ind(4), &
186 problem%get_n_constraints(), n, .false.)
187
189 if (pe_rank .eq. 0) then
190 print *, 'max_iterations for the optimization loop = ', &
191 this%max_iterations
192 end if
193 call profiler_start_region('Optimizer iteration')
194
195 call problem%compute(design, simulation)
196 if (present(simulation) .and. this%enable_output) then
197 call simulation%write_forward(0)
198 end if
199 call problem%compute_sensitivity(design, simulation)
200 if (present(simulation) .and. this%enable_output) then
201 call simulation%write_adjoint(0)
202 end if
203
204 call problem%get_objective_value(objective_value)
205 call problem%get_constraint_values(constraint_value)
206
207 select type (des => design)
208 type is (brinkman_design_t)
209 call des%get_sensitivity(objective_sensitivities)
210 class default
211 call problem%get_objective_sensitivities(objective_sensitivities)
212 end select
213
214 call problem%get_constraint_sensitivities(constraint_sensitivities)
215
216 call profiler_end_region('Optimizer iteration')
217
218 call this%write(0, problem)
219 call design%write(0)
220
221 do iter = 1, this%max_iterations
222 if (this%mma%get_residumax() .lt. this%tolerance) exit
223
224 call profiler_start_region('Optimizer iteration')
225
226 ! Scaling
227 if (this%auto_scale) then
228 this%scaling_factor = abs(this%scale / constraint_value%x(1))
229 end if
230
231 call vector_cmult(constraint_value, this%scaling_factor)
232 call matrix_cmult(constraint_sensitivities, this%scaling_factor)
233
234 ! Use scaled sensitivities to update the design variable
235 call design%get_values(x)
236 call this%mma%update(iter, x, objective_sensitivities, &
237 constraint_value, constraint_sensitivities)
238
239 call design%update_design(x)
240
241 call problem%compute(design, simulation)
242 if (present(simulation) .and. this%enable_output) then
243 call simulation%write_forward(iter)
244 end if
245 call problem%compute_sensitivity(design, simulation)
246 if (present(simulation) .and. this%enable_output) then
247 call simulation%write_adjoint(iter)
248 end if
249
250 call problem%get_constraint_values(constraint_value)
251
252 select type (des => design)
253 type is (brinkman_design_t)
254 call des%get_sensitivity(objective_sensitivities)
255 class default
256 call problem%get_objective_sensitivities(objective_sensitivities)
257 end select
258
259 call problem%get_constraint_sensitivities(constraint_sensitivities)
260
261 call this%mma%KKT(x, objective_sensitivities, &
262 constraint_value, constraint_sensitivities)
263
264 call profiler_end_region('Optimizer iteration')
265
266 ! Log the progress and outputs
267 call this%write(iter, problem)
268 call design%write(iter)
269 end do
270
271 call this%validate(problem, design)
272
273 ! Final state after optimization
274 if (pe_rank .eq. 0) then
275 print *, 'MMA Optimization completed after', iter-1, 'iterations.'
276 end if
277
278 ! Free local resources
279 call neko_scratch_registry%relinquish(ind)
280
281 end subroutine mma_optimizer_run
282
284 subroutine mma_optimizer_validate(this, problem, design)
285 class(mma_optimizer_t), intent(inout) :: this
286 class(problem_t), intent(in) :: problem
287 class(design_t), intent(in) :: design
288
289 type(vector_t), pointer :: constraint_values
290 integer :: ind
291
292 call neko_scratch_registry%request( &
293 constraint_values, ind, problem%get_n_constraints(), .false.)
294
295 call problem%get_constraint_values(constraint_values)
296 if (neko_bcknd_device .eq. 1) then
297 call device_memcpy(constraint_values%x, constraint_values%x_d, &
298 constraint_values%size(), host_to_device, .true.)
299 end if
300
301 if (any(constraint_values%x .gt. 0.0_rp)) then
302 call neko_error('MMA optimizer validation failed: ' // &
303 'Constraints are not satisfied.')
304 end if
305
306 ! Free local resources
307 call neko_scratch_registry%relinquish_vector(ind)
308
309 end subroutine mma_optimizer_validate
310
311 ! Free resources associated with the MMA optimizer
312 subroutine mma_optimizer_free(this)
313 class(mma_optimizer_t), intent(inout) :: this
314
315 ! Free MMA-specific data
316 call this%free_base()
317 call this%mma%free()
318 end subroutine mma_optimizer_free
319
320 subroutine mma_optimizer_write(this, iter, problem)
321 class(mma_optimizer_t), intent(inout) :: this
322 integer, intent(in) :: iter
323 class(problem_t), intent(in) :: problem
324
325 type(vector_t), pointer :: log_data
326 type(vector_t), pointer :: all_objectives
327 type(vector_t), pointer :: constraint_value
328 real(kind=rp) :: objective_value
329 character(len=1024) :: header
330
331 integer :: log_size, ind(3), n, m, i_tmp1, i_tmp2
332
333 if (.not. this%enable_output) return
334 call profiler_start_region('Optimizer logging')
335
336 n = problem%get_n_objectives()
337 m = problem%get_n_constraints()
338 if (this%unconstrained_problem) then
339 log_size = 5 + n
340 else
341 log_size = 5 + n + m
342 endif
343
344 call neko_scratch_registry%request(log_data, ind(1), log_size, .false.)
345 call neko_scratch_registry%request(all_objectives, ind(2), n, .false.)
346 call neko_scratch_registry%request(constraint_value, ind(3), m, .false.)
347
348 if (iter .eq. 0) then
349 header = 'iter, ' // &
350 trim(problem%get_log_header()) // &
351 ', KKTmax, KKTnorm2, scaling factor, ' // &
352 trim(this%mma%get_backend_and_subsolver())
353
354 call this%csv_log%set_header(trim(header))
355 end if
356
357 ! Prepare data for logging
358 call problem%get_objective_value(objective_value)
359 call problem%get_all_objective_values(all_objectives)
360 call problem%get_constraint_values(constraint_value)
361
362 ! Assemble the log data
363 log_data%x(1) = real(iter, kind=rp)
364
365 ! total objective
366 log_data%x(2) = objective_value
367
368 ! individual objectives
369 i_tmp1 = 3
370 i_tmp2 = i_tmp1 + n - 1
371 log_data%x(i_tmp1 : i_tmp2) = all_objectives%x
372
373 ! constraints
374 if (.not. this%unconstrained_problem) then
375 i_tmp1 = i_tmp2 + 1
376 i_tmp2 = i_tmp1 + m - 1
377 log_data%x(i_tmp1 : i_tmp2) = constraint_value%x
378 end if
379
380 ! convergence stuff
381 if (iter .eq. 0) then
382 log_data%x(i_tmp2 + 1) = 0.0_rp
383 log_data%x(i_tmp2 + 2) = 0.0_rp
384 else
385 log_data%x(i_tmp2 + 1) = this%mma%get_residumax()
386 log_data%x(i_tmp2 + 2) = this%mma%get_residunorm()
387 end if
388 log_data%x(i_tmp2 + 3) = this%scaling_factor
389
390 call this%csv_log%write(log_data)
391
392 ! Free local resources
393 call neko_scratch_registry%relinquish(ind)
394
395 call profiler_end_region('Optimizer logging')
396 end subroutine mma_optimizer_write
397
398end module mma_optimizer
399
Implements the constraint_t type.
Implements the design_t.
Definition design.f90:34
Implements the dummy_constraint_t type.
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:70