Neko  0.8.99
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
54  class(simulation_component_wrapper_t), allocatable :: simcomps(:)
56  integer :: n_simcomps
58  type(case_t), pointer :: case
59  contains
61  procedure, pass(this) :: init => simcomp_executor_init
63  procedure, pass(this) :: free => simcomp_executor_free
65  procedure, pass(this) :: add_user_simcomp => simcomp_executor_add
67  procedure, pass(this) :: preprocess => simcomp_executor_preprocess
69  procedure, pass(this) :: compute => simcomp_executor_compute
71  procedure, pass(this) :: restart=> simcomp_executor_restart
73  procedure, pass(this) :: finalize => simcomp_executor_finalize
74  end type simcomp_executor_t
75 
77  type(simcomp_executor_t), target, public :: neko_simcomps
78 
79 contains
80 
85  subroutine simcomp_executor_init(this, case, simcomp_root)
86  class(simcomp_executor_t), intent(inout) :: this
87  type(case_t), target, intent(inout) :: case
88  character(len=*), optional, intent(in) :: simcomp_root
89  integer :: n_simcomps, i
90  type(json_core) :: core
91  type(json_value), pointer :: simcomp_object
92  type(json_file) :: comp_subdict
93  logical :: found, is_user, has_user
94  ! Help array for finding minimal values
95  logical, allocatable :: mask(:)
96  ! The order value for each simcomp in order of appearance in the case file.
97  integer, allocatable :: read_order(:), order(:)
98  ! Location of the min value
99  integer :: loc(1)
100  integer :: max_order
101  character(len=:), allocatable :: root_name, comp_type
102 
103  call this%free()
104  this%case => case
105 
106  ! Get the root name of the simulation components if specified
107  if (present(simcomp_root)) then
108  root_name = simcomp_root
109  else
110  root_name = 'case.simulation_components'
111  end if
112 
113  ! Get the core json object and the simulation components object
114  call case%params%get_core(core)
115  call case%params%get(root_name, simcomp_object, found)
116  if (.not. found) return
117  call neko_log%section('Initialize simcomp')
118 
119  ! Set the number of simcomps and allocate the arrays
120  call case%params%info(root_name, n_children = n_simcomps)
121  this%n_simcomps = n_simcomps
122  allocate(this%simcomps(n_simcomps))
123  allocate(order(n_simcomps))
124  allocate(read_order(n_simcomps))
125  allocate(mask(n_simcomps), source = .true.)
126 
127  ! We need a separate loop to figure out the order, so that we can
128  ! apply the order to the initialization as well.
129  max_order = 0
130  has_user = .false.
131  do i = 1, n_simcomps
132  ! Create a new json containing just the subdict for this simcomp
133  call json_extract_item(core, simcomp_object, i, comp_subdict)
134 
135  call json_get_or_default(comp_subdict, "is_user", is_user, .false.)
136  has_user = has_user .or. is_user
137 
138  call json_get_or_default(comp_subdict, "order", read_order(i), -1)
139  if (read_order(i) .gt. max_order) then
140  max_order = read_order(i)
141  end if
142  end do
143 
144  ! If the order was not specified, we use the order of appearance in the
145  ! case file.
146  do i = 1, n_simcomps
147  if (read_order(i) == -1) then
148  max_order = max_order + 1
149  read_order(i) = max_order
150  end if
151  end do
152 
153  ! Figure out the execution order using a poor man's argsort.
154  ! Searches for the location of the min value, each time masking out the
155  ! found location prior to the next search.
156  do i = 1, n_simcomps
157  loc = minloc(read_order, mask = mask)
158  order(i) = loc(1)
159  mask(loc) = .false.
160  end do
161 
162  ! Init in the determined order.
163  do i = 1, n_simcomps
164  call json_extract_item(core, simcomp_object, order(i), comp_subdict)
165 
166  ! Log the component type if it is not a user component
167  call json_get(comp_subdict, "type", comp_type)
168  call json_get_or_default(comp_subdict, "is_user", is_user, .false.)
169  if (.not. is_user) call neko_log%message('- ' // trim(comp_type))
170 
171  call simulation_component_factory(this%simcomps(i)%simcomp, &
172  comp_subdict, case)
173  end do
174 
175  if (has_user) then
176  call neko_log%message('Initialize user simcomp')
177 
178  comp_subdict = json_file(simcomp_object)
179  call case%usr%init_user_simcomp(comp_subdict)
180  end if
181 
182  ! Cleanup
183  call neko_simcomps%finalize()
184  deallocate(order)
185  deallocate(read_order)
186  deallocate(mask)
187 
188  call neko_log%end_section()
189  end subroutine simcomp_executor_init
190 
192  subroutine simcomp_executor_free(this)
193  class(simcomp_executor_t), intent(inout) :: this
194  integer :: i
195 
196  if (allocated(this%simcomps)) then
197  do i = 1, this%n_simcomps
198  call this%simcomps(i)%simcomp%free
199  end do
200  deallocate(this%simcomps)
201  end if
202  end subroutine simcomp_executor_free
203 
207  subroutine simcomp_executor_add(this, object, settings)
208  class(simcomp_executor_t), intent(inout) :: this
209  class(simulation_component_t), intent(in) :: object
210  type(json_file), intent(inout) :: settings
211 
212  class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
213  integer :: i, position
214 
215  ! Find the first empty position
216  position = 0
217  do i = 1, this%n_simcomps
218  if (.not. allocated(this%simcomps(i)%simcomp)) then
219  position = i
220  exit
221  end if
222  end do
223 
224  ! If no empty position was found, append to the end
225  if (position == 0) then
226  call move_alloc(this%simcomps, tmp_simcomps)
227  allocate(this%simcomps(this%n_simcomps + 1))
228 
229  if (allocated(tmp_simcomps)) then
230  do i = 1, this%n_simcomps
231  call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(i)%simcomp)
232  end do
233  deallocate(tmp_simcomps)
234  end if
235 
236  this%n_simcomps = this%n_simcomps + 1
237  position = this%n_simcomps
238  end if
239 
240  this%simcomps(position)%simcomp = object
241  call this%simcomps(position)%simcomp%init(settings, this%case)
242 
243  if (allocated(tmp_simcomps)) then
244  deallocate(tmp_simcomps)
245  end if
246 
247  end subroutine simcomp_executor_add
248 
253  subroutine simcomp_executor_finalize(this)
254  class(simcomp_executor_t), intent(inout) :: this
255  integer :: i, order, max_order
256  logical :: order_found, previous_found
257 
258  class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
259  integer, allocatable :: order_list(:)
260 
261  ! Check that all components are initialized
262  do i = 1, this%n_simcomps
263  if (.not. allocated(this%simcomps(i)%simcomp)) then
264  call neko_error("Simulation component not initialized.")
265  end if
266  end do
267 
268  ! Check that the order is unique and contiguous
269  previous_found = .true.
270  do order = 1, this%n_simcomps
271  order_found = .false.
272  do i = 1, this%n_simcomps
273  if (this%simcomps(i)%simcomp%order == order .and. order_found) then
274  call neko_error("Simulation component order must be unique.")
275  else if (this%simcomps(i)%simcomp%order == order) then
276  order_found = .true.
277  end if
278  end do
279  if (order_found .and. .not. previous_found) then
280  call neko_error("Simulation component order must be contiguous &
281  &starting at 1.")
282  end if
283  previous_found = order_found
284  end do
285 
286  allocate(order_list(this%n_simcomps))
287  order_list = 0
288  max_order = 0
289  do i = 1, this%n_simcomps
290  order_list(i) = this%simcomps(i)%simcomp%order
291  if (order_list(i) .gt. max_order) then
292  max_order = order_list(i)
293  end if
294  end do
295 
296  do i = 1, this%n_simcomps
297  if (order_list(i) .eq. -1) then
298  order_list(i) = max_order + 1
299  max_order = max_order + 1
300  end if
301  end do
302 
303  ! Check that the order is within bounds
304  do i = 1, this%n_simcomps
305  if (order_list(i) .gt. this%n_simcomps) then
306  deallocate(order_list)
307  call neko_error("Simulation component order is out of bounds.")
308  end if
309  end do
310 
311  ! Reorder the simcomps based on the order specified
312  call move_alloc(this%simcomps, tmp_simcomps)
313  allocate(this%simcomps(this%n_simcomps))
314  do i = 1, this%n_simcomps
315  order = order_list(i)
316  call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(order)%simcomp)
317  end do
318 
319  if (allocated(tmp_simcomps)) then
320  deallocate(tmp_simcomps)
321  end if
322  if (allocated(order_list)) then
323  deallocate(order_list)
324  end if
325 
326  end subroutine simcomp_executor_finalize
330  subroutine simcomp_executor_preprocess(this, t, tstep)
331  class(simcomp_executor_t), intent(inout) :: this
332  real(kind=rp), intent(in) :: t
333  integer, intent(in) :: tstep
334  integer :: i
335 
336  if (allocated(this%simcomps)) then
337  do i = 1, size(this%simcomps)
338  call this%simcomps(i)%simcomp%preprocess(t, tstep)
339  end do
340  end if
341 
342  end subroutine simcomp_executor_preprocess
343 
347  subroutine simcomp_executor_compute(this, t, tstep)
348  class(simcomp_executor_t), intent(inout) :: this
349  real(kind=rp), intent(in) :: t
350  integer, intent(in) :: tstep
351  integer :: i
352 
353  if (allocated(this%simcomps)) then
354  do i = 1, this%n_simcomps
355  call this%simcomps(i)%simcomp%compute(t, tstep)
356  end do
357  end if
358 
359  end subroutine simcomp_executor_compute
360 
363  subroutine simcomp_executor_restart(this, t)
364  class(simcomp_executor_t), intent(inout) :: this
365  real(kind=rp), intent(in) :: t
366  integer :: i
367 
368  if (allocated(this%simcomps)) then
369  do i = 1, this%n_simcomps
370  call this%simcomps(i)%simcomp%restart(t)
371  end do
372  end if
373 
374  end subroutine simcomp_executor_restart
375 
376 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:53
Retrieves a parameter by name or throws an error.
Definition: json_utils.f90:44
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:61
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.
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.