Internals
- In general, the framework is designed such that it allows to easily add support for more kernels, finite element spaces, and excitations.
- Key are assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces.
Basis
Sets of both trial and testing functions are implemented by models following the basis concept. The term basis is somewhat misleading as it is nowhere required nor enforced that these functions are linearly independent. Models implementing the Basis concept need to comply to the following semantics.
numfunctions(basis): number of functions in the Basis.coordtype(basis): type of (the components of) the values taken on by the functions in the Basis.scalartype(d): the scalar field underlying the vector space the basis functions take value in.refspace(basis): returns the ReferenceSpace of local shape functions on which the Basis is built.assemblydata(basis):assemblydatareturns an iterable collectionelementsof geometric elements and a look tableadfor use in assembly of interaction matrices. In particular, for an indexelement_idxintoelementsand an indexlocal_shape_idxin basis of local shape functionsrefspace(basis),ad[element_idx, local_shape_idx]returns the iterable collection of(global_idx, weight)tuples such that the local shape function atlocal_shape_idxdefined on the element atelement_idxcontributes to the basis function atglobal_idxwith a weight ofweight.geometry(basis): returns an iterable collection of Elements. The order in which these Elements are encountered corresponds to the indices used in the assembly data structure.
Reference Space
The reference space concept defines an API for working with spaces of local shape functions. The main role of objects implementing this concept is to allow specialization of the functions that depend on the precise reference space used.
The functions that depend on the type and value of arguments modeling reference space are:
numfunctions(refspace, domain): returns the number of shape functions on each element.
Kernel
A kernel is a fairly simple concept that mainly exists as part of the definition of a Discrete Operator. A kernel should obey the following semantics:
In many function definitions the kernel object is referenced by operator or something similar. This is a misleading name as an operator definition should always be accompanied by the domain and range space.
Discrete Operator
Informally speaking, a Discrete Operator is a concept that allows for the computation of an interaction matrix. It is a kernel together with a test and trial basis. A Discrete Operator can be passed to assemble and friends to compute its matrix representation.
A discrete operator is a triple (kernel, test_basis, trial_basis), where kernel is a Kernel, and test_basis and trial_basis are Bases. In addition, the following expressions should be implemented and behave according to the correct semantics:
quaddata(operator,test_refspace,trial_refspace,test_elements,trial_elements): create the data required for the computation of element-element interactions during assembly of discrete operator matrices.integrate!(operator,test_refspace,trial_refspace,p,test_element,q_trial_element,qd, qs, out, test_space, tptr, trial_space, bptr): this is the single generic function, overloaded twice over. One method, dispatching on the quadrature strategy, builds an integration strategy objectqrdescribing (by its type and data fields) how to compute the interaction for the given pair of elements, using data precomputed inqd; the indicespandqrefer to the position of the elements in the enumeration defined bygeometry(basis)and allow fast retrieval of the relevant pre-stored data. Rather than returningqr, that method immediately calls the other method from within the same method/branch, which computes the local interaction matrix into the target bufferzlocal. Building and consumingqrin the same branch like this, instead of returning it to a separately-compiled caller, is what avoids a dynamic dispatch onqr's type (which depends on the runtime geometry of the interacting elements, so is only known at runtime). Passaction=BEAST.ReturnQRule()to getqrback unevaluated instead of the defaultaction=BEAST.ApplyIntegrate(). (Before BEAST 2.10 these were two separate functions,quadruleandmomintegrals!;quadruleremains a distinct function for a few operator families outsideIntegralOperator, such as local operators, excitations, and farfield/nearfield postprocessing.)
In the context of fast methods such as the Fast Multipole Method other algorithms on Discrete Operators will typically be defined to compute matrix vector products. These algorithms do not explicitly compute and store the interaction matrix (this would lead to unacceptable computational and memory complexity).
BEAST.elements — Function
elements(geo)
Create an iterable collection of the elements stored in geo. The order in which this collection produces the elements determines the index used for lookup in the data structures returned by assemblydata and quaddata.
BEAST.numfunctions — Function
numfunctions(basis)Number of functions in the basis.
BEAST.scalartype — Function
scalartype(x)The scalar field over which the values of a global or local basis function, or an operator are defined. This should always be a scalar type, even if the basis or operator takes on values in a vector or tensor space. This data type is used to determine the eltype of assembled discrete operators.
BEAST.assemblydata — Function
charts, admap, act_to_global = assemblydata(basis; onlyactives=true)Given a basis this function returns a data structure containing the information required for matrix assemble, that is, the vector charts containing Simplex elements, a variable admap of type AssemblyData, and a mapping from indices of actively used simplices to global simplices.
When onlyactives is true, another layer of indices is introduced to filter out all cells of the mesh that are not in the union of the support of the basis functions (i.e., when the basis functions are defined only on a part of the mesh).
admap is, in essence, a three-dimensional array of named tuples, which, by wrapping it in the struct AssemblyData, allows the definition of iterators. The tuple consists of the two entries
admap[i,r,c].globalindex
admap[i,r,c].coefficientHere, c and r are indices in the iterable set of (active) simplices and the set of shape functions on each cell/simplex: r ranges from 1 to the number of shape functions on a cell/simplex, c ranges from 1 to the number of active simplices, and i ranges from 1 to the number of maximal number of basis functions, where any of the shape functions contributes to.
For example, for continuous piecewise linear lagrange functions (c0d1), each of the three shape functions on a triangle are associated with exactly one Lagrange function, and therefore i is limited to 1.
Note: When onlyactives=false, the indices c correspond to the position of the corresponding cell/simplex whilst iterating over geometry(basis). When onlyactives=true, then act_to_global(c) correspond to the position of the corresponding cell/simplex whilst iterating over geometry(basis).
For a triplet (i,r,c), globalindex is the index in the basis of the ith basis function that has a contribution from shape function r on (active) cell/simplex c. coefficient is the coefficient of that contribution in the linear combination defining that basis function in terms of shape function.
BEAST.geometry — Function
geometry(basis)Returns an iterable collection of geometric elements on which the functions in basis are defined. The order the elements are encountered needs correspond to the element indices used in the data structure returned by assemblydata.
BEAST.refspace — Function
refspace(basis)Returns the ReferenceSpace of local shape functions on which the basis is built.
BEAST.quaddata — Function
quaddata(operator, test_refspace, trial_refspace, test_elements, trial_elements)Returns an object cashing data required for the computation of boundary element interactions. It is up to the client programmer to decide what (if any) data is cached. For double numberical quadrature, storing the integration points for example can significantly speed up matrix assembly.
operatoris an integration kernel.test_refspaceandtrial_refspaceare reference space objects.quadata
is typically overloaded on the type of these local spaces of shape functions. (See the implementation in maxwell.jl for an example).
test_elementsandtrial_elementsare iterable collections of the geometric
elements on which the finite element space are defined. These are provided to allow computation of the actual integrations points - as opposed to only their coordinates.
BEAST.integrate! — Function
integrate!(operator, test_refspace, trial_refspace, test_index, test_chart,
trial_index, trial_chart, quad_data, quadstrat,
out, test_space, test_ptr, trial_space, trial_ptr; action::QuadRuleAction=ApplyIntegrate())
integrate!(out, operator, test_space, test_ptr, test_chart,
trial_space, trial_ptr, trial_chart, qrule)For IntegralOperator assembly, integrate! is overloaded twice over (folded together in BEAST 2.10 from what used to be the separate quadrule and momintegrals! functions):
- One family of methods, dispatching on
quadstrat, builds the quadrature rule appropriate for the given pair of elements (the rolequadruleused to play on its own), then either evaluates it intooutor returns it unevaluated, depending onaction(seeApplyIntegrate,ReturnQRule,ApplyIntegrateNonConforming). Doing this in one step, rather than returning the rule to a separate caller for it to dispatch on, avoids a dynamic dispatch on the wide union of rule types a givenquadstratcan produce. - The other family of methods, dispatching on the concrete type of an already-built
qrule(the rolemomintegrals!used to play), performs the actual numerical integration intoout.