Input
The file src/Input.jl
and files contained in src/input/
are collectively referred to as the input module. Here, all calculations prior to the finite element analysis are carried out—including
- specification of the problem scenario, choice of mesh motion, and other parameters
- generation of the finite element mesh
- calculation of the basis functions and their derivatives
- initialization of the surface position and degrees of freedom
- application of boundary conditions
All files below are in the src/input/
directory.
Enums.jl
The following enums are defined to simplify problem input.
MembraneAleFem.Scenario
— TypeScenario
Scenarios to simulate: instances(Scenario)
F_CAVI = 1
–> lid-driven cavityF_COUE = 2
–> Couette flowF_POIS = 3
–> Hagen–Poiseuille flowF_PULL = 4
–> tether pulling from a flat patchF_BEND = 5
–> bending of a flat patch
The first letter indicates mesh Topology
: F
for a flat patch and C
for a cylinder. At present, no cylindrical scenarios are implemented.
To create a new scenario:
- add and export a new entry in the
Scenario
enum - edit
get_scenario_bc_info
insrc/input/Bc.jl
- write the corresponding
apply_<SCENARIO>_bcs
function insrc/input/Mesh.jl
- add the new
scenario
to theimplemented
list insrc/input/Params.jl
MembraneAleFem.Topology
— TypeTopology
Mesh topologies: instances(Topology)
FLAT = 1
CYLINDER = 2
MembraneAleFem.Motion
— TypeMotion
Types of mesh motion: instances(Motion)
STATIC = 1
–> mesh does not moveEUL = 2
–> Eulerian mesh motionLAG = 3
–> Lagrangian mesh motionALEV = 4
–> ALE-viscous mesh motionALEVB = 5
–> ALE-viscous-bending mesh motion
The choice of motion is stored in Params.motion
variable in Params.jl
, and is relevant both to the specification of boundary conditions in Bc.jl
and the finite element analysis.
MembraneAleFem.Boundary
— TypeBoundary
Enumerate four mesh boundaries: instances(Boundary)
, never to be used as an index.
BOTTOM = 1
RIGHT = 2
TOP = 3
LEFT = 4
MembraneAleFem.Corner
— TypeCorner
Enumerate four mesh corners: instances(Corner)
, never to be used as an index.
BOTTOM_LEFT = 1
BOTTOM_RIGHT = 2
TOP_LEFT = 3
TOP_RIGHT = 4
MembraneAleFem.Neumann
— TypeNeumann
Enumerate types of Neumann boundary conditions: instances(Neumann)
SHEAR = 1
–> apply in-plane force/length in the τ directionSTRETCH = 2
–> apply in-plane force/length in the ν directionMOMENT = 3
–> apply boundary moment
Associate finite element calculations are found in calc_bdry_element_residual
MembraneAleFem.Curve
— TypeCurve
Enumerate types of curves: instances(Curve)
CLAMPED
CLOSED
These curves are used to generate B-spline basis functions—see Spline.jl
Params.jl
The requisite parameters for each simulation are split between the Params
struct and the keyword arguments args
. The data contained in Params
is meant to be immutable, even across restarted (or continued) simulations. For this reason, temporal data (the initial time t0
, initial time ID t0_id
, and time steps Δts
) is treated as a keyword argument, despite it being required for every simulation. Additional details about required keywords are contained in check_params
.
MembraneAleFem.Params
— TypeParams
Parameters that must be specified for any simulation.
For Scenario
-specific data that must be included via keyword arguments, see check_params
. The following information is contained:
motion::
Motion
→ type of motionscenario::
Scenario
→ type of scenarionum1el::Int64
→ number of elements in $ζ^1$num2el::Int64
→ number of elements in $ζ^2$output::Bool
→ write output (yes by default)length::Float64
→ length, in real spacekb::Float64
→ mean bending moduluskg::Float64
→ Gaussian bending modulusζv::Float64
→ membrane viscositypn::Float64
→ normal pressure (body force)poly::Int64
→ polynomial order of basis functionsgp1d::Int64
→ number of Gauss points, in 1-Dnders::Int64
→ number of 1-D B-spline derivativesnen::Int64
→ number of element nodesαdb::Float64
→ Dohrmann–Bochev parameterαm::Float64
→ mesh parameterεk::Float64
→ tolerance numerical calculation of matrix Kεnr::Float64
→ Netwon–Raphson tolerance
MembraneAleFem.check_params
— Functioncheck_params(p::Params; args...)
Check relevant parameters, including scenario-specific keyword arguments.
For any simulation, the fields t0
, t0_id
, and Δts
are required. If data is output (as is the default, for p.output
), the output path out_path
and file name out_file
are necessary. Finally, the information needed to solve the various Scenario
s is checked.
Dof.jl
Within our ALE formalism, the membrane position $\boldsymbol{x}$ is not a fundamental unknown. The mesh velocity $\boldsymbol{v}^{\text{m}}$, on the other hand, is a fundamental unknown, and over a time step $\Delta t$ the membrane position is updated according to
\[\boldsymbol{x} (\zeta^\alpha, t + \Delta t) \, = \, \boldsymbol{x} (\zeta^\alpha, t) \, + \, \Delta t \, \boldsymbol{v}^{\text{m}} (\zeta^\alpha, t + \Delta t) ~,\]
where $\Delta t$ is one of the entries of the array ΔTS
specified in Params.jl
. The membrane position is thus treated differently from the fundamental unknowns. For this reason, we define all possible fundamental unknowns and the three Cartesian components of the position in the Dof
module:
MembraneAleFem.Dof.Unknown
— TypeDof.Unknown
Possible degrees of freedom (or fundamental unknowns) at each node.
For different mesh motions, different fundamental unknowns are required. A Lagrangian simulation requires only vx
, vy
, vz
, and λ
, while an ALE simulation requires all eight fundamental unknowns. Currently, the mapping from the choice of Motion
to the corresponding fundamental unknowns is specified in the function get_dofs
and stored in Mesh
.dofs
.
This enum is encapsulated in a module; access elements with e.g. Dof.vx
.
Types of fundamental unknowns:
vx
–> $x$-velocity, $v_x$vy
–> $y$-velocity, $v_y$vz
–> $z$-velocity, $v_z$vmx
–> $x$-mesh velocity, $v^{\text{m}}_x$vmy
–> $y$-mesh velocity, $v^{\text{m}}_y$vmz
–> $z$-mesh velocity, $v^{\text{m}}_z$λ
–> surface tension, $λ$pm
–> mesh pressure, $p^{\text{m}}$
MembraneAleFem.Dof.Position
— TypeDof.Position
Cartesian components of the mesh position at each node.
This enum is encapsulated in a module; access elements with e.g. Dof.xm
.
Options:
xm
–> $x$-positionym
–> $y$-positionzm
–> $z$-position
Spline.jl
All B-spline calculations independent from finite element analysis, based entirely on The NURBS Book (Piegl and Tiller, 1997). See in particular Chapter Two for a description of B-spline functions, and Chapter Three for an explanation of how the basis functions are used to generate 1D curves and 2D surfaces in $\mathbb{R}^3$. We employ the same terminology in our code.
MembraneAleFem.KnotVector
— TypeKnotVector(ζs::Vector{Float64}, nel::Int64, poly::Int64, curve::Curve)
Vector of knots, with which all 1-D B-spline functions are calculated.
The knot vector struct contains four fields:
ζs
–> list of all knots, including repeated onesnel
–> number of 1-D elements, or knot spanspoly
–> polynomial ordercurve
–> type of Curve, which is eitherCLAMPED
orCLOSED
All of these data are not required to generate a knot vector. Two constructors are available—see knot_vector
MembraneAleFem.knot_vector
— Functionknot_vector(ζs::Vector{Float64}, poly::Int64, curve::Curve)
External constructor of the KnotVector
struct, where knots need not be uniform.
The list of knots ζs
, polynomial order poly
, and Curve
type curve
are passed in. If they represent a valid knot vector, then the struct is created.
knot_vector(nel::Int64, poly::Int64, curve::Curve)
External constructor of the KnotVector
struct with uniform knots.
The number of elements nel
, polynomial order poly
, and Curve
type curve
are passed in. If the curve
is CLOSED
, then no knots are repeated. If the curve
is CLAMPED
, then the first and last (poly+1)
knots are repeated. In both cases, the generated list of knots ζs
ranges from 0 to 1.
MembraneAleFem.get_knot_span_index
— Functionget_knot_span_index(kv::KnotVector, ζ::Float64)::Int64
Return knot span index of ζ
in CLAMPED
or CLOSED
KnotVector
kv
.
Algorithm A2.1 in Chap. 2, §5 of Piegl and Tiller (1997) ensures kv.ζs[idx]
≤ ζ
< kv.ζs[idx+1]
, where idx
is the returned value. The algorithm, valid only for CLAMPED
knots, is here extended to CLOSED
knots as well.
MembraneAleFem.get_bspline_vals
— Functionget_bspline_vals(kv::KnotVector, ζ::Float64)::Vector{Float64}
Return kv.poly
+1 nonzero B-spline functions at ζ
for the given KnotVector
kv
.
Algorithm A2.2 in Chap. 2, §5 of Piegl and Tiller (1997), valid for CLAMPED
knot vectors with (kv.poly
+1) repeated knots at the start and end, is also used for CLOSED
knot vectors. The local-to-global mapping returned by get_bspline_indices
is necessary to determine which global basis functions are nonzero at the provided ζ
.
MembraneAleFem.get_bspline_ders
— Functionget_bspline_ders(kv::KnotVector, ζ::Float64, num_ders::Int64)::Matrix{Float64}
Return B-spline functions and num_ders
derivatives at ζ
for the given KnotVector
kv
.
Algorithm A2.3 in Chap. 2, §5 of Piegl and Tiller (1997), valid for CLAMPED
knot vectors with (kv.poly
+1) repeated knots at the start and end, is also used for CLOSED
knot vectors. Here num_ders
is required to not be greater than kv.poly
, and also greater than or equal to zero. The local-to-global mapping returned by get_bspline_indices
is necessary to determine which global basis functions are nonzero at the provided ζ
.
The returned matrix has kv.poly
+1 rows and num_ders
+1 columns. The first column contains the functions themselves, and is thus identical to the output of get_bspline_vals
. The second column contains the first derivatives, and so on.
MembraneAleFem.get_bspline_indices
— Functionget_bspline_indices(kv::KnotVector, ζ::Float64)::Vector{Int64}
Return global indices of nonzero B-splines at the value ζ
in the KnotVector
kv
.
By definition, there are only kv.poly
+1 nonzero B-spline functions at any ζ
. The functions get_bspline_vals
and get_bspline_ders
calculate these nonzero quantities, and the mapping returned here places them within the global ordering. The global indices are the same for all ζ
within a single knot span, and thus we begin by calling get_knot_span_index
.
Both CLAMPED
knot vectors, with (kv.poly
+1) repeats at the start and end, as well as CLOSED
knot vectors, are handled.
get_bspline_indices(kv::KnotVector, ks_id::Int64)::Vector{Int64}
Return global indices of nonzero B-splines at ks_id
$^{\textrm{th}}$ knot span in the KnotVector
kv
.
By definition, there are only kv.poly
+1 nonzero B-spline functions over any knot span. The mapping returned here places these functions within the global ordering. Both CLAMPED
knot vectors, with (kv.poly
+1) repeats at the start and end, as well as CLOSED
knot vectors, are handled. Note that ks_id
is not the element ID eid
; rather, ks_id = eid + kv.poly
MembraneAleFem.get_1d_bspline_cps
— Functionget_1d_bspline_cps(kv::KnotVector, x::Function)::Vector{Float64}
Return the global control points $\{ x^{}_K \}$ for the function $x(ζ)$.
The global control points $\{ x^{}_K \}$ are calculated for the provided KnotVector
kv
such that
\[x(ζ) \, \approx \, \sum_{K = 1}^{\texttt{nn}} N^{}_K (ζ) \, x^{}_K ~,\]
where $x(ζ)$ is the provided x::Function
and here $\texttt{nn}$ is the number of global 1-D nodes. To determine the unknown $\{ x^{}_K \}$, a set of $\texttt{nn}$ collocation points $\{ ζ^{}_J \}$ is chosen via collocate_ζ
. A matrix equation is then obtained as
\[[x (ζ^{}_J)] \, = \, [N^{}_K (ζ^{}_J)] \, [x^{}_K] ~,\]
where the $N^{}_K (ζ^{}_J)$ are determined with get_bspline_indices
and get_bspline_vals
. The $x^{}_K$ are then calculated and returned as a vector.
MembraneAleFem.get_2d_bspline_cps
— Functionget_2d_bspline_cps(kv1::KnotVector, kv2::KnotVector, x::Function)::Matrix{Float64}
Return the global control points $\{ x^{}_K \}$ for the scalar-valued function $z(ζ^1, ζ^2)$.
The procedure of get_1d_bspline_cps
is repeated, except in this case collocation points are chosen across the 2-D parametric domain $(ζ^1, ζ^2)$.
MembraneAleFem.collocate_ζ
— Functioncollocate_ζ(kv::KnotVector)::Vector{Float64}
Take a KnotVector
kv
and return a list of points ζ
used for collocation.
The length of the returned list of ζ values is equal to the number of global basis functions associated with the provided knot vector. Both CLAMPED
and CLOSED
knot vectors are accounted for.
MembraneAleFem.get_unique_1d_elements
— Functionget_unique_1d_elements(kv::KnotVector)
Get all unique elements for a given knot vector kv
.
This function returns several quantities, in the following order:
uel_num::Int64
-> number of unique elementsnum_el::Int64
-> number of elementsuel_ids::Vector{Int64}
-> mapping from element id to unique element iduel_list::Vector{Tuple{Float64, Float64}}
-> start and endζ
for each unique element
GaussPoint.jl
As is standard in finite element analysis, Gauss–Legendre quadrature is used to approximate integrals over the parametric domain. Here, the primary objective is to calculate the 1-D integral
\[I_1 \, = \, \int_{ζ_{\texttt{lo}}}^{ζ_{\texttt{hi}}} \!\!\! f(ζ) \, \text{d} ζ ~.\]
The domain of integration is mapped from $[ ζ_{\texttt{lo}}, ζ_{\texttt{hi}} ]$ to $[-1, +1]$ via the change of variables
\[ζ \, = \, \dfrac{1}{2} \, ξ \, \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) \, + \, \dfrac{1}{2} \, \big( ζ_{\texttt{hi}} + ζ_{\texttt{lo}} \big) ~,\]
for which the integral $I_1$ is equivalently given by
\[I_1 \, = \, \dfrac{1}{2} \, \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) \int_{-1}^{+1} \!\! f \bigg( \dfrac{1}{2} \Big[ ξ \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) + \big( ζ_{\texttt{hi}} + ζ_{\texttt{lo}} \big) \Big] \bigg) \, \text{d} ξ \, = \, \dfrac{1}{2} \, \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) \int_{-1}^{+1} \!\! \hat{f} (ξ) \, \text{d} ξ ~.\]
At this point, the integral is approximated by applying the Gaussian quadrature formula
\[I_1 \, ≈ \, \dfrac{1}{2} \, \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) \, \sum_{k = 1}^{\texttt{ngp}} \hat{w}_k \, \hat{f} (ξ_k) ~,\]
where $\texttt{ngp}$ is the number of 1D Gauss points, $\hat{w}_k$ are the corresponding weights, and $ξ_k$ are the associated Gauss–Legendre points. In our code, GaussPointsξ
contains $\texttt{ngp}$, $\hat{w}_k$, and $ξ_k$. Note that this struct
is the same for every line element, because $ξ ∈ [-1, +1]$ always.
MembraneAleFem.GaussPointsξ
— TypeGaussPointsξ(ngp::Int64)
Weights $\hat{w}_k$ and points $ξ_k$ on the interval $[-1, +1]$ with which a 1-D integral is approximated.
The struct has three fields:
ngp
–> number of Gaussian quadrature pointsξs
–> set of points $ξ_k$ws
–> set of weights $\hat{w}_k$
Calculations from Wikipedia: Gaussian quadrature.
In our finite element implementation, it is often convenient to evaluate integrals over the original variable ζ
rather than the transformed variable ξ
. To this end, we recognize that
\[I_1 \, ≈ \, \sum_{k = 1}^{\texttt{ngp}} w_k \, f (ζ_k) ~,\]
where
\[w_k \, := \,\dfrac{1}{2} \, \hat{w}_k \, \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) \qquad \text{and} \qquad ζ_k \, := \, \dfrac{1}{2} \Big[ ξ_k \big( ζ_{\texttt{hi}} - ζ_{\texttt{lo}} \big) + \big( ζ_{\texttt{hi}} + ζ_{\texttt{lo}} \big) \Big] ~.\]
In our code, GaussPointsζ
contains ngp
, $w_k$, and $ζ_k$.
MembraneAleFem.GaussPointsζ
— TypeGaussPointsζ(gpsξ::GaussPointsξ, ζlo::Float64, ζhi::Float64)
Weights $w_k$ and points $ζ_k$ with which a 1-D integral is approximated over ζlo
≤ ζ
≤ ζhi
.
Calculated via the transform from ξ
to ζ
via the relations
ζ
$_k$ = ξ
$_k$ (ζhi
- ζlo
) / 2 + (ζhi
+ ζlo
) / 2
$w_k$ = $\hat{w}_k$ (ζhi
- ζlo
) / 2
The struct has three fields:
ngp
–> number of Gaussian quadrature pointsζs
–> set of points $ζ_k$ws
–> set of weights $w_k$
GpBasisFn.jl
Calculation of nonzero B-spline basis functions at every Gaussian quadrature point. Since the basis functions are determined upon discretizing the parametric domain, they are calculated once and then stored in the structs listed below—which systematically build up in complexity.
MembraneAleFem.GpBasisFnsζ
— TypeGpBasisFnsζ(gpwζ::Float64, ζ::Float64, kv::KnotVector)
1-D basis functions $N(ζ)$ at the provided quadrature point ζ
with weight gpwζ
.
The B-spline values and derivatives are determined with get_bspline_ders
and stored here. The struct contains four fields:
w
–> Gauss point weight, passed in asgpw
N
–> (poly
+1)×1 vector of nonzero basis functions $N(ζ)$dN
–> (poly
+1)×1 vector of nonzero first derivatives $N'(ζ)$ddN
–> (poly
+1)×1 vector of nonzero second derivatives $N''(ζ)$
MembraneAleFem.GpBasisFnsζα
— TypeGpBasisFnsζα(fns1::GpBasisFnsζ, fns2::GpBasisFnsζ)
2-D basis functions $N(ζ^α)$ at a single Gauss point $ζ^α$, as set by 1-D GpBasisFnsζ
fns1
and fns2
.
Here $ζ^1$ and $ζ^2$ respectively correspond to the ζ
values passed to fns1
and fns2
, even though this information is not stored. The 2-D basis functions are calculated as the tensor product of 1-D functions. The struct contains four fields:
w
–> Gauss point weight, given byfns1.w × fns2.w
N
–>NEN
×1 vector of nonzero basis functions $N(ζ^α)$, ordered by the tensor product structure∂Nα
–>NEN
×2 matrix of first derivatives: rows respectively contain $N_{, 1}$ and $N_{, 2}$∂∂Nαβ
–>NEN
×3 matrix of second derivatives: rows respectively contain $N_{, 1 1}$, $N_{, 2 2}$, and $N_{, 1 2}$
MembraneAleFem.LineGpBasisFns
— TypeLineGpBasisFns(kv::KnotVector, ngp::Int64)
1-D B-spline basis functions and derivatives at all quadrature points on the domain of the KnotVector
kv
.
Basis functions are determined at each of the ngp
Gauss points, over each element. When using B-splines, scenarios often arise where the basis functions are identical across many elements. We accordingly store only the 1-D basis functions over unique elements, as well as a mapping from elements to unique elements. The basis functions at the edges of the 1-D parametric domain are stored separately, as they are required subsequently for application of boundary conditions. The struct has five fields:
nel
–> number of elements in theKnotVector
kv
uel_ids
–> mapping fromelem_id
tounique_elem_id
ufns
–>num_unique_elems
×ngp
matrix of unique 1-D basis functions, of typeGpBasisFnsζ
ζmin_fns
–> 1-D basis functions at the min value of the parametric domainζmax_fns
–> 1-D basis functions at the max value of the parametric domain
Note that this struct contains no information about how basis functions vary in the orthogonal parametric direction, and thus cannot be directly used to apply boundary conditions. See BdryGpBasisFns
.
MembraneAleFem.BdryGpBasisFns
— TypeBdryGpBasisFns(line_gp_fns::LineGpBasisFns, perp_edge_fns::GpBasisFnsζ, bdry::Boundary)
2-D B-spline basis functions and derivatives at all quadrature points on a boundary.
The LineGpBasisFns
struct line_gp_fns
contains all 1-D basis functions and derivatives along the boundary, with ngp
Gauss points for each element. Here perp_edge_fns
are the basis functions and derivatives at the boundary in the orthogonal parametric direction, as contained in a GpBasisFnsζ
struct. For example, on the RIGHT
boundary, line_gp_fns
captures 1-D derivatives in the $ζ^2$ direction, while perp_edge_fns
contains 1-D derivatives in the $ζ^1$ direction on the right edge. With knowledge of the underlying tensor product structure (Piegl and Tiller, 1997), the 2-D basis functions along the specified Boundary
bdry
are generated. The struct has four fields:
nel
–> number of elements associated withLineGpBasisFns
line_gp_fns
uel_ids
–> mapping fromelem_id
tounique_elem_id
ufns
–>num_unique_elems
×ngp
matrix of unique 2-D basis functions, of typeGpBasisFnsζα
bdry
–>Boundary
of the parametric domain
MembraneAleFem.AreaGpBasisFns
— TypeAreaGpBasisFns(line_gp_fns1::LineGpBasisFns, line_gp_fns2::LineGpBasisFns)
2-D B-spline basis functions and derivatives at all quadrature points in the mesh area.
The two LineGpBasisFns
structs passed in, line_gp_fns1
and line_gp_fns2
, respectively contain 1-D derivatives in the $ζ^1$ and $ζ^2$ directions. With the tensor product structure of B-splines over a surface (Piegl and Tiller, 1997), it is straightforward to calculate the 2-D basis functions and their derivatives. As is the case for LineGpBasisFns
, a mapping from elements to unique elements is generated; only unique basis function calculations are stored. This struct has three fields:
nel
–> number of area elementsuel_ids
–> mapping fromelem_id
tounique_elem_id
ufns
–>num_unique_elems
×ngp
matrix of unique 2-D basis functions, of typeGpBasisFnsζα
Mesh.jl and Bc.jl
The mesh is one of the main constructions required for finite element analysis. In this codebase, the functions associated with mesh generation are divided into two files. Mesh.jl
deals with aspects of mesh generation that are independent of the scenario under consideration, while Bc.jl
handles the boundary conditions and their effect on mesh organization—which is scenario-dependent.
Mesh.jl
All relevant information is contained in the Mesh
struct, and will not change over the course of a simulation.
MembraneAleFem.Mesh
— TypeMesh(p::Params; args...)
The generated mesh, which includes the degree-of-freedom numbering and all basis functions for the given Scenario
.
This struct contains the following scenario-independent fields:
num1el
–> number of elements in $ζ^1$ directionnum2el
–> number of elements in $ζ^2$ directionnumel
–> number of area elementsnum1np
–> number of nodal points in $ζ^1$ directionnum2np
–> number of nodal points in $ζ^2$ directionnumnp
–> number of nodal points on the 2-d meshIX
–> matrix containing nonzero node numbers for each area elementbdry_elems
–> mapping fromBoundary
to element numberscrnr_elems
–> mapping fromCorner
to element numberbdry_nodes
–> mapping fromBoundary
to node numbersbdry_inner_nodes
–> mapping fromBoundary
to inner node numberscrnr_nodes
–> mapping fromCorner
to node numbercrnr_inner_nodes
–> mapping fromCorner
to inner node numberkv1
–>KnotVector
along $ζ^1$ directionkv2
–>KnotVector
along $ζ^2$ directionarea_gp_fns
–>AreaGpBasisFns
: 2-D area basis functionsbdry_gp_fns
–> mapping fromBoundary
toBdryGpBasisFns
crnr_gp_fns
–> mapping fromCorner
to 2-D basis functionsGpBasisFnsζα
The generated mesh depends on the problem being solved. The function generate_scenario
uses the scenario-specific results from Bc.jl
to set the following scenario-dependent fields:
topology
–> surfaceTopology
dofs
–> list of activeUnknown
s with global orderingndf
–> number of activeUnknown
sID
–> matrix mapping node number to indices of unknowns at that nodeID_inv
–> inverse ofID
: mapunknown_id
to(node_number, dof_number)
nmdf
–> total number of mesh degrees of freedomLM
–> map from area element to indices of all unknowns of that elementinh_dir_bcs
–> list of inhomogeneous Dirichlet boundary conditionsinh_neu_bcs
–> list of inhomogeneous Neumann boundary conditions
MembraneAleFem.generate_scenario
— Functiongenerate_scenario(numel, numnp, IX, bdry_nodes, ..., p::Params; args...)
Apply scenario-specific boundary conditions towards mesh generation.
Checks to see whether a helper function is available in Bc.jl
for the given Scenario
, and then constructs the ID
matrix that maps from nodes to global degrees of freedom. The inverse ID_inv
is also generated, as is the LM
matrix—which provides the local degrees of freedom for each element.
The following helper functions allow the user to easily obtain basis functions, their derivatives, and Gauss point weights both in the mesh interior and on the mesh boundary.
MembraneAleFem.get_basis_fns
— Functionget_basis_fns(el_id::Int64, gp_id::Int64, mesh::Mesh)::GpBasisFnsζα
Return GpBasisFnsζα
of the gp_id
$^{\text{th}}$ 2-D Gauss point of the el_id
$^{\text{th}}$ area element.
get_basis_fns(bdry::Boundary, el_id::Int64, gp_id::Int64, mesh::Mesh)::GpBasisFnsζα
Return GpBasisFnsζα
of the gp_id
$^{\text{th}}$ 1-D Gauss point of the el_id
$^{\text{th}}$ element on the Boundary
bdry
.
MembraneAleFem.get_gpw
— Functionget_gpw(el_id::Int64, gp_id::Int64, mesh::Mesh)::Float64
Return the Gauss point weight of the call to get_basis_fns
.
get_gpw(bdry::Boundary, el_id::Int64, gp_id::Int64, mesh::Mesh)::Vector{Float64}
Return the Gauss point weight of the call to get_basis_fns
.
MembraneAleFem.get_N
— Functionget_N(el_id::Int64, gp_id::Int64, mesh::Mesh)::SVector{NEN,Float64}
Return local basis functions $[𝐍^e]$ of the call to get_basis_fns
.
get_N(bdry::Boundary, el_id::Int64, gp_id::Int64, mesh::Mesh)
Return local basis functions $[𝐍^e]$ of the call to get_basis_fns
.
MembraneAleFem.get_∂Nα
— Functionget_∂Nα(el_id::Int64, gp_id::Int64, mesh::Mesh)::SMatrix{NEN,ζDIM,Float64}
Return local basis function derivatives $[𝐍^e]_{, α}$ of the call to get_basis_fns
.
get_∂Nα(bdry::Boundary, el_id::Int64, gp_id::Int64, mesh::Mesh)
Return local basis function derivatives $[𝐍^e]_{, α}$ of the call to get_basis_fns
.
MembraneAleFem.get_∂∂Nαβ
— Functionget_∂∂Nαβ(el_id::Int64, gp_id::Int64, mesh::Mesh)::SMatrix{NEN,VOIGT,Float64}
Return local basis function derivatives $[𝐍^e]_{, α β}$ of the call to get_basis_fns
.
get_∂∂Nαβ(bdry::Boundary, el_id::Int64, gp_id::Int64, mesh::Mesh)
Return local basis function derivatives $[𝐍^e]_{, α β}$ of the call to get_basis_fns
.
Bc.jl
When constructing the Mesh
, generate_scenario
(in Mesh.jl
) calls get_scenario_bc_info
—which is a helper function that then calls the appropriate Scenario
-specific function. Each such function returns the following:
dofs
–> global ordering of activeUnknown
sndf
–> number of activeUnknown
sID
–> matrix mapping node number to indices of unknowns at that nodeinh_dir_bcs
–> list of inhomogeneous Dirichlet boundary conditionsinh_neu_bcs
–> list of inhomogeneous Neumann boundary conditions
MembraneAleFem.get_scenario_bc_info
— Functionget_scenario_bc_info(numnp, IX, bdry_nodes, ..., p::Params; args...)
Return Scenario
-specific information for the provided Params
.
Each of the functions called returns (dofs, ndf, ID, inh_dir_bcs, inh_neu_bcs)
, in that order.
MembraneAleFem.get_f_cavi_bc_info
— Functionget_f_cavi_bc_info(numnp, bdry_nodes, crnr_nodes)
Generate Mesh
data for the flat cavity Scenario
F_CAVI
.
In this scenario, a STATIC
mesh Motion
is required; relevant Unknown
s are vx
, vy
, and λ
. The following boundary conditions are prescribed:
- $v_x = 1.0$ along the top edge (corners excluded)
- $v_x = 0.0$ along all other edges (corners included)
- $v_y = 0.0$ along all edges
- $λ = 0.0$ at (or near) the center of the domain
Note that $λ$ is pinned to remove the global indeterminacy of the surface tension, up to a constant.
MembraneAleFem.get_f_coue_bc_info
— Functionget_f_coue_bc_info(numnp, bdry_nodes)
Generate Mesh
data for the flat Couette Scenario
F_COUE
.
In this scenario, a STATIC
mesh Motion
is required; relevant Unknown
s are vx
, vy
, and λ
. The following boundary conditions are prescribed:
- $v_x = 0.0$ along the bottom edge (corners included)
- $v_x = 3.0$ along the top edge (corners included)
- $v_y = 0.0$ along all edges
- $\bm{\bar{F}} = 4\bm{\nu}$ on the left and right edges
MembraneAleFem.get_f_pois_bc_info
— Functionget_f_pois_bc_info(numnp, bdry_nodes)
Generate Mesh
data for the flat Hagen–Poiseuille Scenario
F_POIS
.
In this scenario, a STATIC
mesh Motion
is required; relevant Unknown
s are vx
, vy
, and λ
. The following boundary conditions are prescribed:
- $v_x = 0.0$ along the top and bottom edges (corners included)
- $v_y = 0.0$ on all edges
- $\bm{\bar{F}} = 4 \bm{\nu}$ on the left edge
- $\bm{\bar{F}} = 8 \bm{\nu}$ on the right edge
MembraneAleFem.get_f_pull_bc_info
— Functionget_f_pull_bc_info(numnp, IX, bdry_nodes, bdry_inner_nodes, p::Params; args...)
Generate Mesh
data for the flat tube-pulling Scenario
F_PULL
.
Relevant unknowns are determined via get_dofs
, depending on the choice of mesh Motion
. The following boundary conditions are prescribed for all mesh motions:
- $v_z = 0$ on all boundaries, and all inner boundaries
- $\bm{\bar{F}} = (k_{\text{b}} / 4)\bm{\nu}$ on all boundaries
- $v_x = 0$, $v_y = 0$ at center of each edge
- $v_x = 0$, $v_y = 0$, $v_z = \,$
args[:pull_speed]
$\bm{e}_z$ on nodes of the central element
The following are prescribed when the Motion
is not Lagrangian:
- $v^{\text{m}}_x = 0$, $v^{\text{m}}_y = 0$ at center of each edge
- $v^{\text{m}}_x = 0$, $v^{\text{m}}_y = 0$, $v^{\text{m}}_z = \,$
args[:pull_speed}
$\bm{e}_z$ on nodes of the central element
The following are prescribed when the Motion
is either ALE-viscous or ALE-viscous-bending:
- $\bm{v}^{\text{m}} = \bm{0}$ on all boundaries
The following are prescribed when the Motion
is ALE-viscous-bending:
- $v^{\text{m}}_z = 0$ on all inner boundaries
MembraneAleFem.get_f_bend_bc_info
— Functionget_f_bend_bc_info(numnp, bdry_nodes, p::Params; args...)
Generate Mesh
data for the flat bending Scenario
F_BEND
.
Relevant unknowns are determined via get_dofs
, depending on the choice of mesh Motion
. The following boundary conditions are prescribed for all mesh motions:
LEFT
edge:- $\bm{v} = \bm{0}$
- $M = \,$
args[:bend_mf]
(seeParams.jl
) - if ALE mesh motion: $\bm{v}^{\text{m}} = \bm{0}$
RIGHT
edge:- $f_x = 0$ and $f_y = 0$
- $v_z = 0$
- $M = \,$
args[:bend_mf]
(seeParams.jl
) - if ALE mesh motion: $v^{\text{m}}_z = 0$
BOTTOM
edge:- $f_x = 0$ and $f_z = 0$
- $v_y = 0$
- $M = 0$
- if ALE mesh motion: $v^{\text{m}}_y = 0$
TOP
edge:- $f_x = 0$ and $f_z = 0$
- $v_y = 0$
- $M = 0$
- if ALE mesh motion: $v^{\text{m}}_y = 0$
MembraneAleFem.get_dofs
— Functionget_dofs(motion::Motion)::Dict{Dof.Unknown, Int64}
Return global ordering of Unknown
degrees of freedom, depending on the mesh Motion
.
The following is the mapping from motion to unknowns:
- Lagrangian –> $v_x$, $v_y$, $v_z$, $λ$
- Eulerian –> $v_x$, $v_y$, $v_z$, $v^{\text{m}}_x$, $v^{\text{m}}_y$, $v^{\text{m}}_z$, $λ$
- ALE –> $v_x$, $v_y$, $v_z$, $v^{\text{m}}_x$, $v^{\text{m}}_y$, $v^{\text{m}}_z$, $λ$, $p^{\text{m}}$
Note that the flat, 2-D Scenario
s (F_CAVI
, F_COUE
, F_POIS
) have a reduced number of degrees of freedom, because there are no unknowns in the $z$-direction. Thus, this function is not called in those scenarios.
Input.jl
MembraneAleFem.prepare_input
— Functionprepare_input(p::Params; args...)
Generate and return the mesh
, surface position xms
, and Unknown
control points cps
.
The mesh is generated by instantiating a Mesh
with the provided parameters and arguments, which include the Scenario
and number of elements. The surface positions and initial unknowns are set with calls to get_1d_bspline_cps
and get_2d_bspline_cps
.