eval#

This submodule is responsible for conversion of system from its abstract syntax tree (AST) representation, which is the job of mfv2d.kform submodule, into a form which can be understood and executed by C code in mfv2d._mfv2d.

This process is done in the following steps:

  1. First, the AST is converted into Python bytecode-like objects (subtypes of MatOp).

  2. Resulting expression is simplified as much as possible by the simplify_expression() function.

  3. Simplified expression is translated into C compatible values (sequence of int, float, and MatOpCode values).

After this is done for each of the blocks of the in the system, the two-dimensional square sequence can be passed to the mfv2d._mfv2d.compute_element_matrix() or mfv2d._mfv2d.compute_element_vector() functions.

Matrix Operations#

For the first step in instruction generation, subtypes of the MatOp type are used. These are all documented bellow.

class mfv2d.eval.MatOp[source]#

Matrix operations which can be created.

This is just a base class for all other matrix operations.

class mfv2d.eval.Identity[source]#

Identity operation.

Do nothing to the matrix.

class mfv2d.eval.MassMat(order: UnknownFormOrder, inv: bool)[source]#

Mass matrix multiplication.

Multiply by either mass matrix or its inverse.

Parameters:
  • order (UnknownFormOrder) – Order of the k-form for the mass matrix.

  • inv (bool) – Should the matrix be inverted.

inv: bool#
order: UnknownFormOrder#
class mfv2d.eval.Incidence(begin: UnknownFormOrder, dual: int)[source]#

Incidence matrix.

Specifies application of an incidence matrix.

Parameters:
  • begin (UnknownFormOrder) – Order of the k-form for the incidence matrix from which to apply it.

  • dual (bool) – Should the incidence matrix be applied as the dual.

begin: UnknownFormOrder#
dual: int#
class mfv2d.eval.Push[source]#

Push the matrix on the stack.

Used for matrix multiplication and summation.

class mfv2d.eval.MatMul[source]#

Multiply two matrices.

Multiply the current matrix with the one currently on the top of the stack.

class mfv2d.eval.Scale(k: float)[source]#

Scale the matrix.

Mutliply the entire matrix with a constant.

Parameters:

k (float) – Value of the constant by which to scale the matrix.

k: float#
class mfv2d.eval.Sum(count: int)[source]#

Sum matrices together.

Sum the top count matrices on the stack with the current matrix.

Parameters:

count (int) – Number of matrices to sum to the current matrix. As such must be greater than zero.

count: int#
class mfv2d.eval.InterProd(starting_order: UnknownFormOrder, field_index: int, dual: bool, adjoint: bool)[source]#

Compute interior product.

This is the most complicated operation.

Parameters:
  • starting_order (UnknownFormOrder) – Order of the k-form to which the interior product should be applied.

  • field_index (int) – Index of the vector/scalar field from which the values of are taken.

  • dual (bool) – Should the dual interior product be applied instead of the primal.

  • adjoint (bool) – Should the adjoint interior product be applied, which is used by the Newton-Raphson solver.

adjoint: bool#
dual: bool#
field_index: int#
starting_order: UnknownFormOrder#

AST Translation#

The translation from the AST form into the Python bytecode is performed by translate_equation() function. This is a thin wrapper around _translate_equation() function, which produces very sub-optimal bytecode. As such, the function will (when specified) call simplify_expression() to simplify the bytecode before returning.

mfv2d.eval.translate_equation(form: Term, vec_fields: Sequence[Function2D | KFormUnknown], newton: bool, simplify: bool) dict[Term, list[MatOp]][source]#

Compute the matrix operations on individual forms.

Parameter#

formTerm

Form to evaluate.

vec_fieldsSequence of Function2D or KFormUnknown

Sequence to use when determining the index of vector fields passed to evaluation function.

newtonbool

When True, non-linear terms will yield terms two terms used to determine the derivative. Otherwise, only the terms to evaluate the value are returned.

simplifybool, default: True

Simplify the expressions at the top level

returns:

Dictionary mapping forms to either a matrix that represents the operation to perform on them, or float, if it should be multiplication with a constant.

rtype:

dict of KForm -> array or float

