Neko 1.99.9
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, dp
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) :: preprocess => simcomp_executor_preprocess
71 procedure, pass(this) :: compute => simcomp_executor_compute
73 procedure, pass(this) :: restart=> simcomp_executor_restart
75 procedure, pass(this) :: time_to_next => simcomp_executor_time_to_next
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 this%simcomps(i)%init(comp_subdict, case)
169 end do
170
171 ! Cleanup
172 deallocate(order)
173 deallocate(read_order)
174 deallocate(mask)
175
176 call neko_log%end_section()
177 end subroutine simcomp_executor_init
178
180 subroutine simcomp_executor_free(this)
181 class(simcomp_executor_t), intent(inout) :: this
182 integer :: i
183
184 if (allocated(this%simcomps)) then
185 do i = 1, this%n_simcomps
186 call this%simcomps(i)%free()
187 end do
188 deallocate(this%simcomps)
189 end if
190
191 nullify(this%case)
192 this%finalized = .false.
193 end subroutine simcomp_executor_free
194
200 class(simcomp_executor_t), intent(inout) :: this
201 integer :: i, j, order, max_order
202 logical :: order_found, previous_found
203
204 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
205 integer, allocatable :: order_list(:)
206
207 ! Check that all components are initialized
208 do i = 1, this%n_simcomps
209 if (.not. this%simcomps(i)%is_allocated()) then
210 call neko_error("Simulation component not initialized.")
211 end if
212 end do
213
214 ! Check that the order is unique and contiguous
215 previous_found = .true.
216 do order = 1, this%n_simcomps
217 order_found = .false.
218 do i = 1, this%n_simcomps
219 if (this%simcomps(i)%simcomp%order == order .and. order_found) then
220 call neko_error("Simulation component order must be unique.")
221 else if (this%simcomps(i)%simcomp%order == order) then
222 order_found = .true.
223 end if
224 end do
225 if (order_found .and. .not. previous_found) then
226 call neko_error("Simulation component order must be contiguous " // &
227 "starting at 1.")
228 end if
229 previous_found = order_found
230 end do
231
232 allocate(order_list(this%n_simcomps))
233 order_list = 0
234 max_order = 0
235 do i = 1, this%n_simcomps
236 order_list(i) = this%simcomps(i)%simcomp%order
237 if (order_list(i) .gt. max_order) then
238 max_order = order_list(i)
239 end if
240 end do
241
242 do i = 1, this%n_simcomps
243 if (order_list(i) .eq. -1) then
244 order_list(i) = max_order + 1
245 max_order = max_order + 1
246 end if
247 end do
248
249 ! Check that the order is within bounds
250 do i = 1, this%n_simcomps
251 if (order_list(i) .gt. this%n_simcomps) then
252 deallocate(order_list)
253 call neko_error("Simulation component order is out of bounds.")
254 end if
255 end do
256
257 ! Reorder the simcomps based on the order specified
258 call move_alloc(this%simcomps, tmp_simcomps)
259 allocate(this%simcomps(this%n_simcomps))
260 do i = 1, this%n_simcomps
261 order = order_list(i)
262 call this%simcomps(order)%move_from(tmp_simcomps(i))
263 call tmp_simcomps(i)%free()
264 end do
265
266 if (allocated(tmp_simcomps)) then
267 deallocate(tmp_simcomps)
268 end if
269 if (allocated(order_list)) then
270 deallocate(order_list)
271 end if
272
273 ! Check that names are unique
274 do i = 1, this%n_simcomps - 1
275 do j = i + 1, this%n_simcomps
276 associate(simcomp_i => this%simcomps(i)%simcomp, &
277 simcomp_j => this%simcomps(j)%simcomp)
278 if (simcomp_i%name .eq. simcomp_j%name) then
279 call neko_error("Simulation component names must be unique. " &
280 // "Duplicate name: " // trim(simcomp_i%name))
281 end if
282 end associate
283 end do
284 end do
285
286 this%finalized = .true.
287 end subroutine simcomp_executor_finalize
288
291 subroutine simcomp_executor_preprocess(this, time)
292 class(simcomp_executor_t), intent(inout) :: this
293 type(time_state_t), intent(in) :: time
294 integer :: i
295
296 if (.not. this%finalized) call this%finalize()
297
298 if (allocated(this%simcomps)) then
299 do i = 1, size(this%simcomps)
300 call this%simcomps(i)%simcomp%preprocess(time)
301 end do
302 end if
303
304 end subroutine simcomp_executor_preprocess
305
310 function simcomp_executor_time_to_next(this, time) &
311 result(t)
312 class(simcomp_executor_t), intent(in) :: this
313 type(time_state_t), intent(in) :: time
314 real(kind=dp) :: t
315 integer :: i
316
317 t = huge(0.0_dp)
318 if (allocated(this%simcomps)) then
319 do i = 1, size(this%simcomps)
320 if (allocated(this%simcomps(i)%simcomp)) then
321 t = min(t, this%simcomps(i)%simcomp%time_to_next(time))
322 end if
323 end do
324 end if
325
327
330 subroutine simcomp_executor_compute(this, time)
331 class(simcomp_executor_t), intent(inout) :: this
332 type(time_state_t), intent(in) :: time
333 integer :: i
334
335 if (.not. this%finalized) call this%finalize()
336
337 if (allocated(this%simcomps)) then
338 do i = 1, this%n_simcomps
339 call this%simcomps(i)%simcomp%compute(time)
340 end do
341 end if
342
343 end subroutine simcomp_executor_compute
344
347 subroutine simcomp_executor_restart(this, time)
348 class(simcomp_executor_t), intent(inout) :: this
349 type(time_state_t), intent(in) :: time
350 integer :: i
351
352 if (allocated(this%simcomps)) then
353 do i = 1, this%n_simcomps
354 call this%simcomps(i)%simcomp%restart(time)
355 end do
356 end if
357
358 end subroutine simcomp_executor_restart
359
361 pure function simcomp_executor_get_n(this) result(n)
362 class(simcomp_executor_t), intent(in) :: this
363 integer :: n
364
365 n = this%n_simcomps
366 end function simcomp_executor_get_n
367
368end 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:92
Object for handling masks in Neko.
Definition mask.f90:34
integer, parameter, public dp
Definition num_types.f90:10
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Contains the simcomp_executor_t type.
subroutine simcomp_executor_restart(this, time)
Execute restart for all simcomps.
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.
real(kind=dp) function simcomp_executor_time_to_next(this, time)
The time until the next scheduled execution of preprocess, compute or the output of any simcomp,...
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.