Neko  0.9.99
A portable framework for high-order spectral element flow simulations
case.f90
Go to the documentation of this file.
1 ! Copyright (c) 2020-2023, 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 !
34 module case
35  use num_types, only : rp, sp, dp
36  use fluid_pnpn, only : fluid_pnpn_t
37  use fluid_scheme, only : fluid_scheme_t, fluid_scheme_factory
38  use fluid_output, only : fluid_output_t
39  use chkp_output, only : chkp_output_t
42  use redist, only : redist_mesh
44  use flow_ic, only : set_flow_ic
45  use scalar_ic, only : set_scalar_ic
46  use stats, only : stats_t
47  use file, only : file_t
48  use utils, only : neko_error
49  use mesh, only : mesh_t
50  use comm
54  use user_intf, only : user_t
55  use scalar_pnpn, only : scalar_pnpn_t
56  use json_module, only : json_file, json_core, json_value
60  implicit none
61  private
62 
63  type, public :: case_t
64  type(mesh_t) :: msh
65  type(json_file) :: params
66  type(time_scheme_controller_t) :: ext_bdf
67  real(kind=rp), dimension(10) :: tlag
68  real(kind=rp), dimension(10) :: dtlag
69  real(kind=rp) :: dt
70  real(kind=rp) :: end_time
71  character(len=:), allocatable :: output_directory
73  type(fluid_output_t) :: f_out
74  type(chkp_output_t) :: f_chkp
75  type(user_t) :: usr
76  class(fluid_scheme_t), allocatable :: fluid
77  type(scalar_pnpn_t), allocatable :: scalar
78  end type case_t
79 
80  interface case_init
82  end interface case_init
83 
84  public :: case_init, case_free
85 
86 contains
87 
89  subroutine case_init_from_file(this, case_file)
90  type(case_t), target, intent(inout) :: this
91  character(len=*), intent(in) :: case_file
92  integer :: ierr, integer_val
93  character(len=:), allocatable :: json_buffer
94  logical :: exist
95 
96  ! Check if the file exists
97  inquire(file = trim(case_file), exist = exist)
98  if (.not. exist) then
99  call neko_error('The case file '//trim(case_file)//' does not exist.')
100  end if
101 
102  call neko_log%section('Case')
103  call neko_log%message('Reading case file ' // trim(case_file), &
105 
106  if (pe_rank .eq. 0) then
107  call this%params%load_file(filename = trim(case_file))
108  call this%params%print_to_string(json_buffer)
109  integer_val = len(json_buffer)
110  end if
111 
112  call mpi_bcast(integer_val, 1, mpi_integer, 0, neko_comm, ierr)
113  if (pe_rank .ne. 0) allocate(character(len = integer_val) :: json_buffer)
114  call mpi_bcast(json_buffer, integer_val, mpi_character, 0, neko_comm, ierr)
115  call this%params%load_from_string(json_buffer)
116 
117  deallocate(json_buffer)
118 
119  call case_init_common(this)
120 
121  end subroutine case_init_from_file
122 
124  subroutine case_init_from_json(this, case_json)
125  type(case_t), target, intent(inout) :: this
126  type(json_file), intent(in) :: case_json
127 
128  call neko_log%section('Case')
129  call neko_log%message('Creating case from JSON object', neko_log_quiet)
130 
131  this%params = case_json
132 
133  call case_init_common(this)
134 
135  end subroutine case_init_from_json
136 
138  subroutine case_init_common(this)
139  type(case_t), target, intent(inout) :: this
140  integer :: lx = 0
141  logical :: scalar = .false.
142  type(file_t) :: msh_file, bdry_file, part_file
143  type(mesh_fld_t) :: msh_part, parts
144  logical :: found, logical_val
145  integer :: integer_val
146  real(kind=rp) :: real_val
147  character(len = :), allocatable :: string_val
148  integer :: output_dir_len
149  integer :: precision
150 
151  !
152  ! Load mesh
153  !
154  call json_get_or_default(this%params, 'case.mesh_file', string_val, &
155  'no mesh')
156  if (trim(string_val) .eq. 'no mesh') then
157  call neko_error('The mesh_file keyword could not be found in the .' // &
158  'case file. Often caused by incorrectly formatted json.')
159  end if
160  msh_file = file_t(string_val)
161 
162  call msh_file%read(this%msh)
163 
164  !
165  ! Load Balancing
166  !
167  call json_get_or_default(this%params, 'case.load_balancing', logical_val,&
168  .false.)
169 
170  if (pe_size .gt. 1 .and. logical_val) then
171  call neko_log%section('Load Balancing')
172  call parmetis_partmeshkway(this%msh, parts)
173  call redist_mesh(this%msh, parts)
174  call neko_log%end_section()
175  end if
176 
177  !
178  ! Time step
179  !
180  call this%params%get('case.variable_timestep', logical_val, found)
181  if (.not. logical_val) then
182  call json_get(this%params, 'case.timestep', this%dt)
183  else
184  ! randomly set an initial dt to get cfl when dt is variable
185  this%dt = 1.0_rp
186  end if
187 
188  !
189  ! End time
190  !
191  call json_get(this%params, 'case.end_time', this%end_time)
192 
193  !
194  ! Initialize point_zones registry
195  !
196  call neko_point_zone_registry%init(this%params, this%msh)
197 
198  !
199  ! Setup user defined functions
200  !
201  call this%usr%init()
202  call this%usr%user_mesh_setup(this%msh)
203 
204  !
205  ! Set order of timestepper
206  !
207  call json_get(this%params, 'case.numerics.time_order', integer_val)
208  call this%ext_bdf%init(integer_val)
209 
210  !
211  ! Setup fluid scheme
212  !
213  call json_get(this%params, 'case.fluid.scheme', string_val)
214  call fluid_scheme_factory(this%fluid, trim(string_val))
215 
216  call json_get(this%params, 'case.numerics.polynomial_order', lx)
217  lx = lx + 1 ! add 1 to get number of gll points
218  this%fluid%chkp%tlag => this%tlag
219  this%fluid%chkp%dtlag => this%dtlag
220  call this%fluid%init(this%msh, lx, this%params, this%usr, this%ext_bdf)
221  select type (f => this%fluid)
222  type is (fluid_pnpn_t)
223  f%chkp%abx1 => f%abx1
224  f%chkp%abx2 => f%abx2
225  f%chkp%aby1 => f%aby1
226  f%chkp%aby2 => f%aby2
227  f%chkp%abz1 => f%abz1
228  f%chkp%abz2 => f%abz2
229  end select
230 
231 
232  !
233  ! Setup scratch registry
234  !
235  neko_scratch_registry = scratch_registry_t(this%fluid%dm_Xh, 10, 10)
236 
237  !
238  ! Setup scalar scheme
239  !
240  ! @todo no scalar factory for now, probably not needed
241  if (this%params%valid_path('case.scalar')) then
242  call json_get_or_default(this%params, 'case.scalar.enabled', scalar,&
243  .true.)
244  end if
245 
246  if (scalar) then
247  allocate(this%scalar)
248  this%scalar%chkp%tlag => this%tlag
249  this%scalar%chkp%dtlag => this%dtlag
250  call this%scalar%init(this%msh, this%fluid%c_Xh, this%fluid%gs_Xh, &
251  this%params, this%usr, this%fluid%ulag, this%fluid%vlag, &
252  this%fluid%wlag, this%ext_bdf, this%fluid%rho)
253 
254  call this%fluid%chkp%add_scalar(this%scalar%s)
255 
256  this%fluid%chkp%abs1 => this%scalar%abx1
257  this%fluid%chkp%abs2 => this%scalar%abx2
258  this%fluid%chkp%slag => this%scalar%slag
259  end if
260 
261  !
262  ! Setup user defined conditions
263  !
264  if (this%params%valid_path('case.fluid.inflow_condition')) then
265  call json_get(this%params, 'case.fluid.inflow_condition.type',&
266  string_val)
267  if (trim(string_val) .eq. 'user') then
268  call this%fluid%set_usr_inflow(this%usr%fluid_user_if)
269  end if
270  end if
271 
272  ! Setup user boundary conditions for the scalar.
273  if (scalar) then
274  call this%scalar%set_user_bc(this%usr%scalar_user_bc)
275  end if
276 
277  !
278  ! Setup initial conditions
279  !
280  call json_get(this%params, 'case.fluid.initial_condition.type',&
281  string_val)
282 
283  call neko_log%section("Fluid initial condition ")
284 
285  if (trim(string_val) .ne. 'user') then
286  call set_flow_ic(this%fluid%u, this%fluid%v, this%fluid%w, &
287  this%fluid%p, this%fluid%c_Xh, this%fluid%gs_Xh, string_val, &
288  this%params)
289 
290  else
291  call set_flow_ic(this%fluid%u, this%fluid%v, this%fluid%w, this%fluid%p,&
292  this%fluid%c_Xh, this%fluid%gs_Xh, this%usr%fluid_user_ic, &
293  this%params)
294  end if
295 
296  call neko_log%end_section()
297 
298  if (scalar) then
299 
300  call json_get(this%params, 'case.scalar.initial_condition.type', &
301  string_val)
302 
303  call neko_log%section("Scalar initial condition ")
304 
305  if (trim(string_val) .ne. 'user') then
306  call set_scalar_ic(this%scalar%s, &
307  this%scalar%c_Xh, this%scalar%gs_Xh, string_val, this%params)
308  else
309  call set_scalar_ic(this%scalar%s, &
310  this%scalar%c_Xh, this%scalar%gs_Xh, this%usr%scalar_user_ic, &
311  this%params)
312  end if
313 
314  call neko_log%end_section()
315 
316  end if
317 
318  ! Add initial conditions to BDF scheme (if present)
319  select type (f => this%fluid)
320  type is (fluid_pnpn_t)
321  call f%ulag%set(f%u)
322  call f%vlag%set(f%v)
323  call f%wlag%set(f%w)
324  end select
325 
326  !
327  ! Validate that the case is properly setup for time-stepping
328  !
329  call this%fluid%validate
330 
331  if (scalar) then
332  call this%scalar%slag%set(this%scalar%s)
333  call this%scalar%validate
334  end if
335 
336  !
337  ! Get and process output directory
338  !
339  call json_get_or_default(this%params, 'case.output_directory',&
340  this%output_directory, '')
341 
342  output_dir_len = len(trim(this%output_directory))
343  if (output_dir_len .gt. 0) then
344  if (this%output_directory(output_dir_len:output_dir_len) .ne. "/") then
345  this%output_directory = trim(this%output_directory)//"/"
346  if (pe_rank .eq. 0) then
347  call execute_command_line('mkdir -p '//this%output_directory)
348  end if
349  end if
350  end if
351 
352  !
353  ! Save boundary markings for fluid (if requested)
354  !
355  call json_get_or_default(this%params, 'case.output_boundary',&
356  logical_val, .false.)
357  if (logical_val) then
358  bdry_file = file_t(trim(this%output_directory)//'bdry.fld')
359  call bdry_file%write(this%fluid%bdry)
360  end if
361 
362  !
363  ! Save mesh partitions (if requested)
364  !
365  call json_get_or_default(this%params, 'case.output_partitions',&
366  logical_val, .false.)
367  if (logical_val) then
368  call mesh_field_init(msh_part, this%msh, 'MPI_Rank')
369  msh_part%data = pe_rank
370  part_file = file_t(trim(this%output_directory)//'partitions.vtk')
371  call part_file%write(msh_part)
372  call mesh_field_free(msh_part)
373  end if
374 
375  !
376  ! Setup output precision of the field files
377  !
378  call json_get_or_default(this%params, 'case.output_precision', string_val,&
379  'single')
380 
381  if (trim(string_val) .eq. 'double') then
382  precision = dp
383  else
384  precision = sp
385  end if
386 
387  !
388  ! Setup output_controller
389  !
390  call this%output_controller%init(this%end_time)
391  if (scalar) then
392  this%f_out = fluid_output_t(precision, this%fluid, this%scalar, &
393  path = trim(this%output_directory))
394  else
395  this%f_out = fluid_output_t(precision, this%fluid, &
396  path = trim(this%output_directory))
397  end if
398 
399  call json_get_or_default(this%params, 'case.fluid.output_control',&
400  string_val, 'org')
401 
402  if (trim(string_val) .eq. 'org') then
403  ! yes, it should be real_val below for type compatibility
404  call json_get(this%params, 'case.nsamples', real_val)
405  call this%output_controller%add(this%f_out, real_val, 'nsamples')
406  else if (trim(string_val) .eq. 'never') then
407  ! Fix a dummy 0.0 output_value
408  call json_get_or_default(this%params, 'case.fluid.output_value', &
409  real_val, 0.0_rp)
410  call this%output_controller%add(this%f_out, 0.0_rp, string_val)
411  else
412  call json_get(this%params, 'case.fluid.output_value', real_val)
413  call this%output_controller%add(this%f_out, real_val, string_val)
414  end if
415 
416  !
417  ! Save checkpoints (if nothing specified, default to saving at end of sim)
418  !
419  call json_get_or_default(this%params, 'case.output_checkpoints',&
420  logical_val, .true.)
421  if (logical_val) then
422  call json_get_or_default(this%params, 'case.checkpoint_format', &
423  string_val, "chkp")
424  this%f_chkp = chkp_output_t(this%fluid%chkp, &
425  path = this%output_directory, fmt = trim(string_val))
426  call json_get_or_default(this%params, 'case.checkpoint_control', &
427  string_val, "simulationtime")
428  call json_get_or_default(this%params, 'case.checkpoint_value', real_val,&
429  1e10_rp)
430  call this%output_controller%add(this%f_chkp, real_val, string_val)
431  end if
432 
433  !
434  ! Setup joblimit
435  !
436  if (this%params%valid_path('case.job_timelimit')) then
437  call json_get(this%params, 'case.job_timelimit', string_val)
438  call jobctrl_set_time_limit(string_val)
439  end if
440 
441  call neko_log%end_section()
442 
443  end subroutine case_init_common
444 
446  subroutine case_free(this)
447  type(case_t), intent(inout) :: this
448 
449  if (allocated(this%fluid)) then
450  call this%fluid%free()
451  deallocate(this%fluid)
452  end if
453 
454  if (allocated(this%scalar)) then
455  call this%scalar%free()
456  deallocate(this%scalar)
457  end if
458 
459  call this%msh%free()
460 
461  call this%f_out%free()
462 
463  call this%output_controller%free()
464 
465  end subroutine case_free
466 
467 end module case
Retrieves a parameter by name or assigns a provided default value. In the latter case also adds the m...
Definition: json_utils.f90:54
Retrieves a parameter by name or throws an error.
Definition: json_utils.f90:45
Defines a simulation case.
Definition: case.f90:34
subroutine case_init_from_file(this, case_file)
Initialize a case from an input file case_file.
Definition: case.f90:90
subroutine, public case_free(this)
Deallocate a case.
Definition: case.f90:447
subroutine case_init_from_json(this, case_json)
Initialize a case from a JSON object describing a case.
Definition: case.f90:125
subroutine case_init_common(this)
Initialize a case from its (loaded) params object.
Definition: case.f90:139
Defines an output for a checkpoint.
Definition: chkp_output.f90:34
Definition: comm.F90:1
integer pe_rank
MPI rank.
Definition: comm.F90:28
type(mpi_comm) neko_comm
MPI communicator.
Definition: comm.F90:16
integer pe_size
MPI size of communicator.
Definition: comm.F90:31
Module for file I/O operations.
Definition: file.f90:34
Initial flow condition.
Definition: flow_ic.f90:34
Defines an output for a fluid.
Modular version of the Classic Nek5000 Pn/Pn formulation for fluids.
Definition: fluid_pnpn.f90:34
Fluid formulations.
Job control.
Definition: jobctrl.f90:34
Utilities for retrieving parameters from the case files.
Definition: json_utils.f90:34
Logging routines.
Definition: log.f90:34
integer, parameter, public neko_log_quiet
Always logged.
Definition: log.f90:67
type(log_t), public neko_log
Global log stream.
Definition: log.f90:65
integer, parameter, public log_size
Definition: log.f90:42
Defines a mesh field.
Definition: mesh_field.f90:35
subroutine, public mesh_field_free(fld)
Definition: mesh_field.f90:73
subroutine, public mesh_field_init(fld, msh, fld_name)
Definition: mesh_field.f90:52
Defines a mesh.
Definition: mesh.f90:34
integer, parameter, public dp
Definition: num_types.f90:9
integer, parameter, public sp
Definition: num_types.f90:8
integer, parameter, public rp
Global precision used in computations.
Definition: num_types.f90:12
Implements output_controller_t
Interface to ParMETIS.
Definition: parmetis.F90:34
subroutine, public parmetis_partmeshkway(msh, parts, weights, nprts)
Compute a k-way partitioning of a mesh msh.
Definition: parmetis.F90:111
type(point_zone_registry_t), target, public neko_point_zone_registry
Global point_zone registry.
Redistribution routines.
Definition: redist.f90:34
subroutine, public redist_mesh(msh, parts)
Redistribute a mesh msh according to new partitions.
Definition: redist.f90:56
Scalar initial condition.
Definition: scalar_ic.f90:34
Containts the scalar_pnpn_t type.
Definition: scalar_pnpn.f90:35
Defines a registry for storing and requesting temporary fields This can be used when you have a funct...
type(scratch_registry_t), target, public neko_scratch_registry
Global scratch registry.
Defines a container for all statistics.
Definition: statistics.f90:34
Compound scheme for the advection and diffusion operators in a transport equation.
Interfaces for user interaction with NEKO.
Definition: user_intf.f90:34
Utilities.
Definition: utils.f90:35
A wrapper around a polymorphic generic_file_t that handles its init. This is essentially a factory fo...
Definition: file.f90:54
Base type of all fluid formulations.
Centralized controller for a list of outputs.
Statistics backend.
Definition: statistics.f90:48
Implements the logic to compute the time coefficients for the advection and diffusion operators in a ...