kform

Contents

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.

__str__() str[source]#

Return print-friendly representation of the object.

label: str#

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:

Its exterior derivative is also availabel through the KForm.derivative() method.

Parameters:
  • order (int) – Order of the differential form.

  • label (str) – Label which is used to print form as a string.

__invert__() KHodge[source]#

Hodge the k-form.

__mul__(other: KForm, /) KInnerProduct[source]#
__mul__(other: Function2D, /) KInteriorProduct

Inner product with a weight.

__post_init__() None[source]#

Check that order is correctly set.

__rmul__(other: KForm, /) KInnerProduct[source]#
__rmul__(other: Function2D, /) KInteriorProduct

Inner product with a weight.

__str__() str[source]#

Return print-friendly representation of the object.

property core_form: KWeight | KFormUnknown#

Most basic form, be it unknown or weight.

property derivative: KFormDerivative#

Derivative of the form.

property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if the form is primal.

property is_weight: bool#

Check if the form is a weight.

label: str#
order: UnknownFormOrder#
property primal_order: UnknownFormOrder#

Order in primal basis.

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.

full_unknown_count(order_1: int, order_2: int) int[source]#

Return the total number of DoFs based on orders for a (full) leaf element.

For a system, these UnknownFormOrder can be stored in UnknownOrderings. This is just a glorified tuple.

class mfv2d.kform.UnknownOrderings(*orders: UnknownFormOrder)[source]#

Type for storing ordering of unknowns within an element.

It is intended to just be a sequence of the form orders.

property count: int#

Count of forms.

form_orders: tuple[UnknownFormOrder, ...]#

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, dual: bool = False)[source]#

Differential form which is to be computed.

Parameters:

dual (bool, default: False) – Is the form represented by the dual or primal basis.

__xor__(other: KFormUnknown | KHodge)[source]#

Return a non-linear interior product term.

property core_form: KFormUnknown#

Most basic form, be it unknown or weight.

dual: bool = False#
property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if form is primal or dual.

property is_weight: bool#

Check if the form is a weight.

property primal_order: UnknownFormOrder#

Order of the mass matrix which needs to be used.

property weight: KWeight#

Create a weight based on this form.

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:
  • order (int) – Order of the differential form.

  • label (str) – Label which is used to print form as a string.

  • base_form (KForm) – Form, which the weight is based on.

__matmul__(other: Callable | Literal[0], /) KElementProjection[source]#

Create projection for the right hand side.

__mul__(other: KForm, /) KInnerProduct[source]#
__mul__(other: Callable | Literal[0], /) KElementProjection

Inner product with a weight.

__rmul__(other: KForm, /) KInnerProduct[source]#
__rmul__(other: Callable | Literal[0], /) KElementProjection

Inner product with a weight.

__str__() str[source]#

Return print-friendly representation of the object.

__xor__(other: Callable) KBoundaryProjection[source]#

Create boundary projection for the right hand side.

base_form: KFormUnknown#
property core_form: KWeight#

Most basic form, be it unknown or weight.

property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if form is primal or dual.

property is_weight: bool#

Check if the form is a weight.

property primal_order: UnknownFormOrder#

Order of the mass matrix which needs to be used.

property weight: KWeight#

Return itself.

\(k\)-forms resulting from different operations also each have a different type:

class mfv2d.kform.KHodge(form: KForm)[source]#

Hodge represents a transformation from primal to dual basis.

A continuous Hodge \(\star\) is defined as a mapping, which transfers a k-form on an n-dimensional manifold into a (n-k)-form:

\[\star: \Lambda^{(k)} \leftarrow \Lambda^{(n - k)}\]

The discrete version of the Hodge also maps a k-form onto a (n-k) form, but with a very specific choice of basis. If a polynomial can be written in terms of primal basis matrix \(\boldsymbol{\Psi}^{(k)}\) and degree-of-freedom vector \(\vec{p}^{(k)}\), which are defined by equations (1) and (2), then the duals are defined by \(khodge-dual\). This allows for the resulting system to be sparser, at the cost of having to obtain the primal values in post-processing.

(1)#\[\boldsymbol{\Psi}^{(k)} = \begin{bmatrix} \psi_0 (\vec{\xi}) & \cdots & \psi_n (\vec{\xi}) \end{bmatrix}\]
(2)#\[\begin{split}\vec{p}^{(k)} = \begin{bmatrix} p^0 \\ \vdots \\ p^n \end{bmatrix}\end{split}\]
(3)#\[\begin{align} \tilde{\boldsymbol{\Psi}}^{(n - k)} = \boldsymbol{\Psi}^{(k)} \left(\mathbb{M}^{(k)}\right)^{-1} && \vec{\tilde{p}}^{(n - k)} = \mathbb{M}^{(k)} \vec{p}^{(k)} \end{align}\]
Parameters:

