Neko 0.9.99
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
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
84contains
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
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
395end 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.
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.