Setting up a new simulation case in LEAP is easy. Advanced users may create a new case from scratch very quickly following the guide below. New users are encouraged to review the provided examples in /path/to/leap/cases
first.
There are several examples provided for both CDIFS and GRANS solvers:
- Laminar and turbulent channel flow
- Sphere rebound on a wall
- Settling sphere in an unbounded domain
- Flow Past a Cylinder
- Lamb-Oseen vortex tubes
- Taylor Green vortex
- Vortex dipole
- Vortex-wall Impingement
- Lid-driven cavity flow
- Homogeneous cooling
- Dry collisions between spheres and sphere-walls
- Granular channel flow
Each case directory contains the following basic structure
/path/to/case
-- src/
----- case_name.f90
----- Makefile
----- Makefile.in
-- bin/
-- mod/
-- obj/
If one of these examples suits your needs (e.g. same configuration, boundary conditions, and initialization), you may use it as it is. You still have control over a number of parameters using the simulation input file.
If you want to tweak the case, you may edit the file /path/to/case/src/case_name.f90
, then recompile the case.
Each case is a small Fortran program that generates files required to start up a simulation. Start-up files may include any or all of the following:
block.hdf5
) contains information about the Cartesian grid, number of grid points, and domain periodicity.bcs.hdf5
) describes the different domain boundaries (called Regions in LEAP), Dirichlet and Neumann conditions for each applicable variable.fields_ini.h5
) contains initial conditions for fields like velocity and pressure.ib_ini.h5
) contains Immersed Boundary data at start-up.rp_ini_centers.h5
and rp_ini_markers.h5
) contain initial data for Resolved Particle centers and surface points.pp_ini.h5
) contains initial data for Point Particles.To create your own case, start by creating a repository with the following basic structure
cd /path/to/new_case
mkdir src bin mod obj
Add a Makefile to the src directory.
# Create a Makefile
cat << 'EOF' > src/Makefile
include Makefile.in
MODULES :=
BINFILES := $(patsubst %.f90, $(BDIR)/%, $(notdir $(wildcard *.f90)))
VPATH := $(MODULES)
# Included modules
INCFLAGS += $(H5HUT_INC) $(LEAP_INC) $(HYPRE_INC) $(SILO_INC)
# External libraries
LIBS := $(LEAP_LIB) $(H5HUT_LIB) $(HDF5P_LIB) $(HYPRE_LIB) $(SILO_LIB)
# Source files, module files will add to this
SRC :=
# Add all module files
include $(patsubst %, %/module.mk, $(MODULES))
SRC += $(notdir $(wildcard *.f90))
# Object files
OBJFILES := $(patsubst %.f90,$(ODIR)/%.o, $(notdir $(SRC)))
MODFILES := $(patsubst %.f90,$(MDIR)/%.mod, $(notdir $(SRC)))
all:
@make progs
debug:
@make progs "FLAGS=$(DBGFLAGS)"
opt:
@make progs "FLAGS=$(OPTFLAGS)"
progs: $(BINFILES)
$(BDIR)/% : $(ODIR)/%.o
$(LD) $(LDFLAGS) $(FLAGS) $(ODIR)/$(notdir $@.o) $(LIBS) -o $@
$(ODIR)/%.o: %.f90
$(F90) $(PPFLAGS) $(FLAGS) $(INCFLAGS) -c $< -o $@ $(MODFLAGS)
clean:
rm -f $(OBJFILES)
rm -f $(MODFILES)
rm -f $(BINFILES)
EOF
Next, add a Makefile.in. Make sure to update the paths in this file.
# Create a Makefile.in
cat << 'EOF' > src/Makefile.in
#!bash
HOMEDIR = $(shell pwd | sed -e 's/\/src.*//')
MDIR = $(HOMEDIR)/mod
ODIR = $(HOMEDIR)/obj
BDIR = $(HOMEDIR)/bin
# Compiler and archiver
F90 = mpif90
LD = mpif90
AR = ar rcv
RL = ranlib
# Flags (using GNU compilers)
LDFLAGS =
INCFLAGS = -I$(MDIR)
MODFLAGS = -J$(MDIR)
PPFLAGS = -cpp
DBGFLAGS = -g -fcheck=all -fbacktrace -Wall -Wunderflow -ffpe-trap=invalid -fbounds-check -ffree-line-length-none
OPTFLAGS = -O3 -march=native -ffree-line-length-none
#LEAP
LEAP_DIR = /path/to/leap
LEAP_INC = -I$(LEAP_DIR)/mod
LEAP_LIB = -Wl,-rpath -Wl,$(LEAP_DIR)/lib -L$(LEAP_DIR)/lib \
-lleap
#HF5P
HDF5P_DIR = /path/to/ThirdParty/hdf5
HDF5P_INC = -I$(HDF5P_DIR)/include
HDF5P_LIB = -Wl,-rpath -Wl,$(HDF5P_DIR)/lib -L$(HDF5P_DIR)/lib \
-lhdf5 -lhdf5_fortran
#H5HUT
H5HUT_DIR = /path/to/ThirdParty/h5hut
H5HUT_INC = -I$(H5HUT_DIR)/include
H5HUT_LIB = -Wl,-rpath -Wl,$(H5HUT_DIR)/lib -L$(H5HUT_DIR)/lib \
-lH5hutF -lH5hut
#HYPRE
HYPRE_DIR = /path/to/ThirdParty/hypre
HYPRE_INC = -I$(HYPRE_DIR)/include
HYPRE_LIB = -Wl,-rpath -Wl,$(HYPRE_DIR)/lib -L$(HYPRE_DIR)/lib \
-lHYPRE
#SZIP
SZIP_DIR = /path/to/ThirdParty/szip
SZIP_LIB = -Wl,-rpath -Wl,$(SZIP_DIR)/lib -L$(SZIP_DIR)/lib \
-lsz
#ZLIB
ZLIB_DIR = /path/to/ThirdParty/zlib
ZLIB_LIB = -Wl,-rpath -Wl,$(ZLIB_DIR)/lib -L$(ZLIB_DIR)/lib \
-lz
#SILO
SILO_DIR = /path/to/ThirdParty/silo
SILO_INC = -I$(SILO_DIR)/include
SILO_LIB = -Wl,-rpath -Wl,$(SILO_DIR)/lib -L$(SILO_DIR)/lib \
-lsiloh5 $(SZIP_LIB) $(ZLIB_LIB) \
-L/usr/local/lib -lstdc++
EOF
Create your case program (my_new_case.f90
) and place it under /path/to/new_case/src
. For example a generic case looks like this.
!> Contents of my_new_case.f90
program main
!>--------------------------------------------------------------------------
! This is a generic program, to be filled by user.
! --------------------------------------------------------------------------
use leapKinds
use leapParser
use leapParallel
use leapBlock
use leapEulerian
implicit none
type(parallel_obj) :: parallel !! Utility that handles parallel (MPI) functions
type(parser_obj) :: parser !! Utility that parses input files
type(block_obj) :: block !! Block object manages the Cartesian grid
! Initialize parser
call parser%Initialize()
! Parse input file
call parser%ParseFile()
! Initialize parallel environment
call parallel%Initialize()
! Set the block info
call SetUpCaseBlock()
! Set the initial fields
call SetUpCaseFields()
! Set the immersed boundary
call SetUpCaseIB()
! Set boundary conditions
call SetUpCaseBCS()
! Free up data
call block%Finalize()
call parser%Finalize()
call parallel%Finalize()
contains
subroutine SetUpCaseBlock()
!> Setup and write the block file
implicit none
! Work variables
character(str64) :: filename
! Get info from parser
call parser%Get("Block file", filename)
! ...
! ...
! ...
! Write block to disk
call block%Write(filename)
return
end subroutine SetUpCaseBlock
subroutine SetUpCaseFields()
!> Set the initial flow field
implicit none
! Work variables
type(Eulerian_set) :: fields
character(str64) :: filename
! Get info from parser
call parser%Get("Fields IC file", filename)
! Initialize fields container
call fields%Initialize(block,parallel)
! ...
! ...
! ...
! Write data to disk
call fields%SetWriteFileName(filename)
call fields%Write(0,0.0_wp)
! Clear data
call fields%Finalize()
return
end subroutine SetUpCaseFields
subroutine SetUpCaseIB
!> Setup the immersed boundaries
use immersed_boundaries
implicit none
type(marker_set) :: IB
character(str64) :: filename
! Get info from parser
call parser%Get("IB IC file", filename)
! Initialize Immersed Boundaries
call IB%Initialize('IB',block,parallel)
! ...
! ...
! ...
! Write data to disk
call IB%SetWriteFileName(filename)
call IB%Write(0,0.0_WP)
! Finalize
call IB%Finalize()
return
end subroutine SetUpCaseIB
subroutine SetUpCaseBCS()
!> Setup the boundary conditions
use leapBC
implicit none
! Work variables
type(bc_set) :: bcs
! Initialize utility that handles boundary conditions
call bcs%Initialize(block,parallel)
! ...
! ...
! ...
! Write boundary conditions
call bcs%Write(0,0.0_wp)
! Clear data
call bcs%Finalize()
return
end subroutine SetUpCaseBCS
end program main
The parser utility in this example will read parameters (like density, viscosity, etc) form an input file (a text file), such that not everything needs to be hard coded in the Fortran source code.
Once you are done building your case, compile the program to get the case binaries
cd /path/to/new_case/src
make debug # or make opt
You can now run your simulation.
First, call your case to generate initialization files, then, call LEAP to integrate the solution forward in time:
# Go to simulation folder
cd /path/to/simulation/folder
# Example for a simulation with 4 cores
mpiexec -n 4 /path/to/new_case/bin/my_new_case -i input
mpiexec -n 4 /path/to/leap/bin/leap -i input