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