Boundary Conditions#
SELF uses an extensible boundary condition system that lets you register custom boundary conditions for your models. This page explains how to use boundary conditions in practice.
For details on the internal architecture, see Boundary Condition System (Developer Guide).
Overview#
Every DG model in SELF maintains two boundary condition lists:
- Hyperbolic BCs -- conditions on the solution at domain boundaries
- Parabolic BCs -- conditions on the solution gradient at domain boundaries
You populate these lists by overriding the AdditionalInit method in your model subclass. SELF then automatically determines which mesh faces match each BC and calls your BC routines during time integration.
Available BC Types#
SELF provides the following built-in BC identifiers:
| Constant | Value | Category | Description |
|---|---|---|---|
SELF_BC_PRESCRIBED |
100 | Hyperbolic | Dirichlet-type: set the exterior state to a prescribed value |
SELF_BC_RADIATION |
101 | Hyperbolic | Radiation / non-reflecting outflow |
SELF_BC_NONORMALFLOW |
102 | Hyperbolic | Mirror / no-normal-flow (wall) |
SELF_BC_PRESCRIBED_STRESS |
200 | Parabolic | Prescribed stress on the gradient |
SELF_BC_NOSTRESS |
201 | Parabolic | Zero-stress (free-slip) |
These constants are defined in SELF_Mesh and are used both for tagging mesh faces and for registering BC implementations.
Workflow#
Setting up boundary conditions involves three steps:
- Tag mesh faces with BC identifiers
- Write BC subroutines that set exterior state values
- Register BCs in your model's
AdditionalInit
Step 1: Tag Mesh Faces#
After creating your mesh, assign BC identifiers to boundary faces. The simplest way is ResetBoundaryConditionType:
! Set left boundary to prescribed, right boundary to prescribed
call mesh%ResetBoundaryConditionType(SELF_BC_PRESCRIBED, SELF_BC_PRESCRIBED)
For 2D and 3D meshes, boundary face tagging is typically set via the mesh file or by modifying sideInfo(5,:,:) after mesh creation.
Step 2: Write BC Subroutines#
Each BC subroutine must match the SELF_bcMethod interface:
subroutine my_bc(bc, mymodel)
use SELF_BoundaryConditions
use SELF_Model
class(BoundaryCondition), intent(in) :: bc
class(Model), intent(inout) :: mymodel
endsubroutine
Inside the subroutine, use select type to access your model's data, then loop over bc%nBoundaries to set the exterior state on each boundary face:
subroutine hbc1d_Prescribed_mymodel(bc, mymodel)
class(BoundaryCondition), intent(in) :: bc
class(Model), intent(inout) :: mymodel
integer :: n, iEl, s
select type (m => mymodel)
class is (my_model_type)
do n = 1, bc%nBoundaries
iEl = bc%elements(n) ! element index
s = bc%sides(n) ! local side index
! Set the exterior boundary state
m%solution%extBoundary(s, iEl, 1:m%nvar) = ...
enddo
endselect
endsubroutine
Note
The extBoundary array holds the exterior (ghost) state used by the Riemann solver at domain boundaries. Setting extBoundary = boundary (the interior state) produces a mirror/no-normal-flow condition. Setting it to a specific value produces a Dirichlet condition.
Step 3: Register BCs in AdditionalInit#
Override AdditionalInit in your model subclass and register each BC:
subroutine AdditionalInit_mymodel(this)
class(my_model_type), intent(inout) :: this
procedure(SELF_bcMethod), pointer :: bcfunc
! Register a hyperbolic BC
bcfunc => hbc1d_Prescribed_mymodel
call this%hyperbolicBCs%RegisterBoundaryCondition( &
SELF_BC_PRESCRIBED, "prescribed", bcfunc)
! Register a parabolic BC (if needed)
bcfunc => pbc1d_Prescribed_mymodel
call this%parabolicBCs%RegisterBoundaryCondition( &
SELF_BC_PRESCRIBED, "prescribed", bcfunc)
endsubroutine
SELF calls AdditionalInit during model initialization, before scanning the mesh. After your BCs are registered, SELF automatically determines which mesh faces belong to each BC type and stores the element/side arrays.
Complete Example: Traveling Shock (Burgers 1D)#
This example shows how to extend the built-in burgers1D model with prescribed boundary conditions for a traveling shock solution.
Model Module#
module burgers1d_shock_model
use self_Burgers1D
use SELF_BoundaryConditions
implicit none
type, extends(burgers1D) :: burgers1d_shock
real(prec) :: ul = 1.0_prec ! Left state
real(prec) :: ur = 0.0_prec ! Right state
real(prec) :: x0 = 0.1_prec ! Initial shock position
contains
procedure :: AdditionalInit => AdditionalInit_burgers1d_shock
endtype
contains
subroutine AdditionalInit_burgers1d_shock(this)
class(burgers1d_shock), intent(inout) :: this
procedure(SELF_bcMethod), pointer :: bcfunc
! Register prescribed BC for the solution
bcfunc => hbc1d_Prescribed_burgers1d_shock
call this%hyperbolicBCs%RegisterBoundaryCondition( &
SELF_BC_PRESCRIBED, "prescribed", bcfunc)
! Register prescribed BC for the gradient
bcfunc => pbc1d_Prescribed_burgers1d_shock
call this%parabolicBCs%RegisterBoundaryCondition( &
SELF_BC_PRESCRIBED, "prescribed", bcfunc)
endsubroutine
subroutine hbc1d_Prescribed_burgers1d_shock(bc, mymodel)
class(BoundaryCondition), intent(in) :: bc
class(Model), intent(inout) :: mymodel
integer :: n, iEl, s
real(prec) :: x, jump, spd
select type (m => mymodel)
class is (burgers1d_shock)
do n = 1, bc%nBoundaries
iEl = bc%elements(n)
s = bc%sides(n)
x = m%geometry%x%boundary(s, iEl, 1)
jump = m%ul - m%ur
spd = 0.5_prec*(m%ul + m%ur)
m%solution%extBoundary(s, iEl, 1) = &
spd - 0.5_prec*tanh((x - spd*m%t - m%x0)*jump &
/ (4.0_prec*m%nu))
enddo
endselect
endsubroutine
subroutine pbc1d_Prescribed_burgers1d_shock(bc, mymodel)
class(BoundaryCondition), intent(in) :: bc
class(Model), intent(inout) :: mymodel
integer :: n, iEl, s
real(prec) :: x, jump, spd, r, drdx
select type (m => mymodel)
class is (burgers1d_shock)
do n = 1, bc%nBoundaries
iEl = bc%elements(n)
s = bc%sides(n)
x = m%geometry%x%boundary(s, iEl, 1)
jump = m%ul - m%ur
spd = 0.5_prec*(m%ul + m%ur)
r = (x - spd*m%t - m%x0)*jump/(4.0_prec*m%nu)
drdx = jump/(4.0_prec*m%nu)
m%solutionGradient%extBoundary(s, iEl, 1) = &
-0.5_prec*drdx*(2.0_prec/(exp(r) + exp(-r)))**2
enddo
endselect
endsubroutine
endmodule
Main Program#
program traveling_shock
use self_data
use burgers1d_shock_model
implicit none
type(burgers1d_shock) :: modelobj
type(Lagrange), target :: interp
type(Mesh1D), target :: mesh
type(Geometry1D), target :: geometry
! Create mesh
call mesh%StructuredMesh(nElem=10, x=(/0.0_prec, 1.0_prec/))
! Tag both boundaries as prescribed
call mesh%ResetBoundaryConditionType(SELF_BC_PRESCRIBED, SELF_BC_PRESCRIBED)
! Create interpolant and geometry
call interp%Init(N=7, controlNodeType=GAUSS, M=10, targetNodeType=UNIFORM)
call geometry%Init(interp, mesh%nElem)
call geometry%GenerateFromMesh(mesh)
! Initialize model (this calls AdditionalInit and MapBoundaryConditions)
call modelobj%Init(mesh, geometry)
modelobj%gradient_enabled = .true.
modelobj%nu = 0.01_prec
! Set initial condition, run, clean up...
call modelobj%SetTimeIntegrator('rk3')
call modelobj%ForwardStep(2.0_prec, 1.0e-5_prec, 0.05_prec)
call modelobj%free()
call mesh%free()
call geometry%free()
call interp%free()
endprogram
Extending a Built-in Model's BCs#
When your model extends a built-in SELF model that already registers BCs, call the parent's AdditionalInit first, then register your additional BCs:
subroutine AdditionalInit_my_extended_model(this)
class(my_extended_model), intent(inout) :: this
procedure(SELF_bcMethod), pointer :: bcfunc
! Register parent model's BCs first
call AdditionalInit_LinearEuler2D_t(this)
! Then register additional BCs
bcfunc => hbc2d_Prescribed_my_extended_model
call this%hyperbolicBCs%RegisterBoundaryCondition( &
SELF_BC_PRESCRIBED, "prescribed", bcfunc)
endsubroutine
Replacing a BC Implementation#
If a BC with a given ID is already registered and you call RegisterBoundaryCondition with the same ID again, the procedure pointer is updated to point to your new implementation. The element/side arrays are preserved. This mechanism is used internally by GPU model variants to replace CPU BC routines with GPU-accelerated kernels, but you can also use it to override a parent model's BC behavior.
Tips#
- One BC per ID per list. Each
bcidcan appear at most once in the hyperbolic list and once in the parabolic list. - Mesh tagging must match registration. If you register a BC for
SELF_BC_PRESCRIBEDbut no mesh faces carry that ID, the BC will havenBoundaries = 0and its subroutine will never be called. - Use
select typein BC routines. TheSELF_bcMethodinterface receivesclass(Model), so you must downcast to access your model's fields. - BC routines are called every time step. Keep them efficient. For GPU models, use device kernels rather than host-side loops.