Neko  0.9.0
A portable framework for high-order spectral element flow simulations
simcomp_executor.f90
Go to the documentation of this file.
1 ! Copyright (c) 2024, The Neko Authors
2 ! All rights reserved.
3 !
4 ! Redistribution and use in source and binary forms, with or without
5 ! modification, are permitted provided that the following conditions
6 ! are met:
7 !
8 ! * Redistributions of source code must retain the above copyright
9 ! notice, this list of conditions and the following disclaimer.
10 !
11 ! * Redistributions in binary form must reproduce the above
12 ! copyright notice, this list of conditions and the following
13 ! disclaimer in the documentation and/or other materials provided
14 ! with the distribution.
15 !
16 ! * Neither the name of the authors nor the names of its
17 ! contributors may be used to endorse or promote products derived
18 ! from this software without specific prior written permission.
19 !
20 ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 ! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 ! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 ! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 ! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 ! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 ! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 ! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 ! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 ! POSSIBILITY OF SUCH DAMAGE.
32 !
35  use num_types, only : rp
37  simulation_component_wrapper_t, simulation_component_factory
38  use json_module, only : json_file, json_core, json_value
40  use case, only : case_t
41  use utils, only : neko_error
42  use logger, only : neko_log
43  implicit none
44  private
45 
52  type, public :: simcomp_executor_t
53 
55  class(simulation_component_wrapper_t), allocatable :: simcomps(:)
57  integer, private :: n_simcomps
59  type(case_t), pointer, private :: case
61  logical, private :: finalized = .false.
62  contains
64  procedure, pass(this) :: init => simcomp_executor_init
66  procedure, pass(this) :: free => simcomp_executor_free
68  procedure, pass(this) :: add_user_simcomp => simcomp_executor_add
70  procedure, pass(this) :: preprocess => simcomp_executor_preprocess
72  procedure, pass(this) :: compute => simcomp_executor_compute
74  procedure, pass(this) :: restart=> simcomp_executor_restart
76  procedure, private, pass(this) :: finalize => simcomp_executor_finalize
78  procedure, pass(this) :: get_n => simcomp_executor_get_n
79  end type simcomp_executor_t
80 
82  type(simcomp_executor_t), target, public :: neko_simcomps
83 
84 contains
85 
90  subroutine simcomp_executor_init(this, case, simcomp_root)
91  class(simcomp_executor_t), intent(inout) :: this
92  type(case_t), target, intent(inout) :: case
93  character(len=*), optional, intent(in) :: simcomp_root
94  integer :: n_simcomps, i
95  type(json_core) :: core
96  type(json_value), pointer :: simcomp_object
97  type(json_file) :: comp_subdict
98  logical :: found, is_user, has_user
99  ! Help array for finding minimal values
100  logical, allocatable :: mask(:)
101  ! The order value for each simcomp in order of appearance in the case file.
102  integer, allocatable :: read_order(:), order(:)
103  ! Location of the min value
104  integer :: loc(1)
105  integer :: max_order
106  character(len=:), allocatable :: root_name, comp_type
107 
108  call this%free()
109  this%case => case
110 
111  ! Get the root name of the simulation components if specified
112  if (present(simcomp_root)) then
113  root_name = simcomp_root
114  else
115  root_name = 'case.simulation_components'
116  end if
117 
118  ! Get the core json object and the simulation components object
119  call case%params%get_core(core)
120  call case%params%get(root_name, simcomp_object, found)
121  if (.not. found) return
122  call neko_log%section('Initialize simcomp')
123 
124  ! Set the number of simcomps and allocate the arrays
125  call case%params%info(root_name, n_children = n_simcomps)
126  this%n_simcomps = n_simcomps
127  allocate(this%simcomps(n_simcomps))
128  allocate(order(n_simcomps))
129  allocate(read_order(n_simcomps))
130  allocate(mask(n_simcomps), source = .true.)
131 
132  ! We need a separate loop to figure out the order, so that we can
133  ! apply the order to the initialization as well.
134  max_order = 0
135  has_user = .false.
136  do i = 1, n_simcomps
137  ! Create a new json containing just the subdict for this simcomp
138  call json_extract_item(core, simcomp_object, i, comp_subdict)
139 
140  call json_get_or_default(comp_subdict, "is_user", is_user, .false.)
141  has_user = has_user .or. is_user
142 
143  call json_get_or_default(comp_subdict, "order", read_order(i), -1)
144  if (read_order(i) .gt. max_order) then
145  max_order = read_order(i)
146  end if
147  end do
148 
149  ! If the order was not specified, we use the order of appearance in the
150  ! case file.
151  do i = 1, n_simcomps
152  if (read_order(i) == -1) then
153  max_order = max_order + 1
154  read_order(i) = max_order
155  end if
156  end do
157 
158  ! Figure out the execution order using a poor man's argsort.
159  ! Searches for the location of the min value, each time masking out the
160  ! found location prior to the next search.
161  do i = 1, n_simcomps
162  loc = minloc(read_order, mask = mask)
163  order(i) = loc(1)
164  mask(loc) = .false.
165  end do
166 
167  ! Init in the determined order.
168  do i = 1, n_simcomps
169  call json_extract_item(core, simcomp_object, order(i), comp_subdict)
170 
171  ! Log the component type if it is not a user component
172  call json_get(comp_subdict, "type", comp_type)
173  call json_get_or_default(comp_subdict, "is_user", is_user, .false.)
174  if (.not. is_user) call neko_log%message('- ' // trim(comp_type))
175 
176  call simulation_component_factory(this%simcomps(i)%simcomp, &
177  comp_subdict, case)
178  end do
179 
180  if (has_user) then
181  call neko_log%message('Initialize user simcomp')
182 
183  comp_subdict = json_file(simcomp_object)
184  call case%usr%init_user_simcomp(comp_subdict)
185  end if
186 
187  ! Cleanup
188  deallocate(order)
189  deallocate(read_order)
190  deallocate(mask)
191 
192  call neko_log%end_section()
193  end subroutine simcomp_executor_init
194 
196  subroutine simcomp_executor_free(this)
197  class(simcomp_executor_t), intent(inout) :: this
198  integer :: i
199 
200  if (allocated(this%simcomps)) then
201  do i = 1, this%n_simcomps
202  call this%simcomps(i)%simcomp%free
203  end do
204  deallocate(this%simcomps)
205  end if
206  end subroutine simcomp_executor_free
207 
211  subroutine simcomp_executor_add(this, object, settings)
212  class(simcomp_executor_t), intent(inout) :: this
213  class(simulation_component_t), intent(in) :: object
214  type(json_file), intent(inout), optional :: settings
215 
216  class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
217  integer :: i, position
218 
219  ! Find the first empty position
220  position = 0
221  do i = 1, this%n_simcomps
222  if (.not. allocated(this%simcomps(i)%simcomp)) then
223  position = i
224  exit
225  end if
226  end do
227 
228  ! If no empty position was found, append to the end
229  if (position .eq. 0) then
230  if (this%n_simcomps .gt. 0) call move_alloc(this%simcomps, tmp_simcomps)
231  allocate(this%simcomps(this%n_simcomps + 1))
232 
233  if (allocated(tmp_simcomps)) then
234  do i = 1, this%n_simcomps
235  call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(i)%simcomp)
236  end do
237  end if
238 
239  this%n_simcomps = this%n_simcomps + 1
240  position = this%n_simcomps
241 
242  if (allocated(tmp_simcomps)) deallocate(tmp_simcomps)
243  end if
244 
245  this%simcomps(position)%simcomp = object
246  if (present(settings)) then
247  call this%simcomps(position)%simcomp%init(settings, this%case)
248  end if
249 
250  this%finalized = .false.
251 
252  end subroutine simcomp_executor_add
253 
258  subroutine simcomp_executor_finalize(this)
259  class(simcomp_executor_t), intent(inout) :: this
260  integer :: i, order, max_order
261  logical :: order_found, previous_found
262 
263  class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
264  integer, allocatable :: order_list(:)
265 
266  ! Check that all components are initialized
267  do i = 1, this%n_simcomps
268  if (.not. allocated(this%simcomps(i)%simcomp)) then
269  call neko_error("Simulation component not initialized.")
270  end if
271  end do
272 
273  ! Check that the order is unique and contiguous
274  previous_found = .true.
275  do order = 1, this%n_simcomps
276  order_found = .false.
277  do i = 1, this%n_simcomps
278  if (this%simcomps(i)%simcomp%order == order .and. order_found) then
279  call neko_error("Simulation component order must be unique.")
280  else if (this%simcomps(i)%simcomp%order == order) then
281  order_found = .true.
282  end if
283  end do
284  if (order_found .and. .not. previous_found) then
285  call neko_error("Simulation component order must be contiguous &
286  &starting at 1.")
287  end if
288  previous_found = order_found
289  end do
290 
291  allocate(order_list(this%n_simcomps))
292  order_list = 0
293  max_order = 0
294  do i = 1, this%n_simcomps
295  order_list(i) = this%simcomps(i)%simcomp%order
296  if (order_list(i) .gt. max_order) then
297  max_order = order_list(i)
298  end if
299  end do
300 
301  do i = 1, this%n_simcomps
302  if (order_list(i) .eq. -1) then
303  order_list(i) = max_order + 1
304  max_order = max_order + 1
305  end if
306  end do
307 
308  ! Check that the order is within bounds
309  do i = 1, this%n_simcomps
310  if (order_list(i) .gt. this%n_simcomps) then
311  deallocate(order_list)
312  call neko_error("Simulation component order is out of bounds.")
313  end if
314  end do
315 
316  ! Reorder the simcomps based on the order specified
317  call move_alloc(this%simcomps, tmp_simcomps)
318  allocate(this%simcomps(this%n_simcomps))
319  do i = 1, this%n_simcomps
320  order = order_list(i)
321  call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(order)%simcomp)
322  end do
323 
324  if (allocated(tmp_simcomps)) then
325  deallocate(tmp_simcomps)
326  end if
327  if (allocated(order_list)) then
328  deallocate(order_list)
329  end if
330 
331  this%finalized = .true.
332  end subroutine simcomp_executor_finalize
333 
337  subroutine simcomp_executor_preprocess(this, t, tstep)
338  class(simcomp_executor_t), intent(inout) :: this
339  real(kind=rp), intent(in) :: t
340  integer, intent(in) :: tstep
341  integer :: i
342 
343  if (.not. this%finalized) call this%finalize()
344 
345  if (allocated(this%simcomps)) then
346  do i = 1, size(this%simcomps)
347  call this%simcomps(i)%simcomp%preprocess(t, tstep)
348  end do
349  end if
350 
351  end subroutine simcomp_executor_preprocess
352 
356  subroutine simcomp_executor_compute(this, t, tstep)
357  class(simcomp_executor_t), intent(inout) :: this
358  real(kind=rp), intent(in) :: t
359  integer, intent(in) :: tstep
360  integer :: i
361 
362  if (.not. this%finalized) call this%finalize()
363 
364  if (allocated(this%simcomps)) then
365  do i = 1, this%n_simcomps
366  call this%simcomps(i)%simcomp%compute(t, tstep)
367  end do
368  end if
369 
370  end subroutine simcomp_executor_compute
371 
374  subroutine simcomp_executor_restart(this, t)
375  class(simcomp_executor_t), intent(inout) :: this
376  real(kind=rp), intent(in) :: t
377  integer :: i
378 
379  if (allocated(this%simcomps)) then
380  do i = 1, this%n_simcomps
381  call this%simcomps(i)%simcomp%restart(t)
382  end do
383  end if
384 
385  end subroutine simcomp_executor_restart
386 
388  pure function simcomp_executor_get_n(this) result(n)
389  class(simcomp_executor_t), intent(in) :: this
390  integer :: n
391 
392  n = this%n_simcomps
393  end function simcomp_executor_get_n
394 
395 end module simcomp_executor
Retrieves a parameter by name or assigns a provided default value. In the latter case also adds the m...
Definition: json_utils.f90:54
Retrieves a parameter by name or throws an error.
Definition: json_utils.f90:45
Defines a simulation case.
Definition: case.f90:34
Utilities for retrieving parameters from the case files.
Definition: json_utils.f90:34
Logging routines.
Definition: log.f90:34
type(log_t), public neko_log
Global log stream.
Definition: log.f90:65
integer, parameter, public rp
Global precision used in computations.
Definition: num_types.f90:12
Contains the simcomp_executor_t type.
subroutine simcomp_executor_add(this, object, settings)
Appending a new simcomp to the executor.
type(simcomp_executor_t), target, public neko_simcomps
Global variable for the simulation component driver.
subroutine simcomp_executor_finalize(this)
Finalize the initialization. Sorts the simcomps based on the order property. Additionally we check th...
subroutine simcomp_executor_restart(this, t)
Execute restart for all simcomps.
subroutine simcomp_executor_init(this, case, simcomp_root)
Constructor.
subroutine simcomp_executor_compute(this, t, tstep)
Execute compute_ for all simcomps.
pure integer function simcomp_executor_get_n(this)
Get the number of simcomps.
subroutine simcomp_executor_free(this)
Destructor.
subroutine simcomp_executor_preprocess(this, t, tstep)
Execute preprocess_ for all simcomps.
Simulation components are objects that encapsulate functionality that can be fit to a particular comp...
Utilities.
Definition: utils.f90:35
Singleton type that serves as a driver for the simulation components. Stores all the components in th...
Base abstract class for simulation components.
A helper type that is needed to have an array of polymorphic objects.