Neko 1.99.1
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-2025, 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 => 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
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 do i = 1, n_simcomps
134 ! Create a new json containing just the subdict for this simcomp
135 call json_extract_item(case%params, root_name, i, comp_subdict)
136
137 call json_get_or_default(comp_subdict, "order", read_order(i), -1)
138 if (read_order(i) .gt. max_order) then
139 max_order = read_order(i)
140 end if
141 end do
142
143 ! If the order was not specified, we use the order of appearance in the
144 ! case file.
145 do i = 1, n_simcomps
146 if (read_order(i) == -1) then
147 max_order = max_order + 1
148 read_order(i) = max_order
149 end if
150 end do
151
152 ! Figure out the execution order using a poor man's argsort.
153 ! Searches for the location of the min value, each time masking out the
154 ! found location prior to the next search.
155 do i = 1, n_simcomps
156 loc = minloc(read_order, mask = mask)
157 order(i) = loc(1)
158 mask(loc) = .false.
159 end do
160
161 ! Init in the determined order.
162 do i = 1, n_simcomps
163 call json_extract_item(case%params, root_name, order(i), comp_subdict)
164
165 ! Log the component type
166 call json_get(comp_subdict, "type", comp_type)
167 call neko_log%message('- ' // trim(comp_type))
168
169 call simulation_component_factory(this%simcomps(i)%simcomp, &
170 comp_subdict, case)
171 end do
172
173 ! Cleanup
174 deallocate(order)
175 deallocate(read_order)
176 deallocate(mask)
177
178 call neko_log%end_section()
179 end subroutine simcomp_executor_init
180
182 subroutine simcomp_executor_free(this)
183 class(simcomp_executor_t), intent(inout) :: this
184 integer :: i
185
186 if (allocated(this%simcomps)) then
187 do i = 1, this%n_simcomps
188 call this%simcomps(i)%simcomp%free
189 end do
190 deallocate(this%simcomps)
191 end if
192 end subroutine simcomp_executor_free
193
197 subroutine simcomp_executor_add(this, object, settings)
198 class(simcomp_executor_t), intent(inout) :: this
199 class(simulation_component_t), intent(in) :: object
200 type(json_file), intent(inout), optional :: settings
201
202 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
203 integer :: i, position
204
205 ! Find the first empty position
206 position = 0
207 do i = 1, this%n_simcomps
208 if (.not. allocated(this%simcomps(i)%simcomp)) then
209 position = i
210 exit
211 end if
212 end do
213
214 ! If no empty position was found, append to the end
215 if (position .eq. 0) then
216 if (this%n_simcomps .gt. 0) call move_alloc(this%simcomps, tmp_simcomps)
217 allocate(this%simcomps(this%n_simcomps + 1))
218
219 if (allocated(tmp_simcomps)) then
220 do i = 1, this%n_simcomps
221 call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(i)%simcomp)
222 end do
223 end if
224
225 this%n_simcomps = this%n_simcomps + 1
226 position = this%n_simcomps
227
228 if (allocated(tmp_simcomps)) deallocate(tmp_simcomps)
229 end if
230
231 this%simcomps(position)%simcomp = object
232 if (present(settings)) then
233 call this%simcomps(position)%simcomp%init(settings, this%case)
234 end if
235
236 this%finalized = .false.
237
238 end subroutine simcomp_executor_add
239
245 class(simcomp_executor_t), intent(inout) :: this
246 integer :: i, order, max_order
247 logical :: order_found, previous_found
248
249 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
250 integer, allocatable :: order_list(:)
251
252 ! Check that all components are initialized
253 do i = 1, this%n_simcomps
254 if (.not. allocated(this%simcomps(i)%simcomp)) then
255 call neko_error("Simulation component not initialized.")
256 end if
257 end do
258
259 ! Check that the order is unique and contiguous
260 previous_found = .true.
261 do order = 1, this%n_simcomps
262 order_found = .false.
263 do i = 1, this%n_simcomps
264 if (this%simcomps(i)%simcomp%order == order .and. order_found) then
265 call neko_error("Simulation component order must be unique.")
266 else if (this%simcomps(i)%simcomp%order == order) then
267 order_found = .true.
268 end if
269 end do
270 if (order_found .and. .not. previous_found) then
271 call neko_error("Simulation component order must be contiguous " // &
272 "starting at 1.")
273 end if
274 previous_found = order_found
275 end do
276
277 allocate(order_list(this%n_simcomps))
278 order_list = 0
279 max_order = 0
280 do i = 1, this%n_simcomps
281 order_list(i) = this%simcomps(i)%simcomp%order
282 if (order_list(i) .gt. max_order) then
283 max_order = order_list(i)
284 end if
285 end do
286
287 do i = 1, this%n_simcomps
288 if (order_list(i) .eq. -1) then
289 order_list(i) = max_order + 1
290 max_order = max_order + 1
291 end if
292 end do
293
294 ! Check that the order is within bounds
295 do i = 1, this%n_simcomps
296 if (order_list(i) .gt. this%n_simcomps) then
297 deallocate(order_list)
298 call neko_error("Simulation component order is out of bounds.")
299 end if
300 end do
301
302 ! Reorder the simcomps based on the order specified
303 call move_alloc(this%simcomps, tmp_simcomps)
304 allocate(this%simcomps(this%n_simcomps))
305 do i = 1, this%n_simcomps
306 order = order_list(i)
307 call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(order)%simcomp)
308 end do
309
310 if (allocated(tmp_simcomps)) then
311 deallocate(tmp_simcomps)
312 end if
313 if (allocated(order_list)) then
314 deallocate(order_list)
315 end if
316
317 this%finalized = .true.
318 end subroutine simcomp_executor_finalize
319
322 subroutine simcomp_executor_preprocess(this, time)
323 class(simcomp_executor_t), intent(inout) :: this
324 type(time_state_t), intent(in) :: time
325 integer :: i
326
327 if (.not. this%finalized) call this%finalize()
328
329 if (allocated(this%simcomps)) then
330 do i = 1, size(this%simcomps)
331 call this%simcomps(i)%simcomp%preprocess(time)
332 end do
333 end if
334
335 end subroutine simcomp_executor_preprocess
336
339 subroutine simcomp_executor_compute(this, time)
340 class(simcomp_executor_t), intent(inout) :: this
341 type(time_state_t), intent(in) :: time
342 integer :: i
343
344 if (.not. this%finalized) call this%finalize()
345
346 if (allocated(this%simcomps)) then
347 do i = 1, this%n_simcomps
348 call this%simcomps(i)%simcomp%compute(time)
349 end do
350 end if
351
352 end subroutine simcomp_executor_compute
353
356 subroutine simcomp_executor_restart(this, time)
357 class(simcomp_executor_t), intent(inout) :: this
358 type(time_state_t), intent(in) :: time
359 integer :: i
360
361 if (allocated(this%simcomps)) then
362 do i = 1, this%n_simcomps
363 call this%simcomps(i)%simcomp%restart(time)
364 end do
365 end if
366
367 end subroutine simcomp_executor_restart
368
370 pure function simcomp_executor_get_n(this) result(n)
371 class(simcomp_executor_t), intent(in) :: this
372 integer :: n
373
374 n = this%n_simcomps
375 end function simcomp_executor_get_n
376
377end 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:70
Object for handling masks in Neko.
Definition mask.f90:34
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.