form (KForm) – Form which the Hodge should be applied to. Note that applying the Hodge twice is the identity operation.

base_form: KForm#
property core_form: KWeight | KFormUnknown#

Most basic form, be it unknown or weight.

property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if the form is primal.

property is_weight: bool#

Check if the form is a weight.

property primal_order: UnknownFormOrder#

Order of the mass matrix which needs to be used.

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.

property core_form: KWeight | KFormUnknown#

Most basic form, be it unknown or weight.

form: KForm#
property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if the form is primal.

property is_weight: bool#

Check if the form is a weight.

property primal_order: UnknownFormOrder#

Order in primal basis.

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.

__post_init__() None[source]#

Enforce the conditions for allowing interior product.

property core_form: KWeight | KFormUnknown#

Most basic form, be it unknown or weight.

form: KForm#
property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if the form is primal or not.

property is_weight: bool#

Check if it is a weight form.

property primal_order: UnknownFormOrder#

Return the order of the primal.

vector_field: Function2D#
class mfv2d.kform.KInteriorProductNonlinear(label: str, order: UnknownFormOrder, form: KFormUnknown | KHodge, 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

__post_init__() None[source]#

Enforce the conditions for allowing interior product.

property core_form: KWeight | KFormUnknown#

Most basic form, be it unknown or weight.

form: KFormUnknown | KHodge#
form_field: KFormUnknown#
property is_linear: bool#

Check if the form is linear.

property is_primal: bool#

Check if the form is primal or not.

property is_weight: bool#

Check if it is a weight form.

property primal_order: UnknownFormOrder#

Return the order of the primal.

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.

__div__(other: float | int, /) KSum[source]#

Divide by a constant.

__eq__(other: TermEvaluatable | None | Literal[0], /) KEquation[source]#
__eq__(other, /) bool

Check equality or form an equation.

__mul__(other: float | int, /) KSum[source]#

Multiply by a constant.

__neg__() KSum[source]#

Negate the term.

__post_init__() None[source]#

Check that the weight is indeed a weight.

__radd__(other: TermEvaluatable, /) KSum[source]#

Add the term to another.

__rmul__(other: float | int, /) KSum[source]#

Multiply by a constant.

__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.

weight: KWeight#

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}\]
unknown_form: KForm#
property unknowns: tuple[KFormUnknown, ...]#

Return all unknowns in the sum.

property vector_fields: tuple[Function2D | KFormUnknown, ...]#

Return all vector fields in the sum.

weight_form: KForm#

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.

func: Callable | None = None#
property unknowns: tuple[KFormUnknown, ...]#

Return all unknowns (there are none).

property vector_fields: tuple[Function2D | KFormUnknown, ...]#

Return all vector fields (there are none).

weight: KWeight#

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.

Parameters:
  • weight (KWeight) – Weight form used.

  • func (tuple[str, Callable], optional) – The function to use, specified by a name and the callable to use. If it is not specified or given as None, then \(f = 0\).

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 explicit_terms: tuple[tuple[float, KExplicit], ...]#

Get all explicit terms.

property implicit_terms: tuple[tuple[float, TermEvaluatable], ...]#

Get all implicit terms.

pairs: tuple[tuple[float, KExplicit | KInnerProduct], ...]#
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.

__post_init__() None[source]#

Check that terms are done properly.

left: KSum#
right: KSum#
property weight: KWeight#

Return the weight used by both sides.

A collection of equations form a KFormSystem. This type also provides useful utilities for identifying different terms, extracting vector fileds, weights, unknowns, and others.

class mfv2d.kform.KFormSystem(*equations: KEquation, sorting: Callable[[KForm], Any] | None = None)[source]#

System of equations of differential forms, which are optionally sorted.

This is a collection of equations, which fully describe a problem to be solved for the degrees of freedom of differential forms.

Parameters:
  • *equations (KFormEquation) – Equations which are to be used.

  • sorting ((KForm) -> Any, optional) – Callable passed to the sorted() builtin to sort the primal forms. This corresponds to sorting the columns of the system matrix.

__str__() str[source]#

Create a printable representation of the object.

equations: tuple[KEquation, ...]#
unknown_forms: tuple[KFormUnknown, ...]#
vector_fields: tuple[Function2D | KFormUnknown, ...]#
weight_forms: tuple[KWeight, ...]#