Octopus
interaction_partner.F90
Go to the documentation of this file.
1!! Copyright (C) 2020 M. Oliveira
2!!
3!! This Source Code Form is subject to the terms of the Mozilla Public
4!! License, v. 2.0. If a copy of the MPL was not distributed with this
5!! file, You can obtain one at https://mozilla.org/MPL/2.0/.
6!!
7
8#include "global.h"
9
14 use debug_oct_m
15 use global_oct_m
23 implicit none
24
25 private
26 public :: &
30
31 integer, parameter, public :: &
32 COUPLINGS_UNDEFINED = 0, &
36
42 type, abstract :: interaction_partner_t
43 private
44 type(namespace_t), public :: namespace
45
46 integer, allocatable, public :: supported_interactions_as_partner(:)
48
49 type(quantity_list_t), public :: quantities
50 contains
51 procedure :: add_partners_to_list => interaction_partner_add_partners_to_list
52 procedure :: update_quantity => interaction_partner_update_quantity
53
54 procedure :: update_on_demand_quantity => interaction_partner_update_on_demand_quantity
55
56 procedure :: update_on_demand_quantities => interaction_partner_update_on_demand_quantities
57
58 procedure :: check_couplings_status => interaction_partner_check_couplings_status
59
60 procedure(interaction_partner_init_interaction_as_partner), deferred :: init_interaction_as_partner
61
62 procedure(interaction_partner_copy_quantities_to_interaction), deferred :: copy_quantities_to_interaction
63
65
66 abstract interface
67 ! ---------------------------------------------------------
68 subroutine interaction_partner_init_interaction_as_partner(partner, interaction)
71 class(interaction_partner_t), intent(in) :: partner
72 class(interaction_surrogate_t), intent(inout) :: interaction
74
75 ! ---------------------------------------------------------
76 subroutine interaction_partner_copy_quantities_to_interaction(partner, interaction)
79 class(interaction_partner_t), intent(inout) :: partner
80 class(interaction_surrogate_t), intent(inout) :: interaction
82
83 end interface
84
88 type, extends(linked_list_t) :: partner_list_t
89 private
90 contains
91 procedure :: add => partner_list_add_node
92 end type partner_list_t
93
98 private
99 contains
100 procedure :: get_next => partner_iterator_get_next
101 end type partner_iterator_t
102
103contains
104
105 ! ---------------------------------------------------------
110 recursive subroutine interaction_partner_add_partners_to_list(this, list, interaction_type)
111 class(interaction_partner_t), intent(in) :: this
112 class(partner_list_t), intent(inout) :: list
113 integer, optional, intent(in) :: interaction_type
114
115 if (present(interaction_type)) then
116 if (any(this%supported_interactions_as_partner == interaction_type)) then
117 call list%add(this)
118 end if
119 else
120 call list%add(this)
121 end if
122
125 ! ---------------------------------------------------------
133 subroutine interaction_partner_update_quantity(this, label)
134 class(interaction_partner_t), intent(inout) :: this
135 character(len=*), intent(in) :: label
136
138
139 write(message(1), '(a,a,a,a,a)') 'Interation partner "', trim(this%namespace%get()), &
140 '"does not know how to update quantity"', trim(label), '".'
141 call messages_fatal(1, namespace=this%namespace)
146 !! NOTE: here we update the quantity to be exactly at
147 !! requested_iteration. This is different than what is done in
148 !! interaction_partner_update_on_demand_quantities and it means the caller
149 !! needs to pass the correct requested_iteration.
150 recursive subroutine interaction_partner_update_on_demand_quantity(this, quantity, requested_iteration)
151 class(interaction_partner_t), intent(inout) :: this
152
153 type(quantity_t), intent(inout) :: quantity
154 class(iteration_counter_t), intent(in) :: requested_iteration
156 integer :: i
157 type(quantity_t), pointer :: parent
158
159 ! Before updating this quantity, we also need to update its parents
160 do i = 1, size(quantity%parents)
161 parent => this%quantities%get(quantity%parents(i))
162
163 ! We are only allowed to update on demand quantities that are behind the requested iteration
164 if (parent%iteration >= requested_iteration .or. .not. parent%updated_on_demand) cycle
165
166 call this%update_on_demand_quantity(parent, requested_iteration)
167 end do
168
169 ! If all parents are at the requested time, we can proceed with updating the quantity itself
170 ! Note that here we ignore quantities that are available at any time.
171 if (all(this%quantities%iteration_equal(quantity%parents, requested_iteration) .or. &
172 this%quantities%always_available(quantity%parents))) then
173
174 quantity%iteration = requested_iteration
175 call multisystem_debug_write_marker(this%namespace, event_iteration_update_t("quantity", quantity%label, &
176 quantity%iteration, "set"))
177 call this%update_quantity(quantity%label)
178 end if
179
182
183 ! ---------------------------------------------------------
191 subroutine interaction_partner_update_on_demand_quantities(this, labels, requested_iteration, retardation_allowed)
192 class(interaction_partner_t), target, intent(inout) :: this
193 character(len=*), intent(in) :: labels(:)
194 class(iteration_counter_t), intent(in) :: requested_iteration
195 logical, intent(in) :: retardation_allowed
196
197 integer :: iq
198 type(quantity_t), pointer :: quantity
199
200 do iq = 1, size(labels)
201 ! Get a pointer to the quantity
202 quantity => this%quantities%get(labels(iq))
204 ! We are only updating on demand quantities that are behind the requested iteration
205 if (quantity%iteration >= requested_iteration .or. .not. quantity%updated_on_demand) cycle
206
207 if (quantity%always_available) then
208 ! We set the quantity iteration to the requested one, so that the partner
209 ! can use this information when updating the quantity
210 call this%update_on_demand_quantity(quantity, requested_iteration)
211
212 else if (quantity%iteration + 1 <= requested_iteration .or. &
213 (retardation_allowed .and. quantity%iteration + 1 > requested_iteration)) then
214 ! We can update because the partner will reach this iteration in the next algorithmic step
215 ! For retarded interactions, we need to allow the quantity to get ahead by one iteration
216 call this%update_on_demand_quantity(quantity, quantity%iteration + 1)
217
218 end if
219 end do
220
222
223 ! ---------------------------------------------------------
231 integer function interaction_partner_check_couplings_status(this, couplings, requested_iteration) result(status)
232 class(interaction_partner_t), intent(inout) :: this
233 character(len=*), intent(in) :: couplings(:)
234 class(iteration_counter_t), intent(in) :: requested_iteration
235
236 type(quantity_t), pointer :: coupling
237 integer :: i, ahead, on_time, relevant_couplings
238 character(len=200) :: marker_info
239 character(len=20) :: status_string
240 type(event_handle_t) :: debug_handle
241
242
244
245 debug_handle = multisystem_debug_write_event_in(system_namespace = this%namespace, &
246 event = event_function_call_t("check_couplings_status"), &
247 requested_iteration = requested_iteration)
248
249
250 ! Count couplings on time and ahead
251 on_time = 0
252 ahead = 0
253 relevant_couplings = 0
254 do i = 1, size(couplings)
255 coupling => this%quantities%get(couplings(i))
256
257 ! Couplings that are available at any time do not affect the status, so we will ignore them
258 if (coupling%always_available) cycle
259
260 relevant_couplings = relevant_couplings + 1
261 if (coupling%iteration == requested_iteration) on_time = on_time + 1
262 if (coupling%iteration > requested_iteration) ahead = ahead + 1
263 end do
264
265 ! Determine status
266 if (on_time > 0 .and. ahead > 0) then
267 status = couplings_undefined
268 status_string = "UNDEFINED"
269 else if (on_time + ahead < relevant_couplings) then
271 status_string = "BEHIND"
272 else if (on_time == relevant_couplings) then
273 status = couplings_on_time
274 status_string = "ON_TIME"
275 else if (ahead == relevant_couplings) then
277 status_string = "AHEAD"
278 end if
279
280 write(marker_info, '(A20," check_couplings_status: ahead = ",I5,", on_time = ",I5,", relevant = ",I5, ", status = ",A9)') &
281 trim(this%namespace%get()), ahead, on_time, relevant_couplings, trim(status_string)
282 call multisystem_debug_write_event_out(debug_handle, extra=marker_info, requested_iteration=requested_iteration)
283
286
287 ! ---------------------------------------------------------
290 subroutine partner_list_add_node(this, partner)
291 class(partner_list_t) :: this
292 class(interaction_partner_t), target :: partner
293
294 push_sub(partner_list_add_node)
295
296 call this%add_ptr(partner)
297
298 pop_sub(partner_list_add_node)
299 end subroutine partner_list_add_node
300
301 ! ---------------------------------------------------------
304 function partner_iterator_get_next(this) result(partner)
305 class(partner_iterator_t), intent(inout) :: this
306 class(interaction_partner_t), pointer :: partner
307
309
310 select type (ptr => this%get_next_ptr())
311 class is (interaction_partner_t)
312 partner => ptr
313 class default
314 assert(.false.)
315 end select
316
318 end function partner_iterator_get_next
319
321
322!! Local Variables:
323!! mode: f90
324!! coding: utf-8
325!! End:
This module defines classes and functions for interaction partners.
integer, parameter, public couplings_on_time
recursive subroutine interaction_partner_add_partners_to_list(this, list, interaction_type)
add interaction partner to a list
integer function interaction_partner_check_couplings_status(this, couplings, requested_iteration)
Check the status of some couplings.
class(interaction_partner_t) function, pointer partner_iterator_get_next(this)
get next partner from the list
recursive subroutine interaction_partner_update_on_demand_quantity(this, quantity, requested_iteration)
subroutine partner_list_add_node(this, partner)
add a partner to the list
integer, parameter, public couplings_behind_in_time
subroutine interaction_partner_update_on_demand_quantities(this, labels, requested_iteration, retardation_allowed)
Given a list of quantities, update the ones that can be update on demand.
subroutine interaction_partner_update_quantity(this, label)
Method to be overriden by interaction partners that have quantities that can be updated on demand.
integer, parameter, public couplings_ahead_in_time
This module implements fully polymorphic linked lists, and some specializations thereof.
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
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
abstract class for general interaction partners
surrogate interaction class to avoid circular dependencies between modules.
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 class implements a linked list of unlimited polymorphic values.
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