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.

__matmul__(other: KForm, /) KInnerProduct[source]#

Inner product.

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

Interior product.

__post_init__() None[source]#

Check that order is correctly set.

__rmul__(other: Function2D, /) KInteriorProduct[source]#
__rmul__(other: KFormUnknown, /) KInteriorProductLowered

Interior product.

__str__() str[source]#

Return print-friendly representation of the object.

property derivative: KFormDerivative#

Derivative of the form.

label: str#
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.

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.

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.

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: KForm, /) KInnerProduct[source]#
__matmul__(other: Callable, /) 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 is_linear: bool#

Check if the form is linear.

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

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.

form: KForm#
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.

form: KForm#
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

__post_init__() None[source]#

Enforce the conditions for allowing interior product.

form: KForm#
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.

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

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

Divide by a constant.

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

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.

mfv2d.kform.check_form_linear(form: KForm) bool[source]#

Check if the form is linear.

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

KForm

(0, 1)

<KForm>.derivative

KFormDerivative

interior product (linear)

(callable, KForm)

(1, 2)

func * <KForm>

KInteriorProduct

interior product (non-linear)

(KForm, KForm)

(1, (1, 2))

<KForm> * <KForm>

KInteriorProductLowered

element projection

(KWeight, callable)

Any

<KWeight> @ callable

KElementProjection

boundary projection

(KWeight, callable)

0, 1

<KWeight> ^ callable

KBoundaryProjection

inner product

(KForm, KForm)

(\(k\), \(k\))

<KForm> @ <KForm>

KInnerProduct

scale

Term

None

k * <Term>

KSum

add

(Term, Term)

None

<Term> + <Term>

KSum

sub

(Term, Term)

None

<Term> - <Term>

KSum