mfv2d.eval._translate_equation(form: Term, vec_fields: Sequence[Function2D | KFormUnknown], newton: bool, transpose: bool) dict[Term, list[MatOp]][source]#

Compute the matrix operations on individual forms.

Parameter#

formTerm

Form to evaluate.

vec_fieldsSequence of Function2D or KFormUnknown

Sequence to use when determining the index of vector fields passed to evaluation function.

newtonbool

When True, non-linear terms will yield terms two terms used to determine the derivative. Otherwise, only the terms to evaluate the value are returned.

transposebool

Should instructions be transposed.

returns:

Dictionary mapping forms to either a matrix that represents the operation to perform on them, or float, if it should be multiplication with a constant.

rtype:

dict of KForm -> array or float

To check if the AST was correctly translated, or when debugging, the expected result can be obtained by a call to print_eval_procedure() which will convert the iterable of MatOp into a str.

mfv2d.eval.print_eval_procedure(expr: Iterable[MatOp], /) str[source]#

Print how the terms would be evaluated.

This is primarely just used to test if the procedure is correct.

Bytecode Simplifictaion#

Simplifictaion of the bytecode is handled by the simplify_expression() function. It mainly focuses on eliminating Identity operations, fusing together Scale, or applying MassMat to its invers.

mfv2d.eval.simplify_expression(*operations: MatOp) list[MatOp][source]#

Simplify expressions as much as possible.

Tries to merge operations that can be combined, and removes/simplifies as much as possible.

Parameters:

*operations (MatOp) – Sequence of operations to simplify.

Returns:

Simplified sequence of operations, which should be equivalent to the input.

Return type:

list of MatOp

Conversion to C Bytecode#

Conversion into “C-friendly” bytecode is done by converting the MatOp values into MatOpCode, int, or float values.

class mfv2d.eval.MatOpCode(*values)[source]#

Operation codes.

Notes

These values must be kept in sync with the matrix_op_t enum in the C code, since that is how Python and C communicate with each other.

INVALID = 0#
IDENTITY = 1#
MASS = 2#
INCIDENCE = 3#
PUSH = 4#
MATMUL = 5#
SCALE = 6#
SUM = 7#
INTERPROD = 8#

The actual translation is handled by the translate_to_c_instructions() function.

mfv2d.eval.translate_to_c_instructions(*ops: MatOp) list[MatOpCode | int | float][source]#

Translate the operations into C-compatible values.

This translation is done since the C code can’t handle arbitrary Python objects and instead only deals with integers (or int enums) and floats.

Parameters:

*ops (MatOp) – Operations to translate.

Returns:

List of translated operations.

Return type:

list of MatOpCode | int | float

Putting it All Togehter#

Since the steps and related function and types are never used outside of converting AST code into C bytecode, the functionality is wrapped by the translate_system() function, which performs the conversion of an entire mfv2d.kform.KFormSystem into the bytecode.

mfv2d.eval.translate_system(system: KFormSystem, vector_fields: Sequence[Function2D | KFormUnknown], newton: bool) Sequence[Sequence[Sequence[MatOpCode | int | float] | None]][source]#

Create the two-dimensional instruction array for the C code to execute.

This is abstracted further into a CompiledSystem type, which automatically extracts linear, non-linear, right implicit, and left implicit terms, which is what the solver actually needs.

class mfv2d.eval.CompiledSystem(system: KFormSystem)[source]#

System of equations compiled.

This is a convenience class which first compiles the system and splits it into explicit, linear implicit, and non-linear implicit equations, which are then further used by different parts of the solver.

Parameters:

system (KFormSystem) – System to compile.

lhs_full: Sequence[Sequence[Sequence[MatOpCode | int | float] | None]]#

All left-hand side codes of the equations. When evaluated, this will produce the full left side of the equation.

linear_codes: Sequence[Sequence[Sequence[MatOpCode | int | float] | None]]#

All left-hand side codes of the equations, which are linear.

nonlin_codes: Sequence[Sequence[Sequence[MatOpCode | int | float] | None]] | None#

If not None, contains the non-linear codes that can be used for Newton-Raphson solver.

rhs_codes: Sequence[Sequence[Sequence[MatOpCode | int | float] | None]] | None#

If not None, contains the right-hand side codes of the equations.