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, mpi_allreduce, mpi_max
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, mpi_real_precision, neko_comm
63 character(len=64),
private :: optimizer_type =
''
65 integer,
private :: max_iterations = 0
67 integer,
private :: current_iteration = 0
73 character(len=256),
private :: checkpoint_file =
''
76 character(len=256),
private :: checkpoint_path =
'./checkpoints/'
77 character(len=256),
private :: checkpoint_base =
'optimizer_checkpoint'
78 character(len=256),
private :: checkpoint_format =
'hdf5'
79 integer,
private :: checkpoint_interval = -1
82 real(kind=rp),
private :: max_runtime = -1.0_rp
83 real(kind=rp),
private :: start_time = 0.0_rp
84 real(kind=rp),
private :: average_time = 0.0_rp
85 real(kind=rp),
private :: step_count = 0.0_rp
87 logical,
private :: log_initialized = .false.
88 logical,
private :: log_include_constraints = .true.
89 integer,
private :: log_extra_size = 0
90 type(csv_file_t),
private :: log_file
91 type(vector_t),
private :: log_data
102 procedure(optimizer_free), pass(this),
public,
deferred :: free
105 procedure(optimizer_initialize), pass(this),
public,
deferred :: initialize
107 procedure(optimizer_step), pass(this),
public,
deferred :: step
109 procedure(optimizer_validate), pass(this),
public,
deferred :: validate
112 procedure(optimizer_write), pass(this),
public,
deferred :: write
114 procedure(optimizer_save_checkpoint_components), pass(this),
deferred :: &
115 save_checkpoint_components
117 procedure(optimizer_load_checkpoint_components), pass(this),
deferred :: &
118 load_checkpoint_components
121 procedure, pass(this) :: save_checkpoint => optimizer_save_checkpoint
123 procedure, pass(this) :: load_checkpoint => optimizer_load_checkpoint
129 procedure, pass(this),
public :: run => optimizer_run
135 procedure, pass(this) :: init_base => optimizer_init_base
137 procedure, pass(this) :: free_base => optimizer_free_base
139 procedure, pass(this) :: read_base_settings => optimizer_read_base_settings
141 procedure, pass(this) :: print_status => optimizer_print_status
143 procedure, pass(this) :: out_of_time => optimizer_out_of_time
145 procedure, pass(this) :: init_log => optimizer_init_log
147 procedure, pass(this) :: write_log => optimizer_write_log
158 class(optimizer_t),
intent(inout) :: this
159 type(json_file),
intent(inout) :: parameters
160 class(problem_t),
intent(inout) :: problem
161 class(design_t),
intent(in) :: design
162 type(simulation_t),
optional,
intent(in) :: simulation
168 subroutine optimizer_initialize(this, problem, design, simulation)
170 class(optimizer_t),
intent(inout) :: this
171 class(problem_t),
intent(inout) :: problem
172 class(design_t),
intent(inout) :: design
173 type(simulation_t),
optional,
intent(inout) :: simulation
174 end subroutine optimizer_initialize
177 subroutine optimizer_free(this)
179 class(optimizer_t),
intent(inout) :: this
180 end subroutine optimizer_free
183 logical function optimizer_step(this, iter, problem, design, simulation)
185 class(optimizer_t),
intent(inout) :: this
186 integer,
intent(in) :: iter
187 class(problem_t),
intent(inout) :: problem
188 class(design_t),
intent(inout) :: design
189 type(simulation_t),
optional,
intent(inout) :: simulation
190 end function optimizer_step
193 subroutine optimizer_validate(this, problem, design)
195 class(optimizer_t),
intent(inout) :: this
196 class(problem_t),
intent(in) :: problem
197 class(design_t),
intent(in) :: design
198 end subroutine optimizer_validate
201 subroutine optimizer_write(this, iter, problem)
203 class(optimizer_t),
intent(inout) :: this
204 integer,
intent(in) :: iter
205 class(problem_t),
intent(inout) :: problem
206 end subroutine optimizer_write
209 subroutine optimizer_save_checkpoint_components(this, filename, overwrite)
211 class(optimizer_t),
intent(inout) :: this
212 character(len=*),
intent(in) :: filename
213 logical,
intent(in),
optional :: overwrite
214 end subroutine optimizer_save_checkpoint_components
217 subroutine optimizer_load_checkpoint_components(this, filename)
219 class(optimizer_t),
intent(inout) :: this
220 character(len=*),
intent(in) :: filename
221 end subroutine optimizer_load_checkpoint_components
235 module subroutine optimizer_factory(object, parameters,
problem,
design, &
237 class(optimizer_t),
allocatable,
intent(inout) :: object
238 type(json_file),
intent(inout) :: parameters
239 class(problem_t),
intent(inout) :: problem
240 class(design_t),
intent(in) :: design
241 type(simulation_t),
optional,
intent(in) :: simulation
242 end subroutine optimizer_factory
243 end interface optimizer_factory
250 module subroutine optimizer_save_checkpoint_hdf5(object, filename, iter, &
252 class(optimizer_t),
intent(inout) :: object
253 character(len=*),
intent(in) :: filename
254 integer,
intent(in) :: iter
255 logical,
intent(in),
optional :: overwrite
256 end subroutine optimizer_save_checkpoint_hdf5
259 module subroutine optimizer_load_checkpoint_hdf5(object, filename, iter)
260 class(optimizer_t),
intent(inout) :: object
261 character(len=*),
intent(in) :: filename
262 integer,
intent(out) :: iter
263 end subroutine optimizer_load_checkpoint_hdf5
266 public :: optimizer_factory
284 subroutine optimizer_init_base(this, optimizer_type, max_iterations, &
285 max_runtime, checkpoint_file, checkpoint_path, checkpoint_base, &
286 checkpoint_format, checkpoint_interval)
287 class(optimizer_t),
intent(inout) :: this
288 character(len=*),
intent(in) :: optimizer_type
289 integer,
intent(in) :: max_iterations
290 real(kind=rp),
intent(in),
optional :: max_runtime
291 character(len=*),
intent(in),
optional :: checkpoint_file
292 character(len=*),
intent(in),
optional :: checkpoint_path
293 character(len=*),
intent(in),
optional :: checkpoint_base
294 character(len=*),
intent(in),
optional :: checkpoint_format
295 integer,
intent(in),
optional :: checkpoint_interval
298 this%optimizer_type = optimizer_type
299 this%max_iterations = max_iterations
302 if (
present(max_runtime)) this%max_runtime = max_runtime
303 if (
present(checkpoint_file)) this%checkpoint_file = checkpoint_file
304 if (
present(checkpoint_path)) this%checkpoint_path = checkpoint_path
305 if (
present(checkpoint_base)) this%checkpoint_base = checkpoint_base
306 if (
present(checkpoint_format)) this%checkpoint_format = checkpoint_format
307 if (
present(checkpoint_interval))
then
308 this%checkpoint_interval = checkpoint_interval
312 this%start_time = mpi_wtime()
314 end subroutine optimizer_init_base
318 subroutine optimizer_free_base(this)
319 class(optimizer_t),
intent(inout) :: this
321 this%optimizer_type =
''
322 this%max_iterations = 0
323 this%max_runtime = -1.0_rp
324 this%checkpoint_file =
''
325 this%checkpoint_path =
'./checkpoints/'
326 this%checkpoint_base =
'optimizer_checkpoint'
327 this%checkpoint_format =
'hdf5'
328 this%checkpoint_interval = -1
330 this%start_time = 0.0_rp
331 this%current_iteration = 0
332 call this%log_data%free()
333 this%log_initialized = .false.
334 this%log_extra_size = 0
335 this%log_include_constraints = .true.
337 end subroutine optimizer_free_base
342 subroutine optimizer_read_base_settings(this, solver_params)
343 class(optimizer_t),
intent(inout) :: this
344 type(json_file),
intent(inout) :: solver_params
346 real(kind=rp) :: read_real
347 character(len=:),
allocatable :: read_str
349 call json_get_or_default(solver_params,
'max_runtime', read_real, &
351 this%max_runtime = read_real
352 call json_get_or_default(solver_params,
'restart_file', read_str, &
353 this%checkpoint_file)
354 this%checkpoint_file = read_str
356 call json_get_or_default(solver_params,
'checkpoint.path', read_str, &
357 this%checkpoint_path)
358 this%checkpoint_path = read_str
359 call json_get_or_default(solver_params,
'checkpoint.base', read_str, &
360 this%checkpoint_base)
361 this%checkpoint_base = read_str
362 call json_get_or_default(solver_params,
'checkpoint.format', read_str, &
363 this%checkpoint_format)
364 this%checkpoint_format = read_str
365 call json_get_or_default(solver_params,
'checkpoint.interval', read_int, &
366 this%checkpoint_interval)
367 this%checkpoint_interval = read_int
369 end subroutine optimizer_read_base_settings
387 subroutine optimizer_run(this, problem, design, simulation)
388 class(optimizer_t),
intent(inout) :: this
389 class(problem_t),
intent(inout) :: problem
390 class(design_t),
intent(inout) :: design
391 type(simulation_t),
optional,
intent(inout) :: simulation
392 real(kind=rp) :: iteration_time
393 character(len=1024) :: checkpoint_file
394 logical :: converged, file_exists
402 if (trim(this%checkpoint_file) .ne.
'')
then
403 checkpoint_file = trim(this%checkpoint_file)
405 select case (trim(this%checkpoint_format))
406 case (
'h5',
'hdf5',
'hf5',
'hdf')
407 checkpoint_file = trim(this%checkpoint_path) // &
408 'optimizer_rt_checkpoint.h5'
412 inquire(file = checkpoint_file, exist = file_exists)
413 if (file_exists)
then
414 call this%load_checkpoint(checkpoint_file, this%current_iteration, &
419 if (this%current_iteration .ne. 0)
then
420 call design%set_output_counter(this%current_iteration - 1)
421 if (
present(simulation))
then
422 call simulation%set_output_counter(this%current_iteration - 1)
429 call this%write(this%current_iteration,
problem)
430 call design%write(this%current_iteration)
432 call neko_log%section(
'Optimization Loop')
434 do while (this%current_iteration .lt. this%max_iterations)
435 this%current_iteration = this%current_iteration + 1
436 if (pe_rank .eq. 0)
then
437 write(*,*)
'Starting iteration ', this%current_iteration
440 call profiler_start_region(
'Optimizer iteration')
441 iteration_time = mpi_wtime()
444 call nekotop_continuation%update(this%current_iteration)
446 converged = this%step(this%current_iteration,
problem,
design, &
449 iteration_time = mpi_wtime() - iteration_time
450 call profiler_end_region(
'Optimizer iteration')
453 call this%write(this%current_iteration,
problem)
454 call design%write(this%current_iteration)
457 if (this%checkpoint_interval .gt. 0 .and. &
458 mod(this%current_iteration, this%checkpoint_interval) == 0)
then
459 call this%save_checkpoint(this%current_iteration,
design, .false.)
468 else if (this%out_of_time(iteration_time))
then
469 call this%save_checkpoint(this%current_iteration,
design, .true., &
470 basename =
'optimizer_rt_checkpoint')
478 call this%print_status(stop_flag, this%current_iteration)
480 call neko_log%end_section()
482 end subroutine optimizer_run
496 subroutine optimizer_print_status(this, stop_flag, iter)
497 class(optimizer_t),
intent(in) :: this
498 integer,
intent(in) :: stop_flag
499 integer,
intent(in) :: iter
500 character(len=256) :: msg
502 select case (stop_flag)
504 write(msg,
'(A,I0,A)')
'Optimizer converged successfully after ', &
506 call neko_log%message(msg)
508 write(msg,
'(A,I0,A)')
'Optimizer did not converge in ', &
509 this%max_iterations,
' iterations.'
510 call neko_log%warning(msg)
512 write(msg,
'(A,A,F8.2,A)')
'Optimizer stopped after reaching the ', &
513 'maximum runtime of ', this%max_runtime,
' seconds.'
514 call neko_error(trim(msg))
517 write(msg,
'(A)')
'Optimizer stopped for an unknown reason.'
520 end subroutine optimizer_print_status
528 function optimizer_out_of_time(this, step_time)
result(out_of_time)
529 class(optimizer_t),
intent(inout) :: this
530 real(kind=rp),
intent(in) :: step_time
531 logical :: out_of_time
532 real(kind=rp) :: elapsed_time, time, old_avg_weight
534 out_of_time = .false.
536 if (this%max_runtime .lt. 0.0_rp)
then
540 call mpi_allreduce(step_time, time, 1, mpi_real_precision, mpi_max, &
543 elapsed_time = mpi_wtime() - this%start_time
544 this%step_count = this%step_count + 1.0_rp
545 old_avg_weight = (this%step_count - 1) / this%step_count
548 this%average_time = time / this%step_count + &
549 this%average_time * old_avg_weight
552 out_of_time = (elapsed_time + this%average_time) .gt. this%max_runtime
554 end function optimizer_out_of_time
565 subroutine optimizer_init_log(this, problem, extra_headers, &
566 include_constraints, filename)
567 class(optimizer_t),
intent(inout) :: this
568 class(problem_t),
intent(in) :: problem
569 character(len=*),
intent(in),
optional :: extra_headers(:)
570 logical,
intent(in),
optional :: include_constraints
571 character(len=*),
intent(in),
optional :: filename
573 character(len=4096) :: header
574 integer :: total_size, base_size, i, n_cont
575 character(len=256) :: log_name
577 if (
present(include_constraints))
then
578 this%log_include_constraints = include_constraints
580 n_cont = nekotop_continuation%get_n_params()
582 base_size =
problem%get_log_size(this%log_include_constraints)
584 this%log_extra_size = 0
585 if (
present(extra_headers)) this%log_extra_size =
size(extra_headers)
587 total_size = 1 + base_size + this%log_extra_size + n_cont
588 call this%log_data%init(total_size)
590 if (
present(filename))
then
591 log_name = trim(filename)
593 log_name =
'optimization_data.csv'
596 call this%log_file%init(trim(log_name))
598 header =
'iter, ' // &
599 trim(
problem%get_log_header(this%log_include_constraints))
600 if (
present(extra_headers))
then
601 do i = 1,
size(extra_headers)
602 if (trim(extra_headers(i)) .eq.
'')
then
603 call neko_error(
'some headers are empty')
605 header = trim(header) //
', ' // trim(extra_headers(i))
611 header = trim(header) //
', ' // &
612 trim(nekotop_continuation%get_param_name(i))
614 call this%log_file%set_header(trim(header))
616 this%log_initialized = .true.
617 end subroutine optimizer_init_log
624 subroutine optimizer_write_log(this, iter, problem, extra_values)
625 class(optimizer_t),
intent(inout) :: this
626 integer,
intent(in) :: iter
627 class(problem_t),
intent(in) :: problem
628 real(kind=rp),
intent(in),
optional :: extra_values(:)
629 integer :: base_size, offset, n_cont, i
631 if (.not. this%log_initialized)
return
634 n_cont = nekotop_continuation%get_n_params()
636 base_size =
problem%get_log_size(this%log_include_constraints)
637 this%log_data%x(1) = real(iter, kind=rp)
640 this%log_data%x(2:1 + base_size), &
641 this%log_include_constraints)
643 offset = 2 + base_size
644 if (
present(extra_values))
then
645 if (this%log_extra_size .eq. 0)
then
646 call neko_error(
'got extra values but no headers')
648 this%log_data%x(offset:offset +
size(extra_values) - 1) = extra_values
653 this%log_data%x(offset +
size(extra_values) - 1 + i) = &
654 nekotop_continuation%params(i)%target
657 call this%log_file%write(this%log_data)
659 end subroutine optimizer_write_log
672 subroutine optimizer_save_checkpoint(this, iter, design, overwrite, &
673 path, basename, format)
674 class(optimizer_t),
intent(inout) :: this
675 integer,
intent(in) :: iter
676 class(design_t),
intent(inout) :: design
677 logical,
intent(in) :: overwrite
678 character(len=*),
intent(in),
optional :: path
679 character(len=*),
intent(in),
optional :: basename
680 character(len=*),
intent(in),
optional :: format
681 character(len=:),
allocatable :: checkpoint_format
682 character(len=256) :: file_path, file_base, file_ext, file_full
686 if (.not.
present(path)) file_path = trim(this%checkpoint_path)
687 if (.not.
present(basename)) file_base = trim(this%checkpoint_base)
688 if (.not.
present(format)) checkpoint_format = trim(this%checkpoint_format)
690 if (
present(path)) file_path = trim(path)
691 if (
present(basename)) file_base = trim(basename)
692 if (
present(format)) checkpoint_format = trim(format)
695 if (len_trim(file_path) .eq. 0)
then
697 else if (file_path(len_trim(file_path):len_trim(file_path)) .ne.
'/')
then
698 file_path = trim(file_path) //
'/'
701 inquire(file=file_path, exist=exist)
702 if (.not. exist)
then
703 call execute_command_line(
'mkdir -p "' // trim(file_path) //
'"')
706 select case (trim(checkpoint_format))
707 case (
'h5',
'hdf5',
'hf5',
'hdf')
710 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
711 trim(checkpoint_format) //
'"')
716 write(file_full,
'(4A)') &
717 trim(file_path), trim(file_base),
".", trim(file_ext)
719 write(file_full,
'(3A,I5.5,2A)') &
720 trim(file_path), trim(file_base),
"_", iter,
".", trim(file_ext)
723 select case (trim(file_ext))
724 case (
'h5',
'hdf5',
'hf5')
725 call optimizer_save_checkpoint_hdf5(this, file_full, iter, overwrite)
727 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
728 trim(file_ext) //
'"')
731 call this%save_checkpoint_components(file_full, overwrite)
732 call design%save_checkpoint(file_full, overwrite)
734 end subroutine optimizer_save_checkpoint
741 subroutine optimizer_load_checkpoint(this, filename, iter, design)
742 class(optimizer_t),
intent(inout) :: this
743 character(len=*),
intent(in) :: filename
744 integer,
intent(out) :: iter
745 class(design_t),
intent(inout) :: design
746 character(len=12) :: file_ext
749 call filename_suffix(filename, file_ext)
751 select case (trim(file_ext))
752 case (
'h5',
'hdf5',
'hf5')
753 call optimizer_load_checkpoint_hdf5(this, filename, iter)
755 call neko_error(
'optimizer: Unsupported checkpoint format: "' // &
756 trim(file_ext) //
'"')
759 call this%load_checkpoint_components(filename)
760 call design%load_checkpoint(filename)
763 this%current_iteration = iter
765 if (pe_rank .eq. 0)
then
766 write(*,*)
'Restarted simulation from checkpoint.'
767 write(*,*)
' Checkpoint file: "', trim(filename),
'"'
768 write(*,*)
' Iteration : ', this%current_iteration
771 end subroutine optimizer_load_checkpoint
777 module subroutine optimizer_save_checkpoint_hdf5(object, filename, iter, &
779 class(optimizer_t),
intent(inout) :: object
780 character(len=*),
intent(in) :: filename
781 integer,
intent(in) :: iter
782 logical,
intent(in),
optional :: overwrite
783 call neko_error(
'optimizer: HDF5 support not enabled rebuild with ' // &
785 end subroutine optimizer_save_checkpoint_hdf5
787 module subroutine optimizer_load_checkpoint_hdf5(object, filename, iter)
788 class(optimizer_t),
intent(inout) :: object
789 character(len=*),
intent(in) :: filename
790 integer,
intent(out) :: iter
791 call neko_error(
'optimizer: HDF5 support not enabled rebuild with ' // &
793 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.