60 use num_types,
only: rp
61 use utils,
only: neko_error
62 use json_utils,
only: json_get, json_get_or_default
63 use logger,
only: neko_log
64 use profiler,
only: profiler_start_region, profiler_end_region
65 use scratch_registry,
only: neko_scratch_registry
70 use json_module,
only: json_file
71 use vector,
only: vector_t
72 use vector_math,
only: vector_copy, vector_cfill, vector_sub3, &
73 vector_absval, vector_glmax, vector_glmin, vector_glsc2, &
75 use,
intrinsic :: ieee_arithmetic, only: ieee_is_nan, ieee_is_finite
86 type(vector_t),
private :: xold1
88 type(vector_t),
private :: xold2
90 type(vector_t),
private :: delta
93 real(kind=rp),
private :: xmin = 0.0_rp
95 real(kind=rp),
private :: xmax = 1.0_rp
97 real(kind=rp),
private :: step_length = 1.0_rp
99 real(kind=rp),
private :: move_limit_initial = 0.1_rp
101 real(kind=rp),
private :: move_limit_min = 0.001_rp
103 real(kind=rp),
private :: move_limit_max = 0.1_rp
105 real(kind=rp),
private :: move_limit_decrease = 0.7_rp
107 real(kind=rp),
private :: move_limit_increase = 1.1_rp
109 real(kind=rp),
private :: last_max_gradient = 0.0_rp
112 logical,
private :: enable_output = .true.
118 procedure, pass(this) :: free => sd_optimizer_free
119 procedure, pass(this) :: initialize => sd_optimizer_initialize
120 procedure, pass(this) :: step => sd_optimizer_step
121 procedure, pass(this) :: validate => sd_optimizer_validate
122 procedure, pass(this) :: write => sd_optimizer_write
124 procedure, pass(this) :: save_checkpoint_components => &
125 sd_optimizer_save_checkpoint_components
126 procedure, pass(this) :: load_checkpoint_components => &
127 sd_optimizer_load_checkpoint_components
136 module subroutine sd_optimizer_save_checkpoint_hdf5(object, filename, &
138 class(steepest_descent_optimizer_t),
intent(inout) :: object
139 character(len=*),
intent(in) :: filename
140 logical,
intent(in),
optional :: overwrite
141 end subroutine sd_optimizer_save_checkpoint_hdf5
144 module subroutine sd_optimizer_load_checkpoint_hdf5(object, filename)
145 class(steepest_descent_optimizer_t),
intent(inout) :: object
146 character(len=*),
intent(in) :: filename
147 end subroutine sd_optimizer_load_checkpoint_hdf5
164 subroutine sd_optimizer_init_from_json(this, parameters, problem, design, &
166 class(steepest_descent_optimizer_t),
intent(inout) :: this
167 type(json_file),
intent(inout) :: parameters
168 class(
problem_t),
intent(inout) :: problem
169 class(
design_t),
intent(in) :: design
172 type(json_file) :: solver_parameters
173 integer :: max_iterations, n
177 call neko_log%section(
'Optimizer Initialization')
179 call json_get(parameters,
'optimization.solver', solver_parameters)
180 call this%read_base_settings(solver_parameters)
182 if (
problem%get_n_constraints() .gt. 0)
then
183 call neko_error(
'steepest_descent: the problem has constraints, ' // &
184 'which this optimizer does not support; use the "mma" ' // &
189 call json_get_or_default(solver_parameters,
'max_iterations', &
191 call json_get_or_default(solver_parameters,
'enable_output', &
192 this%enable_output, .true.)
194 call json_get_or_default(solver_parameters,
'steepest_descent.xmin', &
196 call json_get_or_default(solver_parameters,
'steepest_descent.xmax', &
198 call json_get_or_default(solver_parameters, &
199 'steepest_descent.step_length', this%step_length, 1.0_rp)
200 call json_get_or_default(solver_parameters, &
201 'steepest_descent.move_limit.initial', this%move_limit_initial, &
203 call json_get_or_default(solver_parameters, &
204 'steepest_descent.move_limit.min', this%move_limit_min, 0.001_rp)
205 call json_get_or_default(solver_parameters, &
206 'steepest_descent.move_limit.max', this%move_limit_max, 0.1_rp)
207 call json_get_or_default(solver_parameters, &
208 'steepest_descent.move_limit.decrease', this%move_limit_decrease, &
210 call json_get_or_default(solver_parameters, &
211 'steepest_descent.move_limit.increase', this%move_limit_increase, &
214 if (this%xmin .ge. this%xmax)
then
215 call neko_error(
'steepest_descent: "steepest_descent.xmin" must ' // &
216 'be smaller than "steepest_descent.xmax"')
218 if (this%step_length .le. 0.0_rp)
then
219 call neko_error(
'steepest_descent: ' // &
220 '"steepest_descent.step_length" must be positive')
222 if (this%move_limit_min .le. 0.0_rp)
then
223 call neko_error(
'steepest_descent: ' // &
224 '"steepest_descent.move_limit.min" must be positive')
226 if (this%move_limit_initial .lt. this%move_limit_min)
then
227 call neko_error(
'steepest_descent: ' // &
228 '"steepest_descent.move_limit.initial" must not be smaller ' // &
229 'than "steepest_descent.move_limit.min"')
231 if (this%move_limit_max .lt. this%move_limit_initial)
then
232 call neko_error(
'steepest_descent: ' // &
233 '"steepest_descent.move_limit.max" must not be smaller ' // &
234 'than "steepest_descent.move_limit.initial"')
236 if (this%move_limit_decrease .le. 0.0_rp .or. &
237 this%move_limit_decrease .gt. 1.0_rp)
then
238 call neko_error(
'steepest_descent: ' // &
239 '"steepest_descent.move_limit.decrease" must lie in (0, 1]')
241 if (this%move_limit_increase .lt. 1.0_rp)
then
242 call neko_error(
'steepest_descent: ' // &
243 '"steepest_descent.move_limit.increase" must be at least 1')
248 call this%xold1%init(n)
249 call this%xold2%init(n)
250 call this%delta%init(n)
251 call design%get_values(this%xold1)
252 call design%get_values(this%xold2)
253 call vector_cfill(this%delta, this%move_limit_initial)
255 if (this%enable_output)
then
256 call this%init_log(
problem, extra_headers = [
'max_abs_gradient'], &
257 include_constraints = .false.)
260 call this%init_base(
'steepest_descent', max_iterations)
262 call solver_parameters%destroy()
263 call neko_log%end_section()
265 end subroutine sd_optimizer_init_from_json
269 subroutine sd_optimizer_free(this)
270 class(steepest_descent_optimizer_t),
intent(inout) :: this
272 call this%xold1%free()
273 call this%xold2%free()
274 call this%delta%free()
275 this%last_max_gradient = 0.0_rp
277 call this%free_base()
279 end subroutine sd_optimizer_free
289 subroutine sd_optimizer_initialize(this, problem, design, simulation)
290 class(steepest_descent_optimizer_t),
intent(inout) :: this
291 class(
problem_t),
intent(inout) :: problem
292 class(
design_t),
intent(inout) :: design
293 type(
simulation_t),
optional,
intent(inout) :: simulation
296 if (
present(simulation) .and. this%enable_output)
then
297 call simulation%write_forward(0)
300 if (
present(simulation) .and. this%enable_output)
then
301 call simulation%write_adjoint(0)
304 end subroutine sd_optimizer_initialize
315 function sd_optimizer_step(this, iter, problem, design, simulation) &
317 class(steepest_descent_optimizer_t),
intent(inout) :: this
318 integer,
intent(in) :: iter
319 class(
problem_t),
intent(inout) :: problem
320 class(
design_t),
intent(inout) :: design
321 type(
simulation_t),
optional,
intent(inout) :: simulation
324 type(vector_t),
pointer :: x, x_new, s
325 integer :: n, indices(3)
326 real(kind=rp) :: smax
331 call neko_scratch_registry%request(x, indices(1), n, .false.)
332 call neko_scratch_registry%request(x_new, indices(2), n, .false.)
333 call neko_scratch_registry%request(s, indices(3), n, .false.)
337 select type (des =>
design)
339 call des%get_sensitivity(s)
341 call des%project_sensitivity(s)
343 call problem%get_objective_sensitivities(s)
351 if (ieee_is_nan(vector_glsc2(s, s)))
then
352 call neko_scratch_registry%relinquish(indices)
353 call neko_error(
'steepest_descent: the projected gradient is NaN')
357 smax = max(vector_glmax(s), -vector_glmin(s))
358 if (.not. ieee_is_finite(smax))
then
359 call neko_scratch_registry%relinquish(indices)
360 call neko_error(
'steepest_descent: the projected gradient is ' // &
366 if (smax .le. tiny(1.0_rp))
then
367 this%max_design_change = 0.0_rp
368 this%norm2_design_change = 0.0_rp
369 this%last_max_gradient = 0.0_rp
372 call neko_scratch_registry%relinquish(indices)
375 this%last_max_gradient = smax
378 if (iter .ge. 3)
then
380 this%move_limit_decrease, this%move_limit_increase, &
381 this%move_limit_min, this%move_limit_max)
386 this%xmin, this%xmax)
387 call design%update_design(x_new)
391 if (
present(simulation) .and. this%enable_output)
then
392 call simulation%write_forward(iter)
395 if (
present(simulation) .and. this%enable_output)
then
396 call simulation%write_adjoint(iter)
400 this%norm2_design_change = vector_glsubnorm(x_new, x)
403 call vector_sub3(s, x_new, x)
404 call vector_absval(s)
405 this%max_design_change = vector_glmax(s)
408 call vector_copy(this%xold2, this%xold1)
409 call vector_copy(this%xold1, x)
413 call neko_scratch_registry%relinquish(indices)
415 end function sd_optimizer_step
424 subroutine sd_optimizer_validate(this, problem, design)
425 class(steepest_descent_optimizer_t),
intent(inout) :: this
427 class(
design_t),
intent(in) :: design
429 end subroutine sd_optimizer_validate
438 subroutine sd_optimizer_write(this, iter, problem)
439 class(steepest_descent_optimizer_t),
intent(inout) :: this
440 integer,
intent(in) :: iter
441 class(
problem_t),
intent(inout) :: problem
443 if (.not. this%enable_output)
return
444 call profiler_start_region(
'Optimizer logging')
446 call this%write_log(iter,
problem, [this%last_max_gradient])
448 call profiler_end_region(
'Optimizer logging')
449 end subroutine sd_optimizer_write
455 subroutine sd_optimizer_save_checkpoint_components(this, filename, &
457 class(steepest_descent_optimizer_t),
intent(inout) :: this
458 character(len=*),
intent(in) :: filename
459 logical,
intent(in),
optional :: overwrite
461 call sd_optimizer_save_checkpoint_hdf5(this, filename, overwrite)
462 end subroutine sd_optimizer_save_checkpoint_components
467 subroutine sd_optimizer_load_checkpoint_components(this, filename)
468 class(steepest_descent_optimizer_t),
intent(inout) :: this
469 character(len=*),
intent(in) :: filename
471 call sd_optimizer_load_checkpoint_hdf5(this, filename)
472 end subroutine sd_optimizer_load_checkpoint_components
478 module subroutine sd_optimizer_save_checkpoint_hdf5(object, filename, &
480 class(steepest_descent_optimizer_t),
intent(inout) :: object
481 character(len=*),
intent(in) :: filename
482 logical,
intent(in),
optional :: overwrite
483 call neko_error(
'steepest_descent: HDF5 support not enabled rebuild ' // &
485 end subroutine sd_optimizer_save_checkpoint_hdf5
487 module subroutine sd_optimizer_load_checkpoint_hdf5(object, filename)
488 class(steepest_descent_optimizer_t),
intent(inout) :: object
489 character(len=*),
intent(in) :: filename
490 call neko_error(
'steepest_descent: HDF5 support not enabled rebuild ' // &
492 end subroutine sd_optimizer_load_checkpoint_hdf5
Defines the abstract type optimizer.
Module for handling the optimization problem.
Implements the steady_problem_t type.
Vector-level kernels of the steepest-descent optimizer.
subroutine, public sd_update_move_limits(delta, x, x1, x2, dec, inc, dmin, dmax, n)
Adapt the move limits (Eq. D23).
subroutine, public sd_update_design(x_new, x, s, delta, psi, smax, xmin, xmax, n)
Move-limited, box-constrained design update (Eq. D24).
subroutine, public sd_direction(s, x, xmin, xmax, n)
Bound-projected steepest-descent direction (Eq. D22).
Steepest descent optimizer with move limits.
subroutine sd_optimizer_init_from_json(this, parameters, problem, design, simulation)
Initialise the steepest descent optimizer from the case file.
A topology optimization design variable.
Abstract optimizer class.
The abstract problem type.
Steepest descent optimizer with move limits.