Neko-TOP
A portable framework for high-order spectral element flow toplogy optimization.
Loading...
Searching...
No Matches
optimizer.f90
Go to the documentation of this file.
1
34
41 use json_module, only: json_file
42 use simulation_m, only: simulation_t
43 use problem, only: problem_t
44 use design, only: design_t
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, mpi_bcast, &
49 mpi_logical, mpi_barrier
50 use utils, only: neko_error, filename_path, filename_suffix, read_duration, &
51 mkdir
52 use csv_file, only: csv_file_t
53 use vector, only: vector_t
54 use json_utils, only: json_get_or_default
55 use comm, only: pe_rank, mpi_real_precision, neko_comm
56 use continuation_scheduler, only: nekotop_continuation
57 use math, only: neko_eps
58 use, intrinsic :: iso_fortran_env, only : error_unit, output_unit
59
60 implicit none
61 private
62
64 type, abstract, public :: optimizer_t
65
67 character(len=64), private :: optimizer_type = ''
69 integer, private :: max_iterations = 0
71 integer, private :: current_iteration = 0
73 real(kind=rp), private :: stop_design_change = neko_eps
75 real(kind=rp), public :: max_design_change = 0.0_rp
77 real(kind=rp), public :: norm2_design_change = 0.0_rp
78
79 ! ----------------------------------------------------------------------- !
80 ! Restart related members
81
82 ! Internal initialization flag to ensure correct ordering.
83 logical, private :: initialized = .false.
84
86 character(len=256), private :: restart_file = ''
87
88 ! Checkpoint related information
89 character(len=256), private :: checkpoint_path = './checkpoints/'
90 character(len=256), private :: checkpoint_base = 'optimizer_checkpoint'
91 character(len=256), private :: checkpoint_format = 'hdf5'
92 integer, private :: checkpoint_interval = -1
93 logical, private :: checkpoint_overwrite = .false.
94
95 ! Variables for the runtime-based stopping criteria
96 real(kind=rp), private :: max_runtime = -1.0_rp
97 real(kind=rp), private :: start_time = 0.0_rp
98 real(kind=rp), private :: average_time = 0.0_rp
99 real(kind=rp), private :: step_count = 0.0_rp
100
101 ! Logging state
102 logical, private :: log_initialized = .false.
103 logical, private :: log_include_constraints = .true.
104 integer, private :: log_extra_size = 0
105 type(csv_file_t), private :: log_file
106 type(vector_t), private :: log_data
107
108 contains
109
110 ! ---------------------------------------------------------------------- !
111 ! Deferred procedures for specific optimizers
112
114 procedure(optimizer_init_from_json), pass(this), public, deferred :: &
115 init_from_json
117 procedure(optimizer_free), pass(this), public, deferred :: free
118
120 procedure(optimizer_initialize), pass(this), public, deferred :: initialize
122 procedure(optimizer_step), pass(this), public, deferred :: step
124 procedure(optimizer_validate), pass(this), public, deferred :: validate
125
127 procedure(optimizer_write), pass(this), public, deferred :: write
129 procedure(optimizer_save_checkpoint_components), pass(this), deferred :: &
130 save_checkpoint_components
132 procedure(optimizer_load_checkpoint_components), pass(this), deferred :: &
133 load_checkpoint_components
134
136 procedure, pass(this) :: save_checkpoint => optimizer_save_checkpoint
138 procedure, pass(this) :: load_checkpoint => optimizer_load_checkpoint
139
140 ! ----------------------------------------------------------------------- !
141 ! Public procedures
142
144 procedure, pass(this), public :: run => optimizer_run
145
146 ! ----------------------------------------------------------------------- !
147 ! Private procedures
148
150 procedure, pass(this) :: init_base => optimizer_init_base
152 procedure, pass(this) :: free_base => optimizer_free_base
154 procedure, pass(this) :: read_base_settings => optimizer_read_base_settings
156 procedure, pass(this) :: get_iteration => optimizer_get_iteration
158 procedure, pass(this) :: print_status => optimizer_print_status
160 procedure, pass(this) :: out_of_time => optimizer_out_of_time
162 procedure, pass(this) :: init_log => optimizer_init_log
164 procedure, pass(this) :: write_log => optimizer_write_log
165 end type optimizer_t
166
167 ! -------------------------------------------------------------------------- !
168 ! Interface for the optimizer module.
169
170 abstract interface
171
172 subroutine optimizer_init_from_json(this, parameters, problem, design, &
173 simulation)
174 import optimizer_t, json_file, simulation_t, problem_t, design_t, rp
175 class(optimizer_t), intent(inout) :: this
176 type(json_file), intent(inout) :: parameters
177 class(problem_t), intent(inout) :: problem
178 class(design_t), intent(in) :: design
179 type(simulation_t), optional, intent(in) :: simulation
180 end subroutine optimizer_init_from_json
181
185 subroutine optimizer_initialize(this, problem, design, simulation)
187 class(optimizer_t), intent(inout) :: this
188 class(problem_t), intent(inout) :: problem
189 class(design_t), intent(inout) :: design
190 type(simulation_t), optional, intent(inout) :: simulation
191 end subroutine optimizer_initialize
192
194 subroutine optimizer_free(this)
195 import optimizer_t
196 class(optimizer_t), intent(inout) :: this
197 end subroutine optimizer_free
198
200 logical function optimizer_step(this, iter, problem, design, simulation)
202 class(optimizer_t), intent(inout) :: this
203 integer, intent(in) :: iter
204 class(problem_t), intent(inout) :: problem
205 class(design_t), intent(inout) :: design
206 type(simulation_t), optional, intent(inout) :: simulation
207 end function optimizer_step
208
210 subroutine optimizer_validate(this, problem, design)
212 class(optimizer_t), intent(inout) :: this
213 class(problem_t), intent(in) :: problem
214 class(design_t), intent(in) :: design
215 end subroutine optimizer_validate
216
218 subroutine optimizer_write(this, iter, problem)
220 class(optimizer_t), intent(inout) :: this
221 integer, intent(in) :: iter
222 class(problem_t), intent(inout) :: problem
223 end subroutine optimizer_write
224
226 subroutine optimizer_save_checkpoint_components(this, filename, overwrite)
227 import optimizer_t
228 class(optimizer_t), intent(inout) :: this
229 character(len=*), intent(in) :: filename
230 logical, intent(in), optional :: overwrite
231 end subroutine optimizer_save_checkpoint_components
232
234 subroutine optimizer_load_checkpoint_components(this, filename)
235 import optimizer_t
236 class(optimizer_t), intent(inout) :: this
237 character(len=*), intent(in) :: filename
238 end subroutine optimizer_load_checkpoint_components
239
240 end interface
241
242 ! -------------------------------------------------------------------------- !
243 ! Interfaces for the factory functions
244
252 module subroutine optimizer_factory(object, parameters, problem, design, &
253 simulation)
254 class(optimizer_t), allocatable, intent(inout) :: object
255 type(json_file), intent(inout) :: parameters
256 class(problem_t), intent(inout) :: problem
257 class(design_t), intent(in) :: design
258 type(simulation_t), optional, intent(in) :: simulation
259 end subroutine optimizer_factory
260 end interface optimizer_factory
261
262 ! -------------------------------------------------------------------------- !
263 ! IO routines for HDF5 checkpoints
264
265 interface
266
267 module subroutine optimizer_save_checkpoint_hdf5(object, filename, &
268 overwrite)
269 class(optimizer_t), intent(inout) :: object
270 character(len=*), intent(in) :: filename
271 logical, intent(in), optional :: overwrite
272 end subroutine optimizer_save_checkpoint_hdf5
273
275 module subroutine optimizer_load_checkpoint_hdf5(object, filename)
276 class(optimizer_t), intent(inout) :: object
277 character(len=*), intent(in) :: filename
278 end subroutine optimizer_load_checkpoint_hdf5
279 end interface
280
281 public :: optimizer_factory
282
283contains
284
285 ! -------------------------------------------------------------------------- !
286 ! Base initializer and free routines
287
301 subroutine optimizer_init_base(this, optimizer_type, max_iterations, &
302 max_runtime, stop_design_change, restart_file, checkpoint_path, &
303 checkpoint_base, checkpoint_format, checkpoint_interval, &
304 checkpoint_overwrite)
305 class(optimizer_t), intent(inout) :: this
306 character(len=*), intent(in) :: optimizer_type
307 integer, intent(in) :: max_iterations
308 real(kind=rp), intent(in), optional :: max_runtime
309 real(kind=rp), intent(in), optional :: stop_design_change
310 character(len=*), intent(in), optional :: restart_file
311 character(len=*), intent(in), optional :: checkpoint_path
312 character(len=*), intent(in), optional :: checkpoint_base
313 character(len=*), intent(in), optional :: checkpoint_format
314 integer, intent(in), optional :: checkpoint_interval
315 logical, intent(in), optional :: checkpoint_overwrite
316 character(len=LOG_SIZE) :: msg
317 character(len=256) :: file_name
318 logical :: exists
319
320 ! Initialize internals
321 this%start_time = mpi_wtime()
322 this%initialized = .true.
323
324 ! Mandatory settings
325 this%optimizer_type = optimizer_type
326 this%max_iterations = max_iterations
327
328 ! Optional settings
329 if (present(max_runtime)) this%max_runtime = max_runtime
330 if (present(stop_design_change)) this%stop_design_change = stop_design_change
331 if (present(restart_file)) this%restart_file = restart_file
332 if (present(checkpoint_path)) this%checkpoint_path = checkpoint_path
333 if (present(checkpoint_base)) this%checkpoint_base = checkpoint_base
334 if (present(checkpoint_format)) this%checkpoint_format = checkpoint_format
335 if (present(checkpoint_interval)) then
336 this%checkpoint_interval = checkpoint_interval
337 end if
338 if (present(checkpoint_overwrite)) then
339 this%checkpoint_overwrite = checkpoint_overwrite
340 end if
341
342 if (this%max_runtime .gt. 0.0_rp) then
343 write(msg, '(A,F0.0,A)') 'Optimizer will stop after: ', &
344 this%max_runtime, ' seconds.'
345 call neko_log%message(msg)
346
347 ! Look up the runtime checkpoint file if no restart file is specified
348 if (len_trim(this%restart_file) .eq. 0) then
349 exists = .false.
350 file_name = checkpoint_filename(this, &
351 basename = "optimizer_rt_checkpoint")
352 if (pe_rank .eq. 0) inquire(file = file_name, exist = exists)
353 call mpi_bcast(exists, 1, mpi_logical, 0, neko_comm)
354 if (exists) this%restart_file = file_name
355 end if
356 end if
357
358 end subroutine optimizer_init_base
359
362 subroutine optimizer_free_base(this)
363 class(optimizer_t), intent(inout) :: this
364
365 this%optimizer_type = ''
366 this%max_iterations = 0
367 this%max_runtime = -1.0_rp
368 this%stop_design_change = -1.0_rp
369 this%max_design_change = 0.0_rp
370 this%restart_file = ''
371 this%checkpoint_path = './checkpoints/'
372 this%checkpoint_base = 'optimizer_checkpoint'
373 this%checkpoint_format = 'hdf5'
374 this%checkpoint_interval = -1
375 this%checkpoint_overwrite = .false.
376
377 this%start_time = 0.0_rp
378 this%current_iteration = 0
379 call this%log_data%free()
380 this%log_initialized = .false.
381 this%log_extra_size = 0
382 this%log_include_constraints = .true.
383
384 ! Most files have no "free" method, must be done manually here
385 this%log_file%header = ''
386 this%log_file%header_is_written = .false.
387 call this%log_file%set_overwrite(.false.)
388
389 this%initialized = .false.
390
391 end subroutine optimizer_free_base
392
396 subroutine optimizer_read_base_settings(this, solver_params)
397 class(optimizer_t), intent(inout) :: this
398 type(json_file), intent(inout) :: solver_params
399 integer :: read_int
400 logical :: read_logical
401 character(len=:), allocatable :: read_str
402
403 if (this%initialized) then
404 call neko_error('optimizer: Base settings already initialized.')
405 end if
406
407 call json_get_or_default(solver_params, 'max_runtime', read_str, "")
408 call read_duration(read_str, this%max_runtime)
409 call json_get_or_default(solver_params, 'stop_design_change', &
410 this%stop_design_change, neko_eps)
411
412 call json_get_or_default(solver_params, 'restart_file', read_str, &
413 this%restart_file)
414 this%restart_file = read_str
415
416 call json_get_or_default(solver_params, 'checkpoint.path', read_str, &
417 this%checkpoint_path)
418 this%checkpoint_path = read_str
419 call json_get_or_default(solver_params, 'checkpoint.base', read_str, &
420 this%checkpoint_base)
421 this%checkpoint_base = read_str
422 call json_get_or_default(solver_params, 'checkpoint.format', read_str, &
423 this%checkpoint_format)
424 this%checkpoint_format = read_str
425 call json_get_or_default(solver_params, 'checkpoint.interval', read_int, &
426 this%checkpoint_interval)
427 this%checkpoint_interval = read_int
428 call json_get_or_default(solver_params, 'checkpoint.overwrite', &
429 read_logical, this%checkpoint_overwrite)
430 this%checkpoint_overwrite = read_logical
431
432 end subroutine optimizer_read_base_settings
433
434 ! -------------------------------------------------------------------------- !
435 ! Optimization loop routine
436
453 subroutine optimizer_run(this, problem, design, simulation)
454 class(optimizer_t), intent(inout) :: this
455 class(problem_t), intent(inout) :: problem
456 class(design_t), intent(inout) :: design
457 type(simulation_t), optional, intent(inout) :: simulation
458 real(kind=rp) :: iteration_time
459 logical :: converged
460 integer :: stop_flag
461
462 ! Initialize variables
463 stop_flag = 1
464 converged = .false.
465
466 if (len_trim(this%restart_file) .gt. 0) then
467 call this%load_checkpoint(design, this%restart_file)
468 end if
469
470 ! compute potential internals for for a potential restart
471 if (this%current_iteration .ne. 0) then
472 call design%set_output_counter(this%current_iteration - 1)
473 if (present(simulation)) then
474 call simulation%set_output_counter(this%current_iteration - 1)
475 end if
476 end if
477
478 ! On a restart, restore the continuation parameters of the checkpointed
479 ! iteration, so that the problem is re-evaluated with the same values as
480 ! in the original run. A fresh start has nothing to restore, and update(0)
481 ! would index the parameter values at 0.
482 if (this%current_iteration .gt. 0) then
483 call nekotop_continuation%update(this%current_iteration)
484 end if
485
486 ! Prepare the problem state before starting the optimization
487 if (present(simulation)) then
488 call simulation%set_design_iteration(this%current_iteration)
489 end if
490 call this%initialize(problem, design, simulation)
491
492 ! Log the initial state of the problem.
493 if (this%current_iteration .eq. 0) then
494 call this%write(this%current_iteration, problem)
495 end if
496
497 ! Save the current design state.
498 call design%write(this%current_iteration)
499
500 call neko_log%section('Optimization Loop')
501
502 do while (this%current_iteration .lt. this%max_iterations)
503 this%current_iteration = this%current_iteration + 1
504 if (pe_rank .eq. 0) then
505 write(*,*) 'Starting iteration ', this%current_iteration
506 end if
507
508 call profiler_start_region('Optimizer iteration')
509 iteration_time = mpi_wtime()
510
511 ! Update the parameters in continuation scheduler
512 call nekotop_continuation%update(this%current_iteration)
513
514 if (present(simulation)) then
515 call simulation%set_design_iteration(this%current_iteration)
516 end if
517 converged = this%step(this%current_iteration, problem, design, &
518 simulation)
519
520 iteration_time = mpi_wtime() - iteration_time
521 call profiler_end_region('Optimizer iteration')
522
523 ! Log the progress and outputs
524 call this%write(this%current_iteration, problem)
525 call design%write(this%current_iteration)
526
527 ! Save checkpoint if enabled
528 if (this%checkpoint_interval .gt. 0 .and. &
529 mod(this%current_iteration, this%checkpoint_interval) == 0) then
530 call this%save_checkpoint(design)
531 end if
532
533 ! --------------------------------------------------------------------- !
534 ! Check stopping criteria
535
536 if (converged) then
537 stop_flag = 0
538 exit
539 else if (this%current_iteration .ge. this%max_iterations) then
540 stop_flag = 1
541 exit
542 else if (this%max_design_change .lt. this%stop_design_change) then
543 stop_flag = 2
544 exit
545 else if (this%out_of_time(iteration_time)) then
546 block
547 character(len=256) :: filename
548 filename = checkpoint_filename(this, &
549 basename = "optimizer_rt_checkpoint")
550 call this%save_checkpoint(design, .true., filename)
551 end block
552 stop_flag = 3
553 exit
554 end if
555 end do
556
557 ! Check that the final design is valid
558 call this%validate(problem, design)
559 call this%print_status(stop_flag, this%current_iteration)
560
561 call neko_log%end_section()
562
563 end subroutine optimizer_run
564
565 ! ========================================================================== !
566 ! Helper routines
567
571 pure function optimizer_get_iteration(this) result(iteration)
572 class(optimizer_t), intent(in) :: this
573 integer :: iteration
574
575 iteration = this%current_iteration
576 end function optimizer_get_iteration
577
589 subroutine optimizer_print_status(this, stop_flag, iteration)
590 class(optimizer_t), intent(in) :: this
591 integer, intent(in) :: stop_flag
592 integer, intent(in) :: iteration
593 character(len=LOG_SIZE) :: msg
594
595 select case (stop_flag)
596 case (0)
597 write(msg, '(A,I0,A)') 'Optimizer converged successfully after ', &
598 iteration, ' iterations.'
599 call neko_log%message(msg)
600 case (1)
601 write(msg, '(A,I0,A)') 'Optimizer did not converge in ', &
602 this%max_iterations, ' iterations.'
603 call neko_log%warning(msg)
604 case (2)
605 write(msg, '(A,A,F8.2,A)') 'Optimizer stopped, design change ', &
606 'below threshold of: ', this%stop_design_change, '.'
607 call neko_log%warning(msg)
608 case (3)
609 if (pe_rank .eq. 0) then
610 write(error_unit, '(A)') &
611 '*** Error: Optimizer stopped due to runtime limit. ***'
612 end if
613
614 case default
615 write(msg, '(A)') 'Optimizer stopped for an unknown reason.'
616 call neko_error(msg)
617 end select
618 end subroutine optimizer_print_status
619
626 function optimizer_out_of_time(this, step_time) result(out_of_time)
627 class(optimizer_t), intent(inout) :: this
628 real(kind=rp), intent(in) :: step_time
629 logical :: out_of_time
630 real(kind=rp) :: elapsed, step, old_avg_weight
631
632 out_of_time = .false.
633 if (this%max_runtime .lt. 0.0_rp) then
634 return
635 end if
636
637 call mpi_allreduce(step_time, step, 1, mpi_real_precision, mpi_max, &
638 neko_comm)
639
640 this%step_count = this%step_count + 1.0_rp
641 old_avg_weight = (this%step_count - 1.0_rp) / this%step_count
642
643 ! Estimate Cumulative Average iteration step
644 this%average_time = step / this%step_count + &
645 this%average_time * old_avg_weight
646
647 ! Determine if next iteration would exceed max runtime
648 elapsed = mpi_wtime() - this%start_time
649 out_of_time = (elapsed + this%average_time) .gt. 0.99_rp * this%max_runtime
650
651 end function optimizer_out_of_time
652
653 ! ========================================================================== !
654 ! Logging helpers
655
664 subroutine optimizer_init_log(this, problem, extra_headers, &
665 include_constraints, filename)
666 class(optimizer_t), intent(inout) :: this
667 class(problem_t), intent(in) :: problem
668 character(len=*), intent(in), optional :: extra_headers(:)
669 logical, intent(in), optional :: include_constraints
670 character(len=*), intent(in), optional :: filename
671
672 character(len=4096) :: header
673 integer :: total_size, base_size, i, n_cont
674 character(len=256) :: log_name
675
676 if (present(include_constraints)) then
677 this%log_include_constraints = include_constraints
678 end if
679
680 n_cont = nekotop_continuation%get_n_params()
681 base_size = problem%get_log_size(this%log_include_constraints)
682 this%log_extra_size = 0
683 if (present(extra_headers)) this%log_extra_size = size(extra_headers)
684
685 total_size = 1 + base_size + this%log_extra_size + 2 + n_cont
686 call this%log_data%init(total_size)
687
688 if (present(filename)) then
689 log_name = trim(filename)
690 else
691 log_name = 'optimization_data.csv'
692 end if
693
694 call this%log_file%init(trim(log_name))
695
696 header = 'iter, ' // &
697 trim(problem%get_log_header(this%log_include_constraints))
698 if (present(extra_headers)) then
699 do i = 1, size(extra_headers)
700 if (trim(extra_headers(i)) .eq. '') then
701 call neko_error('some headers are empty')
702 end if
703 header = trim(header) // ', ' // trim(extra_headers(i))
704 end do
705 end if
706
707 header = trim(header) // ', max_design_change, norm2_design_change'
708
709 ! continuation parameters
710 do i = 1, n_cont
711 header = trim(header) // ', ' // &
712 trim(nekotop_continuation%get_param_name(i))
713 end do
714 call this%log_file%set_header(trim(header))
715
716 this%log_initialized = .true.
717 end subroutine optimizer_init_log
718
724 subroutine optimizer_write_log(this, iter, problem, extra_values)
725 class(optimizer_t), intent(inout) :: this
726 integer, intent(in) :: iter
727 class(problem_t), intent(in) :: problem
728 real(kind=rp), intent(in), optional :: extra_values(:)
729 integer :: base_size, offset, n_cont, i
730
731 if (.not. this%log_initialized) return
732
733 ! Iteration 0 starts a new log; later iterations append to the existing
734 ! log without writing its header again.
735 if (this%current_iteration .eq. 0) then
736 call this%log_file%set_overwrite(.true.)
737 this%log_file%header_is_written = .false.
738 else
739 call this%log_file%set_overwrite(.false.)
740 this%log_file%header_is_written = .true.
741 end if
742
743 ! Number of continuation parameters
744 n_cont = nekotop_continuation%get_n_params()
745
746 base_size = problem%get_log_size(this%log_include_constraints)
747 this%log_data%x(1) = real(iter, kind=rp)
748
749 call problem%get_log_values( &
750 this%log_data%x(2:1 + base_size), &
751 this%log_include_constraints)
752
753 offset = 2 + base_size
754 if (present(extra_values)) then
755 if (this%log_extra_size .eq. 0) then
756 call neko_error('got extra values but no headers')
757 end if
758 this%log_data%x(offset:offset + size(extra_values) - 1) = extra_values
759 end if
760
761 ! Save maximum design change
762 offset = offset + this%log_extra_size
763 this%log_data%x(offset:offset + 1 - 1) = this%max_design_change
764 this%log_data%x(offset + 1:offset + 2 - 1) = this%norm2_design_change
765
766 ! Continuation parameter values, starting at the first slot after the
767 ! design change entries
768 offset = offset + 2
769 do i = 1, n_cont
770 this%log_data%x(offset + i - 1) = &
771 nekotop_continuation%params(i)%target
772 end do
773
774 call this%log_file%write(this%log_data)
775
776 end subroutine optimizer_write_log
777
778 ! ========================================================================== !
779 ! IO Functions
780
791 function checkpoint_filename(this, path, basename, format, iteration) &
792 result(file_full)
793 class(optimizer_t), intent(in) :: this
794 character(len=*), intent(in), optional :: path
795 character(len=*), intent(in), optional :: basename
796 character(len=*), intent(in), optional :: format
797 integer, intent(in), optional :: iteration
798 character(len=256) :: file_full
799 character(len=:), allocatable :: file_format
800 character(len=256) :: file_path, file_base, file_ext
801
802 ! Default, read from the optimizer's stored checkpoint settings
803 file_path = trim(this%checkpoint_path)
804 file_base = trim(this%checkpoint_base)
805 file_format = trim(this%checkpoint_format)
806
807 ! Override with any provided arguments
808 if (present(path)) file_path = trim(path)
809 if (present(basename)) file_base = trim(basename)
810 if (present(format)) file_format = trim(format)
811
812 ! Ensure the path ends with a '/'
813 if (len_trim(file_path) .eq. 0) then
814 file_path = './'
815 else if (file_path(len_trim(file_path):len_trim(file_path)) .ne. '/') then
816 file_path = trim(file_path) // '/'
817 end if
818
819 select case (trim(file_format))
820 case ('h5', 'hdf5', 'hf5', 'hdf')
821 file_ext = 'h5'
822 case default
823 call neko_error('optimizer: Unsupported checkpoint format: "' // &
824 trim(file_format) // '"')
825 end select
826
827 ! Construct the full filename based on overwrite flag
828 if (present(iteration)) then
829 write(file_full, '(3A,I5.5,2A)') trim(file_path), &
830 trim(file_base), "_", iteration, ".", trim(file_ext)
831 else
832 write(file_full, '(4A)') &
833 trim(file_path), trim(file_base), ".", trim(file_ext)
834 end if
835
836 end function checkpoint_filename
837
847 subroutine optimizer_save_checkpoint(this, design, overwrite, filename, &
848 path, basename, format, iteration)
849 class(optimizer_t), intent(inout) :: this
850 class(design_t), intent(inout) :: design
851 logical, intent(in), optional :: overwrite
852 character(len=*), intent(in), optional :: filename
853 character(len=*), intent(in), optional :: path
854 character(len=*), intent(in), optional :: basename
855 character(len=*), intent(in), optional :: format
856 integer, intent(in), optional :: iteration
857 character(len=256) :: file_full, file_path
858 character(len=12) :: suffix
859 logical :: exists, overwrite_flag
860
861 call neko_log%section('Optimizer save checkpoint')
862 call profiler_start_region('Optimizer save checkpoint')
863
864 ! Ensure support for external iterators.
865 if (present(iteration)) this%current_iteration = iteration
866 if (.not. present(overwrite)) then
867 overwrite_flag = this%checkpoint_overwrite
868 else
869 overwrite_flag = overwrite
870 end if
871
872 ! Create the fill filename and extract relevant components
873 if (present(filename) .and. (present(path) .or. present(basename) .or. &
874 present(format) .or. present(iteration))) then
875 call neko_error('optimizer: Cannot specify both filename and ' // &
876 'path/basename/format for checkpoint loading.')
877 else if (present(filename)) then
878 file_full = trim(filename)
879 else
880 file_full = checkpoint_filename(this, path, basename, format, &
881 this%current_iteration)
882 end if
883
884 ! Throw an error if the file already exists
885 if (pe_rank .eq. 0) then
886 inquire(file = file_full, exist = exists)
887 if (exists .and. .not. overwrite_flag) then
888 call neko_error('optimizer: Checkpoint file "' // &
889 trim(file_full) // '" already exists and overwrite is false.')
890 end if
891 end if
892
893 ! Extract the path and suffix from the full filename.
894 call filename_path(trim(file_full), file_path)
895 call filename_suffix(trim(file_full), suffix)
896
897 ! Create the path if it does not exist
898 exists = .false.
899 if (pe_rank .eq. 0) inquire(file=file_path, exist=exists)
900 call mpi_bcast(exists, 1, mpi_logical, 0, neko_comm)
901 if (.not. exists) then
902 if (pe_rank .eq. 0) call mkdir(trim(file_path))
903 call mpi_barrier(neko_comm)
904 end if
905
906 call neko_log%message('Save general optimizer components')
907 select case (trim(suffix))
908 case ('h5', 'hdf5', 'hf5')
909 call optimizer_save_checkpoint_hdf5(this, file_full, overwrite_flag)
910 case default
911 call neko_error('optimizer: Unsupported checkpoint format: "' // &
912 trim(suffix) // '"')
913 end select
914
915 call neko_log%message('Saving components of ' // this%optimizer_type)
916 call this%save_checkpoint_components(file_full, overwrite_flag)
917
918 call neko_log%message('Save design checkpoint')
919 call design%save_checkpoint(file_full, overwrite_flag)
920
921 call profiler_end_region('Optimizer save checkpoint')
922 call neko_log%end_section()
923
924 end subroutine optimizer_save_checkpoint
925
938 subroutine optimizer_load_checkpoint(this, design, filename, &
939 path, basename, format, iteration)
940 class(optimizer_t), intent(inout) :: this
941 class(design_t), intent(inout) :: design
942 character(len=*), intent(in), optional :: filename
943 character(len=*), intent(in), optional :: path
944 character(len=*), intent(in), optional :: basename
945 character(len=*), intent(in), optional :: format
946 integer, intent(in), optional :: iteration
947 character(len=256) :: file_full
948 character(len=12) :: suffix
949
950 call neko_log%section('Optimizer loading checkpoint')
951 call profiler_start_region('Optimizer load checkpoint')
952
953 ! Check that the provided inputs are a valid combination.
954 if (present(filename) .and. (present(path) .or. present(basename) .or. &
955 present(format) .or. present(iteration))) then
956 call neko_error('optimizer: Cannot specify both filename and ' // &
957 'path/basename/format for checkpoint loading.')
958 else if (present(filename)) then
959 file_full = trim(filename)
960 else
961 file_full = checkpoint_filename(this, path, basename, format, iteration)
962 end if
963
964 if (pe_rank .eq. 0) then
965 block
966 logical :: exists
967 character(len=256) :: msg
968 inquire(file = file_full, exist = exists)
969 if (.not. exists) then
970 write(msg, '(3A)') 'optimizer: Checkpoint file "', trim(file_full), &
971 '" does not exist.'
972 call neko_error(msg)
973 end if
974 end block
975 end if
976 call mpi_barrier(neko_comm)
977
978 ! Get the file extension
979 call filename_suffix(trim(file_full), suffix)
980 select case (trim(suffix))
981 case ('h5', 'hdf5', 'hf5')
982 call optimizer_load_checkpoint_hdf5(this, trim(file_full))
983 case default
984 call neko_error('optimizer: Unsupported checkpoint format: "' // &
985 trim(suffix) // '"')
986 end select
987
988 call this%load_checkpoint_components(trim(file_full))
989 call design%load_checkpoint(trim(file_full))
990
991 ! Set the current iteration to the loaded iteration
992 if (pe_rank .eq. 0) then
993 write(*,*) 'Restarted simulation from checkpoint.'
994 write(*,*) ' Checkpoint file: "', trim(file_full), '"'
995 write(*,*) ' Iteration : ', this%current_iteration
996 end if
997
998 call profiler_end_region('Optimizer load checkpoint')
999 call neko_log%end_section()
1000
1001 end subroutine optimizer_load_checkpoint
1002
1003 ! ========================================================================== !
1004 ! Dummy implementations for module procedures
1005
1006#if !HAVE_HDF5
1007 module subroutine optimizer_save_checkpoint_hdf5(object, filename, &
1008 overwrite)
1009 class(optimizer_t), intent(inout) :: object
1010 character(len=*), intent(in) :: filename
1011 logical, intent(in), optional :: overwrite
1012 call neko_error('optimizer: HDF5 support not enabled rebuild with ' // &
1013 'HAVE_HDF5')
1014 end subroutine optimizer_save_checkpoint_hdf5
1015
1016 module subroutine optimizer_load_checkpoint_hdf5(object, filename)
1017 class(optimizer_t), intent(inout) :: object
1018 character(len=*), intent(in) :: filename
1019 call neko_error('optimizer: HDF5 support not enabled rebuild with ' // &
1020 'HAVE_HDF5')
1021 end subroutine optimizer_load_checkpoint_hdf5
1022#endif
1023
1024end module optimizer
Factory function for the optimizer.
Interface for optimizer initialization.
Continuation scheduler for the optimization loop.
Implements the design_t.
Definition design.f90:36
Defines the abstract type optimizer.
Definition optimizer.f90:40
Module for handling the optimization problem.
Definition problem.f90:41
Implements the steady_problem_t type.
An abstract design type.
Definition design.f90:53
Abstract optimizer class.
Definition optimizer.f90:64
The abstract problem type.
Definition problem.f90:68