Loading [MathJax]/extensions/tex2jax.js
Neko 0.9.99
A portable framework for high-order spectral element flow simulations
All Classes Namespaces Files Functions Variables Typedefs Enumerator Macros Pages
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
41 use case, only : case_t
42 use time_state, only : time_state_t
43 use utils, only : neko_error
44 use logger, only : neko_log
45 implicit none
46 private
47
54 type, public :: simcomp_executor_t
55
57 class(simulation_component_wrapper_t), allocatable :: simcomps(:)
59 integer, private :: n_simcomps
61 type(case_t), pointer :: case
63 logical, private :: finalized = .false.
64 contains
66 procedure, pass(this) :: init => simcomp_executor_init
68 procedure, pass(this) :: free => simcomp_executor_free
70 procedure, pass(this) :: add_user_simcomp => simcomp_executor_add
72 procedure, pass(this) :: preprocess => simcomp_executor_preprocess
74 procedure, pass(this) :: compute => simcomp_executor_compute
76 procedure, pass(this) :: restart=> simcomp_executor_restart
78 procedure, private, pass(this) :: finalize => simcomp_executor_finalize
80 procedure, pass(this) :: get_n => simcomp_executor_get_n
81 end type simcomp_executor_t
82
84 type(simcomp_executor_t), target, public :: neko_simcomps
85
86contains
87
92 subroutine simcomp_executor_init(this, case, simcomp_root)
93 class(simcomp_executor_t), intent(inout) :: this
94 type(case_t), target, intent(inout) :: case
95 character(len=*), optional, intent(in) :: simcomp_root
96 integer :: n_simcomps, i
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 if (.not. (root_name .in. case%params)) return
120 call neko_log%section('Initialize simcomp')
121
122 ! Set the number of simcomps and allocate the arrays
123 call case%params%info(root_name, n_children = n_simcomps)
124 this%n_simcomps = n_simcomps
125 allocate(this%simcomps(n_simcomps))
126 allocate(order(n_simcomps))
127 allocate(read_order(n_simcomps))
128 allocate(mask(n_simcomps), source = .true.)
129
130 ! We need a separate loop to figure out the order, so that we can
131 ! apply the order to the initialization as well.
132 max_order = 0
133 has_user = .false.
134 do i = 1, n_simcomps
135 ! Create a new json containing just the subdict for this simcomp
136 call json_extract_item(case%params, root_name, i, comp_subdict)
137
138 call json_get_or_default(comp_subdict, "is_user", is_user, .false.)
139 has_user = has_user .or. is_user
140
141 call json_get_or_default(comp_subdict, "order", read_order(i), -1)
142 if (read_order(i) .gt. max_order) then
143 max_order = read_order(i)
144 end if
145 end do
146
147 ! If the order was not specified, we use the order of appearance in the
148 ! case file.
149 do i = 1, n_simcomps
150 if (read_order(i) == -1) then
151 max_order = max_order + 1
152 read_order(i) = max_order
153 end if
154 end do
155
156 ! Figure out the execution order using a poor man's argsort.
157 ! Searches for the location of the min value, each time masking out the
158 ! found location prior to the next search.
159 do i = 1, n_simcomps
160 loc = minloc(read_order, mask = mask)
161 order(i) = loc(1)
162 mask(loc) = .false.
163 end do
164
165 ! Init in the determined order.
166 do i = 1, n_simcomps
167 call json_extract_item(case%params, root_name, order(i), comp_subdict)
168
169 ! Log the component type if it is not a user component
170 call json_get(comp_subdict, "type", comp_type)
171 call json_get_or_default(comp_subdict, "is_user", is_user, .false.)
172 if (.not. is_user) call neko_log%message('- ' // trim(comp_type))
173
174 call simulation_component_factory(this%simcomps(i)%simcomp, &
175 comp_subdict, case)
176 end do
177
178 if (has_user) then
179 call neko_log%message('Initialize user simcomp')
180
181 call json_extract_object(case%params, root_name, comp_subdict)
182 call case%usr%init_user_simcomp(comp_subdict)
183 end if
184
185 ! Cleanup
186 deallocate(order)
187 deallocate(read_order)
188 deallocate(mask)
189
190 call neko_log%end_section()
191 end subroutine simcomp_executor_init
192
194 subroutine simcomp_executor_free(this)
195 class(simcomp_executor_t), intent(inout) :: this
196 integer :: i
197
198 if (allocated(this%simcomps)) then
199 do i = 1, this%n_simcomps
200 call this%simcomps(i)%simcomp%free
201 end do
202 deallocate(this%simcomps)
203 end if
204 end subroutine simcomp_executor_free
205
209 subroutine simcomp_executor_add(this, object, settings)
210 class(simcomp_executor_t), intent(inout) :: this
211 class(simulation_component_t), intent(in) :: object
212 type(json_file), intent(inout), optional :: settings
213
214 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
215 integer :: i, position
216
217 ! Find the first empty position
218 position = 0
219 do i = 1, this%n_simcomps
220 if (.not. allocated(this%simcomps(i)%simcomp)) then
221 position = i
222 exit
223 end if
224 end do
225
226 ! If no empty position was found, append to the end
227 if (position .eq. 0) then
228 if (this%n_simcomps .gt. 0) call move_alloc(this%simcomps, tmp_simcomps)
229 allocate(this%simcomps(this%n_simcomps + 1))
230
231 if (allocated(tmp_simcomps)) then
232 do i = 1, this%n_simcomps
233 call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(i)%simcomp)
234 end do
235 end if
236
237 this%n_simcomps = this%n_simcomps + 1
238 position = this%n_simcomps
239
240 if (allocated(tmp_simcomps)) deallocate(tmp_simcomps)
241 end if
242
243 this%simcomps(position)%simcomp = object
244 if (present(settings)) then
245 call this%simcomps(position)%simcomp%init(settings, this%case)
246 end if
247
248 this%finalized = .false.
249
250 end subroutine simcomp_executor_add
251
257 class(simcomp_executor_t), intent(inout) :: this
258 integer :: i, order, max_order
259 logical :: order_found, previous_found
260
261 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
262 integer, allocatable :: order_list(:)
263
264 ! Check that all components are initialized
265 do i = 1, this%n_simcomps
266 if (.not. allocated(this%simcomps(i)%simcomp)) then
267 call neko_error("Simulation component not initialized.")
268 end if
269 end do
270
271 ! Check that the order is unique and contiguous
272 previous_found = .true.
273 do order = 1, this%n_simcomps
274 order_found = .false.
275 do i = 1, this%n_simcomps
276 if (this%simcomps(i)%simcomp%order == order .and. order_found) then
277 call neko_error("Simulation component order must be unique.")
278 else if (this%simcomps(i)%simcomp%order == order) then
279 order_found = .true.
280 end if
281 end do
282 if (order_found .and. .not. previous_found) then
283 call neko_error("Simulation component order must be contiguous " // &
284 "starting at 1.")
285 end if
286 previous_found = order_found
287 end do
288
289 allocate(order_list(this%n_simcomps))
290 order_list = 0
291 max_order = 0
292 do i = 1, this%n_simcomps
293 order_list(i) = this%simcomps(i)%simcomp%order
294 if (order_list(i) .gt. max_order) then
295 max_order = order_list(i)
296 end if
297 end do
298
299 do i = 1, this%n_simcomps
300 if (order_list(i) .eq. -1) then
301 order_list(i) = max_order + 1
302 max_order = max_order + 1
303 end if
304 end do
305
306 ! Check that the order is within bounds
307 do i = 1, this%n_simcomps
308 if (order_list(i) .gt. this%n_simcomps) then
309 deallocate(order_list)
310 call neko_error("Simulation component order is out of bounds.")
311 end if
312 end do
313
314 ! Reorder the simcomps based on the order specified
315 call move_alloc(this%simcomps, tmp_simcomps)
316 allocate(this%simcomps(this%n_simcomps))
317 do i = 1, this%n_simcomps
318 order = order_list(i)
319 call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(order)%simcomp)
320 end do
321
322 if (allocated(tmp_simcomps)) then
323 deallocate(tmp_simcomps)
324 end if
325 if (allocated(order_list)) then
326 deallocate(order_list)
327 end if
328
329 this%finalized = .true.
330 end subroutine simcomp_executor_finalize
331
334 subroutine simcomp_executor_preprocess(this, time)
335 class(simcomp_executor_t), intent(inout) :: this
336 type(time_state_t), intent(in) :: time
337 integer :: i
338
339 if (.not. this%finalized) call this%finalize()
340
341 if (allocated(this%simcomps)) then
342 do i = 1, size(this%simcomps)
343 call this%simcomps(i)%simcomp%preprocess(time)
344 end do
345 end if
346
347 end subroutine simcomp_executor_preprocess
348
351 subroutine simcomp_executor_compute(this, time)
352 class(simcomp_executor_t), intent(inout) :: this
353 type(time_state_t), intent(in) :: time
354 integer :: i
355
356 if (.not. this%finalized) call this%finalize()
357
358 if (allocated(this%simcomps)) then
359 do i = 1, this%n_simcomps
360 call this%simcomps(i)%simcomp%compute(time)
361 end do
362 end if
363
364 end subroutine simcomp_executor_compute
365
368 subroutine simcomp_executor_restart(this, time)
369 class(simcomp_executor_t), intent(inout) :: this
370 type(time_state_t), intent(in) :: time
371 integer :: i
372
373 if (allocated(this%simcomps)) then
374 do i = 1, this%n_simcomps
375 call this%simcomps(i)%simcomp%restart(time)
376 end do
377 end if
378
379 end subroutine simcomp_executor_restart
380
382 pure function simcomp_executor_get_n(this) result(n)
383 class(simcomp_executor_t), intent(in) :: this
384 integer :: n
385
386 n = this%n_simcomps
387 end function simcomp_executor_get_n
388
389end module simcomp_executor
Retrieves a parameter by name or assigns a provided default value. In the latter case also adds the m...
Retrieves a parameter by name or throws an error.
Defines a simulation case.
Definition case.f90:34
Utilities for retrieving parameters from the case files.
subroutine, public json_extract_object(json, name, object)
Extract object as a separate JSON dictionary.
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_restart(this, time)
Execute restart for all simcomps.
subroutine simcomp_executor_add(this, object, settings)
Appending a new simcomp to the executor.
subroutine simcomp_executor_compute(this, time)
Execute compute_ for all simcomps.
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_init(this, case, simcomp_root)
Constructor.
pure integer function simcomp_executor_get_n(this)
Get the number of simcomps.
subroutine simcomp_executor_preprocess(this, time)
Execute preprocess_ for all simcomps.
subroutine simcomp_executor_free(this)
Destructor.
Simulation components are objects that encapsulate functionality that can be fit to a particular comp...
Module with things related to the simulation time.
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.
A struct that contains all info about the time, expand as needed.