41 use json_module,
only: json_file
45 use num_types,
only: rp
46 use logger,
only: neko_log
47 use profiler,
only: profiler_start_region, profiler_end_region
48 use mpi_f08,
only: mpi_wtime
49 use utils,
only: neko_error, filename_suffix
50 use csv_file,
only: csv_file_t
51 use vector,
only: vector_t
52 use json_utils,
only: json_get_or_default
53 use comm,
only: pe_rank
61 character(len=64),
private :: optimizer_type =
''
63 integer,
private :: max_iterations = 0
65 integer,
private :: current_iteration = 0
71 character(len=256),
private :: checkpoint_file =
''
74 character(len=256),
private :: checkpoint_path =
'./checkpoints/'
75 character(len=256),
private :: checkpoint_base =
'optimizer_checkpoint'
76 character(len=256),
private :: checkpoint_format =
'h5'
77 integer,
private :: checkpoint_interval = -1
80 real(kind=rp),
private :: max_runtime = -1.0_rp
81 real(kind=rp),
private :: start_time = 0.0_rp
82 real(kind=rp),
private :: average_time = 0.0_rp
83 real(kind=rp),
private :: step_count = 0.0_rp
85 logical,
private :: log_initialized = .false.
86 logical,
private :: log_include_constraints = .true.
87 integer,
private :: log_extra_size = 0
88 type(csv_file_t),
private :: log_file
89 type(vector_t),
private :: log_data
100 procedure(optimizer_free), pass(this),
public,
deferred :: free
103 procedure(optimizer_initialize), pass(this),
public,
deferred :: initialize
105 procedure(optimizer_step), pass(this),
public,
deferred :: step
107 procedure(optimizer_validate), pass(this),
public,
deferred :: validate
110 procedure(optimizer_write), pass(this),
public,
deferred :: write
112 procedure(optimizer_save_checkpoint_components), pass(this),
deferred :: &
113 save_checkpoint_components
115 procedure(optimizer_load_checkpoint_components), pass(this),
deferred :: &
116 load_checkpoint_components
119 procedure, pass(this) :: save_checkpoint => optimizer_save_checkpoint
121 procedure, pass(this) :: load_checkpoint => optimizer_load_checkpoint
127 procedure, pass(this),
public :: run => optimizer_run
133 procedure, pass(this) :: init_base => optimizer_init_base
135 procedure, pass(this) :: free_base => optimizer_free_base
137 procedure, pass(this) :: read_base_settings => optimizer_read_base_settings
139 procedure, pass(this) :: print_status => optimizer_print_status
141 procedure, pass(this) :: out_of_time => optimizer_out_of_time
143 procedure, pass(this) :: init_log => optimizer_init_log
145 procedure, pass(this) :: write_log => optimizer_write_log
156 class(optimizer_t),
intent(inout) :: this
157 type(json_file),
intent(inout) :: parameters
158 class(problem_t),
intent(inout) :: problem
159 class(design_t),
intent(in) :: design
160 type(simulation_t),
optional,
intent(in) :: simulation
166 subroutine optimizer_initialize(this, problem, design, simulation)
168 class(optimizer_t),
intent(inout) :: this
169 class(problem_t),
intent(inout) :: problem
170 class(design_t),
intent(inout) :: design
171 type(simulation_t),
optional,
intent(inout) :: simulation
172 end subroutine optimizer_initialize
175 subroutine optimizer_free(this)
177 class(optimizer_t),
intent(inout) :: this
178 end subroutine optimizer_free
181 logical function optimizer_step(this, iter, problem, design, simulation)
183 class(optimizer_t),
intent(inout) :: this
184 integer,
intent(in) :: iter
185 class(problem_t),
intent(inout) :: problem
186 class(design_t),
intent(inout) :: design
187 type(simulation_t),
optional,
intent(inout) :: simulation
188 end function optimizer_step
191 subroutine optimizer_validate(this, problem, design)
193 class(optimizer_t),
intent(inout) :: this
194 class(problem_t),
intent(in) :: problem
195 class(design_t),
intent(in) :: design
196 end subroutine optimizer_validate
199 subroutine optimizer_write(this, iter, problem)
201 class(optimizer_t),
intent(inout) :: this
202 integer,
intent(in) :: iter
203 class(problem_t),
intent(inout) :: problem
204 end subroutine optimizer_write
207 subroutine optimizer_save_checkpoint_components(this, filename, overwrite)
209 class(optimizer_t),
intent(inout) :: this
210 character(len=*),
intent(in) :: filename
211 logical,
intent(in),
optional :: overwrite
212 end subroutine optimizer_save_checkpoint_components
215 subroutine optimizer_load_checkpoint_components(this, filename)
217 class(optimizer_t),
intent(inout) :: this
218 character(len=*),
intent(in) :: filename
219 end subroutine optimizer_load_checkpoint_components
233 module subroutine optimizer_factory(object, parameters,
problem,
design, &
235 class(optimizer_t),
allocatable,
intent(inout) :: object
236 type(json_file),
intent(inout) :: parameters
237 class(problem_t),
intent(inout) :: problem
238 class(design_t),
intent(in) :: design
239 type(simulation_t),
optional,
intent(in) :: simulation
240 end subroutine optimizer_factory
241 end interface optimizer_factory
248 module subroutine optimizer_save_checkpoint_hdf5(object, filename, iter, &
250 class(optimizer_t),
intent(inout) :: object
251 character(len=*),
intent(in) :: filename
252 integer,
intent(in) :: iter
253 logical,
intent(in),
optional :: overwrite
254 end subroutine optimizer_save_checkpoint_hdf5
257 module subroutine optimizer_load_checkpoint_hdf5(object, filename, iter)
258 class(optimizer_t),
intent(inout) :: object
259 character(len=*),
intent(in) :: filename
260 integer,
intent(out) :: iter
261 end subroutine optimizer_load_checkpoint_hdf5
264 public :: optimizer_factory
282 subroutine optimizer_init_base(this, optimizer_type, max_iterations, &
283 max_runtime, checkpoint_file, checkpoint_path, checkpoint_base, &
284 checkpoint_format, checkpoint_interval)
285 class(optimizer_t),
intent(inout) :: this
286 character(len=*),
intent(in) :: optimizer_type
287 integer,
intent(in) :: max_iterations
288 real(kind=rp),
intent(in),
optional :: max_runtime
289 character(len=*),
intent(in),
optional :: checkpoint_file
290 character(len=*),
intent(in),
optional :: checkpoint_path
291 character(len=*),
intent(in),
optional :: checkpoint_base
292 character(len=*),
intent(in),
optional :: checkpoint_format
293 integer,
intent(in),
optional :: checkpoint_interval
296 this%optimizer_type = optimizer_type
297 this%max_iterations = max_iterations
300 if (
present(max_runtime)) this%max_runtime = max_runtime
301 if (
present(checkpoint_file)) this%checkpoint_file = checkpoint_file
302 if (
present(checkpoint_path)) this%checkpoint_path = checkpoint_path
303 if (
present(checkpoint_base)) this%checkpoint_base = checkpoint_base
304 if (
present(checkpoint_format)) this%checkpoint_format = checkpoint_format
305 if (
present(checkpoint_interval))
then
306 this%checkpoint_interval = checkpoint_interval
310 this%start_time = mpi_wtime()
312 end subroutine optimizer_init_base
316 subroutine optimizer_free_base(this)
317 class(optimizer_t),
intent(inout) :: this
319 this%optimizer_type =
''
320 this%max_iterations = 0
321 this%max_runtime = -1.0_rp
322 this%checkpoint_file =
''
323 this%checkpoint_path =
'./checkpoints/'
324 this%checkpoint_base =
'optimizer_checkpoint'
325 this%checkpoint_format =
'h5'
326 this%checkpoint_interval = -1
328 this%start_time = 0.0_rp
329 this%current_iteration = 0
330 call this%log_data%free()
331 this%log_initialized = .false.
332 this%log_extra_size = 0
333 this%log_include_constraints = .true.
335 end subroutine optimizer_free_base
340 subroutine optimizer_read_base_settings(this, solver_params)
341 class(optimizer_t),
intent(inout) :: this
342 type(json_file),
intent(inout) :: solver_params
344 real(kind=rp) :: read_real
345 character(len=:),
allocatable :: read_str
347 call json_get_or_default(solver_params,
'max_runtime', read_real, &
349 this%max_runtime = read_real
351 call json_get_or_default(solver_params,
'checkpoint.file', read_str, &
352 this%checkpoint_file)
353 this%checkpoint_file = read_str
354 call json_get_or_default(solver_params,
'checkpoint.path', read_str, &
355 this%checkpoint_path)
356 this%checkpoint_path = read_str
357 call json_get_or_default(solver_params,
'checkpoint.base', read_str, &
358 this%checkpoint_base)
359 this%checkpoint_base = read_str
360 call json_get_or_default(solver_params,
'checkpoint.format', read_str, &
361 this%checkpoint_format)
362 this%checkpoint_format = read_str
363 call json_get_or_default(solver_params,
'checkpoint.interval', read_int, &
364 this%checkpoint_interval)
365 this%checkpoint_interval = read_int
367 end subroutine optimizer_read_base_settings
385 subroutine optimizer_run(this, problem, design, simulation)
386 class(optimizer_t),
intent(inout) :: this
387 class(problem_t),
intent(inout) :: problem
388 class(design_t),
intent(inout) :: design
389 type(simulation_t),
optional,
intent(inout) :: simulation
390 real(kind=rp) :: iteration_time
391 logical :: converged, file_exists
399 inquire(file = this%checkpoint_file, exist = file_exists)
400 if (file_exists)
then
401 call this%load_checkpoint(this%checkpoint_file, this%current_iteration, &
404 inquire(file =
'optimizer_rt_checkpoint.' // this%checkpoint_format, &
406 if (file_exists)
then
407 call this%load_checkpoint(
'optimizer_rt_checkpoint.' // &
408 this%checkpoint_format, this%current_iteration,
design)
415 call this%write(this%current_iteration,
problem)
416 call design%write(this%current_iteration)
418 call neko_log%section(
'Optimization Loop')
420 do while (this%current_iteration .lt. this%max_iterations)
421 this%current_iteration = this%current_iteration + 1
422 call profiler_start_region(
'Optimizer iteration')
423 iteration_time = mpi_wtime()
425 converged = this%step(this%current_iteration,
problem,
design, &
428 iteration_time = mpi_wtime() - iteration_time
429 call profiler_end_region(
'Optimizer iteration')
432 call this%write(this%current_iteration,
problem)
433 call design%write(this%current_iteration)
436 if (this%checkpoint_interval .gt. 0 .and. &
437 mod(this%current_iteration, this%checkpoint_interval) == 0)
then
438 call this%save_checkpoint(this%current_iteration,
design, .false.)
447 else if (this%out_of_time(iteration_time))
then
448 call this%save_checkpoint(this%current_iteration,
design, .true., &
449 basename =
'optimizer_rt_checkpoint')
457 call this%print_status(stop_flag, this%current_iteration)
459 call neko_log%end_section()
461 end subroutine optimizer_run
475 subroutine optimizer_print_status(this, stop_flag, iter)
476 class(optimizer_t),
intent(in) :: this
477 integer,
intent(in) :: stop_flag
478 integer,
intent(in) :: iter
479 character(len=256) :: msg
481 select case (stop_flag)
483 write(msg,
'(A,I0,A)')
'Optimizer converged successfully after ', &
485 call neko_log%message(msg)
487 write(msg,
'(A,I0,A)')
'Optimizer did not converge in ', &
488 this%max_iterations,
' iterations.'
489 call neko_log%warning(msg)
491 write(msg,
'(A,A,F8.2,A)')
'Optimizer stopped after reaching the ', &
492 'maximum runtime of ', this%max_runtime,
' seconds.'
493 call neko_error(trim(msg))
496 write(msg,
'(A)')
'Optimizer stopped for an unknown reason.'
499 end subroutine optimizer_print_status
507 function optimizer_out_of_time(this, step_time)
result(out_of_time)
508 class(optimizer_t),
intent(inout) :: this
509 real(kind=rp),
intent(in) :: step_time
510 logical :: out_of_time
511 real(kind=rp) :: elapsed_time, old_avg_weight
513 out_of_time = .false.
515 if (this%max_runtime .lt. 0.0_rp)
then
519 elapsed_time = mpi_wtime() - this%start_time
520 this%step_count = this%step_count + 1.0_rp
521 old_avg_weight = (this%step_count - 1) / this%step_count
524 this%average_time = step_time / this%step_count + &
525 this%average_time * old_avg_weight
528 out_of_time = (elapsed_time + this%average_time) .gt. this%max_runtime
530 end function optimizer_out_of_time
541 subroutine optimizer_init_log(this, problem, extra_headers, &
542 include_constraints, filename)
543 class(optimizer_t),
intent(inout) :: this
544 class(problem_t),
intent(in) :: problem
545 character(len=*),
intent(in),
optional :: extra_headers(:)
546 logical,
intent(in),
optional :: include_constraints
547 character(len=*),
intent(in),
optional :: filename
549 character(len=4096) :: header
550 integer :: total_size, base_size, i
551 character(len=256) :: log_name
553 if (
present(include_constraints))
then
554 this%log_include_constraints = include_constraints
557 base_size =
problem%get_log_size(this%log_include_constraints)
559 this%log_extra_size = 0
560 if (
present(extra_headers)) this%log_extra_size =
size(extra_headers)
562 total_size = 1 + base_size + this%log_extra_size
563 call this%log_data%init(total_size)
565 if (
present(filename))
then
566 log_name = trim(filename)
568 log_name =
'optimization_data.csv'
571 call this%log_file%init(trim(log_name))
573 header =
'iter, ' // &
574 trim(
problem%get_log_header(this%log_include_constraints))
575 if (
present(extra_headers))
then
576 do i = 1,
size(extra_headers)
577 if (trim(extra_headers(i)) .eq.
'')
then
578 call neko_error(
'some headers are empty')
580 header = trim(header) //
', ' // trim(extra_headers(i))
584 call this%log_file%set_header(trim(header))
586 this%log_initialized = .true.
587 end subroutine optimizer_init_log
594 subroutine optimizer_write_log(this, iter, problem, extra_values)
595 class(optimizer_t),
intent(inout) :: this
596 integer,
intent(in) :: iter
597 class(problem_t),
intent(in) :: problem
598 real(kind=rp),
intent(in),
optional :: extra_values(:)
599 integer :: base_size, offset
601 if (.not. this%log_initialized)
return
603 base_size =
problem%get_log_size(this%log_include_constraints)
604 this%log_data%x = 0.0_rp
605 this%log_data%x(1) = real(iter, kind=rp)
608 this%log_data%x(2:1 + base_size), &
609 this%log_include_constraints)
611 offset = 2 + base_size
612 if (
present(extra_values))
then
613 if (this%log_extra_size .eq. 0)
then
614 call neko_error(
'got extra values but no headers')
616 this%log_data%x(offset:offset +
size(extra_values) - 1) = extra_values
619 call this%log_file%write(this%log_data)
621 end subroutine optimizer_write_log
634 subroutine optimizer_save_checkpoint(this, iter, design, overwrite, &
635 path, basename, extension)
636 class(optimizer_t),
intent(inout) :: this
637 integer,
intent(in) :: iter
638 class(design_t),
intent(inout) :: design
639 logical,
intent(in) :: overwrite
640 character(len=*),
intent(in),
optional :: path
641 character(len=*),
intent(in),
optional :: basename
642 character(len=*),
intent(in),
optional :: extension
643 character(len=256) :: file_path, file_base, file_ext, file_full
647 if (.not.
present(path)) file_path = trim(this%checkpoint_path)
648 if (.not.
present(basename)) file_base = trim(this%checkpoint_base)
649 if (.not.
present(extension)) file_ext = trim(this%checkpoint_format)
651 if (
present(path)) file_path = trim(path)
652 if (
present(basename)) file_base = trim(basename)
653 if (
present(extension)) file_ext = trim(extension)
656 if (len_trim(file_path) .eq. 0)
then
658 else if (file_path(len_trim(file_path):len_trim(file_path)) .ne.
'/')
then
659 file_path = trim(file_path) //
'/'
662 inquire(file=file_path, exist=exist)
663 if (.not. exist)
then
664 call system(
'mkdir -p ' // trim(file_path))
669 write(file_full,
'(4A)') &
670 trim(file_path), trim(file_base),
".", trim(file_ext)
672 write(file_full,
'(3A,I5.5,2A)') &
673 trim(file_path), trim(file_base),
"_", iter,
".", trim(file_ext)
676 select case (trim(file_ext))
677 case (
'h5',
'hdf5',
'hf5')
678 call optimizer_save_checkpoint_hdf5(this, file_full, iter, overwrite)
680 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
681 trim(file_ext) //
'"')
684 call this%save_checkpoint_components(file_full, overwrite)
685 call design%save_checkpoint(file_full, overwrite)
687 end subroutine optimizer_save_checkpoint
694 subroutine optimizer_load_checkpoint(this, filename, iter, design)
695 class(optimizer_t),
intent(inout) :: this
696 character(len=*),
intent(in) :: filename
697 integer,
intent(out) :: iter
698 class(design_t),
intent(inout) :: design
699 character(len=12) :: file_ext
702 call filename_suffix(filename, file_ext)
704 select case (trim(file_ext))
705 case (
'h5',
'hdf5',
'hf5')
706 call optimizer_load_checkpoint_hdf5(this, filename, iter)
708 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
709 trim(file_ext) //
'"')
712 call this%load_checkpoint_components(filename)
713 call design%load_checkpoint(filename)
716 this%current_iteration = iter
718 if (pe_rank .eq. 0)
then
719 write(*,*)
'Restarted simulation from checkpoint.'
720 write(*,*)
' Checkpoint file: "', trim(filename),
'"'
721 write(*,*)
' Iteration : ', this%current_iteration
724 end subroutine optimizer_load_checkpoint
730 module subroutine optimizer_save_checkpoint_hdf5(object, filename, iter, &
732 class(optimizer_t),
intent(inout) :: object
733 character(len=*),
intent(in) :: filename
734 integer,
intent(in) :: iter
735 logical,
intent(in),
optional :: overwrite
736 call neko_error(
'optimizer: HDF5 support not enabled rebuild with ' // &
738 end subroutine optimizer_save_checkpoint_hdf5
740 module subroutine optimizer_load_checkpoint_hdf5(object, filename, iter)
741 class(optimizer_t),
intent(inout) :: object
742 character(len=*),
intent(in) :: filename
743 integer,
intent(out) :: iter
744 call neko_error(
'optimizer: HDF5 support not enabled rebuild with ' // &
746 end subroutine optimizer_load_checkpoint_hdf5
Factory function for the optimizer.
Interface for optimizer initialization.
Defines the abstract type optimizer.
Module for handling the optimization problem.
Implements the steady_problem_t type.
Abstract optimizer class.
The abstract problem type.