Setting Up a New Case

  • Mohamed Houssem Kasbaoui

Setting Up a New Case

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.

Modifying an existing case

There are several examples provided for both CDIFS and GRANS solvers:

  • CDIFS cases:
- 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
  • GRANS cases:
- 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.

Creating a case from scratch

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 file: (typically named block.hdf5) contains information about the Cartesian grid, number of grid points, and domain periodicity.
  • BCS file: (typically named bcs.hdf5) describes the different domain boundaries (called Regions in LEAP), Dirichlet and Neumann conditions for each applicable variable.
  • Fields file: (typically named fields_ini.h5) contains initial conditions for fields like velocity and pressure.
  • IB file: (typically named ib_ini.h5) contains Immersed Boundary data at start-up.
  • RP files: (2 files, typically named rp_ini_centers.h5 and rp_ini_markers.h5) contain initial data for Resolved Particle centers and surface points.
  • PP file: (typically named 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