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
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 end subroutine simcomp_executor_free
192
196 subroutine simcomp_executor_add(this, object, settings)
197 class(simcomp_executor_t), intent(inout) :: this
198 class(simulation_component_t), intent(in) :: object
199 type(json_file), intent(inout), optional :: settings
200
201 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
202 integer :: i, position
203
204 ! Find the first empty position
205 position = 0
206 do i = 1, this%n_simcomps
207 if (.not. allocated(this%simcomps(i)%simcomp)) then
208 position = i
209 exit
210 end if
211 end do
212
213 ! If no empty position was found, append to the end
214 if (position .eq. 0) then
215 if (this%n_simcomps .gt. 0) call move_alloc(this%simcomps, tmp_simcomps)
216 allocate(this%simcomps(this%n_simcomps + 1))
217
218 if (allocated(tmp_simcomps)) then
219 do i = 1, this%n_simcomps
220 call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(i)%simcomp)
221 end do
222 end if
223
224 this%n_simcomps = this%n_simcomps + 1
225 position = this%n_simcomps
226
227 if (allocated(tmp_simcomps)) deallocate(tmp_simcomps)
228 end if
229
230 this%simcomps(position)%simcomp = object
231 if (present(settings)) then
232 call this%simcomps(position)%simcomp%init(settings, this%case)
233 end if
234
235 this%finalized = .false.
236
237 end subroutine simcomp_executor_add
238
244 class(simcomp_executor_t), intent(inout) :: this
245 integer :: i, order, max_order
246 logical :: order_found, previous_found
247
248 class(simulation_component_wrapper_t), allocatable :: tmp_simcomps(:)
249 integer, allocatable :: order_list(:)
250
251 ! Check that all components are initialized
252 do i = 1, this%n_simcomps
253 if (.not. allocated(this%simcomps(i)%simcomp)) then
254 call neko_error("Simulation component not initialized.")
255 end if
256 end do
257
258 ! Check that the order is unique and contiguous
259 previous_found = .true.
260 do order = 1, this%n_simcomps
261 order_found = .false.
262 do i = 1, this%n_simcomps
263 if (this%simcomps(i)%simcomp%order == order .and. order_found) then
264 call neko_error("Simulation component order must be unique.")
265 else if (this%simcomps(i)%simcomp%order == order) then
266 order_found = .true.
267 end if
268 end do
269 if (order_found .and. .not. previous_found) then
270 call neko_error("Simulation component order must be contiguous " // &
271 "starting at 1.")
272 end if
273 previous_found = order_found
274 end do
275
276 allocate(order_list(this%n_simcomps))
277 order_list = 0
278 max_order = 0
279 do i = 1, this%n_simcomps
280 order_list(i) = this%simcomps(i)%simcomp%order
281 if (order_list(i) .gt. max_order) then
282 max_order = order_list(i)
283 end if
284 end do
285
286 do i = 1, this%n_simcomps
287 if (order_list(i) .eq. -1) then
288 order_list(i) = max_order + 1
289 max_order = max_order + 1
290 end if
291 end do
292
293 ! Check that the order is within bounds
294 do i = 1, this%n_simcomps
295 if (order_list(i) .gt. this%n_simcomps) then
296 deallocate(order_list)
297 call neko_error("Simulation component order is out of bounds.")
298 end if
299 end do
300
301 ! Reorder the simcomps based on the order specified
302 call move_alloc(this%simcomps, tmp_simcomps)
303 allocate(this%simcomps(this%n_simcomps))
304 do i = 1, this%n_simcomps
305 order = order_list(i)
306 call move_alloc(tmp_simcomps(i)%simcomp, this%simcomps(order)%simcomp)
307 end do
308
309 if (allocated(tmp_simcomps)) then
310 deallocate(tmp_simcomps)
311 end if
312 if (allocated(order_list)) then
313 deallocate(order_list)
314 end if
315
316 this%finalized = .true.
317 end subroutine simcomp_executor_finalize
318
321 subroutine simcomp_executor_preprocess(this, time)
322 class(simcomp_executor_t), intent(inout) :: this
323 type(time_state_t), intent(in) :: time
324 integer :: i
325
326 if (.not. this%finalized) call this%finalize()
327
328 if (allocated(this%simcomps)) then
329 do i = 1, size(this%simcomps)
330 call this%simcomps(i)%simcomp%preprocess(time)
331 end do
332 end if
333
334 end subroutine simcomp_executor_preprocess
335
338 subroutine simcomp_executor_compute(this, time)
339 class(simcomp_executor_t), intent(inout) :: this
340 type(time_state_t), intent(in) :: time
341 integer :: i
342
343 if (.not. this%finalized) call this%finalize()
344
345 if (allocated(this%simcomps)) then
346 do i = 1, this%n_simcomps
347 call this%simcomps(i)%simcomp%compute(time)
348 end do
349 end if
350
351 end subroutine simcomp_executor_compute
352
355 subroutine simcomp_executor_restart(this, time)
356 class(simcomp_executor_t), intent(inout) :: this
357 type(time_state_t), intent(in) :: time
358 integer :: i
359
360 if (allocated(this%simcomps)) then
361 do i = 1, this%n_simcomps
362 call this%simcomps(i)%simcomp%restart(time)
363 end do
364 end if
365
366 end subroutine simcomp_executor_restart
367
369 pure function simcomp_executor_get_n(this) result(n)
370 class(simcomp_executor_t), intent(in) :: this
371 integer :: n
372
373 n = this%n_simcomps
374 end function simcomp_executor_get_n
375
376end 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: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.