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