kform#
This submodule is provides types which allow for creating a description of the system to solve using Python’s operator overloading. It also allows for caching any syntax errors with it. It could be even further improved in the future if Python’s generic typing will ever support inheritence for generic types.
Different \(k\)-Form Types#
Most of this module’s contents consists of types that allow describing
aforementioned \(k\)-form relations. These are all subtypes of the
Term type. It only describes how to describe/print a term and
nothing more.
- class mfv2d.kform.Term(label: str)[source]#
Represents a term in the k-form expressions.
This type contains the most basic functionality and is mainly intended to help with type hints.
- Parameters:
label (str) – How to identify the term.
Next is the basic KForm type. In addition to its label it also
has its order. This can have a value of 0, 1, or 2. It has quite a few
useful operators implemented.
- class mfv2d.kform.KForm(label: str, order: UnknownFormOrder)[source]#
Differential K form.
It is described by and order and identifier, that is used to print it. It offers the following overloaded operations:
*with anotherKFormresults inKInnerProduct*with a callable results inKInteriorProduct~will result in aKHodgeof itself
Its exterior derivative is also availabel through the
KForm.derivative()method.- Parameters:
- __matmul__(other: KForm, /) KInnerProduct[source]#
Inner product.
- __mul__(other: Function2D, /) KInteriorProduct[source]#
Interior product.
- __rmul__(other: Function2D, /) KInteriorProduct[source]#
- __rmul__(other: KFormUnknown, /) KInteriorProductLowered
Interior product.
- property derivative: KFormDerivative#
Derivative of the form.
- order: UnknownFormOrder#
To describe order of a form older functions made use of just taking the
integers and assuming they were in the desired range. Newly written code should
instead make use of UnknownFormOrder type, which is an enum.
- class mfv2d.kform.UnknownFormOrder(*values)[source]#
Orders of unknown differential forms.
This enum is intended to replace just passing integers for the order of forms, since this is easier to catch by type-checkers.
- FORM_ORDER_0 = 1#
- FORM_ORDER_1 = 2#
- FORM_ORDER_2 = 3#
- property dual: UnknownFormOrder#
Return what the dual of the form is.
To denote variables that should be solved for, KFormUnknown
are used. They also can be used for non-linear interior products
using the * operator.
- class mfv2d.kform.KFormUnknown(label: str, order: UnknownFormOrder)[source]#
Differential form which is to be computed.
- Parameters:
dual (bool, default: False) – Is the form represented by the dual or primal basis.
- __mul__(other: Function2D, /) KInteriorProduct[source]#
- __mul__(other: KForm, /) KInteriorProductLowered
Interior product.
Each unknown form can also be used to create a weight form with
the type KWeight throught the KFormUnknown.weight()
method. These are used for forming interior products, as well as
forming either element projections
(described by KElementProjection) or boundary projections
for weak boundary conditions (described by KBoundaryProjection).
- class mfv2d.kform.KWeight(label: str, order: UnknownFormOrder, base_form: KFormUnknown)[source]#
Differential K form represented with the dual basis.
Provides operators for forming element and boundary projections throught the
@and^operators respectively.- Parameters:
- __matmul__(other: KForm, /) KInnerProduct[source]#
- __matmul__(other: Callable, /) KElementProjection
Inner product with a weight.
- __xor__(other: Callable) KBoundaryProjection[source]#
Create boundary projection for the right hand side.
- base_form: KFormUnknown#
\(k\)-forms resulting from different operations also each have a different type:
For the derivative
KFormDerivativeis usedFor the interor product with a vector function
KInteriorProductFor the interor product with an unknown form
KInteriorProductLowered
- class mfv2d.kform.KFormDerivative(form: KForm)[source]#
Exterior derivative of a form.
An exterior derivative maps a differential k-form into a (k + 1) form:
\[\mathrm{d}: p^{(k)} \in \Lambda^{(k)}(\mathcal{M}) \leftarrow q^{(k + 1)} \in \Lambda^{(k + 1)}(\mathcal{M})\]This operation is expressed in terms of a so called incidence matrix \(\mathbb{E}^{(k, k + 1)}\), which maps degrees of freedom from basis of k-forms to those of (k + 1)-forms
Note that applying the operator \(\mathrm{d}\) twice will always result in a form which is zero everywhere:
\[\mathrm{d}\left( \mathrm{d} p^{(k)} \right) = 0\]- Parameters:
form (KForm) – The form of which the derivative is to be taken.
- class mfv2d.kform.KInteriorProduct(label: str, order: UnknownFormOrder, form: KForm, vector_field: Function2D)[source]#
Represents an interior product of a K-form with a tangent vector field.
- vector_field: Function2D#
- class mfv2d.kform.KInteriorProductLowered(label: str, order: UnknownFormOrder, form: KForm, form_field: KFormUnknown)[source]#
Represents an interior product of a K-form with a lowered 1-form.
In two dimentions there are at total of four different types of interior products:
primal 1-form
primal 2-form
dual 1-form
dual 2-form
These all correspond to a different operation:
primal 1-form: scalar cross product
primal 2-form: multiplication with a vector field
dual 1-form: dot product
dual 2-form: vector cross product
- form_field: KFormUnknown#
Forming Expressions#
From these basic building blocks, TermEvaluatable objects can
be created. These represent an expression, which can be evaluated,
provided degrees of freedom of associated KFormUnknown are known.
Quite a few operators are implemented on it, which allow for scaling,
adding, or subtracting these together.
- class mfv2d.kform.TermEvaluatable(label: str, weight: KWeight)[source]#
Terms which can be evaluated as blocks of the system matrix.
This is a base class for all terms which can be evaluated as blocks of the system.
- __add__(other: TermEvaluatable, /) KSum[source]#
Add the term to another.
- __eq__(other: TermEvaluatable | Literal[0], /) KEquation[source]#
- __eq__(other, /) bool
Check equality or form an equation.
- __radd__(other: TermEvaluatable, /) KSum[source]#
Add the term to another.
- __rsub__(other: TermEvaluatable, /) KSum[source]#
Subtract the combination to another.
- __sub__(other: TermEvaluatable, /) KSum[source]#
Subtract the term from another.
- property unknowns: tuple[KFormUnknown, ...]#
Return all unknowns in the term.
- property vector_fields: tuple[Function2D | KFormUnknown, ...]#
Return all vector fields.
The most basic of these is the KInnerProduct, which represents
an inner product between a weight form and an unknown form.
- class mfv2d.kform.KInnerProduct(a: KForm, b: KForm, /)[source]#
Inner product of a primal and dual form.
An inner product must be taken with primal and dual forms of the same k-order. The discrete version of an inner product of two k-forms is expressed as a discrete inner product on the mass matrix:
\[\left< p^{(k)}, q^{(k)} \right> = \int_{\mathcal{K}} p^{(k)} \wedge \star q^{(k)} = \vec{p}^T \mathbb{M}^k \vec{q}\]
Next is the family of KExplicit terms. These do not depend on any
unknown forms and can as such be evaluated explicity. These also may only
appear on the right side of any KEquation formed.
- class mfv2d.kform.KExplicit(label: str, weight: KWeight, func: Callable | None = None)[source]#
Base class for explicit terms.
This type just implements some common functionality.
- property unknowns: tuple[KFormUnknown, ...]#
Return all unknowns (there are none).
- property vector_fields: tuple[Function2D | KFormUnknown, ...]#
Return all vector fields (there are none).
First of the KExplicit subtypes is the KElementProjection,
which is used to represent the L^2 projection on the element.
- class mfv2d.kform.KElementProjection(label: str, weight: KWeight, func: Callable | None = None)[source]#
Element integral of the function with the basis.
This is used to form the right side of the systems of equations coming from a forcing function.
Second is the the KBoundaryProjection, which represents a boundary
integral for an element. It is used to describe weak boundary conditions.
- class mfv2d.kform.KBoundaryProjection(label: str, weight: KWeight, func: Callable | None = None)[source]#
Boundary integral of a forcing.
This is intended to be used to define boundary conditions. Given that the function to be projected is denoted by \(f\) and the weight function is denoted by \(w\), this term represents the integral
\[\int_{\partial \Omega} f^{(k)} \wedge \star w^{(k + 1)}\]Such terms typically arise from weak boundary conditions.
As the last TermEvaluatable subtype there is KSum type.
This represents a linear combination of other TermEvaluatable
types. It is actually a linear combination of the terms, as each can
have its own coefficient. When two KSum objects are added together,
they are concatenated, so KSum should never contain another
KSum. All terms in the sum must use the exact same weight form.
- class mfv2d.kform.KSum(*pairs: tuple[float, TermEvaluatable])[source]#
Linear combination of differential form inner products.
- Parameters:
*pairs (tuple of float and KFormInnerProduct) – Coefficients and the inner products.
- property implicit_terms: tuple[tuple[float, TermEvaluatable], ...]#
Get all implicit terms.
- split_terms_linear_nonlinear() tuple[KSum | None, KSum | None][source]#
Split the sum into linear implicit and non-linear implicit terms.
- Returns:
KSum – All linear terms. If there are no linear implicit terms, it is None instead.
KSum – All non-linear terms. If there are no non-linear implicit terms, it is None instead.
- property unknowns: tuple[KFormUnknown, ...]#
Return all unknowns in the sum.
- property vector_fields: tuple[Function2D | KFormUnknown, ...]#
Return all vector fields in the sum.
Equations and Systems#
When two expressions are related as being equal with the == operator,
the result is a KEquation. It consists of a left and right side.
The left must contain at least one KInnerProduct term and no
KExplicit terms. Both sides must also use a matching weight form.
- class mfv2d.kform.KEquation(left: KSum, right: KSum)[source]#
Equation of differential forms and weights, consisting of a left and a right side.
The equation represents an equation where all the implicit terms are on the left side and all explicit ones are on the right side.
- Parameters:
left (KSum or KInnerProduct) – Term representing the implicit part of the equation with all the unknown forms.
right (KFormProjection) – The form representing the explicit part of the equation.
Supporting Functions#
Some minor support functions for parsing and dealing with \(k\)-forms
are also provided. These used to be methods, but having them as functions
is easier to deal with. And to refactor, since all implementations for
different types are in the same spot (God bless Python’s match statement).
- mfv2d.kform.extract_base_form(form: KForm, max_depth: int = 100) KFormUnknown | KWeight[source]#
Extract base for from the k-form.
- mfv2d.kform.extract_unknown_forms(form: KForm) list[KFormUnknown][source]#
Extract unknown forms from the form, otherwise raises type error.
Operations#
Different operators are supported by different \(k\)-forms. The overview is shown in the table bellow.
operation |
required type(s) |
required order(s) |
operator |
result |
|---|---|---|---|---|
derivative |
(0, 1) |
|
||
interior product (linear) |
(callable, |
(1, 2) |
|
|
interior product (non-linear) |
(1, (1, 2)) |
|
||
element projection |
( |
Any |
|
|
boundary projection |
( |
0, 1 |
|
|
inner product |
(\(k\), \(k\)) |
|
||
scale |
None |
|
||
add |
None |
|
||
sub |
None |
|