41 use json_module,
only: json_file
45 use num_types,
only: rp
46 use logger,
only: neko_log, log_size
47 use profiler,
only: profiler_start_region, profiler_end_region
48 use mpi_f08,
only: mpi_wtime, mpi_allreduce, mpi_max
49 use utils,
only: neko_error, filename_suffix, read_duration
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, mpi_real_precision, neko_comm
63 character(len=64),
private :: optimizer_type =
''
65 integer,
private :: max_iterations = 0
67 integer,
private :: current_iteration = 0
69 real(kind=rp),
private :: stop_design_change = -1.0_rp
71 real(kind=rp),
public :: max_design_change = 0.0_rp
77 character(len=256),
private :: checkpoint_file =
''
80 character(len=256),
private :: checkpoint_path =
'./checkpoints/'
81 character(len=256),
private :: checkpoint_base =
'optimizer_checkpoint'
82 character(len=256),
private :: checkpoint_format =
'hdf5'
83 integer,
private :: checkpoint_interval = -1
86 real(kind=rp),
private :: max_runtime = -1.0_rp
87 real(kind=rp),
private :: start_time = 0.0_rp
88 real(kind=rp),
private :: average_time = 0.0_rp
89 real(kind=rp),
private :: step_count = 0.0_rp
92 logical,
private :: log_initialized = .false.
93 logical,
private :: log_include_constraints = .true.
94 integer,
private :: log_extra_size = 0
95 type(csv_file_t),
private :: log_file
96 type(vector_t),
private :: log_data
107 procedure(optimizer_free), pass(this),
public,
deferred :: free
110 procedure(optimizer_initialize), pass(this),
public,
deferred :: initialize
112 procedure(optimizer_step), pass(this),
public,
deferred :: step
114 procedure(optimizer_validate), pass(this),
public,
deferred :: validate
117 procedure(optimizer_write), pass(this),
public,
deferred :: write
119 procedure(optimizer_save_checkpoint_components), pass(this),
deferred :: &
120 save_checkpoint_components
122 procedure(optimizer_load_checkpoint_components), pass(this),
deferred :: &
123 load_checkpoint_components
126 procedure, pass(this) :: save_checkpoint => optimizer_save_checkpoint
128 procedure, pass(this) :: load_checkpoint => optimizer_load_checkpoint
134 procedure, pass(this),
public :: run => optimizer_run
140 procedure, pass(this) :: init_base => optimizer_init_base
142 procedure, pass(this) :: free_base => optimizer_free_base
144 procedure, pass(this) :: read_base_settings => optimizer_read_base_settings
146 procedure, pass(this) :: print_status => optimizer_print_status
148 procedure, pass(this) :: out_of_time => optimizer_out_of_time
150 procedure, pass(this) :: init_log => optimizer_init_log
152 procedure, pass(this) :: write_log => optimizer_write_log
163 class(optimizer_t),
intent(inout) :: this
164 type(json_file),
intent(inout) :: parameters
165 class(problem_t),
intent(inout) :: problem
166 class(design_t),
intent(in) :: design
167 type(simulation_t),
optional,
intent(in) :: simulation
173 subroutine optimizer_initialize(this, problem, design, simulation)
175 class(optimizer_t),
intent(inout) :: this
176 class(problem_t),
intent(inout) :: problem
177 class(design_t),
intent(inout) :: design
178 type(simulation_t),
optional,
intent(inout) :: simulation
179 end subroutine optimizer_initialize
182 subroutine optimizer_free(this)
184 class(optimizer_t),
intent(inout) :: this
185 end subroutine optimizer_free
188 logical function optimizer_step(this, iter, problem, design, simulation)
190 class(optimizer_t),
intent(inout) :: this
191 integer,
intent(in) :: iter
192 class(problem_t),
intent(inout) :: problem
193 class(design_t),
intent(inout) :: design
194 type(simulation_t),
optional,
intent(inout) :: simulation
195 end function optimizer_step
198 subroutine optimizer_validate(this, problem, design)
200 class(optimizer_t),
intent(inout) :: this
201 class(problem_t),
intent(in) :: problem
202 class(design_t),
intent(in) :: design
203 end subroutine optimizer_validate
206 subroutine optimizer_write(this, iter, problem)
208 class(optimizer_t),
intent(inout) :: this
209 integer,
intent(in) :: iter
210 class(problem_t),
intent(inout) :: problem
211 end subroutine optimizer_write
214 subroutine optimizer_save_checkpoint_components(this, filename, overwrite)
216 class(optimizer_t),
intent(inout) :: this
217 character(len=*),
intent(in) :: filename
218 logical,
intent(in),
optional :: overwrite
219 end subroutine optimizer_save_checkpoint_components
222 subroutine optimizer_load_checkpoint_components(this, filename)
224 class(optimizer_t),
intent(inout) :: this
225 character(len=*),
intent(in) :: filename
226 end subroutine optimizer_load_checkpoint_components
240 module subroutine optimizer_factory(object, parameters,
problem,
design, &
242 class(optimizer_t),
allocatable,
intent(inout) :: object
243 type(json_file),
intent(inout) :: parameters
244 class(problem_t),
intent(inout) :: problem
245 class(design_t),
intent(in) :: design
246 type(simulation_t),
optional,
intent(in) :: simulation
247 end subroutine optimizer_factory
248 end interface optimizer_factory
255 module subroutine optimizer_save_checkpoint_hdf5(object, filename, iter, &
257 class(optimizer_t),
intent(inout) :: object
258 character(len=*),
intent(in) :: filename
259 integer,
intent(in) :: iter
260 logical,
intent(in),
optional :: overwrite
261 end subroutine optimizer_save_checkpoint_hdf5
264 module subroutine optimizer_load_checkpoint_hdf5(object, filename, iter)
265 class(optimizer_t),
intent(inout) :: object
266 character(len=*),
intent(in) :: filename
267 integer,
intent(out) :: iter
268 end subroutine optimizer_load_checkpoint_hdf5
271 public :: optimizer_factory
290 subroutine optimizer_init_base(this, optimizer_type, max_iterations, &
291 max_runtime, stop_design_change, checkpoint_file, checkpoint_path, &
292 checkpoint_base, checkpoint_format, checkpoint_interval)
293 class(optimizer_t),
intent(inout) :: this
294 character(len=*),
intent(in) :: optimizer_type
295 integer,
intent(in) :: max_iterations
296 real(kind=rp),
intent(in),
optional :: max_runtime
297 real(kind=rp),
intent(in),
optional :: stop_design_change
298 character(len=*),
intent(in),
optional :: checkpoint_file
299 character(len=*),
intent(in),
optional :: checkpoint_path
300 character(len=*),
intent(in),
optional :: checkpoint_base
301 character(len=*),
intent(in),
optional :: checkpoint_format
302 integer,
intent(in),
optional :: checkpoint_interval
305 this%optimizer_type = optimizer_type
306 this%max_iterations = max_iterations
309 if (
present(max_runtime)) this%max_runtime = max_runtime
310 if (
present(stop_design_change)) this%stop_design_change = stop_design_change
311 if (
present(checkpoint_file)) this%checkpoint_file = checkpoint_file
312 if (
present(checkpoint_path)) this%checkpoint_path = checkpoint_path
313 if (
present(checkpoint_base)) this%checkpoint_base = checkpoint_base
314 if (
present(checkpoint_format)) this%checkpoint_format = checkpoint_format
315 if (
present(checkpoint_interval))
then
316 this%checkpoint_interval = checkpoint_interval
320 this%start_time = mpi_wtime()
322 end subroutine optimizer_init_base
326 subroutine optimizer_free_base(this)
327 class(optimizer_t),
intent(inout) :: this
329 this%optimizer_type =
''
330 this%max_iterations = 0
331 this%max_runtime = -1.0_rp
332 this%stop_design_change = -1.0_rp
333 this%max_design_change = 0.0_rp
334 this%checkpoint_file =
''
335 this%checkpoint_path =
'./checkpoints/'
336 this%checkpoint_base =
'optimizer_checkpoint'
337 this%checkpoint_format =
'hdf5'
338 this%checkpoint_interval = -1
340 this%start_time = 0.0_rp
341 this%current_iteration = 0
342 call this%log_data%free()
343 this%log_initialized = .false.
344 this%log_extra_size = 0
345 this%log_include_constraints = .true.
347 end subroutine optimizer_free_base
352 subroutine optimizer_read_base_settings(this, solver_params)
353 class(optimizer_t),
intent(inout) :: this
354 type(json_file),
intent(inout) :: solver_params
356 character(len=:),
allocatable :: read_str
358 call json_get_or_default(solver_params,
'max_runtime', read_str,
"")
359 call read_duration(read_str, this%max_runtime)
360 call json_get_or_default(solver_params,
'stop_design_change', &
361 this%stop_design_change, -1.0_rp)
363 call json_get_or_default(solver_params,
'restart_file', read_str, &
364 this%checkpoint_file)
365 this%checkpoint_file = read_str
367 call json_get_or_default(solver_params,
'checkpoint.path', read_str, &
368 this%checkpoint_path)
369 this%checkpoint_path = read_str
370 call json_get_or_default(solver_params,
'checkpoint.base', read_str, &
371 this%checkpoint_base)
372 this%checkpoint_base = read_str
373 call json_get_or_default(solver_params,
'checkpoint.format', read_str, &
374 this%checkpoint_format)
375 this%checkpoint_format = read_str
376 call json_get_or_default(solver_params,
'checkpoint.interval', read_int, &
377 this%checkpoint_interval)
378 this%checkpoint_interval = read_int
380 end subroutine optimizer_read_base_settings
397 subroutine optimizer_run(this, problem, design, simulation)
398 class(optimizer_t),
intent(inout) :: this
399 class(problem_t),
intent(inout) :: problem
400 class(design_t),
intent(inout) :: design
401 type(simulation_t),
optional,
intent(inout) :: simulation
402 real(kind=rp) :: iteration_time
403 character(len=1024) :: checkpoint_file
404 logical :: converged, file_exists
412 if (trim(this%checkpoint_file) .ne.
'')
then
413 checkpoint_file = trim(this%checkpoint_file)
415 select case (trim(this%checkpoint_format))
416 case (
'h5',
'hdf5',
'hf5',
'hdf')
417 checkpoint_file = trim(this%checkpoint_path) // &
418 'optimizer_rt_checkpoint.h5'
422 inquire(file = checkpoint_file, exist = file_exists)
423 if (file_exists)
then
424 call this%load_checkpoint(checkpoint_file, this%current_iteration, &
429 if (this%current_iteration .ne. 0)
then
430 call design%set_output_counter(this%current_iteration - 1)
431 if (
present(simulation))
then
432 call simulation%set_output_counter(this%current_iteration - 1)
439 call this%write(this%current_iteration,
problem)
440 call design%write(this%current_iteration)
442 call neko_log%section(
'Optimization Loop')
444 do while (this%current_iteration .lt. this%max_iterations)
445 this%current_iteration = this%current_iteration + 1
446 if (pe_rank .eq. 0)
then
447 write(*,*)
'Starting iteration ', this%current_iteration
450 call profiler_start_region(
'Optimizer iteration')
451 iteration_time = mpi_wtime()
454 call nekotop_continuation%update(this%current_iteration)
456 converged = this%step(this%current_iteration,
problem,
design, &
459 iteration_time = mpi_wtime() - iteration_time
460 call profiler_end_region(
'Optimizer iteration')
463 call this%write(this%current_iteration,
problem)
464 call design%write(this%current_iteration)
467 if (this%checkpoint_interval .gt. 0 .and. &
468 mod(this%current_iteration, this%checkpoint_interval) == 0)
then
469 call this%save_checkpoint(this%current_iteration,
design, .false.)
478 else if (this%current_iteration .ge. this%max_iterations)
then
481 else if (this%max_design_change .lt. this%stop_design_change)
then
484 else if (this%out_of_time(iteration_time))
then
485 call this%save_checkpoint(this%current_iteration,
design, .true., &
486 basename =
'optimizer_rt_checkpoint')
494 call this%print_status(stop_flag, this%current_iteration)
496 call neko_log%end_section()
498 end subroutine optimizer_run
512 subroutine optimizer_print_status(this, stop_flag, iter)
513 class(optimizer_t),
intent(in) :: this
514 integer,
intent(in) :: stop_flag
515 integer,
intent(in) :: iter
516 character(len=256) :: msg
518 select case (stop_flag)
520 write(msg,
'(A,I0,A)')
'Optimizer converged successfully after ', &
522 call neko_log%message(msg)
524 write(msg,
'(A,I0,A)')
'Optimizer did not converge in ', &
525 this%max_iterations,
' iterations.'
526 call neko_log%warning(msg)
528 write(msg,
'(A,A,F8.2,A)')
'Optimizer stopped after reaching the ', &
529 'maximum runtime of ', this%max_runtime,
' seconds.'
530 call neko_error(trim(msg))
532 write(msg,
'(A)')
'Optimizer stopped due to runtime limit.'
536 write(msg,
'(A)')
'Optimizer stopped for an unknown reason.'
539 end subroutine optimizer_print_status
547 function optimizer_out_of_time(this, step_time)
result(out_of_time)
548 class(optimizer_t),
intent(inout) :: this
549 real(kind=rp),
intent(in) :: step_time
550 logical :: out_of_time
551 real(kind=rp) :: elapsed_time, time, old_avg_weight
553 out_of_time = .false.
555 if (this%max_runtime .lt. 0.0_rp)
then
559 call mpi_allreduce(step_time, time, 1, mpi_real_precision, mpi_max, &
562 elapsed_time = mpi_wtime() - this%start_time
563 this%step_count = this%step_count + 1.0_rp
564 old_avg_weight = (this%step_count - 1) / this%step_count
567 this%average_time = time / this%step_count + &
568 this%average_time * old_avg_weight
571 out_of_time = (elapsed_time + this%average_time) .gt. this%max_runtime
573 end function optimizer_out_of_time
584 subroutine optimizer_init_log(this, problem, extra_headers, &
585 include_constraints, filename)
586 class(optimizer_t),
intent(inout) :: this
587 class(problem_t),
intent(in) :: problem
588 character(len=*),
intent(in),
optional :: extra_headers(:)
589 logical,
intent(in),
optional :: include_constraints
590 character(len=*),
intent(in),
optional :: filename
592 character(len=4096) :: header
593 integer :: total_size, base_size, i, n_cont
594 character(len=256) :: log_name
596 if (
present(include_constraints))
then
597 this%log_include_constraints = include_constraints
600 n_cont = nekotop_continuation%get_n_params()
601 base_size =
problem%get_log_size(this%log_include_constraints)
602 this%log_extra_size = 0
603 if (
present(extra_headers)) this%log_extra_size =
size(extra_headers)
605 total_size = 1 + base_size + this%log_extra_size + 1 + n_cont
606 call this%log_data%init(total_size)
608 if (
present(filename))
then
609 log_name = trim(filename)
611 log_name =
'optimization_data.csv'
614 call this%log_file%init(trim(log_name))
616 header =
'iter, ' // &
617 trim(
problem%get_log_header(this%log_include_constraints))
618 if (
present(extra_headers))
then
619 do i = 1,
size(extra_headers)
620 if (trim(extra_headers(i)) .eq.
'')
then
621 call neko_error(
'some headers are empty')
623 header = trim(header) //
', ' // trim(extra_headers(i))
627 header = trim(header) //
', max_design_change'
631 header = trim(header) //
', ' // &
632 trim(nekotop_continuation%get_param_name(i))
634 call this%log_file%set_header(trim(header))
636 this%log_initialized = .true.
637 end subroutine optimizer_init_log
644 subroutine optimizer_write_log(this, iter, problem, extra_values)
645 class(optimizer_t),
intent(inout) :: this
646 integer,
intent(in) :: iter
647 class(problem_t),
intent(in) :: problem
648 real(kind=rp),
intent(in),
optional :: extra_values(:)
649 integer :: base_size, offset, n_cont, i
651 if (.not. this%log_initialized)
return
654 n_cont = nekotop_continuation%get_n_params()
656 base_size =
problem%get_log_size(this%log_include_constraints)
657 this%log_data%x(1) = real(iter, kind=rp)
660 this%log_data%x(2:1 + base_size), &
661 this%log_include_constraints)
663 offset = 2 + base_size
664 if (
present(extra_values))
then
665 if (this%log_extra_size .eq. 0)
then
666 call neko_error(
'got extra values but no headers')
668 this%log_data%x(offset:offset +
size(extra_values) - 1) = extra_values
672 offset = offset + this%log_extra_size
673 this%log_data%x(offset:offset + 1 - 1) = this%max_design_change
678 this%log_data%x(offset + i) = &
679 nekotop_continuation%params(i)%target
682 call this%log_file%write(this%log_data)
684 end subroutine optimizer_write_log
697 subroutine optimizer_save_checkpoint(this, iter, design, overwrite, &
698 path, basename, format)
699 class(optimizer_t),
intent(inout) :: this
700 integer,
intent(in) :: iter
701 class(design_t),
intent(inout) :: design
702 logical,
intent(in) :: overwrite
703 character(len=*),
intent(in),
optional :: path
704 character(len=*),
intent(in),
optional :: basename
705 character(len=*),
intent(in),
optional :: format
706 character(len=:),
allocatable :: checkpoint_format
707 character(len=256) :: file_path, file_base, file_ext, file_full
708 character(len=LOG_SIZE) :: msg
709 real(kind=rp) :: t_start, t_total
712 call neko_log%section(
'Optimizer checkpoint')
713 t_start = mpi_wtime()
716 file_path = trim(this%checkpoint_path)
717 file_base = trim(this%checkpoint_base)
718 checkpoint_format = trim(this%checkpoint_format)
721 if (
present(path)) file_path = trim(path)
722 if (
present(basename)) file_base = trim(basename)
723 if (
present(format)) checkpoint_format = trim(format)
726 if (len_trim(file_path) .eq. 0)
then
728 else if (file_path(len_trim(file_path):len_trim(file_path)) .ne.
'/')
then
729 file_path = trim(file_path) //
'/'
732 inquire(file=file_path, exist=exist)
733 if (.not. exist)
then
734 call execute_command_line(
'mkdir -p "' // trim(file_path) //
'"')
737 select case (trim(checkpoint_format))
738 case (
'h5',
'hdf5',
'hf5',
'hdf')
741 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
742 trim(checkpoint_format) //
'"')
747 write(file_full,
'(4A)') &
748 trim(file_path), trim(file_base),
".", trim(file_ext)
750 write(file_full,
'(3A,I5.5,2A)') &
751 trim(file_path), trim(file_base),
"_", iter,
".", trim(file_ext)
754 call neko_log%message(
'Save general optimizer components')
755 select case (trim(file_ext))
756 case (
'h5',
'hdf5',
'hf5')
757 call optimizer_save_checkpoint_hdf5(this, file_full, iter, overwrite)
759 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
760 trim(file_ext) //
'"')
763 call neko_log%message(
'Saving components of ' // this%optimizer_type)
764 call this%save_checkpoint_components(file_full, overwrite)
766 call neko_log%message(
'Save design checkpoint')
767 call design%save_checkpoint(file_full, overwrite)
769 t_total = mpi_wtime() - t_start
770 write(msg,
'(A,F6.2)')
"Checkpoint time: ", t_total
771 call neko_log%end_section(msg)
773 end subroutine optimizer_save_checkpoint
780 subroutine optimizer_load_checkpoint(this, filename, iter, design)
781 class(optimizer_t),
intent(inout) :: this
782 character(len=*),
intent(in) :: filename
783 integer,
intent(out) :: iter
784 class(design_t),
intent(inout) :: design
785 character(len=12) :: file_ext
788 call filename_suffix(filename, file_ext)
790 select case (trim(file_ext))
791 case (
'h5',
'hdf5',
'hf5')
792 call optimizer_load_checkpoint_hdf5(this, filename, iter)
794 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
795 trim(file_ext) //
'"')
798 call this%load_checkpoint_components(filename)
799 call design%load_checkpoint(filename)
802 this%current_iteration = iter
804 if (pe_rank .eq. 0)
then
805 write(*,*)
'Restarted simulation from checkpoint.'
806 write(*,*)
' Checkpoint file: "', trim(filename),
'"'
807 write(*,*)
' Iteration : ', this%current_iteration
810 end subroutine optimizer_load_checkpoint
816 module subroutine optimizer_save_checkpoint_hdf5(object, filename, iter, &
818 class(optimizer_t),
intent(inout) :: object
819 character(len=*),
intent(in) :: filename
820 integer,
intent(in) :: iter
821 logical,
intent(in),
optional :: overwrite
822 call neko_error(
'optimizer: HDF5 support not enabled rebuild with ' // &
824 end subroutine optimizer_save_checkpoint_hdf5
826 module subroutine optimizer_load_checkpoint_hdf5(object, filename, iter)
827 class(optimizer_t),
intent(inout) :: object
828 character(len=*),
intent(in) :: filename
829 integer,
intent(out) :: iter
830 call neko_error(
'optimizer: HDF5 support not enabled rebuild with ' // &
832 end subroutine optimizer_load_checkpoint_hdf5
Factory function for the optimizer.
Interface for optimizer initialization.
Continuation scheduler for the optimization loop.
Defines the abstract type optimizer.
Module for handling the optimization problem.
Implements the steady_problem_t type.
Abstract optimizer class.
The abstract problem type.