39 use num_types,
only: rp
40 use utils,
only: neko_error
41 use json_utils,
only: json_get, json_get_or_default
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
70 type(mma_t),
private :: mma
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
84 logical,
private :: unconstrained_problem = .false.
87 logical,
private :: enable_output = .true.
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
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
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
115 subroutine mma_optimizer_init_from_json(this, parameters, problem, design, &
118 type(json_file),
intent(inout) :: parameters
119 class(
problem_t),
intent(inout) :: problem
120 class(
design_t),
intent(in) :: design
124 type(json_file) :: solver_parameters
125 logical :: enable_output
126 integer :: max_iterations
127 real(kind=rp) :: tolerance
130 call json_get(parameters,
'optimization.solver', solver_parameters)
131 call json_get_or_default(solver_parameters,
'max_iterations', &
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)
139 call this%init_from_components(
problem,
design, max_iterations, tolerance, &
140 enable_output, solver_parameters, simulation)
142 end subroutine mma_optimizer_init_from_json
145 subroutine mma_optimizer_init_from_components(this, problem, design, &
146 max_iterations, tolerance, enable_output, &
147 solver_parameters, simulation)
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
158 type(vector_t),
pointer :: x
160 character(len=32) :: extra_headers(4)
163 call neko_log%section(
'Optimizer Initialization')
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.')
172 select type (con => dummy_con)
174 call con%init_from_attributes(
design)
177 call problem%add_constraint(dummy_con)
178 if (
allocated(dummy_con))
deallocate(dummy_con)
183 call neko_scratch_registry%request(x, ind,
design%size(), .false.)
186 call this%mma%init(x,
design%size(),
problem%get_n_constraints(), &
187 solver_parameters, this%scale, this%auto_scale)
189 call neko_scratch_registry%relinquish(ind)
192 this%enable_output = enable_output
193 this%scaling_factor = this%scale
194 this%tolerance = tolerance
197 if (this%enable_output)
then
198 extra_headers(1) =
'KKTmax'
199 extra_headers(2) =
'KKTnorm2'
200 extra_headers(3) =
'scaling factor'
201 extra_headers(4) = this%mma%get_backend_and_subsolver()
202 call this%init_log(
problem, extra_headers = extra_headers, &
203 include_constraints = .not. this%unconstrained_problem, &
204 filename =
'optimization_data.csv')
207 call this%init_base(
'MMA', max_iterations)
209 call neko_log%end_section()
211 end subroutine mma_optimizer_init_from_components
214 subroutine mma_optimizer_free(this)
218 call this%free_base()
220 end subroutine mma_optimizer_free
226 subroutine mma_optimizer_initialize(this, problem, design, simulation)
228 class(
problem_t),
intent(inout) :: problem
229 class(
design_t),
intent(inout) :: design
230 type(
simulation_t),
optional,
intent(inout) :: simulation
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)
239 n_constraint =
problem%get_n_constraints()
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), &
247 call neko_scratch_registry%request(constraint_sensitivities, indices(4), &
248 n_constraint, n_design, .false.)
252 if (
present(simulation) .and. this%enable_output)
then
253 call simulation%write_forward(0)
256 if (
present(simulation) .and. this%enable_output)
then
257 call simulation%write_adjoint(0)
262 call problem%get_constraint_values(constraint_value)
263 call problem%get_constraint_sensitivities(constraint_sensitivities)
265 select type (des =>
design)
267 call des%get_sensitivity(objective_sensitivities)
269 call des%project_sensitivity(objective_sensitivities)
270 call des%project_sensitivity(constraint_sensitivities)
272 call problem%get_objective_sensitivities(objective_sensitivities)
276 call this%mma%KKT(x, objective_sensitivities, &
277 constraint_value, constraint_sensitivities)
279 call neko_scratch_registry%relinquish(indices)
280 end subroutine mma_optimizer_initialize
283 function mma_optimizer_step(this, iter, problem, design, simulation) &
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
291 type(vector_t),
pointer :: x
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(4)
300 n_constraint =
problem%get_n_constraints()
303 call neko_scratch_registry%request(x, indices(1), n_design, .false.)
304 call neko_scratch_registry%request(constraint_value, indices(2), &
305 n_constraint, .false.)
306 call neko_scratch_registry%request(objective_sensitivities, indices(3), &
308 call neko_scratch_registry%request(constraint_sensitivities, indices(4), &
309 n_constraint, n_design, .false.)
313 call problem%get_constraint_values(constraint_value)
314 call problem%get_constraint_sensitivities(constraint_sensitivities)
316 select type (des =>
design)
318 call des%get_sensitivity(objective_sensitivities)
320 call des%project_sensitivity(objective_sensitivities)
321 call des%project_sensitivity(constraint_sensitivities)
323 call problem%get_objective_sensitivities(objective_sensitivities)
327 if (this%auto_scale)
then
328 call constraint_value%copy_from(device_to_host, sync = .true.)
329 this%scaling_factor = abs(this%scale / constraint_value%x(1))
332 if (.not. abscmp(this%scaling_factor, 1.0_rp))
then
333 call vector_cmult(constraint_value, this%scaling_factor)
334 call matrix_cmult(constraint_sensitivities, this%scaling_factor)
338 call this%mma%update(iter, x, objective_sensitivities, &
339 constraint_value, constraint_sensitivities)
340 call design%update_design(x)
344 if (
present(simulation) .and. this%enable_output)
then
345 call simulation%write_forward(iter)
348 if (
present(simulation) .and. this%enable_output)
then
349 call simulation%write_adjoint(iter)
353 call problem%get_constraint_values(constraint_value)
354 call problem%get_constraint_sensitivities(constraint_sensitivities)
356 select type (des =>
design)
358 call des%get_sensitivity(objective_sensitivities)
360 call des%project_sensitivity(objective_sensitivities)
361 call des%project_sensitivity(constraint_sensitivities)
363 call problem%get_objective_sensitivities(objective_sensitivities)
367 call this%mma%KKT(x, objective_sensitivities, &
368 constraint_value, constraint_sensitivities)
369 converged = this%mma%get_residumax() .lt. this%tolerance
372 call neko_scratch_registry%relinquish(indices)
374 end function mma_optimizer_step
377 subroutine mma_optimizer_validate(this, problem, design)
380 class(
design_t),
intent(in) :: design
382 type(vector_t),
pointer :: constraint_values
385 call neko_scratch_registry%request(constraint_values, ind, &
386 problem%get_n_constraints(), .false.)
388 call problem%get_constraint_values(constraint_values)
389 call constraint_values%copy_from(device_to_host, sync = .true.)
391 if (any(constraint_values%x .gt. 0.0_rp))
then
392 call neko_error(
'MMA optimizer validation failed: ' // &
393 'Constraints are not satisfied.')
397 call neko_scratch_registry%relinquish(ind)
399 end subroutine mma_optimizer_validate
410 subroutine mma_optimizer_write(this, iter, problem)
412 integer,
intent(in) :: iter
413 class(
problem_t),
intent(inout) :: problem
414 real(kind=rp) :: extras(3)
416 if (.not. this%enable_output)
return
417 call profiler_start_region(
'Optimizer logging')
419 if (iter .eq. 0)
then
423 extras(1) = this%mma%get_residumax()
424 extras(2) = this%mma%get_residunorm()
426 extras(3) = this%scaling_factor
428 call this%write_log(iter,
problem, extras)
430 call profiler_end_region(
'Optimizer logging')
431 end subroutine mma_optimizer_write
437 subroutine mma_optimizer_save_checkpoint_components(this, filename, overwrite)
439 character(len=*),
intent(in) :: filename
440 logical,
intent(in),
optional :: overwrite
442 call this%mma%save_checkpoint(filename, overwrite)
443 end subroutine mma_optimizer_save_checkpoint_components
446 subroutine mma_optimizer_load_checkpoint_components(this, filename)
448 character(len=*),
intent(in) :: filename
450 call this%mma%load_checkpoint(filename)
451 end subroutine mma_optimizer_load_checkpoint_components
453end module mma_optimizer
Implements the constraint_t type.
Implements the dummy_constraint_t type.
Defines the abstract type optimizer.
Module for handling the optimization problem.
Implements the steady_problem_t type.
A topology optimization design variable.
The abstract constraint type.
Abstract optimizer class.
The abstract problem type.