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