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 clock_oct_m
29 use debug_oct_m
31 use global_oct_m
36 use mpi_oct_m
40 use parser_oct_m
43 use space_oct_m
44 use unit_oct_m
47 implicit none
48
49 private
50 public :: &
51 system_t, &
63 system_end, &
66
67 type :: barrier_t
68 logical :: active
69 float :: 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 class(algorithm_t), pointer, public :: algo => null()
82
83 integer, public :: accumulated_loop_ticks
84
85 integer, public :: interaction_timing
87
88 type(integer_list_t), public :: supported_interactions
89 type(interaction_list_t), public :: interactions
90
91 type(mpi_grp_t), public :: grp
92
93 type(barrier_t) :: barrier(NUMBER_BARRIERS)
94 float, public :: kinetic_energy
95 float, public :: potential_energy
96 float, public :: internal_energy
97 float, public :: total_energy
98
99 contains
100 procedure :: execute_algorithm => system_execute_algorithm
101 procedure :: reset_clocks => system_reset_clocks
102 procedure :: update_exposed_quantities => system_update_exposed_quantities
103 procedure :: init_algorithm => system_init_algorithm
104 procedure :: algorithm_finished => system_algorithm_finished
105 procedure :: init_clocks => system_init_clocks
106 procedure :: init_all_interactions => system_init_all_interactions
107 procedure :: init_parallelization => system_init_parallelization
108 procedure :: update_interactions => system_update_interactions
109 procedure :: update_interactions_start => system_update_interactions_start
110 procedure :: update_interactions_finish => system_update_interactions_finish
111 procedure :: propagation_start => system_propagation_start
112 procedure :: propagation_finish => system_propagation_finish
113 procedure :: iteration_info => system_iteration_info
114 procedure :: restart_write => system_restart_write
115 procedure :: restart_read => system_restart_read
116 procedure :: output_start => system_output_start
117 procedure :: output_write => system_output_write
118 procedure :: output_finish => system_output_finish
119 procedure :: process_is_slave => system_process_is_slave
120 procedure :: start_barrier => system_start_barrier
121 procedure :: end_barrier => system_end_barrier
122 procedure :: arrived_at_barrier => system_arrived_at_barrier
123 procedure :: arrived_at_any_barrier => system_arrived_at_any_barrier
124 procedure :: update_potential_energy => system_update_potential_energy
125 procedure :: update_internal_energy => system_update_internal_energy
126 procedure :: update_total_energy => system_update_total_energy
127 procedure(system_init_interaction), deferred :: init_interaction
128 procedure(system_initial_conditions), deferred :: initial_conditions
129 procedure(system_do_algorithmic_operation), deferred :: do_algorithmic_operation
130 procedure(system_is_tolerance_reached), deferred :: is_tolerance_reached
131 procedure(system_update_quantity), deferred :: update_quantity
132 procedure(system_update_exposed_quantity), deferred :: update_exposed_quantity
133 procedure(system_restart_write_data), deferred :: restart_write_data
134 procedure(system_restart_read_data), deferred :: restart_read_data
135 procedure(system_update_kinetic_energy), deferred :: update_kinetic_energy
136 end type system_t
137
138 abstract interface
139
140 ! ---------------------------------------------------------
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_initial_conditions(this)
152 import system_t
153 class(system_t), intent(inout) :: this
155
156 ! ---------------------------------------------------------
163 logical function system_do_algorithmic_operation(this, operation, updated_quantities) result(done)
164 import system_t
166 class(system_t), intent(inout) :: this
167 class(algorithmic_operation_t), intent(in) :: operation
168 integer, allocatable, intent(out) :: updated_quantities(:)
171 ! ---------------------------------------------------------
173 logical function system_is_tolerance_reached(this, tol)
174 import system_t
175 class(system_t), intent(in) :: this
176 float, intent(in) :: tol
177 end function system_is_tolerance_reached
179 ! ---------------------------------------------------------
181 import system_t
182 class(system_t), intent(inout) :: this
183 end subroutine system_store_current_status
184
185 ! ---------------------------------------------------------
186 subroutine system_update_quantity(this, iq)
187 import system_t
188 import clock_t
189 class(system_t), intent(inout) :: this
190 integer, intent(in) :: iq
193 ! ---------------------------------------------------------
194 subroutine system_update_exposed_quantity(partner, iq)
195 import system_t
196 import clock_t
197 class(system_t), intent(inout) :: partner
198 integer, intent(in) :: iq
201 ! ---------------------------------------------------------
203 import system_t
204 class(system_t), intent(inout) :: this
207 ! ---------------------------------------------------------
208 ! this function returns true if restart data could be read
209 logical function system_restart_read_data(this)
210 import system_t
211 class(system_t), intent(inout) :: this
214 import system_t
215 class(system_t), intent(inout) :: this
218 end interface
224 type, extends(partner_list_t) :: system_list_t
225 private
226 contains
227 procedure :: add => system_list_add_node
228 procedure :: contains => system_list_contains
229 end type system_list_t
230
232 private
233 contains
234 procedure :: get_next => system_iterator_get_next
235 end type system_iterator_t
237contains
238
239 ! ---------------------------------------------------------
252 subroutine system_execute_algorithm(this)
253 class(system_t), intent(inout) :: this
254
255 type(algorithmic_operation_t) :: operation
256 logical :: all_updated, at_barrier, operation_done
257 type(event_handle_t) :: debug_handle
258 integer :: iq, iuq
259 integer, allocatable :: updated_quantities(:)
260
262
263 at_barrier = .false.
264
265 do while (.not. at_barrier)
266
267 operation = this%algo%get_current_operation()
268
269 debug_handle = multisystem_debug_write_event_in(this%namespace, event_function_call_t("dt_operation", operation), &
270 system_clock=this%clock, prop_clock=this%algo%clock)
272 ! First try to execute the operation as a system specific operation
273 operation_done = this%do_algorithmic_operation(operation, updated_quantities)
274 if (allocated(updated_quantities)) then
275 ! Update the quantities clock
276 do iuq = 1, size(updated_quantities)
277 iq = updated_quantities(iuq)
278 if (.not. this%algo%inside_scf) then
279 this%quantities(iq)%clock = this%quantities(iq)%clock + clock_tick
280 else
281 ! We set it to the propagation time to avoid double increment
282 call this%quantities(iq)%clock%set_time(this%algo%clock)
283 end if
284
285 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("quantity", quantity_label(iq), &
286 this%quantities(iq)%clock, "set"))
287 end do
288 end if
289
290 ! If not done, we try to execute it as an algorithm-specific operation.
291 if (.not. operation_done) then
292 operation_done = this%algo%do_operation(operation)
293 else
294 call this%algo%next()
295 end if
296
297 ! If still not done, the operation must be a generic operation
298 if (.not. operation_done) then
299
300 select case (operation%id)
301 case (skip)
302 ! Do nothing
303 call this%algo%next()
304
305 case (step_done)
306 ! Increment the system clock by one time-step
307 this%clock = this%clock + clock_tick
308 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("system", "", this%clock, "tick"))
310 ! Recompute the total energy
311 call this%update_total_energy()
313 ! Write output
314 call this%output_write()
315
316 ! Update elapsed time
317 call this%algo%update_elapsed_time()
318
319 ! Print information about the current iteration
320 ! (NB: needs to be done after marking the propagation step as finished,
321 ! so that the timings are correct)
322 call this%iteration_info()
323
324 call this%algo%next()
325
326 case (rewind_algorithm)
327 if (.not. this%arrived_at_any_barrier() .and. .not. this%algorithm_finished()) then
328 ! Reset propagator for next step if not waiting at barrier and if
329 ! the algorithm is not finished
330 call this%algo%rewind()
331 else
332 at_barrier = .true.
333 end if
334
336 ! We increment by one algorithmic step
337 this%algo%clock = this%algo%clock + clock_tick
338 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("propagator", "", this%algo%clock, "tick"))
339
340 ! Try to update all the interactions
341 all_updated = this%update_interactions()
342
343 ! Move to next algorithm step if all interactions have been
344 ! updated. Otherwise try again later.
345 if (all_updated) then
346 this%accumulated_loop_ticks = this%accumulated_loop_ticks + 1
347 call this%algo%next()
348 else
349 this%algo%clock = this%algo%clock - clock_tick
350 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("propagator", "", this%algo%clock, "reverse"))
351 end if
352
353 ! Interactions are implicit barriers
354 at_barrier = .true.
355
356 case default
357 message(1) = "Unsupported algorithmic operation."
358 write(message(2), '(A,A,A)') trim(operation%id), ": ", trim(operation%label)
359 call messages_fatal(2, namespace=this%namespace)
360 end select
361 end if
362
363 call multisystem_debug_write_event_out(debug_handle, system_clock=this%clock, prop_clock=this%algo%clock)
364 end do
365
367 end subroutine system_execute_algorithm
368
369 ! ---------------------------------------------------------
370 subroutine system_reset_clocks(this, accumulated_ticks)
371 class(system_t), intent(inout) :: this
372 integer, intent(in) :: accumulated_ticks
373
374 integer :: iq
375 type(interaction_iterator_t) :: iter
376 class(interaction_t), pointer :: interaction
377
378 character(len=MAX_INFO_LEN) :: extended_label
379
381
382 ! Propagator clock
383 this%algo%clock = this%algo%clock - accumulated_ticks*clock_tick
384 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("propagator", "", this%algo%clock, "reset"))
385
386 ! Interaction clocks
387 call iter%start(this%interactions)
388 do while (iter%has_next())
389 interaction => iter%get_next()
390 interaction%clock = interaction%clock - accumulated_ticks*clock_tick
391
392 select type (interaction)
394 extended_label = trim(interaction%label)//"-"//trim(interaction%partner%namespace%get())
395 class default
396 extended_label = trim(interaction%label)
397 end select
398 call multisystem_debug_write_marker(this%namespace, event_clock_update_t( "interaction", extended_label, &
399 interaction%clock, "reset"))
400 end do
401
402 ! Internal quantities clocks
403 do iq = 1, max_quantities
404 if (this%quantities(iq)%required) then
405 this%quantities(iq)%clock = this%quantities(iq)%clock - accumulated_ticks*clock_tick
406 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("quantity", quantity_label(iq), &
407 this%quantities(iq)%clock, "reset"))
408 end if
409 end do
410
412 end subroutine system_reset_clocks
413
414 ! ---------------------------------------------------------
418 logical function system_update_exposed_quantities(partner, requested_time, interaction) result(allowed_to_update)
419 class(system_t), intent(inout) :: partner
420 type(clock_t), intent(in) :: requested_time
421 class(interaction_t), intent(inout) :: interaction
422
423 logical :: ahead_in_time, right_on_time, need_to_copy
424 integer :: iq, q_id
425
426 type(event_handle_t) :: debug_handle
427
429
430 debug_handle = multisystem_debug_write_event_in(system_namespace = partner%namespace, &
431 event = event_function_call_t("system_update_exposed_quantities"), &
432 partner_clock = partner%clock, &
433 requested_clock = requested_time, &
434 interaction_clock = interaction%clock)
435
436 select type (interaction)
438
439 if (partner%algo%inside_scf .or. partner%algo%clock + clock_tick < requested_time) then
440 ! we are inside an SCF cycle and therefore are not allowed to expose any quantities.
441 ! or we are too much behind the requested time
442 allowed_to_update = .false.
443 else
444 allowed_to_update = .true.
445 need_to_copy = .true.
446 do iq = 1, size(interaction%partner_quantities)
447 ! Get the requested quantity ID
448 q_id = interaction%partner_quantities(iq)
449
450 ! All needed quantities must have been marked as required. If not, then fix your code!
451 assert(partner%quantities(q_id)%required)
452
453 ! First update the exposed quantities that are updated on demand
454 if (partner%quantities(q_id)%updated_on_demand) then
455 if(partner%quantities(q_id)%clock < requested_time .and. &
456 (partner%quantities(q_id)%clock + clock_tick <= requested_time .or. &
457 (partner%interaction_timing == option__interactiontiming__timing_retarded .and. &
458 partner%quantities(q_id)%clock + clock_tick > requested_time))) then
459 ! We can update because the partner will reach this time in the next sub-timestep
460 ! For retarded interactions, we need to allow the quantity to get ahead by one step
461 call partner%update_exposed_quantity(q_id)
462
463 partner%quantities(q_id)%clock = partner%quantities(q_id)%clock + clock_tick
464 call multisystem_debug_write_marker(partner%namespace, event_clock_update_t("quantity", quantity_label(q_id), &
465 partner%quantities(q_id)%clock, "set"))
466 end if
467 end if
468
469 if (partner%quantities(q_id)%available_at_any_time) then
470 ! We ignore the clock times when a quantity is available at any time
471 ahead_in_time = .false.
472 right_on_time = .true.
473 else
474 ! Compare the clock times
475 ahead_in_time = partner%quantities(q_id)%clock > requested_time
476 right_on_time = partner%quantities(q_id)%clock == requested_time
477 end if
478
479 select case (partner%interaction_timing)
480 case (option__interactiontiming__timing_exact)
481 ! only allow interaction at exactly the same time
482 allowed_to_update = allowed_to_update .and. right_on_time
483 need_to_copy = allowed_to_update
484 case (option__interactiontiming__timing_retarded)
485 ! allow retarded interaction
486 allowed_to_update = allowed_to_update .and. &
487 (right_on_time .or. ahead_in_time)
488 need_to_copy = need_to_copy .and. .not. ahead_in_time
489 case default
490 call messages_not_implemented("Method for interaction quantity timing", namespace=partner%namespace)
491 end select
492
493 end do
494
495 ! If the quantities have been updated, we copy them to the interaction
496 if (allowed_to_update .and. need_to_copy) then
497 select type (interaction)
498 type is (ghost_interaction_t)
499 ! Nothing to copy. We still need to check that we are at the right
500 ! time for the update though!
501 class default
502 call partner%copy_quantities_to_interaction(interaction)
503 end select
504 end if
505 end if
506
507 class default
508 message(1) = "A system can only expose quantities to an interaction as a partner."
509 call messages_fatal(1, namespace=partner%namespace)
510 end select
511
512 call multisystem_debug_write_event_out(debug_handle, update=allowed_to_update, &
513 partner_clock = partner%clock, &
514 requested_clock = requested_time, &
515 interaction_clock = interaction%clock)
516
519
520 ! ---------------------------------------------------------
521 subroutine system_init_all_interactions(this)
522 class(system_t), intent(inout) :: this
523
524 type(interaction_iterator_t) :: iter
525 class(interaction_t), pointer :: interaction
526
528
529 call iter%start(this%interactions)
530 do while (iter%has_next())
531 interaction => iter%get_next()
532 select type (interaction)
533 type is (ghost_interaction_t)
534 ! Skip the ghost interactions
536 call this%init_interaction(interaction)
537 call interaction%partner%init_interaction_as_partner(interaction)
538 class default
539 call this%init_interaction(interaction)
540 end select
541 end do
542
544 end subroutine system_init_all_interactions
545
546 ! ---------------------------------------------------------
547 logical function system_update_interactions(this) result(all_updated)
548 class(system_t), intent(inout) :: this
549
550 logical :: none_updated
551 integer :: iq, q_id, n_quantities
552 class(interaction_t), pointer :: interaction
553 type(interaction_iterator_t) :: iter
554
556
557 ! Some systems might need to perform some specific operations before the
558 ! update. This should only be done if no interaction has been updated yet,
559 ! so that it is only done once.
560 none_updated = .true.
561 call iter%start(this%interactions)
562 do while (iter%has_next())
563 interaction => iter%get_next()
564 if (interaction%clock == this%algo%clock) then
565 none_updated = .false.
566 exit
567 end if
568 end do
569 if (none_updated) then
570 call this%update_interactions_start()
571 end if
572
573 !Loop over all interactions
574 all_updated = .true.
575 call iter%start(this%interactions)
576 do while (iter%has_next())
577 interaction => iter%get_next()
578
579 if (.not. interaction%clock == this%algo%clock) then
580 if (allocated(interaction%system_quantities)) then
581 n_quantities = size(interaction%system_quantities)
582 else
583 n_quantities = 0
584 end if
585
586 ! Update the system quantities that will be needed for computing the interaction
587 do iq = 1, n_quantities
588 ! Get requested quantity ID
589 q_id = interaction%system_quantities(iq)
590
591 ! All needed quantities must have been marked as required. If not, then fix your code!
592 assert(this%quantities(q_id)%required)
593
594 if (.not. this%quantities(q_id)%updated_on_demand) then
595 ! Quantity must be at the correct time, otherwise the algorithm and
596 ! the interaction are incompatible
597 if (.not. this%quantities(q_id)%clock == this%algo%clock .and. .not. this%quantities(q_id)%available_at_any_time) then
598 write(message(1), '(5a)') "Interaction ", trim(interaction%label), " is incompatible with the selected algorithm."
599 write(message(2), '(3a)') "The interaction requires the ", trim(quantity_label(q_id)), &
600 " at a time it is not available."
601 call messages_fatal(2, namespace=this%namespace)
602 end if
603
604 ! We do not need to update quantities that are not updated on
605 ! demand, as the algorithm takes care of doing that, so move on to
606 ! next quantity
607 cycle
608 end if
609
610 if (.not. this%quantities(q_id)%clock == this%algo%clock) then
611 ! The requested quantity is not at the requested time, so we try to update it
612
613 ! Sanity check: it should never happen that the quantity is in advance
614 ! with respect to the requested time.
615 if (this%quantities(q_id)%clock > this%algo%clock) then
616 message(1) = "The quantity clock is in advance compared to the requested time."
617 call messages_fatal(1, namespace=this%namespace)
618 end if
619
620 call this%update_quantity(q_id)
621 call multisystem_debug_write_marker(this%namespace, event_clock_update_t("quantity", quantity_label(q_id), &
622 this%quantities(q_id)%clock, "set"))
623 end if
624
625 end do
626
627 ! We can now try to update the interaction
628 all_updated = interaction%update(this%algo%clock) .and. all_updated
629 end if
630 end do
631
632 ! Some systems might need to perform some specific operations after all the
633 ! interactions have been updated
634 if (all_updated) then
635 call this%update_interactions_finish()
636 end if
637
639 end function system_update_interactions
640
641 ! ---------------------------------------------------------
642 subroutine system_update_interactions_start(this)
643 class(system_t), intent(inout) :: this
644
646
647 ! By default nothing is done just before updating the interactions. Child
648 ! classes that wish to change this behaviour should override this method.
649
652
653 ! ---------------------------------------------------------
654 subroutine system_update_interactions_finish(this)
655 class(system_t), intent(inout) :: this
656
658
659 ! By default nothing is done just after updating the interactions. Child
660 ! classes that wish to change this behaviour should override this method.
661
664
665 ! ---------------------------------------------------------
666 subroutine system_restart_write(this)
667 class(system_t), intent(inout) :: this
668
669 logical :: restart_write
670 type(interaction_iterator_t) :: iter
671 class(interaction_t), pointer :: interaction
672 integer :: ii
673
675
676 call parse_variable(this%namespace, 'RestartWrite', .true., restart_write)
677
678 if (restart_write) then
679 ! do some generic restart steps here
680 ! write clock restart data
681 call this%clock%restart_write('restart_clock_system', this%namespace)
682 call this%algo%clock%restart_write('restart_clock_propagator', this%namespace)
683 call iter%start(this%interactions)
684 do while (iter%has_next())
685 interaction => iter%get_next()
686 call interaction%restart_write(this%namespace)
687 end do
688 do ii = 1, max_quantities
689 if (this%quantities(ii)%required) then
690 call this%quantities(ii)%clock%restart_write('restart_clock_quantity_'//trim(quantity_label(ii)), &
691 this%namespace)
692 end if
693 end do
694 ! the following call is delegated to the corresponding system
695 call this%restart_write_data()
696 message(1) = "Wrote restart data for system "//trim(this%namespace%get())
697 call messages_info(1, namespace=this%namespace)
698 end if
699
701 end subroutine system_restart_write
702
703 ! ---------------------------------------------------------
704 ! this function returns true if restart data could be read
705 logical function system_restart_read(this)
706 class(system_t), intent(inout) :: this
707
708 type(interaction_iterator_t) :: iter
709 class(interaction_t), pointer :: interaction
710 integer :: ii
711
713
714 ! do some generic restart steps here
715 ! read clock data
716 system_restart_read = this%clock%restart_read('restart_clock_system', this%namespace)
718 this%algo%clock%restart_read('restart_clock_propagator', this%namespace)
719 call iter%start(this%interactions)
720 do while (iter%has_next())
721 interaction => iter%get_next()
722 system_restart_read = system_restart_read .and. interaction%restart_read(this%namespace)
723 ! reduce by one because of the first UPDATE_INTERACTIONS
724 interaction%clock = interaction%clock - clock_tick
725 end do
726 do ii = 1, max_quantities
727 if (this%quantities(ii)%required) then
729 this%quantities(ii)%clock%restart_read('restart_clock_quantity_'//trim(quantity_label(ii)), &
730 this%namespace)
731 end if
732 if (this%quantities(ii)%updated_on_demand) then
733 ! decrease clock by one for on-demand quantities to account for initial update_interactions step
734 this%quantities(ii)%clock = this%quantities(ii)%clock - clock_tick
735 end if
736 end do
737 ! the following call is delegated to the corresponding system
738 system_restart_read = system_restart_read .and. this%restart_read_data()
740 if (system_restart_read) then
741 message(1) = "Successfully read restart data for system "//trim(this%namespace%get())
742 call messages_info(1, namespace=this%namespace)
743 end if
744
746 end function system_restart_read
747
748 ! ---------------------------------------------------------
749 subroutine system_output_start(this)
750 class(system_t), intent(inout) :: this
753
754 ! By default nothing is done to regarding output. Child classes that wish
755 ! to change this behaviour should override this method.
756
758 end subroutine system_output_start
759
760 ! ---------------------------------------------------------
761 subroutine system_output_write(this)
762 class(system_t), intent(inout) :: this
763
765
766 ! By default nothing is done to regarding output. Child classes that wish
767 ! to change this behaviour should override this method.
768
770 end subroutine system_output_write
771
772 ! ---------------------------------------------------------
773 subroutine system_output_finish(this)
774 class(system_t), intent(inout) :: this
775
777
778 ! By default nothing is done to regarding output. Child classes that wish
779 ! to change this behaviour should override this method.
780
782 end subroutine system_output_finish
783
784 ! ---------------------------------------------------------
785 subroutine system_init_algorithm(this, factory)
786 class(system_t), intent(inout) :: this
787 class(algorithm_factory_t), intent(in) :: factory
788
789 integer :: ii
792
793 call messages_experimental('Multi-system framework')
794
795 this%algo => factory%create(this)
796
797 call this%init_clocks()
798
799 !%Variable InteractionTiming
800 !%Type integer
801 !%Default timing_exact
802 !%Section Time-Dependent::Propagation
803 !%Description
804 !% A parameter to determine if interactions should use the quantities
805 !% at the exact time or if retardation is allowed.
806 !%Option timing_exact 1
807 !% Only allow interactions at exactly the same times
808 !%Option timing_retarded 2
809 !% Allow retarded interactions
810 !%End
811 call parse_variable(this%namespace, 'InteractionTiming', &
812 option__interactiontiming__timing_exact, &
813 this%interaction_timing)
814 if (.not. varinfo_valid_option('InteractionTiming', this%interaction_timing)) then
815 call messages_input_error(this%namespace, 'InteractionTiming')
816 end if
817 call messages_print_var_option('InteractionTiming', this%interaction_timing, namespace=this%namespace)
818
819 do ii = 1, number_barriers
820 this%barrier(ii)%active = .false.
821 this%barrier(ii)%target_time = m_zero
822 end do
823
825 end subroutine system_init_algorithm
826
827 ! ---------------------------------------------------------------------------------------
828 recursive function system_algorithm_finished(this) result(finished)
829 class(system_t), intent(in) :: this
830 logical :: finished
831
832 finished = this%algo%finished()
833
835
836 ! ---------------------------------------------------------
837 subroutine system_init_clocks(this)
838 class(system_t), intent(inout) :: this
839
840 type(interaction_iterator_t) :: iter
841 class(interaction_t), pointer :: interaction
842
844
845 ! Initialize propagator clock
846 this%algo%clock = clock_t(time_step=this%algo%dt/this%algo%algo_steps)
847
848 ! Initialize system clock
849 this%clock = clock_t(time_step=this%algo%dt)
850
851 ! Interactions clocks
852 call iter%start(this%interactions)
853 do while (iter%has_next())
854 interaction => iter%get_next()
855 interaction%clock = this%algo%clock - clock_tick
856 end do
857
858 ! Required quantities clocks
859 where (this%quantities%required)
860 this%quantities%clock = this%algo%clock
861 end where
862 where (this%quantities%updated_on_demand)
863 this%quantities%clock = this%algo%clock - clock_tick
864 end where
865
867 end subroutine system_init_clocks
868
869 ! ---------------------------------------------------------
871 class(system_t), intent(inout) :: this
872
873 logical :: all_updated
874 type(event_handle_t) :: debug_handle
875 integer, allocatable :: updated_quantities(:)
876
878
879 debug_handle = multisystem_debug_write_event_in(this%namespace, event_function_call_t("system_propagation_start"), &
880 system_clock = this%clock, prop_clock = this%algo%clock)
881
882 ! Update interactions at initial time
883 all_updated = this%update_interactions()
884 if (.not. all_updated) then
885 message(1) = "Unable to update interactions when initializing the propagation."
886 call messages_fatal(1, namespace=this%namespace)
887 end if
888
889 ! System-specific and propagator-specific initialization step
890 if (this%algo%start_step%id /= skip) then
891 if (.not. this%do_algorithmic_operation(this%algo%start_step, updated_quantities)) then
892 message(1) = "Unsupported algorithmic operation."
893 write(message(2), '(A,A,A)') trim(this%algo%start_step%id), ": ", trim(this%algo%start_step%label)
894 call messages_fatal(2, namespace=this%namespace)
895 else if (allocated(updated_quantities)) then
896 message(1) = "Update of quantities not allowed in algorithmic operation."
897 write(message(2), '(A,A,A)') trim(this%algo%final_step%id), ": ", trim(this%algo%final_step%label)
898 call messages_fatal(2, namespace=this%namespace)
899 end if
900 end if
901
902 ! Compute the total energy at the beginning of the simulation
903 call this%update_total_energy()
904
905 ! Start output
906 call this%output_start()
907
908 ! Write header for propagation log
909 call messages_print_with_emphasis(msg="Multi-system propagation", namespace=this%namespace)
910 write(message(1), '(a6,1x,a14,1x,a13,1x,a10,1x,a15)') 'Iter', 'Time', 'Energy', 'SC Steps', 'Elapsed Time'
911 call messages_info(1, namespace=this%namespace)
912 call messages_print_with_emphasis(namespace=this%namespace)
914 ! Rewind propagator (will also set the initial time to compute the elapsed time)
915 call this%algo%rewind()
916
917 call multisystem_debug_write_event_out(debug_handle, system_clock = this%clock, prop_clock = this%algo%clock)
918
920 end subroutine system_propagation_start
921
922 ! ---------------------------------------------------------
923 subroutine system_propagation_finish(this)
924 class(system_t), intent(inout) :: this
925 type(event_handle_t) :: debug_handle
926
927 integer, allocatable :: updated_quantities(:)
928
930
931 debug_handle = multisystem_debug_write_event_in(this%namespace, event_function_call_t("system_propagation_finish"), &
932 system_clock = this%clock, prop_clock = this%algo%clock)
933
934 ! Finish output
935 call this%output_finish()
936
937 ! System-specific and propagator-specific finalization step
938 if (this%algo%final_step%id /= skip) then
939 if (.not. this%do_algorithmic_operation(this%algo%final_step, updated_quantities)) then
940 message(1) = "Unsupported algorithmic operation."
941 write(message(2), '(A,A,A)') trim(this%algo%final_step%id), ": ", trim(this%algo%final_step%label)
942 call messages_fatal(2, namespace=this%namespace)
943 else if (allocated(updated_quantities)) then
944 message(1) = "Update of quantities not allowed in algorithmic operation."
945 write(message(2), '(A,A,A)') trim(this%algo%final_step%id), ": ", trim(this%algo%final_step%label)
946 call messages_fatal(2, namespace=this%namespace)
947 end if
948 end if
949
950 call multisystem_debug_write_event_out(debug_handle, system_clock = this%clock, prop_clock = this%algo%clock)
951
953 end subroutine system_propagation_finish
954
955 ! ---------------------------------------------------------
956 subroutine system_iteration_info(this)
957 class(system_t), intent(in) :: this
958
959 float :: energy
960 character(len=40) :: fmt
961
963
964 energy = units_from_atomic(units_out%energy, this%total_energy)
965 if (abs(energy) >= 1e5) then
966 fmt = '(i7,1x,f14.6,1X,es13.6,1X,i9,1X,'
967 else
968 fmt = '(i7,1x,f14.6,1X,f13.6,1X,i9,1X,'
969 end if
970 if (this%algo%elapsed_time < 1e-3) then
971 fmt = trim(fmt)//'es13.3)'
972 else
973 fmt = trim(fmt)//'f13.3)'
974 end if
975
976 write(message(1), fmt) this%clock%get_tick(), &
977 units_from_atomic(units_out%time, this%clock%time()), energy, &
978 0, this%algo%elapsed_time
979 call messages_info(1, namespace=this%namespace)
980
982 end subroutine system_iteration_info
983
984 ! ---------------------------------------------------------
985 logical function system_process_is_slave(this)
986 class(system_t), intent(in) :: this
987
989
990 ! By default an MPI process is not a slave
992
994 end function system_process_is_slave
995
996 ! ---------------------------------------------------------
997 subroutine system_end(this)
998 class(system_t), intent(inout) :: this
999
1000 type(interaction_iterator_t) :: iter
1001 class(interaction_t), pointer :: interaction
1002
1004
1005 ! No call to safe_deallocate macro here, as it gives an ICE with gfortran
1006 if (associated(this%algo)) then
1007 deallocate(this%algo)
1008 end if
1009
1010 call iter%start(this%interactions)
1011 do while (iter%has_next())
1012 interaction => iter%get_next()
1013 safe_deallocate_p(interaction)
1014 end do
1015
1017 end subroutine system_end
1018
1019 ! ---------------------------------------------------------
1020 subroutine system_list_add_node(this, partner)
1021 class(system_list_t) :: this
1022 class(interaction_partner_t), target :: partner
1023
1025
1026 select type (partner)
1027 class is (system_t)
1028 call this%add_ptr(partner)
1029 class default
1030 assert(.false.)
1031 end select
1032
1034 end subroutine system_list_add_node
1035
1036 ! ---------------------------------------------------------
1037 recursive logical function system_list_contains(this, partner) result(contains)
1038 class(system_list_t) :: this
1039 class(interaction_partner_t), target :: partner
1040
1041 type(partner_iterator_t) :: iterator
1042 class(interaction_partner_t), pointer :: system
1043
1045
1046 contains = .false.
1047
1048 select type (partner)
1049 class is (system_t)
1050
1051 call iterator%start(this)
1052 do while (iterator%has_next() .and. .not. contains)
1053 system => iterator%get_next()
1054 contains = associated(system, partner)
1055 end do
1056
1057 class default
1058 contains = .false.
1059 end select
1060
1062 end function system_list_contains
1063
1064 ! ---------------------------------------------------------
1065 function system_iterator_get_next(this) result(system)
1066 class(system_iterator_t), intent(inout) :: this
1067 class(system_t), pointer :: system
1068
1071 select type (ptr => this%get_next_ptr())
1072 class is (system_t)
1073 system => ptr
1074 class default
1075 assert(.false.)
1076 end select
1077
1079 end function system_iterator_get_next
1080
1081 ! ---------------------------------------------------------
1085 subroutine system_init_parallelization(this, grp)
1086 class(system_t), intent(inout) :: this
1087 type(mpi_grp_t), intent(in) :: grp
1088
1090
1091 call mpi_grp_copy(this%grp, grp)
1092 call messages_update_mpi_grp(this%namespace, grp)
1093
1095 end subroutine system_init_parallelization
1096
1097
1098
1099 ! ---------------------------------------------------------
1100 subroutine system_start_barrier(this, target_time, barrier_index)
1101 class(system_t), intent(inout) :: this
1102 float, intent(in) :: target_time
1103 integer, intent(in) :: barrier_index
1104
1106
1107 this%barrier(barrier_index)%active = .true.
1108 this%barrier(barrier_index)%target_time = target_time
1109
1111 end subroutine system_start_barrier
1112
1113 ! ---------------------------------------------------------
1114 subroutine system_end_barrier(this, barrier_index)
1115 class(system_t), intent(inout) :: this
1116 integer, intent(in) :: barrier_index
1117
1119
1120 this%barrier(barrier_index)%active = .false.
1121 this%barrier(barrier_index)%target_time = m_zero
1124 end subroutine system_end_barrier
1125
1126 ! ---------------------------------------------------------
1127 logical function system_arrived_at_barrier(this, barrier_index)
1128 class(system_t), intent(inout) :: this
1129 integer, intent(in) :: barrier_index
1130
1131 type(clock_t) :: clock
1132
1134
1136 if (this%barrier(barrier_index)%active) then
1137 clock = this%clock + clock_tick
1138 if (clock%time() > this%barrier(barrier_index)%target_time) then
1140 end if
1141 end if
1142
1144 end function system_arrived_at_barrier
1145
1146 ! ---------------------------------------------------------
1147 logical function system_arrived_at_any_barrier(this)
1148 class(system_t), intent(inout) :: this
1149
1150 integer :: ii
1151
1153
1155 do ii = 1, number_barriers
1157 .or. this%arrived_at_barrier(ii)
1158 end do
1159
1162
1163
1164 ! ---------------------------------------------------------
1169 subroutine system_update_potential_energy(this)
1170 class(system_t), intent(inout) :: this
1171
1172 type(interaction_iterator_t) :: iter
1173 class(interaction_t), pointer :: interaction
1174
1176
1177 this%potential_energy = m_zero
1178
1179 call iter%start(this%interactions)
1180 do while (iter%has_next())
1181 interaction => iter%get_next()
1182 if(.not. interaction%intra_interaction) then
1183 call interaction%calculate_energy()
1184 this%potential_energy = this%potential_energy + interaction%energy
1185 end if
1186 end do
1187
1189 end subroutine system_update_potential_energy
1190
1191 ! ---------------------------------------------------------
1196 subroutine system_update_internal_energy(this)
1197 class(system_t), intent(inout) :: this
1198
1200 class(interaction_t), pointer :: interaction
1201
1203
1204 this%internal_energy = m_zero
1205 call iter%start(this%interactions)
1206 do while (iter%has_next())
1207 interaction => iter%get_next()
1208 if(interaction%intra_interaction) then
1209 call interaction%calculate_energy()
1210 this%internal_energy = this%internal_energy + interaction%energy
1211 end if
1212 end do
1213
1215 end subroutine system_update_internal_energy
1216
1217 ! ---------------------------------------------------------
1221 subroutine system_update_total_energy(this)
1222 class(system_t), intent(inout) :: this
1223
1225
1226 call this%update_kinetic_energy()
1227 this%total_energy = this%kinetic_energy
1228
1230 call this%update_potential_energy()
1231 this%total_energy = this%total_energy + this%potential_energy
1234 call this%update_internal_energy()
1235 this%total_energy = this%total_energy + this%internal_energy
1236
1238 end subroutine system_update_total_energy
1239
1240end module system_oct_m
1241
1242!! Local Variables:
1243!! mode: f90
1244!! coding: utf-8
1245!! End:
Execute one operation that is part of a larger algorithm. Returns true if the operation was successfu...
Definition: system.F90:248
initialize a given interaction of the system
Definition: system.F90:227
set initial conditions for a system
Definition: system.F90:236
check whether a system has reached a given tolerance
Definition: system.F90:258
The basic elements defining algorithms.
Definition: algorithm.F90:107
character(len=algo_label_len), parameter, public update_interactions
Definition: algorithm.F90:139
character(len=algo_label_len), parameter, public rewind_algorithm
Definition: algorithm.F90:139
character(len=algo_label_len), parameter, public step_done
Definition: algorithm.F90:139
character(len=algo_label_len), parameter, public skip
Definition: algorithm.F90:139
integer, parameter, public clock_tick
Definition: clock.F90:120
real(8), parameter, public m_zero
Definition: global.F90:170
This module defines the abstract interaction_t class, and some auxiliary classes for interactions.
This module defines classes and functions for interaction partners.
This module implements fully polymorphic linked lists, and some specializations thereof.
subroutine, public messages_print_with_emphasis(msg, iunit, namespace)
Definition: messages.F90:918
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1236
character(len=512), private msg
Definition: messages.F90:162
subroutine, public push_sub(sub_name)
Definition: messages.F90:1046
subroutine, public messages_update_mpi_grp(namespace, mpigrp)
Definition: messages.F90:368
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
Definition: messages.F90:611
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:157
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:406
subroutine, public pop_sub(sub_name)
Definition: messages.F90:1101
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:710
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1208
subroutine mpi_grp_copy(mpi_grp_out, mpi_grp_in)
Definition: mpi.F90:350
This module implements the multisystem debug functionality.
subroutine, public multisystem_debug_write_marker(system_namespace, event)
subroutine, public multisystem_debug_write_event_out(handle, extra, update, system_clock, prop_clock, interaction_clock, partner_clock, requested_clock)
type(event_handle_t) function, public multisystem_debug_write_event_in(system_namespace, event, extra, system_clock, prop_clock, interaction_clock, partner_clock, requested_clock)
This module defines the quantity_t class and the IDs for quantities, which can be exposed by a system...
Definition: quantity.F90:108
character(len=17), dimension(max_quantities), parameter, public quantity_label
Definition: quantity.F90:135
integer, parameter, public max_quantities
Definition: quantity.F90:117
This module implements the abstract system type.
Definition: system.F90:110
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:1307
subroutine system_iteration_info(this)
Definition: system.F90:1042
integer, parameter, public barrier_restart
Definition: system.F90:157
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:1282
logical function system_arrived_at_any_barrier(this)
Definition: system.F90:1233
logical function system_update_exposed_quantities(partner, requested_time, interaction)
update all exposed quantities of the system.
Definition: system.F90:504
recursive logical function system_list_contains(this, partner)
Definition: system.F90:1123
subroutine system_init_all_interactions(this)
Definition: system.F90:607
subroutine, public system_restart_write(this)
Definition: system.F90:752
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:1255
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:1171
subroutine system_output_start(this)
Definition: system.F90:835
subroutine system_update_interactions_start(this)
Definition: system.F90:728
subroutine system_start_barrier(this, target_time, barrier_index)
Definition: system.F90:1186
subroutine, public system_init_clocks(this)
Definition: system.F90:923
subroutine, public system_propagation_finish(this)
Definition: system.F90:1009
subroutine, public system_end(this)
Definition: system.F90:1083
subroutine system_output_write(this)
Definition: system.F90:847
subroutine system_update_interactions_finish(this)
Definition: system.F90:740
subroutine, public system_execute_algorithm(this)
perform one or more algorithmic operations
Definition: system.F90:338
class(system_t) function, pointer system_iterator_get_next(this)
Definition: system.F90:1151
subroutine, public system_reset_clocks(this, accumulated_ticks)
Definition: system.F90:456
logical function system_process_is_slave(this)
Definition: system.F90:1071
logical function system_arrived_at_barrier(this, barrier_index)
Definition: system.F90:1213
recursive logical function system_algorithm_finished(this)
Definition: system.F90:914
logical function, public system_restart_read(this)
Definition: system.F90:791
subroutine system_output_finish(this)
Definition: system.F90:859
subroutine system_list_add_node(this, partner)
Definition: system.F90:1106
subroutine, public system_propagation_start(this)
Definition: system.F90:956
subroutine, public system_init_algorithm(this, factory)
Definition: system.F90:871
subroutine system_end_barrier(this, barrier_index)
Definition: system.F90:1200
logical function system_update_interactions(this)
Definition: system.F90:633
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:124
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
clock_t clock(void)
Definition: oct_f.c:3427
Abstract class for the algoritm factories.
Descriptor of one algorithmic operation.
Definition: algorithm.F90:130
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 defining only minimal structures
abstract class for general interaction partners
Some interactions involve two systems. In this case the interaction is a unidirectional relationship ...
This class implements an iterator for the polymorphic linked list.
This is defined even when running serial.
Definition: mpi.F90:125
handle to keep track of in- out- events
These classes extends the list and list iterator to create a system list.
Definition: system.F90:309
Abstract class for systems.
Definition: system.F90:164
int true(void)