Octopus
system.F90
Go to the documentation of this file.
1!! Copyright (C) 2019 N. Tancogne-Dejean
2!! Copyright (C) 2020 M. Oliveira, Heiko Appel
3!! Copyright (C) 2021 S. Ohlmann
4!!
5!! This program is free software; you can redistribute it and/or modify
6!! it under the terms of the GNU General Public License as published by
7!! the Free Software Foundation; either version 2, or (at your option)
8!! any later version.
9!!
10!! This program is distributed in the hope that it will be useful,
11!! but WITHOUT ANY WARRANTY; without even the implied warranty of
12!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13!! GNU General Public License for more details.
14!!
15!! You should have received a copy of the GNU General Public License
16!! along with this program; if not, write to the Free Software
17!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18!! 02110-1301, USA.
19!!
20
21#include "global.h"
22
25module system_oct_m
28 use debug_oct_m
30 use global_oct_m
36 use mpi_oct_m
40 use parser_oct_m
43 use unit_oct_m
46 implicit none
47
48 private
49 public :: &
50 system_t, &
63 system_end, &
66
67 type :: barrier_t
68 private
69 logical :: active
70 real(real64) :: target_time
71 end type barrier_t
72
73 integer, parameter, public :: &
74 NUMBER_BARRIERS = 1, &
76
80 type, extends(interaction_partner_t), abstract :: system_t
81 private
82 type(iteration_counter_t), public :: iteration
83 class(algorithm_t), pointer, public :: algo => null()
84
85 integer, allocatable, public :: supported_interactions(:)
86 type(interaction_list_t), public :: interactions
87
88 type(mpi_grp_t), public :: grp
89
90 type(barrier_t) :: barrier(NUMBER_BARRIERS)
91 real(real64), public :: kinetic_energy
92 real(real64), public :: potential_energy
93 real(real64), public :: internal_energy
94 ! !! the kinetic energy of its constituents
95 real(real64), public :: total_energy
96
97 contains
98 procedure :: execute_algorithm => system_execute_algorithm
99 procedure :: reset_iteration_counters => system_reset_iteration_counters
100 procedure :: new_algorithm => system_new_algorithm
101 procedure :: algorithm_finished => system_algorithm_finished
102 procedure :: init_iteration_counters => system_init_iteration_counters
103 procedure :: create_interactions => system_create_interactions
104 procedure :: init_parallelization => system_init_parallelization
105 procedure :: update_couplings => system_update_couplings
106 procedure :: update_interactions => system_update_interactions
107 procedure :: update_interactions_start => system_update_interactions_start
108 procedure :: update_interactions_finish => system_update_interactions_finish
109 procedure :: algorithm_start => system_algorithm_start
110 procedure :: algorithm_finish => system_algorithm_finish
111 procedure :: iteration_info => system_iteration_info
112 procedure :: restart_write => system_restart_write
113 procedure :: restart_read => system_restart_read
114 procedure :: output_start => system_output_start
115 procedure :: output_write => system_output_write
116 procedure :: output_finish => system_output_finish
117 procedure :: process_is_slave => system_process_is_slave
118 procedure :: start_barrier => system_start_barrier
119 procedure :: end_barrier => system_end_barrier
120 procedure :: arrived_at_barrier => system_arrived_at_barrier
121 procedure :: arrived_at_any_barrier => system_arrived_at_any_barrier
122 procedure :: update_potential_energy => system_update_potential_energy
123 procedure :: update_internal_energy => system_update_internal_energy
124 procedure :: update_total_energy => system_update_total_energy
125 procedure(system_init_interaction), deferred :: init_interaction
126 procedure(system_initialize), deferred :: initialize
127 procedure(system_do_algorithmic_operation), deferred :: do_algorithmic_operation
128 procedure(system_is_tolerance_reached), deferred :: is_tolerance_reached
129 procedure(system_restart_write_data), deferred :: restart_write_data
130 procedure(system_restart_read_data), deferred :: restart_read_data
131 procedure(system_update_kinetic_energy), deferred :: update_kinetic_energy
132 end type system_t
133
134 abstract interface
135
136 ! ---------------------------------------------------------
142 subroutine system_init_interaction(this, interaction)
143 import system_t
144 import interaction_t
145 class(system_t), target, intent(inout) :: this
146 class(interaction_t), intent(inout) :: interaction
147 end subroutine system_init_interaction
148
149 ! ---------------------------------------------------------
151 subroutine system_initialize(this)
152 import system_t
153 class(system_t), intent(inout) :: this
154 end subroutine system_initialize
155
156 ! ---------------------------------------------------------
166 logical function system_do_algorithmic_operation(this, operation, updated_quantities) result(done)
167 import system_t
169 class(system_t), intent(inout) :: this
170 class(algorithmic_operation_t), intent(in) :: operation
171 character(len=:), allocatable, intent(out) :: updated_quantities(:)
173
174 ! ---------------------------------------------------------
176 logical function system_is_tolerance_reached(this, tol)
177 use, intrinsic :: iso_fortran_env
178 import system_t
179 class(system_t), intent(in) :: this
180 real(real64), intent(in) :: tol
182
183 ! ---------------------------------------------------------
188 import system_t
189 class(system_t), intent(inout) :: this
191
192 ! ---------------------------------------------------------
194 import system_t
195 class(system_t), intent(inout) :: this
198 ! ---------------------------------------------------------
199 ! this function returns true if restart data could be read
200 logical function system_restart_read_data(this)
201 import system_t
202 class(system_t), intent(inout) :: this
205 import system_t
206 class(system_t), intent(inout) :: this
209 end interface
216 private
217 contains
218 procedure :: add => system_list_add_node
219 procedure :: contains => system_list_contains
223 private
224 contains
225 procedure :: get_next => system_iterator_get_next
227
228contains
229
230 ! ---------------------------------------------------------
243 subroutine system_execute_algorithm(this)
244 class(system_t), intent(inout) :: this
245
246 type(algorithmic_operation_t) :: operation
247 logical :: all_updated, at_barrier, operation_done
248 type(event_handle_t) :: debug_handle
249 integer :: i
250 type(quantity_t), pointer :: quantity
251 character(len=:), allocatable :: updated_quantities(:)
252
254
255 at_barrier = .false.
256
257 do while (.not. at_barrier)
258
259 operation = this%algo%get_current_operation()
260
261 debug_handle = multisystem_debug_write_event_in(this%namespace, event_function_call_t("dt_operation", operation), &
262 system_iteration=this%iteration, algo_iteration=this%algo%iteration)
263
264 ! First try to execute the operation as a system specific operation
265 operation_done = this%do_algorithmic_operation(operation, updated_quantities)
266 if (allocated(updated_quantities)) then
267 ! Update the quantities iteration counters
268 do i = 1, size(updated_quantities)
269 quantity => this%quantities%get(updated_quantities(i))
270 call quantity%iteration%set(this%algo%iteration + 1)
271 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("quantity", quantity%label, &
272 quantity%iteration, "set"))
273 end do
274 end if
275
276 ! If not done, we try to execute it as an algorithm-specific operation.
277 if (.not. operation_done) then
278 operation_done = this%algo%do_operation(operation)
279 else
280 call this%algo%next()
281 end if
283 ! If still not done, the operation must be a generic operation
284 if (.not. operation_done) then
285
286 select case (operation%id)
287 case (skip)
288 ! Do nothing
289 call this%algo%next()
290
291 case (iteration_done)
292 ! Increment the system iteration by one
293 this%iteration = this%iteration + 1
294 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("system", "", this%iteration, "tick"))
296 ! Recompute the total energy
297 call this%update_total_energy()
298
299 ! Write output
300 call this%output_write()
301
302 ! Update elapsed time
303 call this%algo%update_elapsed_time()
304
305 ! Print information about the current iteration
306 ! (NB: needs to be done after marking the execution step as finished,
307 ! so that the timings are correct)
308 call this%iteration_info()
309
310 call this%algo%next()
311
312 case (rewind_algorithm)
313 if (.not. this%algo%finished()) then
314 if (.not. this%arrived_at_any_barrier()) then
315 ! Reset propagator for next step if not waiting at barrier
316 call this%algo%rewind()
317 else
318 at_barrier = .true.
319 end if
320 else
321 if (this%algo%continues_after_finished()) then
322 call this%algo%rewind()
323 else
324 at_barrier = .true.
325 end if
326 end if
327
328 case (update_couplings)
329 ! We increment by one algorithmic step
330 this%algo%iteration = this%algo%iteration + 1
331 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("algorithm", "", &
332 this%algo%iteration, "tick"))
333
334 ! Try to update all needed quantities from the interaction partners
335 all_updated = this%update_couplings()
336
337 ! Move to next algorithm step if all couplings have been
338 ! updated. Otherwise roll back the iteration counter and try again later.
339 if (all_updated) then
340 call this%algo%next()
341 else
342 this%algo%iteration = this%algo%iteration - 1
343 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("algorithm", "", &
344 this%algo%iteration, "reverse"))
345 end if
346
347 ! Couplings are implicit barriers
348 at_barrier = .true.
349
351 ! Update all the interactions
352 call this%update_interactions()
353 call this%algo%next()
354
355 case default
356 message(1) = "Unsupported algorithmic operation."
357 write(message(2), '(A,A,A)') trim(operation%id), ": ", trim(operation%label)
358 call messages_fatal(2, namespace=this%namespace)
359 end select
360 end if
361
362 call multisystem_debug_write_event_out(debug_handle, system_iteration=this%iteration, algo_iteration=this%algo%iteration)
363 end do
364
366 end subroutine system_execute_algorithm
367
368 ! ---------------------------------------------------------
369 subroutine system_reset_iteration_counters(this, accumulated_iterations)
370 class(system_t), intent(inout) :: this
371 integer, intent(in) :: accumulated_iterations
372
373 type(interaction_iterator_t) :: iter
374 class(interaction_t), pointer :: interaction
375 type(quantity_iterator_t) :: qiter
376 class(quantity_t), pointer :: quantity
377
378 character(len=MAX_INFO_LEN) :: extended_label
379
381
382 ! Propagator counter
383 this%algo%iteration = this%algo%iteration - accumulated_iterations
384 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("algorithm", "", this%algo%iteration, "reset"))
385
386 ! Interaction counters
387 call iter%start(this%interactions)
388 do while (iter%has_next())
389 interaction => iter%get_next()
390 interaction%iteration = interaction%iteration - accumulated_iterations
391
392 extended_label = trim(interaction%label)//"-"//trim(interaction%partner%namespace%get())
393 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t( "interaction", extended_label, &
394 interaction%iteration, "reset"))
395 end do
396
397 ! Internal quantities counters
398 call qiter%start(this%interactions)
399 do while(qiter%has_next())
400 quantity => qiter%get_next()
401 quantity%iteration = quantity%iteration - accumulated_iterations
402 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("quantity", quantity%label, &
403 quantity%iteration, "reset"))
404 end do
405
408
409
410 ! ---------------------------------------------------------
423 recursive subroutine system_create_interactions(this, interaction_factory, available_partners)
424 class(system_t), intent(inout) :: this
425 class(interactions_factory_abst_t), intent(in) :: interaction_factory
426 class(partner_list_t), target, intent(in) :: available_partners
427 ! !! for the given system.
428
429 logical :: in_list
430 integer :: i, ip, iq, interaction_type
431 type(partner_list_t) :: partners
432 type(partner_iterator_t) :: iter
433 class(interaction_partner_t), pointer :: partner
434 class(interaction_t), pointer :: interaction
435 type(interactions_factory_options_t), allocatable :: options(:)
436
438
439 ! Sanity checks
440 assert(allocated(this%supported_interactions))
441 assert(allocated(this%supported_interactions_as_partner))
442
443
444 ! All systems need to be connected to make sure they remain synchronized.
445 ! We enforce that be adding a ghost interaction between all systems.
446 ! Note that this recursively adds subsystems of containers.
447 call iter%start(available_partners)
448 do while (iter%has_next())
449 partner => iter%get_next()
450 call partner%add_partners_to_list(partners)
451 end do
452
453 call iter%start(partners)
454 do while (iter%has_next())
455 partner => iter%get_next()
456
457 ! No self-interaction
458 if (partner%namespace%get() == this%namespace%get()) cycle
459
460 call this%interactions%add(ghost_interaction_t(partner))
461 end do
462
463
464 ! Now create the non-ghost interactions:
465 ! Here we only add systems to the parters list, which support the given interaction as a partner
466 ! Note that ensemble containers do not add their subsystems to the list.
467 ! (see ensemble_oct_m::ensemble_add_parters_to_list())
468
469 call partners%empty()
470
471 ! Get options to use to create all the interactions supported by this system
472 allocate(options(0)) ! Workaround to avoid a gfortran warning
473 options = interaction_factory%options(this%namespace, this%supported_interactions)
474
475 ! Loop over all interactions supported by this system and create them according to the provided options
476 do i = 1, size(this%supported_interactions)
477 interaction_type = this%supported_interactions(i)
478
479 ! Supported interactions should only appear once in the corresponding
480 ! list, otherwise more than one interaction of the same type will be
481 ! created
482 assert(count(this%supported_interactions == interaction_type) == 1)
483
484 ! Get the list of partners that support this interaction type
485 call iter%start(available_partners)
486 do while (iter%has_next())
487 partner => iter%get_next()
488 call partner%add_partners_to_list(partners, interaction_type)
489 end do
490
491 ! Create list of partners for this interaction taking into account the selected mode
492 select case (options(i)%mode)
493 case (all_partners)
494 ! Use all available partners, so no need to modify the list
495
496 case (no_partners)
497 ! No partners for this interaction, so empty the list
498 call partners%empty()
499
500 case (only_partners)
501 ! Start with full list and remove the partners not listed in the options
502 call iter%start(partners)
503 do while (iter%has_next())
504 partner => iter%get_next()
505 in_list = .false.
506 do ip = 1, size(options(i)%partners)
507 if (partner%namespace%is_contained_in(options(i)%partners(ip))) then
508 in_list = .true.
509 exit
510 end if
511 end do
512 if (.not. in_list) then
513 call partners%delete(partner)
514 end if
515 end do
516
517 case (all_except)
518 ! Start with full list and remove the partners listed in the options
519 do ip = 1, size(options(i)%partners)
520 call iter%start(partners)
521 do while (iter%has_next())
522 partner => iter%get_next()
523 if (partner%namespace%is_contained_in(options(i)%partners(ip))) then
524 call partners%delete(partner)
525 end if
526 end do
527 end do
528
529 end select
530
531 ! Now actually create the interactions for the selected partners
532 call iter%start(partners)
533 do while (iter%has_next())
534 partner => iter%get_next()
535
536 interaction => interaction_factory%create(interaction_type, partner)
537
538
539 ! Set flag for self-interaction
540 interaction%intra_interaction = partner%namespace%get() == this%namespace%get()
541
542 ! Set timing and perform sanity checks
543 interaction%timing = options(i)%timing
544 if (interaction%timing == timing_exact) then
545 select type (partner => interaction%partner)
546 class is (system_t)
547 if (this%algo%iteration%global_step() /= partner%algo%iteration%global_step() .and. &
548 .not. all(partner%quantities%always_available(interaction%couplings_from_partner))) then
549 write(message(1), '(2a)') "InteractionTiming was set to exact timing, but systems ", &
550 trim(this%namespace%get())
551 write(message(2), '(3a)') "and ", trim(partner%namespace%get()), " have incompatible steps."
552 call messages_fatal(2, namespace=this%namespace)
553 end if
554 end select
555 end if
556
557 ! Initialize interaction from system and from partner
558 call this%init_interaction(interaction)
559 call interaction%partner%init_interaction_as_partner(interaction)
560
561 ! Make sure all quantities required by the interaction are available in the system and partner
562 if (allocated(interaction%system_quantities)) then
563 do iq = 1, size(interaction%system_quantities)
564 if (.not. associated(this%quantities%get(interaction%system_quantities(iq)))) then
565 write(message(1), '(5a)') "Interaction '", trim(interaction%label), "' requires quantity '", &
566 trim(interaction%system_quantities(iq)), "'"
567 write(message(2), '(3a)') "from system '", trim(this%namespace%get()), "' but it is not available."
568 call messages_fatal(2, namespace=this%namespace)
569 end if
570 end do
571 end if
572 if (allocated(interaction%couplings_from_partner)) then
573 do iq = 1, size(interaction%couplings_from_partner)
574 if (.not. associated(partner%quantities%get(interaction%couplings_from_partner(iq)))) then
575 write(message(1), '(5a)') "Interaction '", trim(interaction%label), "' requires coupling '", &
576 trim(interaction%couplings_from_partner(iq)), "'"
577 write(message(2), '(3a)') "from partner '", trim(partner%namespace%get()), "' but it is not available."
578 call messages_fatal(2, namespace=this%namespace)
579 end if
580 end do
581 end if
582
583 ! Add interaction to list
584 call this%interactions%add(interaction)
585 end do
586
587 ! Empty partner list for next interaction
588 call partners%empty()
589 end do
590
592 end subroutine system_create_interactions
593
594 ! ---------------------------------------------------------
600 logical function system_update_couplings(this) result(all_updated)
601 class(system_t), intent(inout) :: this
602
603 class(interaction_t), pointer :: interaction
604 type(interaction_iterator_t) :: iter
605
607
608 all_updated = .true.
609 call iter%start(this%interactions)
610 do while (iter%has_next())
611 interaction => iter%get_next()
612
613 select type (partner => interaction%partner)
614 class is (system_t)
615 ! If the partner is a system, we can only update its couplings if it
616 ! is not too much behind the requested iteration
617 if (partner%algo%iteration + 1 >= this%algo%iteration) then
618 call interaction%update_partner_couplings(this%algo%iteration)
619 end if
620
621 class default
622 ! Partner that are not systems can have their couplings updated at any iteration
623 call interaction%update_partner_couplings(this%algo%iteration)
624 end select
625
626 all_updated = all_updated .and. interaction%partner_couplings_up_to_date
627 end do
628
630 end function system_update_couplings
631
632 ! ---------------------------------------------------------
637 subroutine system_update_interactions(this)
638 class(system_t), intent(inout) :: this
639
640 integer :: iq, n_quantities
641 class(interaction_t), pointer :: interaction
642 type(interaction_iterator_t) :: iter
643 class(quantity_t), pointer :: quantity
644
646
647 ! Some systems might need to perform some specific operations before the
648 ! update.
649 call this%update_interactions_start()
650
651 !Loop over all interactions
652 call iter%start(this%interactions)
653 do while (iter%has_next())
654 interaction => iter%get_next()
655
656 ! Update the system quantities that will be needed for computing the interaction
657 if (allocated(interaction%system_quantities)) then
658 n_quantities = size(interaction%system_quantities)
659 else
660 n_quantities = 0
661 end if
662 do iq = 1, n_quantities
663 ! Get requested quantity
664 quantity => this%quantities%get(interaction%system_quantities(iq))
665
666 if (.not. quantity%iteration == this%algo%iteration) then
667 ! The requested quantity is not at the requested iteration
668
669 ! Sanity check: it should never happen that the quantity is in advance
670 ! with respect to the requested iteration.
671 if (quantity%iteration > this%algo%iteration) then
672 message(1) = "The quantity "//trim(quantity%label)//" is in advance compared to the requested iteration."
673 message(2) = "The interaction is "//trim(interaction%label)//"."
674 call messages_fatal(2, namespace=this%namespace)
675 end if
676
677 ! If the quantity can be updated on demand, we try to update it
678 if (quantity%updated_on_demand) then
679 call this%update_on_demand_quantity(quantity, this%algo%iteration)
680 end if
681
682 ! Quantity must be at the correct iteration, otherwise the algorithm and
683 ! the interaction are incompatible
684 if (.not. quantity%iteration == this%algo%iteration .and. .not. quantity%always_available) then
685 write(message(1), '(5a)') "Interaction ", trim(interaction%label), " is incompatible with the selected algorithm."
686 write(message(2), '(3a)') "The interaction requires the ", trim(quantity%label), &
687 " at an iteration it is not available."
688 call messages_fatal(2, namespace=this%namespace)
689 end if
690
691 end if
692
693 end do
694
695 call interaction%update(this%algo%iteration)
696 end do
697
698 ! Some systems might need to perform some specific operations after all the
699 ! interactions have been updated
700 call this%update_interactions_finish()
701
703 end subroutine system_update_interactions
704
705 ! ---------------------------------------------------------
706 subroutine system_update_interactions_start(this)
707 class(system_t), intent(inout) :: this
708
710
711 ! By default nothing is done just before updating the interactions. Child
712 ! classes that wish to change this behaviour should override this method.
713
716
717 ! ---------------------------------------------------------
718 subroutine system_update_interactions_finish(this)
719 class(system_t), intent(inout) :: this
720
722
723 ! By default nothing is done just after updating the interactions. Child
724 ! classes that wish to change this behaviour should override this method.
725
728
729 ! ---------------------------------------------------------
730 subroutine system_restart_write(this)
731 class(system_t), intent(inout) :: this
733 logical :: restart_write
734 type(interaction_iterator_t) :: iter
735 class(interaction_t), pointer :: interaction
736 type(quantity_iterator_t) :: qiter
737 class(quantity_t), pointer :: quantity
738
739
740 push_sub(system_restart_write)
741
742 call parse_variable(this%namespace, 'RestartWrite', .true., restart_write)
743
744 if (restart_write) then
745 ! do some generic restart steps here
746 ! write iteration counter restart data
747 call this%iteration%restart_write('restart_iteration_system', this%namespace)
748 call this%algo%iteration%restart_write('restart_iteration_algorithm', this%namespace)
749 call iter%start(this%interactions)
750 do while (iter%has_next())
751 interaction => iter%get_next()
752 call interaction%restart_write(this%namespace)
753 end do
754 call qiter%start(this%quantities)
755 do while (qiter%has_next())
756 quantity => qiter%get_next()
757 call quantity%iteration%restart_write('restart_iteration_quantity_'//trim(quantity%label), &
758 this%namespace)
759 end do
760 ! the following call is delegated to the corresponding system
761 call this%restart_write_data()
762 message(1) = "Wrote restart data for system "//trim(this%namespace%get())
763 call messages_info(1, namespace=this%namespace)
764 end if
765
766 pop_sub(system_restart_write)
767 end subroutine system_restart_write
768
769 ! ---------------------------------------------------------
770 ! this function returns true if restart data could be read
771 logical function system_restart_read(this)
772 class(system_t), intent(inout) :: this
773
774 type(interaction_iterator_t) :: iter
775 class(interaction_t), pointer :: interaction
776 type(quantity_iterator_t) :: qiter
777 class(quantity_t), pointer :: quantity
778
779 push_sub(system_restart_read)
780
781 ! do some generic restart steps here
782 ! read iteration data
783 system_restart_read = this%iteration%restart_read('restart_iteration_system', this%namespace)
785 this%algo%iteration%restart_read('restart_iteration_algorithm', this%namespace)
786 call iter%start(this%interactions)
787 do while (iter%has_next())
788 interaction => iter%get_next()
789 system_restart_read = system_restart_read .and. interaction%restart_read(this%namespace)
790 ! reduce by one because of the first UPDATE_INTERACTIONS
791 interaction%iteration = interaction%iteration - 1
792 end do
793 call qiter%start(this%quantities)
794 do while (qiter%has_next())
795 quantity => qiter%get_next()
797 quantity%iteration%restart_read('restart_iteration_quantity_'//trim(quantity%label), &
798 this%namespace)
799 if (quantity%updated_on_demand) then
800 ! decrease iteration by one for on-demand quantities to account for initial update_interactions step
801 quantity%iteration = quantity%iteration - 1
802 end if
803 end do
804 ! the following call is delegated to the corresponding system
805 system_restart_read = system_restart_read .and. this%restart_read_data()
806
807 if (system_restart_read) then
808 message(1) = "Successfully read restart data for system "//trim(this%namespace%get())
809 call messages_info(1, namespace=this%namespace)
810 end if
811
812 pop_sub(system_restart_read)
814
815 ! ---------------------------------------------------------
816 subroutine system_output_start(this)
817 class(system_t), intent(inout) :: this
818
819 push_sub(system_output_start)
820
821 ! By default nothing is done to regarding output. Child classes that wish
822 ! to change this behaviour should override this method.
823
824 pop_sub(system_output_start)
825 end subroutine system_output_start
826
827 ! ---------------------------------------------------------
828 subroutine system_output_write(this)
829 class(system_t), intent(inout) :: this
830
831 push_sub(system_output_write)
832
833 ! By default nothing is done to regarding output. Child classes that wish
834 ! to change this behaviour should override this method.
835
836 pop_sub(system_output_write)
837 end subroutine system_output_write
838
839 ! ---------------------------------------------------------
840 subroutine system_output_finish(this)
841 class(system_t), intent(inout) :: this
842
843 push_sub(system_output_finish)
844
845 ! By default nothing is done to regarding output. Child classes that wish
846 ! to change this behaviour should override this method.
847
848 pop_sub(system_output_finish)
849 end subroutine system_output_finish
850
851 ! ---------------------------------------------------------
852 subroutine system_new_algorithm(this, factory)
853 class(system_t), intent(inout) :: this
854 class(algorithm_factory_t), intent(in) :: factory
855
856 integer :: ii
857
858 push_sub(system_new_algorithm)
859
860 call messages_experimental('Multi-system framework')
861
862 this%algo => factory%create(this)
863
864 call this%init_iteration_counters()
865
866 do ii = 1, number_barriers
867 this%barrier(ii)%active = .false.
868 this%barrier(ii)%target_time = m_zero
869 end do
870
871 pop_sub(system_new_algorithm)
872 end subroutine system_new_algorithm
873
874 ! ---------------------------------------------------------------------------------------
875 recursive function system_algorithm_finished(this) result(finished)
876 class(system_t), intent(in) :: this
877 logical :: finished
878
879 finished = this%algo%finished()
880
881 end function system_algorithm_finished
882
883 ! ---------------------------------------------------------
889 !
890 subroutine system_init_iteration_counters(this)
891 class(system_t), intent(inout) :: this
892
893 type(interaction_iterator_t) :: iter
894 class(interaction_t), pointer :: interaction
895 type(quantity_iterator_t) :: qiter
896 class(quantity_t), pointer :: quantity
897
899
900 ! Initialize algorithm and system counters
901 call this%algo%init_iteration_counters()
902
903 ! Interactions counters
904 call iter%start(this%interactions)
905 do while (iter%has_next())
906 interaction => iter%get_next()
907 interaction%iteration = this%algo%iteration - 1
908 end do
909
910 ! Quantities counters
911 call qiter%start(this%quantities)
912 do while (qiter%has_next())
913 quantity => qiter%get_next()
914 if (quantity%updated_on_demand) then
915 ! On demand quantities will be updated before first use,
916 ! hence we set the iteration counter one iteration behind the algorithms iteration counter
917 quantity%iteration = this%algo%iteration - 1
918 else
919 quantity%iteration = this%algo%iteration
920 end if
921 end do
922
924 end subroutine system_init_iteration_counters
925
926 ! ---------------------------------------------------------
927 subroutine system_algorithm_start(this)
928 class(system_t), intent(inout) :: this
929
930 logical :: all_updated
931 type(event_handle_t) :: debug_handle
932 character(len=:), allocatable :: updated_quantities(:)
933
934 push_sub(system_algorithm_start)
936 debug_handle = multisystem_debug_write_event_in(this%namespace, event_function_call_t("system_algorithm_start"), &
937 system_iteration = this%iteration, algo_iteration = this%algo%iteration)
938
939 ! Update interactions at initial iteration
940 all_updated = this%update_couplings()
941 if (.not. all_updated) then
942 message(1) = "Unable to update interactions when initializing the algorithm."
943 call messages_fatal(1, namespace=this%namespace)
944 end if
945 call this%update_interactions()
946
947 ! System-specific and algorithm-specific initialization step
948 if (this%algo%start_operation%id /= skip) then
949 if (.not. this%do_algorithmic_operation(this%algo%start_operation, updated_quantities)) then
950 message(1) = "Unsupported algorithmic operation."
951 write(message(2), '(A,A,A)') trim(this%algo%start_operation%id), ": ", trim(this%algo%start_operation%label)
952 call messages_fatal(2, namespace=this%namespace)
953 end if
954 if (allocated(updated_quantities)) then
955 message(1) = "Update of quantities not allowed in algorithmic operation."
956 write(message(2), '(A,A,A)') trim(this%algo%start_operation%id), ": ", trim(this%algo%start_operation%label)
957 call messages_fatal(2, namespace=this%namespace)
958 end if
959 end if
960
961 ! Compute the total energy at the beginning of the simulation
962 call this%update_total_energy()
963
964 ! Start output
965 call this%output_start()
966
967 ! Write header for execution log
968 call this%algo%write_output_header()
969
970 ! Rewind algorithm (will also set the initial time to compute the elapsed time)
971 call this%algo%rewind()
972
973 call multisystem_debug_write_event_out(debug_handle, system_iteration = this%iteration, algo_iteration = this%algo%iteration)
974
976 end subroutine system_algorithm_start
977
978 ! ---------------------------------------------------------
979 subroutine system_algorithm_finish(this)
980 class(system_t), intent(inout) :: this
981 type(event_handle_t) :: debug_handle
982
983 character(len=:), allocatable :: updated_quantities(:)
984
986
987 debug_handle = multisystem_debug_write_event_in(this%namespace, event_function_call_t("system_algorithm_finish"), &
988 system_iteration = this%iteration, algo_iteration = this%algo%iteration)
989
990 ! Finish output
991 call this%output_finish()
992
993 ! System-specific and algorithm-specific finalization step
994 if (this%algo%final_operation%id /= skip) then
995 if (.not. this%do_algorithmic_operation(this%algo%final_operation, updated_quantities)) then
996 message(1) = "Unsupported algorithmic operation."
997 write(message(2), '(A,A,A)') trim(this%algo%final_operation%id), ": ", trim(this%algo%final_operation%label)
998 call messages_fatal(2, namespace=this%namespace)
999 end if
1000 if (allocated(updated_quantities)) then
1001 message(1) = "Update of quantities not allowed in algorithmic operation."
1002 write(message(2), '(A,A,A)') trim(this%algo%final_operation%id), ": ", trim(this%algo%final_operation%label)
1003 call messages_fatal(2, namespace=this%namespace)
1004 end if
1005 end if
1006
1007 call this%algo%print_speed()
1008
1009 call multisystem_debug_write_event_out(debug_handle, system_iteration = this%iteration, algo_iteration = this%algo%iteration)
1010
1012 end subroutine system_algorithm_finish
1013
1014 ! ---------------------------------------------------------
1015 subroutine system_iteration_info(this)
1016 class(system_t), intent(in) :: this
1017
1018 real(real64) :: energy
1019 character(len=40) :: fmt
1020
1021 push_sub(system_iteration_info)
1023 energy = units_from_atomic(units_out%energy, this%total_energy)
1024 if (abs(energy) >= 1e5) then
1025 fmt = '(i7,1x,f14.6,1X,es13.6,1X,i9,1X,'
1026 else
1027 fmt = '(i7,1x,f14.6,1X,f13.6,1X,i9,1X,'
1028 end if
1029 if (this%algo%elapsed_time < 1e-3) then
1030 fmt = trim(fmt)//'es13.3)'
1031 else
1032 fmt = trim(fmt)//'f13.3)'
1033 end if
1034
1035 write(message(1), fmt) this%iteration%counter(), &
1036 units_from_atomic(units_out%time, this%iteration%value()), energy, &
1037 0, this%algo%elapsed_time
1038 call messages_info(1, namespace=this%namespace)
1039
1040 pop_sub(system_iteration_info)
1041 end subroutine system_iteration_info
1042
1043 ! ---------------------------------------------------------
1044 logical function system_process_is_slave(this)
1045 class(system_t), intent(in) :: this
1046
1047 push_sub(system_process_is_slave)
1048
1049 ! By default an MPI process is not a slave
1050 system_process_is_slave = .false.
1051
1053 end function system_process_is_slave
1054
1055 ! ---------------------------------------------------------
1056 subroutine system_end(this)
1057 class(system_t), intent(inout) :: this
1058
1059 type(interaction_iterator_t) :: iter
1060 class(interaction_t), pointer :: interaction
1061
1062 push_sub(system_end)
1063
1064 ! No call to safe_deallocate macro here, as it gives an ICE with gfortran
1065 if (associated(this%algo)) then
1066 deallocate(this%algo)
1067 end if
1068
1069 call iter%start(this%interactions)
1070 do while (iter%has_next())
1071 interaction => iter%get_next()
1072 if (associated(interaction)) then
1073 deallocate(interaction)
1074 end if
1075 end do
1076
1077 pop_sub(system_end)
1078 end subroutine system_end
1079
1080 ! ---------------------------------------------------------
1082 subroutine system_list_add_node(this, partner)
1083 class(system_list_t) :: this
1084 class(interaction_partner_t), target :: partner
1085
1086 push_sub(system_list_add_node)
1087
1088 select type (partner)
1089 class is (system_t)
1090 call this%add_ptr(partner)
1091 class default
1092 assert(.false.)
1093 end select
1094
1095 pop_sub(system_list_add_node)
1096 end subroutine system_list_add_node
1097
1098 ! ---------------------------------------------------------
1099 recursive logical function system_list_contains(this, partner) result(contains)
1100 class(system_list_t) :: this
1101 class(interaction_partner_t), target :: partner
1102
1103 type(partner_iterator_t) :: iterator
1104 class(interaction_partner_t), pointer :: system
1105
1106 push_sub(system_list_contains)
1107
1108 contains = .false.
1109
1110 select type (partner)
1111 class is (system_t)
1112
1113 call iterator%start(this)
1114 do while (iterator%has_next() .and. .not. contains)
1115 system => iterator%get_next()
1116 contains = associated(system, partner)
1117 end do
1118
1119 class default
1120 contains = .false.
1121 end select
1122
1123 pop_sub(system_list_contains)
1124 end function system_list_contains
1125
1126 ! ---------------------------------------------------------
1127 function system_iterator_get_next(this) result(system)
1128 class(system_iterator_t), intent(inout) :: this
1129 class(system_t), pointer :: system
1130
1131 push_sub(system_iterator_get_next)
1132
1133 select type (ptr => this%get_next_ptr())
1134 class is (system_t)
1135 system => ptr
1136 class default
1137 assert(.false.)
1138 end select
1141 end function system_iterator_get_next
1142
1143 ! ---------------------------------------------------------
1147 subroutine system_init_parallelization(this, grp)
1148 class(system_t), intent(inout) :: this
1149 type(mpi_grp_t), intent(in) :: grp
1150
1152
1153 call mpi_grp_copy(this%grp, grp)
1154 call messages_update_mpi_grp(this%namespace, grp)
1155
1157 end subroutine system_init_parallelization
1158
1159
1160
1161 ! ---------------------------------------------------------
1162 subroutine system_start_barrier(this, target_time, barrier_index)
1163 class(system_t), intent(inout) :: this
1164 real(real64), intent(in) :: target_time
1165 integer, intent(in) :: barrier_index
1166
1167 push_sub(system_start_barrier)
1168
1169 this%barrier(barrier_index)%active = .true.
1170 this%barrier(barrier_index)%target_time = target_time
1171
1172 pop_sub(system_start_barrier)
1173 end subroutine system_start_barrier
1174
1175 ! ---------------------------------------------------------
1176 subroutine system_end_barrier(this, barrier_index)
1177 class(system_t), intent(inout) :: this
1178 integer, intent(in) :: barrier_index
1179
1180 push_sub(system_end_barrier)
1181
1182 this%barrier(barrier_index)%active = .false.
1183 this%barrier(barrier_index)%target_time = m_zero
1184
1185 pop_sub(system_end_barrier)
1186 end subroutine system_end_barrier
1187
1188 ! ---------------------------------------------------------
1189 logical function system_arrived_at_barrier(this, barrier_index)
1190 class(system_t), intent(inout) :: this
1191 integer, intent(in) :: barrier_index
1192
1193 type(iteration_counter_t) :: iteration
1196
1198 if (this%barrier(barrier_index)%active) then
1199 iteration = this%iteration + 1
1200 if (iteration%value() > this%barrier(barrier_index)%target_time) then
1202 end if
1203 end if
1204
1206 end function system_arrived_at_barrier
1207
1208 ! ---------------------------------------------------------
1209 logical function system_arrived_at_any_barrier(this)
1210 class(system_t), intent(inout) :: this
1211
1212 integer :: ii
1213
1215
1217 do ii = 1, number_barriers
1219 .or. this%arrived_at_barrier(ii)
1220 end do
1221
1224
1225
1226 ! ---------------------------------------------------------
1231 subroutine system_update_potential_energy(this)
1232 class(system_t), intent(inout) :: this
1233
1234 type(interaction_iterator_t) :: iter
1235 class(interaction_t), pointer :: interaction
1236
1238
1239 this%potential_energy = m_zero
1240
1241 call iter%start(this%interactions)
1242 do while (iter%has_next())
1243 interaction => iter%get_next()
1244 if(.not. interaction%intra_interaction) then
1245 call interaction%calculate_energy()
1246 this%potential_energy = this%potential_energy + interaction%energy
1247 end if
1248 end do
1249
1251 end subroutine system_update_potential_energy
1252
1253 ! ---------------------------------------------------------
1258 subroutine system_update_internal_energy(this)
1259 class(system_t), intent(inout) :: this
1260
1261 type(interaction_iterator_t) :: iter
1262 class(interaction_t), pointer :: interaction
1263
1265
1266 this%internal_energy = m_zero
1267 call iter%start(this%interactions)
1268 do while (iter%has_next())
1269 interaction => iter%get_next()
1270 if(interaction%intra_interaction) then
1271 call interaction%calculate_energy()
1272 this%internal_energy = this%internal_energy + interaction%energy
1273 end if
1274 end do
1275
1277 end subroutine system_update_internal_energy
1278
1279 ! ---------------------------------------------------------
1283 subroutine system_update_total_energy(this)
1284 class(system_t), intent(inout) :: this
1285
1287
1288 call this%update_kinetic_energy()
1289 this%total_energy = this%kinetic_energy
1290
1292 call this%update_potential_energy()
1293 this%total_energy = this%total_energy + this%potential_energy
1294
1296 call this%update_internal_energy()
1297 this%total_energy = this%total_energy + this%internal_energy
1298
1300 end subroutine system_update_total_energy
1301
1302end module system_oct_m
1303
1304!! Local Variables:
1305!! mode: f90
1306!! coding: utf-8
1307!! End:
Execute one operation that is part of a larger algorithm. Returns true if the operation was successfu...
Definition: system.F90:261
initialize a given interaction of the system
Definition: system.F90:237
set initial conditions for a system
Definition: system.F90:246
check whether a system has reached a given tolerance
Definition: system.F90:271
For some algorithms it might be necessary to store the status of a system at a given algorithmic step...
Definition: system.F90:282
This module defines the abstract interfact for algorithm factories.
This module implements the basic elements defining algorithms.
Definition: algorithm.F90:143
character(len=algo_label_len), parameter, public update_interactions
Definition: algorithm.F90:174
character(len=algo_label_len), parameter, public rewind_algorithm
Definition: algorithm.F90:174
character(len=algo_label_len), parameter, public update_couplings
Definition: algorithm.F90:174
character(len=algo_label_len), parameter, public iteration_done
Definition: algorithm.F90:174
character(len=algo_label_len), parameter, public skip
Operations that can be used by any algorithm and, therefore, should be implemented by all systems.
Definition: algorithm.F90:174
real(real64), parameter, public m_zero
Definition: global.F90:200
This module defines the abstract interaction_t class, and some auxiliary classes for interactions.
integer, parameter, public timing_exact
This module defines classes and functions for interaction partners.
This module defines the abstract class for the interaction factory.
integer, parameter, public only_partners
This module implements fully polymorphic linked lists, and some specializations thereof.
subroutine, public messages_update_mpi_grp(namespace, mpigrp)
Definition: messages.F90:367
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1040
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
subroutine mpi_grp_copy(mpi_grp_out, mpi_grp_in)
MPI_THREAD_FUNNELED allows for calls to MPI from an OMP region if the thread is the team master.
Definition: mpi.F90:383
This module implements the multisystem debug functionality.
subroutine, public multisystem_debug_write_marker(system_namespace, event)
type(event_handle_t) function, public multisystem_debug_write_event_in(system_namespace, event, extra, system_iteration, algo_iteration, interaction_iteration, partner_iteration, requested_iteration)
subroutine, public multisystem_debug_write_event_out(handle, extra, update, system_iteration, algo_iteration, interaction_iteration, partner_iteration, requested_iteration)
This module defines the quantity_t class and the IDs for quantities, which can be exposed by a system...
Definition: quantity.F90:140
This module implements the abstract system type.
Definition: system.F90:120
subroutine, public system_algorithm_start(this)
Definition: system.F90:1023
subroutine, public system_update_total_energy(this)
Calculate the total energy of the system. The total energy is defined as the sum of the kinetic,...
Definition: system.F90:1379
subroutine system_iteration_info(this)
Definition: system.F90:1111
subroutine, public system_init_iteration_counters(this)
Initialize the iteration counters of the system and its interactions, algorithms and quantities.
Definition: system.F90:986
integer, parameter, public barrier_restart
Definition: system.F90:168
subroutine system_update_internal_energy(this)
Calculate the internal energy of the system. The internal energy is defined as the sum of all energie...
Definition: system.F90:1354
logical function system_arrived_at_any_barrier(this)
Definition: system.F90:1305
recursive logical function system_list_contains(this, partner)
Definition: system.F90:1195
subroutine, public system_restart_write(this)
Definition: system.F90:826
subroutine, public system_update_potential_energy(this)
Calculate the potential energy of the system. The potential energy is defined as the sum of all energ...
Definition: system.F90:1327
subroutine, public system_init_parallelization(this, grp)
Basic functionality: copy the MPI group. This function needs to be implemented by extended types that...
Definition: system.F90:1243
subroutine system_output_start(this)
Definition: system.F90:912
subroutine system_update_interactions_start(this)
Definition: system.F90:802
subroutine, public system_algorithm_finish(this)
Definition: system.F90:1075
subroutine system_start_barrier(this, target_time, barrier_index)
Definition: system.F90:1258
recursive subroutine, public system_create_interactions(this, interaction_factory, available_partners)
create the interactions of the system
Definition: system.F90:519
subroutine, public system_end(this)
Definition: system.F90:1152
subroutine system_output_write(this)
Definition: system.F90:924
subroutine system_update_interactions_finish(this)
Definition: system.F90:814
subroutine, public system_execute_algorithm(this)
perform one or more algorithmic operations
Definition: system.F90:339
subroutine system_update_interactions(this)
Attempt to update all interactions of the system.
Definition: system.F90:733
class(system_t) function, pointer system_iterator_get_next(this)
Definition: system.F90:1223
logical function system_update_couplings(this)
Update the couplings (quantities) of the interaction partners.
Definition: system.F90:696
logical function system_process_is_slave(this)
Definition: system.F90:1140
logical function system_arrived_at_barrier(this, barrier_index)
Definition: system.F90:1285
recursive logical function system_algorithm_finished(this)
Definition: system.F90:971
logical function, public system_restart_read(this)
Definition: system.F90:867
subroutine, public system_new_algorithm(this, factory)
Definition: system.F90:948
subroutine system_output_finish(this)
Definition: system.F90:936
subroutine system_list_add_node(this, partner)
add system to list
Definition: system.F90:1178
subroutine, public system_reset_iteration_counters(this, accumulated_iterations)
Definition: system.F90:465
subroutine system_end_barrier(this, barrier_index)
Definition: system.F90:1272
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:134
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
Abstract class for the algorithm factories.
Descriptor of one algorithmic operation.
Definition: algorithm.F90:165
The ghost ineraction is a dummy interaction, which needs to be setup between otherwise non-interactin...
These class extend the list and list iterator to make an interaction list.
abstract interaction class
abstract class for general interaction partners
type for storing options to be used when creating a given interaction
This class implements the iteration counter used by the multisystem algorithms. As any iteration coun...
This class implements an iterator for the polymorphic linked list.
This is defined even when running serial.
Definition: mpi.F90:144
handle to keep track of in- out- events
Systems (system_t) can expose quantities that can be used to calculate interactions with other system...
Definition: quantity.F90:173
These classes extends the list and list iterator to create a system list.
Definition: system.F90:310
Abstract class for systems.
Definition: system.F90:175
int true(void)