Symbolic operator representation

Based on pymbolic.

DOF Description

class pytential.symbolic.dof_desc.DEFAULT_SOURCE[source]

Symbolic identifier for the default source. Geometries with this value get replaced with the default source given to pytential.bind().

class pytential.symbolic.dof_desc.DEFAULT_TARGET[source]

Symbolic identifier for the default target. Geometries with this value get replaced with the default target given to pytential.bind().

class pytential.symbolic.dof_desc.QBX_SOURCE_STAGE1[source]

Symbolic identifier for the Stage 1 discretization of a pytential.qbx.QBXLayerPotentialSource.

class pytential.symbolic.dof_desc.QBX_SOURCE_STAGE2[source]

Symbolic identifier for the Stage 2 discretization of a pytential.qbx.QBXLayerPotentialSource.

class pytential.symbolic.dof_desc.QBX_SOURCE_QUAD_STAGE2[source]

Symbolic identifier for the upsampled Stage 2 discretization of a pytential.qbx.QBXLayerPotentialSource.

class pytential.symbolic.dof_desc.GRANULARITY_NODE[source]

DOFs are per node.

class pytential.symbolic.dof_desc.GRANULARITY_CENTER[source]

DOFs interleaved per expansion center (two per node, one on each side).

class pytential.symbolic.dof_desc.GRANULARITY_ELEMENT[source]

DOFs per discretization element.

class pytential.symbolic.dof_desc.DOFDescriptor(geometry: GeometryId | None = None, discr_stage: DiscretizationStage | None = None, granularity: DOFGranularity | None = None)[source]

A data structure specifying the meaning of a vector of degrees of freedom that is handled by pytential (a “DOF vector”). In particular, using geometry, this data structure describes the geometric object on which the (scalar) function described by the DOF vector exists. Using granularity, the data structure describes how the geometric object is discretized (e.g. conventional nodal data, per-element scalars, etc.)

geometry: GeometryId

An identifier for the geometry on which the DOFs exist. This can be a simple string or any other hashable identifier for the geometric object. The geometric objects are generally subclasses of PotentialSource, TargetBase or Discretization.

discr_stage: DiscretizationStage | None

Specific to a pytential.source.LayerPotentialSourceBase, this describes on which of the discretizations the DOFs are defined. Can be one of QBX_SOURCE_STAGE1, QBX_SOURCE_STAGE2 or QBX_SOURCE_QUAD_STAGE2.

granularity: DOFGranularity

Describes the level of granularity of the DOF vector. Can be one of GRANULARITY_NODE (one DOF per node), GRANULARITY_CENTER (two DOFs per node, one per side) or GRANULARITY_ELEMENT (one DOF per element).

copy(geometry: GeometryId | None = None, discr_stage: DiscretizationStage | None = <class 'pytential.symbolic.dof_desc._NoArgSentinel'>, granularity: DOFGranularity | None = None) DOFDescriptor[source]
to_stage1() DOFDescriptor[source]
to_stage2() DOFDescriptor[source]
to_quad_stage2() DOFDescriptor[source]
pytential.symbolic.dof_desc.as_dofdesc(desc: DOFDescriptorLike) DOFDescriptor[source]
pytential.symbolic.dof_desc.GeometryId: TypeAlias = <class 'collections.abc.Hashable'>[source]

Type used for geometry identifiers.

pytential.symbolic.dof_desc.DiscretizationStage: TypeAlias = type[pytential.symbolic.dof_desc.QBX_SOURCE_STAGE1] | type[pytential.symbolic.dof_desc.QBX_SOURCE_STAGE2] | type[pytential.symbolic.dof_desc.QBX_SOURCE_QUAD_STAGE2]

A Union of all the allowed discretization stages.

pytential.symbolic.dof_desc.DOFGranularity: TypeAlias = type[pytential.symbolic.dof_desc.GRANULARITY_NODE] | type[pytential.symbolic.dof_desc.GRANULARITY_CENTER] | type[pytential.symbolic.dof_desc.GRANULARITY_ELEMENT]

A Union of all the allowed DOF granularity types.

pytential.symbolic.dof_desc.DOFDescriptorLike: TypeAlias = pytential.symbolic.dof_desc.DOFDescriptor | collections.abc.Hashable

Types convertible to a DOFDescriptor by as_dofdesc().

Basic objects

Object types

Based on the mathematical quantity being represented, the following types of objects occur as part of a symbolic operator representation:

  • If a quantity is a scalar, it is just a symbolic expression–that is, an element of the set of formal expressions recursively generated by the placeholders (see Placeholders), constants, and arithmetic operations on them (see pymbolic.primitives). Objects of this type are created simply by doing arithmetic on placeholders and scalar constants.

  • If the quantity is “just a bunch of scalars” (like, say, rows in a system of integral equations), the symbolic representation is an object array. Each element of the object array contains a symbolic expression.

    pytools.obj_array.new_1d() and pytools.obj_array.flat() can help create those.

  • If it is a geometric quantity (that makes sense without explicit reference to coordinates), it is a pymbolic.geometric_algebra.MultiVector. This can be converted to an object array by calling: pymbolic.geometric_algebra.MultiVector.as_vector().

pyopencl.array.Array and meshmode.dof_array.DOFArray instances hold per-node degrees of freedom (and only those). Such instances do not occur on the symbolic side of pytential at all. They’re only visible either as bound inputs (see pytential.bind()) or outputs of evaluation. Which one is used depends on the meaning of the data being represented. If the data is associated with a Discretization, then DOFArray is used and otherwise Array is used.

pytential.symbolic.primitives.Expression

alias of int | integer | float | complex | inexact | bool | bool | ExpressionNode | tuple[Expression, …]

pytential.symbolic.primitives.for_each_expression(f: Callable[Concatenate[ArithmeticExpression, P], ArithmeticExpression]) Callable[Concatenate[OperandTc, P], OperandTc][source]

A decorator that takes a function that can only work on expressions and transforms it into a function that can be applied componentwise on numpy.ndarray or MultiVector.

pytential.symbolic.primitives.Operand

alias of int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, …], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]

class pytential.symbolic.primitives.OperandTc

alias of TypeVar(‘OperandTc’, int | ~numpy.integer | float | complex | ~numpy.inexact | ExpressionNode, ~pytools.obj_array.ObjectArray[tuple[int, …], int | ~numpy.integer | float | complex | ~numpy.inexact | ExpressionNode], ~pymbolic.geometric_algebra.MultiVector[int | ~numpy.integer | float | complex | ~numpy.inexact | ExpressionNode], int | ~numpy.integer | float | complex | ~numpy.inexact | ExpressionNode | ~pytools.obj_array.ObjectArray[tuple[int, …], int | ~numpy.integer | float | complex | ~numpy.inexact | ExpressionNode] | ~pymbolic.geometric_algebra.MultiVector[int | ~numpy.integer | float | complex | ~numpy.inexact | ExpressionNode])

class pytential.symbolic.primitives.ArithmeticExpressionT

alias of TypeVar(‘ArithmeticExpressionT’, bound=int | integer | float | complex | inexact | ExpressionNode)

pytential.symbolic.primitives.QBXForcedLimit

alias of Literal[-2, -1, 1, 2, ‘avg’] | None

class pytential.symbolic.primitives.P

See pytools.P

class pytential.symbolic.primitives.ExpressionNode[source]

See pymbolic.ExpressionNode.

Diagnostics

class pytential.symbolic.primitives.ErrorExpression(message: str)[source]

Bases: ExpressionNode

An expression that, if evaluated, causes an error with the supplied message.

message: str

The error message to raise when this expression is encountered.

mapper_method: ClassVar[str] = 'map_error_expression'

Placeholders

pytential.symbolic.primitives.var[source]

alias of Variable

class pytential.symbolic.primitives.SpatialConstant(name: str)[source]

Bases: Variable

A symbolic constant to represent a symbolic variable that is spatially constant.

For example the wave-number \(k\) in the setting of a constant-coefficient Helmholtz problem. For use in sumpy.kernel.ExpressionKernel.expression. Any variable occurring there that is not a SpatialConstant is assumed to have a spatial dependency.

prefix: ClassVar[str] = '_spatial_constant_'

Prefix used in code generation for variables of this type.

as_sympy() Symbol[source]

Convert variable to a sympy expression.

classmethod from_sympy(expr: Symbol) SpatialConstant[source]

Convert sympy expression to a constant.

mapper_method: ClassVar[str] = 'map_spatial_constant'
pytential.symbolic.primitives.make_sym_mv(name: str, num_components: int) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.make_sym_surface_mv(name: str, ambient_dim: int, dim: int, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]

Functions

pytential.symbolic.primitives.real
pytential.symbolic.primitives.imag
pytential.symbolic.primitives.conj
pytential.symbolic.primitives.abs
pytential.symbolic.primitives.sqrt
pytential.symbolic.primitives.sin
pytential.symbolic.primitives.cos
pytential.symbolic.primitives.tan
pytential.symbolic.primitives.arcsin
pytential.symbolic.primitives.arccos
pytential.symbolic.primitives.arctan
pytential.symbolic.primitives.arctan2
pytential.symbolic.primitives.sinh
pytential.symbolic.primitives.cosh
pytential.symbolic.primitives.tanh
pytential.symbolic.primitives.arcsinh
pytential.symbolic.primitives.arccosh
pytential.symbolic.primitives.arctanh
pytential.symbolic.primitives.exp
pytential.symbolic.primitives.log

Discretization properties

class pytential.symbolic.primitives.DiscretizationProperty(dofdesc: DOFDescriptor)[source]

Bases: ExpressionNode

A quantity that depends exclusively on the discretization.

dofdesc: DOFDescriptor

The descriptor for this quantity that selects its geometry on evaluation.

mapper_method: ClassVar[str] = 'map_discretization_property'
class pytential.symbolic.primitives.IsShapeClass(shape: type[Shape], dofdesc: DOFDescriptor)[source]

Bases: DiscretizationProperty

A predicate that is True if the elements of the discretization have a unique type that matches shape.

shape: type[Shape]

A modepy.Shape subclass.

mapper_method: ClassVar[str] = 'map_is_shape_class'
class pytential.symbolic.primitives.QWeight(dofdesc: DOFDescriptor)[source]

Bases: DiscretizationProperty

Bare quadrature weights (without Jacobians).

mapper_method: ClassVar[str] = 'map_q_weight'
pytential.symbolic.primitives.nodes(ambient_dim: int, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
Returns:

a pymbolic.geometric_algebra.MultiVector of node coordinates.

pytential.symbolic.primitives.parametrization_derivative(ambient_dim: int, dim: int, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
Returns:

a pymbolic.geometric_algebra.MultiVector representing the derivative of the reference-to-global parametrization.

pytential.symbolic.primitives.parametrization_derivative_matrix(ambient_dim: int, dim: int, dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int, int], int | integer | float | complex | inexact | ExpressionNode][source]
Returns:

a numpy.ndarray representing the derivative of the reference-to-global parametrization.

pytential.symbolic.primitives.pseudoscalar(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]

Same as the outer product of all parametrization derivative columns.

pytential.symbolic.primitives.area_element(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]
pytential.symbolic.primitives.sqrt_jac_q_weight(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]
pytential.symbolic.primitives.normal(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]

Exterior unit normals.

pytential.symbolic.primitives.mean_curvature(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]

(Numerical) mean curvature.

pytential.symbolic.primitives.first_fundamental_form(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int, int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.second_fundamental_form(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int, int], int | integer | float | complex | inexact | ExpressionNode][source]

Compute the second fundamental form of a surface. This is in reference to the reference-to-global mapping in use for each element.

Note

Some references assume that the second fundamental form is computed with respect to an orthonormal basis, which this is not.

pytential.symbolic.primitives.shape_operator(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int, int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.expansion_radii(ambient_dim: int, dim: int | None = None, granularity: DOFGranularity | None = None, dofdesc: DOFDescriptorLike = None) ArithmeticExpression[source]
pytential.symbolic.primitives.expansion_centers(ambient_dim: int, side: Literal[-1, 1], dim: int | None = None, dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.h_max(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]

Defines a maximum element size in the discretization.

pytential.symbolic.primitives.weights_and_area_elements(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]

Combines area_element() and QWeight.

Elementary numerics

class pytential.symbolic.primitives.NumReferenceDerivative(ref_axes: int | tuple[tuple[int, int], ...] | None = None, operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode] | None = None, dofdesc: DOFDescriptor | None = None)[source]

Bases: DiscretizationProperty

An operator that takes a derivative of operand with respect to the the element reference coordinates.

ref_axes: tuple[tuple[int, int], ...]

A tuple of pairs (axis, derivative_order) that define the reference derivatives taken on the given operand. The tuple must be sorted with respect to the axis index. For example, ((0, 2), (1, 1)) is a correct input as it is sorted and each axis is unique. It denotes a second derivative with respect to $x$ (0) and a first derivative with respect to $y$ (1).

operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]

An operand to differentiate.

mapper_method: ClassVar[str] = 'map_num_reference_derivative'
pytential.symbolic.primitives.num_reference_derivative(expr: ArithmeticExpression, ref_axes: tuple[tuple[int, int], ...] | int, dofdesc: DOFDescriptorLike | None) NumReferenceDerivative[source]

Take a derivative of expr with respect to the the element reference coordinates.

See NumReferenceDerivative.

class pytential.symbolic.primitives.NodeSum(operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode] | None = None)[source]

Bases: Expression.

Implements a global sum over all discretization nodes.

mapper_method: ClassVar[str] = 'map_node_sum'
pytential.symbolic.primitives.node_sum(expr: int | integer | float | complex | inexact | ExpressionNode) NodeSum[source]
class pytential.symbolic.primitives.NodeMax(operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode] | None = None)[source]

Bases: Expression.

Implements a global maximum over all discretization nodes.

mapper_method: ClassVar[str] = 'map_node_max'
pytential.symbolic.primitives.node_max(expr: int | integer | float | complex | inexact | ExpressionNode) NodeMax[source]
class pytential.symbolic.primitives.NodeMin(operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode] | None = None)[source]

Bases: Expression.

Implements a global minimum over all discretization nodes.

mapper_method: ClassVar[str] = 'map_node_min'
pytential.symbolic.primitives.node_min(expr: int | integer | float | complex | inexact | ExpressionNode) NodeMin[source]
class pytential.symbolic.primitives.ElementwiseSum(operand: Operand | None = None, dofdesc: DOFDescriptorLike | None = None)[source]

Bases: Expression.

Returns a vector of DOFs with all entries on each element set to the sum of DOFs on that element.

mapper_method: ClassVar[str] = 'map_elementwise_sum'
pytential.symbolic.primitives.elementwise_sum(expr: int | integer | float | complex | inexact | ExpressionNode, dofdesc: DOFDescriptorLike = None) ElementwiseSum[source]
class pytential.symbolic.primitives.ElementwiseMin(operand: Operand | None = None, dofdesc: DOFDescriptorLike | None = None)[source]

Bases: Expression.

Returns a vector of DOFs with all entries on each element set to the minimum of DOFs on that element.

mapper_method: ClassVar[str] = 'map_elementwise_min'
pytential.symbolic.primitives.elementwise_min(expr: int | integer | float | complex | inexact | ExpressionNode, dofdesc: DOFDescriptorLike = None) ElementwiseMin[source]
class pytential.symbolic.primitives.ElementwiseMax(operand: Operand | None = None, dofdesc: DOFDescriptorLike | None = None)[source]

Bases: Expression.

Returns a vector of DOFs with all entries on each element set to the maximum of DOFs on that element.

mapper_method: ClassVar[str] = 'map_elementwise_max'
pytential.symbolic.primitives.elementwise_max(expr: int | integer | float | complex | inexact | ExpressionNode, dofdesc: DOFDescriptorLike = None) ElementwiseMax[source]
pytential.symbolic.primitives.integral(ambient_dim: int, dim: int, operand: OperandTc, dofdesc: DOFDescriptorLike = None) OperandTc[source]

A volume integral of operand.

class pytential.symbolic.primitives.Ones(dofdesc: ~pytential.symbolic.dof_desc.DOFDescriptor = <factory>)[source]

Bases: ExpressionNode

A DOF-vector that is constant one on the whole discretization.

mapper_method: ClassVar[str] = 'map_ones'
pytential.symbolic.primitives.ones_vec(dim: int, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.area(ambient_dim: int, dim: int, dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]
pytential.symbolic.primitives.mean(ambient_dim: int, dim: int, operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode], dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
class pytential.symbolic.primitives.IterativeInverse(expression: int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode, rhs: int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode, variable_name: str, extra_vars: dict[str, int | ~numpy.integer | float | complex | ~numpy.inexact | bool | ~numpy.bool | ~pytential.symbolic.primitives.ExpressionNode | tuple[int | ~numpy.integer | float | complex | ~numpy.inexact | bool | ~numpy.bool | ~pytential.symbolic.primitives.ExpressionNode | tuple[Expression, ...], ...]] = <factory>, dofdesc: ~pytential.symbolic.dof_desc.DOFDescriptor = <factory>)[source]

Bases: ExpressionNode

A symbolic \(A x = b\) linear solve expression.

expression: int | integer | float | complex | inexact | ExpressionNode

The operator A used in the linear solve.

rhs: int | integer | float | complex | inexact | ExpressionNode

The right-hand side variable used in the linear solve.

variable_name: str

The name of the variable to solve for.

extra_vars: dict[str, int | integer | float | complex | inexact | bool | bool | ExpressionNode | tuple[int | integer | float | complex | inexact | bool | bool | ExpressionNode | tuple[Expression, ...], ...]]

A dictionary of additional variables required to define the operator.

dofdesc: DOFDescriptor

A descriptor for the geometry on which the solution is defined.

mapper_method: ClassVar[str] = 'map_iterative_inverse'

Operators

class pytential.symbolic.primitives.Interpolation(from_dd: DOFDescriptorLike, to_dd: DOFDescriptorLike, operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode])[source]

Bases: ExpressionNode

Interpolate quantity from a DOF described by from_dd to a DOF described by to_dd.”

from_dd: DOFDescriptor

A descriptor for the geometry on which operand is defined.

to_dd: DOFDescriptor

A descriptor for the geometry to which to interpolate operand to.

operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]

An expression or array of expressions to interpolate. Arrays are interpolated componentwise.

mapper_method: ClassVar[str] = 'map_interpolation'
pytential.symbolic.primitives.interpolate(operand: int | integer | float | complex | inexact | ExpressionNode, from_dd: DOFDescriptorLike, to_dd: DOFDescriptorLike) Interpolation[source]

Geometric Calculus (based on Geometric/Clifford Algebra)

class pytential.symbolic.primitives.Derivative[source]

A symbolic derivative.

This mechanism cannot be used to take more than one derivative at a time.

__call__(operand)[source]

Call self as a function.

dnabla(ambient_dim: int)[source]
static resolve(expr: int | integer | float | complex | inexact | ExpressionNode) int | integer | float | complex | inexact | ExpressionNode[source]

Conventional Calculus

pytential.symbolic.primitives.dd_axis(axis: int, ambient_dim: int, operand: OperandTc) OperandTc[source]
Returns:

the derivative along (XYZ) axis axis (in ambient_dim-dimensional space) of operand.

pytential.symbolic.primitives.d_dx()
pytential.symbolic.primitives.d_dy()
pytential.symbolic.primitives.d_dz()
pytential.symbolic.primitives.grad_mv(ambient_dim: int, operand: int | integer | float | complex | inexact | ExpressionNode) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
Returns:

the ambient_dim-dimensional gradient of operand.

pytential.symbolic.primitives.grad(ambient_dim: int, operand: int | integer | float | complex | inexact | ExpressionNode) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
Returns:

the ambient_dim-dimensional gradient of operand.

pytential.symbolic.primitives.laplace(ambient_dim: int, operand: OperandTc) OperandTc[source]

Layer potentials

pytential.symbolic.primitives.KernelArgumentMapping

alias of dict[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, …], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]]

pytential.symbolic.primitives.KernelArgumentLike

alias of dict[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, …], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]] | tuple[tuple[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, …], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]], …]

class pytential.symbolic.primitives.IntG(target_kernel: Kernel, source_kernels: Iterable[Kernel], densities: Iterable[ArithmeticExpression], qbx_forced_limit: QBXForcedLimit, source: DOFDescriptorLike | None = None, target: DOFDescriptorLike | None = None, kernel_arguments: KernelArgumentLike | None = None, **kwargs: Operand)[source]

Bases: ExpressionNode

\[\int_\Gamma T \left[\sum S_k[G](x-y) \sigma_k(y)\right] \,\mathrm{d} S_y\]

where \(\sigma_k\) is the k-th density, \(G\) is a Green’s function, \(S_k\) are source derivative operators and \(T\) is a target derivative operator.

target_kernel: Kernel

An instance of Kernel with only target dervatives attached. This represents the target derivative operator \(T\) above.

Note that the term target_kernel is bad as it’s not a kernel and merely represents a target derivative operator. This name will change once sumpy properly supports derivative operators. This also means that the user has to make sure that base kernels of all the kernels passed are the same.

source_kernels: tuple[Kernel, ...]

A tuple of instances of Kernel with only source derivatives attached. k-th elements represents the k-th source derivative operator above.

densities: tuple[int | integer | float | complex | inexact | ExpressionNode, ...]

A tuple of density expressions. Length of this tuple must match the length of the source_kernels arguments.

qbx_forced_limit: Literal[-2, -1, 1, 2, 'avg'] | None

Limit used for the QBX expansions. Can take one of the values

  • None: may be used to avoid expressing a side preference for close evaluation.

  • +1: if the output is required to originate from a QBX center on the “+” side of the boundary.

  • -1: for the “-” side.

  • 'avg': may be used as a shorthand to evaluate this potential as an average of the +1 and the -1 value.

  • +2 may be used to allow evaluation QBX center on the “+” side of the (but disallow evaluation using a center on the “-” side).

  • -2: for the “-” side.

Evaluation at a target with a value of ±1 in qbx_forced_limit will fail if no QBX center is found. To allow potential evaluation at the target to succeeds even if no applicable QBX center is found use ±2.

source: DOFDescriptor

The symbolic name of the source discretization. This name is bound to a concrete LayerPotentialSourceBase by pytential.bind().

target: DOFDescriptor

The symbolic name of the set of targets. This name gets assigned to a concrete target set by pytential.bind().

kernel_arguments: dict[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]]

A dictionary mapping named Kernel arguments (see get_args() and get_source_args()) to expressions that determine them.

mapper_method: ClassVar[str] = 'map_int_g'
pytential.symbolic.primitives.int_g_dsource(ambient_dim: int, dsource: MultiVector[ArithmeticExpression], kernel: Kernel, density: Operand, qbx_forced_limit: QBXForcedLimit, source: DOFDescriptorLike | None = None, target: DOFDescriptorLike | None = None, kernel_arguments: KernelArgumentLike | None = None, **kwargs: Operand) MultiVector[ArithmeticExpression][source]
\[\int_\Gamma \operatorname{dsource} \dot \nabla_y G(x-y) \sigma(y) dS_y\]

where \(\sigma\) is density and dsource is a multivector. Note that the first product in the integrand is a geometric product.

pytential.symbolic.primitives.int_g_vec(kernel: Kernel, density: int | integer | float | complex | inexact | ExpressionNode, qbx_forced_limit: Literal[-2, -1, 1, 2, 'avg'] | None, source: DOFDescriptorLike = None, target: DOFDescriptorLike = None, kernel_arguments: dict[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]] | tuple[tuple[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]], ...] | None = None, **kwargs: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]) IntG[source]
pytential.symbolic.primitives.int_g_vec(kernel: Kernel, density: ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode], qbx_forced_limit: Literal[-2, -1, 1, 2, 'avg'] | None, source: DOFDescriptorLike = None, target: DOFDescriptorLike = None, kernel_arguments: dict[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]] | tuple[tuple[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]], ...] | None = None, **kwargs: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]) ObjectArray[tuple[int, ...], IntG]
pytential.symbolic.primitives.int_g_vec(kernel: Kernel, density: MultiVector[int | integer | float | complex | inexact | ExpressionNode], qbx_forced_limit: Literal[-2, -1, 1, 2, 'avg'] | None, source: DOFDescriptorLike = None, target: DOFDescriptorLike = None, kernel_arguments: dict[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]] | tuple[tuple[str, int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]], ...] | None = None, **kwargs: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]) MultiVector[int | integer | float | complex | inexact | ExpressionNode]

Creates a vector of IntG objects from one kernel with source and target derivatives and maps a vector of densities into a vector of IntG objects.

Historically IntG objects supported only one source kernel and allowed multiple densities to get a vector of objects as a convenience function. Now that IntG objects supports multiple source kernels with one density associated to each source kernel, the previous interface would lead to ambiguity. This function is intended to preserve the “vectorizing” behavior of of the constructor of IntG for use cases where that is preferred.

pytential.symbolic.primitives.S(kernel: ~sumpy.kernel.Kernel, density: int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode | ~pytools.obj_array.ObjectArray[tuple[int, ...], int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode] | ~pymbolic.geometric_algebra.MultiVector[int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode], qbx_forced_limit: ~typing.Literal[-2, -1, 1, 2, 'avg'] | None = <class 'pytential.symbolic.primitives._unspecified'>, source: pytential.symbolic.dof_desc.DOFDescriptorLike = None, target: pytential.symbolic.dof_desc.DOFDescriptorLike = None, kernel_arguments: dict[str, int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode | ~pytools.obj_array.ObjectArray[tuple[int, ...], int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode] | ~pymbolic.geometric_algebra.MultiVector[int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode]] | tuple[tuple[str, int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode | ~pytools.obj_array.ObjectArray[tuple[int, ...], int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode] | ~pymbolic.geometric_algebra.MultiVector[int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode]], ...] | None = None, **kwargs: int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode | ~pytools.obj_array.ObjectArray[tuple[int, ...], int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode] | ~pymbolic.geometric_algebra.MultiVector[int | ~numpy.integer | float | complex | ~numpy.inexact | ~pytential.symbolic.primitives.ExpressionNode]) int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.Sp(kernel: Kernel, density: Operand, qbx_forced_limit: QBXForcedLimit = <class 'pytential.symbolic.primitives._unspecified'>, source: DOFDescriptorLike | None = None, target: DOFDescriptorLike | None = None, kernel_arguments: KernelArgumentLike | None = None, ambient_dim: int | None = None, dim: int | None = None, **kwargs: Operand) Operand[source]
pytential.symbolic.primitives.Spp(kernel: Kernel, density: Operand, qbx_forced_limit: QBXForcedLimit = <class 'pytential.symbolic.primitives._unspecified'>, source: DOFDescriptorLike | None = None, target: DOFDescriptorLike | None = None, kernel_arguments: KernelArgumentLike | None = None, ambient_dim: int | None = None, dim: int | None = None, **kwargs: Operand) Operand[source]
pytential.symbolic.primitives.D(kernel: Kernel, density: Operand, qbx_forced_limit: QBXForcedLimit = <class 'pytential.symbolic.primitives._unspecified'>, source: DOFDescriptorLike | None = None, target: DOFDescriptorLike | None = None, kernel_arguments: KernelArgumentLike | None = None, ambient_dim: int | None = None, dim: int | None = None, **kwargs: Operand) Operand[source]
pytential.symbolic.primitives.Dp(kernel: Kernel, density: Operand, qbx_forced_limit: QBXForcedLimit = <class 'pytential.symbolic.primitives._unspecified'>, source: DOFDescriptorLike | None = None, target: DOFDescriptorLike | None = None, kernel_arguments: KernelArgumentLike | None = None, ambient_dim: int | None = None, dim: int | None = None, **kwargs: Operand) Operand[source]
pytential.symbolic.primitives.normal_derivative(ambient_dim: int, operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode], dim: int | None = None, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.tangential_derivative(ambient_dim: int, operand: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode], dim: int | None = None, dofdesc: DOFDescriptorLike = None) MultiVector[int | integer | float | complex | inexact | ExpressionNode][source]

“Conventional” Vector Calculus

pytential.symbolic.primitives.tangential_onb(ambient_dim: int, dim: int | None = None, dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int, int], int | integer | float | complex | inexact | ExpressionNode][source]

Return a matrix of shape (ambient_dim, dim) with orthogonal columns spanning the tangential space of the surface of dofdesc.

pytential.symbolic.primitives.xyz_to_tangential(xyz_vec: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode], dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.tangential_to_xyz(tangential_vec: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode], dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.project_to_tangential(xyz_vec: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode], dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.cross(vec_a: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode], vec_b: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode]) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.n_dot(vec: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode], dofdesc: DOFDescriptorLike = None) int | integer | float | complex | inexact | ExpressionNode[source]
pytential.symbolic.primitives.n_cross(vec: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode], dofdesc: DOFDescriptorLike = None) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]
pytential.symbolic.primitives.curl(vec: ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode]) ObjectArray[tuple[int], int | integer | float | complex | inexact | ExpressionNode][source]

Pretty-printing expressions

pytential.symbolic.primitives.pretty(expr: int | integer | float | complex | inexact | ExpressionNode | ObjectArray[tuple[int, ...], int | integer | float | complex | inexact | ExpressionNode] | MultiVector[int | integer | float | complex | inexact | ExpressionNode]) str[source]

Matrix Builders

class pytential.symbolic.matrix.MatrixBuilderBase(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, context: dict[str, Any])[source]
__init__(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, context: dict[str, Any]) None[source]
Parameters:
  • dep_expr – symbolic expression for the input block column that the builder is evaluating.

  • other_dep_exprs – symbolic expressions for the remaining input block columns.

  • dep_discr – a concrete Discretization for the given dep_expr.

  • places – a GeometryCollection for all the sources and targets the builder is expected to encounter.

class pytential.symbolic.matrix.ClusterMatrixBuilderBase(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, tgt_src_index: TargetAndSourceClusterList, context: dict[str, Any])[source]

Bases: MatrixBuilderBase

Evaluate individual clusters of a matrix operator, as defined by a TargetAndSourceClusterList.

Unlike, e.g. MatrixBuilder, matrix cluster builders are significantly reduced in scope. They are basically just meant to evaluate linear combinations of layer potential operators. For example, they do not support composition of operators because we assume that each operator acts directly on the density.

__init__(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, tgt_src_index: TargetAndSourceClusterList, context: dict[str, Any]) None[source]
Parameters:

tgt_src_index – a TargetAndSourceClusterList class describing which clusters are going to be evaluated.

Full Matrix Builders

class pytential.symbolic.matrix.MatrixBuilder(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, context: dict[str, Any], _weighted: bool = True)[source]

Bases: MatrixBuilderBase

A matrix builder that evaluates the full QBX-mediated operator.

__init__(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, context: dict[str, Any], _weighted: bool = True) None[source]
Parameters:

_weighted – if True, the quadrature weights and area elements are added to the operator matrix evaluation.

class pytential.symbolic.matrix.P2PMatrixBuilder(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, context: dict[str, Any], exclude_self: bool = True, _weighted: bool = False)[source]

Bases: MatrixBuilderBase

A matrix builder that evaluates the full point-to-point kernel interactions.

__init__(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, context: dict[str, Any], exclude_self: bool = True, _weighted: bool = False) None[source]
Parameters:
  • exclude_self – if True, interactions where the source and target indices are the same are ignored. This should be True in most cases where this is possible, since the kernels are singular at those points.

  • _weighted – if True, the quadrature weights and area elements are added to the operator matrix evaluation.

Cluster Matrix Builders

class pytential.symbolic.matrix.QBXClusterMatrixBuilder(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, tgt_src_index: TargetAndSourceClusterList, context: dict[str, Any], exclude_self: bool = False, _weighted: bool = True)[source]

Bases: ClusterMatrixBuilderBase

A matrix builder that evaluates a cluster (block) using the QBX method.

__init__(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, tgt_src_index: TargetAndSourceClusterList, context: dict[str, Any], exclude_self: bool = False, _weighted: bool = True) None[source]
Parameters:
  • exclude_self – this argument is ignored.

  • _weighted – if True, the quadrature weights and area elements are added to the operator matrix evaluation.

class pytential.symbolic.matrix.P2PClusterMatrixBuilder(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, tgt_src_index: TargetAndSourceClusterList, context: dict[str, Any], exclude_self: bool = False, _weighted: bool = False)[source]

Bases: ClusterMatrixBuilderBase

A matrix builder that evaluates a cluster (block) point-to-point.

__init__(actx: PyOpenCLArrayContext, dep_expr: Variable, other_dep_exprs: Sequence[Variable], dep_discr: Discretization, places: GeometryCollection, tgt_src_index: TargetAndSourceClusterList, context: dict[str, Any], exclude_self: bool = False, _weighted: bool = False) None[source]
Parameters:
  • exclude_self – if True, interactions where the source and target indices are the same are ignored. This should be True in most cases where this is possible, since the kernels are singular at those points.

  • _weighted – if True, the quadrature weights and area elements are added to the operator matrix evaluation.

Binding an operator to a discretization

pytential.bind(places: GeometryCollection | GeometryLike | tuple[GeometryLike, GeometryLike] | Mapping[Hashable, GeometryLike], expr: OperandTc, auto_where: AutoWhereLike | None = None)[source]
Parameters:
  • places – a pytential.collection.GeometryCollection. Alternatively, any list or mapping that is a valid argument for its constructor can also be used.

  • auto_where – for simple source-to-self or source-to-target evaluations, find ‘where’ attributes automatically. This is a 2-tuple, single identifier or None to determine the default geometries. When None, a tuple of unspecified unique identifiers are used.

  • expr – one or multiple expressions consisting of primitives form pytential.symbolic.primitives (aka pytential.sym). Multiple expressions can be combined into one object to pass here in the form of a numpy object array

Returns:

a pytential.symbolic.execution.BoundExpression

PDE operators

Scalar PDEs

class pytential.symbolic.pde.scalar.L2WeightedPDEOperator(kernel: Kernel, use_l2_weighting: bool)[source]

\(L^2\)-weighting for scalar IEs.

\(L^2\)-weighting is performed to help with the solution of IEs that yield locally singular densities. It does this by matching the \(\ell^2\)-norm used by the iterative method (e.g. GMRES) with the (approximate) \(L^2\)-norm. Clearly, singular densities might not have finite \(\ell^2\)-norm, hampering convergence of the iterative method. See Bremer, J. On the Nyström discretization of integral equations on planar curves with corners. ACHA, 2011..

kernel
use_l2_weighting
get_weight(dofdesc=None)[source]
Returns:

a symbolic expression for the weights (quadrature weights and area elements) on dofdesc if use_l2_weighting is True and 1 otherwise.

get_sqrt_weight(dofdesc=None)[source]
Returns:

the square root of get_weight().

get_density_var(name: str)[source]
Parameters:

name – a string name for the density.

Returns:

a symbolic variable or array (problem dependent) corresponding to the density with the given name.

prepare_rhs(b)[source]

Modify the right-hand side (e.g. boundary conditions) to match the operator constructed in operator().

representation(u, qbx_forced_limit=None, **kwargs)[source]
Returns:

a representation for the unknowns based on an integral equation with density u. If qbx_forced_limit denotes an on-surface evaluation, the corresponding jump relations are not added to the representation.

operator(u, **kwargs)[source]
Returns:

a boundary integral operator with corresponding jump relations that can be used to solve for the density u.

__init__(kernel: Kernel, use_l2_weighting: bool)[source]
class pytential.symbolic.pde.scalar.DirichletOperator(kernel: Kernel, loc_sign: int, *, alpha: int | float | complex | None = None, use_l2_weighting: bool = False, kernel_arguments: dict[str, Any] | None = None)[source]

IE operator and field representation for solving Dirichlet boundary value problems with scalar kernels (e.g. LaplaceKernel, HelmholtzKernel, YukawaKernel)

Note

When testing this as a potential matcher, note that it can only access potentials that come from charge distributions having no net charge. (This is true at least in 2D)

Inherits from L2WeightedPDEOperator.

is_unique_only_up_to_constant()[source]
representation(u, map_potentials=None, qbx_forced_limit=None, **kwargs)[source]
Parameters:
  • u – symbolic variable for the density.

  • map_potentials – a callable that can be used to apply additional transformations on all the layer potentials in the representation, e.g. to take a target derivative.

  • kwargs – additional keyword arguments passed on to the layer potential constructor.

operator(u, **kwargs)[source]
Parameters:
  • u – symbolic variable for the density.

  • kwargs – additional keyword arguments passed on to the layer potential constructor.

__init__(kernel: Kernel, loc_sign: int, *, alpha: int | float | complex | None = None, use_l2_weighting: bool = False, kernel_arguments: dict[str, Any] | None = None)[source]
Parameters:
  • loc_sign\(+1\) for exterior or \(-1\) for interior problems.

  • alpha – a complex coefficient with positive imaginary part for the combined-field integral representation (CFIE) for the Helmholtz equation (based on Brakhage and Werner). For other kernels, it does does not offer any benefits.

class pytential.symbolic.pde.scalar.NeumannOperator(kernel: Kernel, loc_sign: int, *, alpha: int | float | complex | None = None, use_improved_operator: bool = True, use_l2_weighting: bool = False, kernel_arguments: dict[str, Any] | None = None)[source]

IE operator and field representation for solving Neumann boundary value problems with scalar kernels (e.g. LaplaceKernel, HelmholtzKernel, YukawaKernel)

Note

When testing this as a potential matcher, note that it can only access potentials that come from charge distributions having no net charge. (This is true at least in 2D)

Inherits from L2WeightedPDEOperator.

is_unique_only_up_to_constant()[source]
representation(u, map_potentials=None, qbx_forced_limit=None, **kwargs)[source]
Parameters:
  • u – symbolic variable for the density.

  • map_potentials – a callable that can be used to apply additional transformations on all the layer potentials in the representation, e.g. to take a target derivative.

operator(u, **kwargs)[source]
Parameters:
  • u – symbolic variable for the density.

  • kwargs – additional keyword arguments passed on to the layer potential constructor.

__init__(kernel: Kernel, loc_sign: int, *, alpha: int | float | complex | None = None, use_improved_operator: bool = True, use_l2_weighting: bool = False, kernel_arguments: dict[str, Any] | None = None)[source]
Parameters:
  • loc_sign\(+1\) for exterior or \(-1\) for interior problems.

  • alpha – a complex coefficient with positive imaginary part for the combined-field integral representation (CFIE) for the Helmholtz equation (based on Brakhage and Werner). For other kernels, it does does not offer any benefits.

  • use_improved_operator – if True use the least singular operator available. Only used when alpha is not \(0\).

class pytential.symbolic.pde.scalar.BiharmonicClampedPlateOperator(kernel: Kernel, loc_sign: int)[source]

IE operator and field representation for solving clamped plate Biharmonic equation where,

\[\begin{split}\begin{cases} \Delta^2 u = 0, & \quad \text{ on } D, \\ u = g_1, & \quad \text{ on } \partial D, \\ \mathbf{n} \cdot \nabla u = g_2, & \quad \text{ on } \partial D. \end{cases}\end{split}\]

This operator assumes that the boundary data \((g_1, g_2)\) are represented as column vectors and vertically stacked. For details on the formulation see Problem C in [Farkas1990].

Note

This operator supports only interior problem.

[Farkas1990] (1,2)

Farkas, Peter, Mathematical foundations for fast algorithms for the biharmonic equation, Technical Report 765, Department of Computer Science, Yale University, 1990, PDF.

representation(sigma, map_potentials=None, qbx_forced_limit=None, **kwargs)[source]
Parameters:
  • sigma – symbolic variable for the density.

  • map_potentials – a callable that can be used to apply additional transformations on all the layer potentials in the representation, e.g. to take a target derivative.

  • kwargs – additional keyword arguments passed on to the layer potential constructor.

operator(sigma, **kwargs)[source]
Parameters:
  • u – symbolic variable for the density.

  • kwargs – additional keyword arguments passed on to the layer potential constructor.

Returns:

the second kind integral operator for the clamped plate problem from [Farkas1990].

__init__(kernel: Kernel, loc_sign: int)[source]
Parameters:

loc_sign\(+1\) for exterior or \(-1\) for interior problems.

Maxwell’s equations

pytential.symbolic.pde.maxwell.get_sym_maxwell_point_source(kernel, jxyz, k)[source]

Return a symbolic expression that, when bound to a pytential.source.PointPotentialSource will yield a field satisfying Maxwell’s equations.

Uses the sign convention \(\exp(-i \omega t)\) for the time dependency.

This will return an object of six entries, the first three of which represent the electric, and the second three of which represent the magnetic field. This satisfies the time-domain Maxwell’s equations as verified by sumpy.point_calculus.frequency_domain_maxwell().

pytential.symbolic.pde.maxwell.get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, dofdesc=None)[source]

Return a symbolic expression that, when bound to a pytential.source.PointPotentialSource will yield a field satisfying Maxwell’s equations.

Parameters:
  • amplitude_vec – should be orthogonal to v. If it is not, it will be orthogonalized.

  • v – a three-vector representing the phase velocity of the wave (may be an object array of variables or a vector of concrete numbers) While v may mathematically be complex-valued, this function is for now only tested for real values.

  • omega – Accepts the “Helmholtz k” to be compatible with other parts of this module.

Uses the sign convention \(\exp(-1 \omega t)\) for the time dependency.

This will return an object of six entries, the first three of which represent the electric, and the second three of which represent the magnetic field. This satisfies the time-domain Maxwell’s equations as verified by sumpy.point_calculus.frequency_domain_maxwell().

class pytential.symbolic.pde.maxwell.PECChargeCurrentMFIEOperator(k=None)[source]

Magnetic Field Integral Equation operator with PEC boundary conditions, under the assumption of no surface charges.

See contrib/notes/mfie.tm in the repository for a derivation.

The arguments loc below decide between the exterior and the interior MFIE. The “exterior” (loc=1) MFIE enforces a zero field on the interior of the integration surface, whereas the “interior” MFIE (loc=-1) enforces a zero field on the exterior.

Uses the sign convention \(\exp(-1 \omega t)\) for the time dependency.

for the sinusoidal time dependency.

j_operator(loc, Jt)[source]
j_rhs(Hinc_xyz)[source]
rho_operator(loc, rho)[source]
rho_rhs(Jt, Einc_xyz)[source]
scattered_volume_field(Jt, rho, qbx_forced_limit=None)[source]

This will return an object array of six entries, the first three of which represent the electric, and the second three of which represent the magnetic field. This satisfies the time-domain Maxwell’s equations as verified by sumpy.point_calculus.frequency_domain_maxwell().

Stokes’ equations

class pytential.symbolic.stokes.StokesletWrapper(dim: int)[source]

Wrapper class for the StokesletKernel kernel.

This class is meant to shield the user from the messiness of writing out every term in the expansion of the double-indexed Stokeslet kernel applied to the density vector. The object is created to do some of the set-up and bookkeeping once, rather than every time we want to create a symbolic expression based on the kernel – say, once when we solve for the density, and once when we want a symbolic representation for the solution, for example.

The apply() function returns the integral expressions needed for the vector velocity resulting from convolution with the vector density, and is meant to work similarly to calling S() (which is IntG).

Similar functions are available for other useful things related to the flow: apply_pressure(), apply_derivative() (target derivative), apply_stress() (applies symmetric viscous stress tensor in the requested direction).

kernel_dict

The dictionary allows us to exploit symmetry – that \(S_{01}\) is identical to \(S_{10}\) – and avoid creating multiple expansions for the same kernel in a different ordering.

__init__(dim: int)[source]
apply(density_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic expressions for integrating Stokeslet kernel.

Returns an object array of symbolic expressions for the vector resulting from integrating the dyadic Stokeslet kernel with variable density_vec_sym.

Parameters:
  • density_vec_sym – a symbolic vector variable for the density vector.

  • mu_sym – a symbolic variable for the viscosity.

  • qbx_forced_limit – the qbx_forced_limit argument to be passed on to IntG.

apply_pressure(density_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic expression for pressure field associated with the Stokeslet.

apply_derivative(deriv_dir, density_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic derivative of velocity from Stokeslet.

Returns an object array of symbolic expressions for the vector resulting from integrating the deriv_dir target derivative of the dyadic Stokeslet kernel with variable density_vec_sym.

Parameters:
  • deriv_dir – integer denoting the axis direction for the derivative.

  • density_vec_sym – a symbolic vector variable for the density vector.

  • mu_sym – a symbolic variable for the viscosity.

  • qbx_forced_limit – the qbx_forced_limit argument to be passed on to IntG.

apply_stress(density_vec_sym, dir_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic expression for viscous stress applied to a direction.

Returns a vector of symbolic expressions for the force resulting from the viscous stress

\[-p \delta_{ij} + \mu (\nabla_i u_j + \nabla_j u_i)\]

applied in the direction of dir_vec_sym.

Note that this computation is very similar to computing a double-layer potential with the Stresslet kernel in StressletWrapper. The difference is that here the direction vector is applied at the target points, while in the Stresslet the direction is applied at the source points.

Parameters:
  • density_vec_sym – a symbolic vector variable for the density vector.

  • dir_vec_sym – a symbolic vector for the application direction.

  • mu_sym – a symbolic variable for the viscosity.

  • qbx_forced_limit – the qbx_forced_limit argument to be passed on to IntG.

class pytential.symbolic.stokes.StressletWrapper(dim: int)[source]

Wrapper class for the StressletKernel kernel.

This class is meant to shield the user from the messiness of writing out every term in the expansion of the triple-indexed Stresslet kernel applied to both a normal vector and the density vector. The object is created to do some of the set-up and bookkeeping once, rather than every time we want to create a symbolic expression based on the kernel – say, once when we solve for the density, and once when we want a symbolic representation for the solution, for example.

The apply() function returns the integral expressions needed for convolving the kernel with a vector density, and is meant to work similarly to S() (which is IntG).

Similar functions are available for other useful things related to the flow: apply_pressure(), apply_derivative() (target derivative), apply_stress() (applies symmetric viscous stress tensor in the requested direction).

kernel_dict

The dictionary allows us to exploit symmetry – that \(T_{012}\) is identical to \(T_{120}\) – and avoid creating multiple expansions for the same kernel in a different ordering.

__init__(dim: int)[source]
apply(density_vec_sym, dir_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic expressions for integrating Stresslet kernel.

Returns an object array of symbolic expressions for the vector resulting from integrating the dyadic Stresslet kernel with variable density_vec_sym and source direction vectors dir_vec_sym.

Parameters:
  • density_vec_sym – a symbolic vector variable for the density vector.

  • dir_vec_sym – a symbolic vector variable for the direction vector.

  • mu_sym – a symbolic variable for the viscosity.

  • qbx_forced_limit – the qbx_forced_limit argument to be passed on to IntG.

apply_pressure(density_vec_sym, dir_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic expression for pressure field associated with the Stresslet.

apply_derivative(deriv_dir, density_vec_sym, dir_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic derivative of velocity from stresslet.

Returns an object array of symbolic expressions for the vector resulting from integrating the deriv_dir target derivative of the dyadic Stresslet kernel with variable density_vec_sym and source direction vectors dir_vec_sym.

Parameters:
  • deriv_dir – integer denoting the axis direction for the derivative.

  • density_vec_sym – a symbolic vector variable for the density vector.

  • dir_vec_sym – a symbolic vector variable for the normal direction.

  • mu_sym – a symbolic variable for the viscosity.

  • qbx_forced_limit – the qbx_forced_limit argument to be passed on to IntG.

apply_stress(density_vec_sym, normal_vec_sym, dir_vec_sym, mu_sym, qbx_forced_limit)[source]

Symbolic expression for viscous stress applied to a direction.

Returns a vector of symbolic expressions for the force resulting from the viscous stress

\[-p \delta_{ij} + \mu (\nabla_i u_j + \nabla_j u_i)\]

applied in the direction of dir_vec_sym.

Parameters:
  • density_vec_sym – a symbolic vector variable for the density vector.

  • normal_vec_sym – a symbolic vector variable for the normal vectors (outward facing normals at source locations).

  • dir_vec_sym – a symbolic vector for the application direction.

  • mu_sym – a symbolic variable for the viscosity.

  • qbx_forced_limit – the qbx_forced_limit argument to be passed on to IntG.

class pytential.symbolic.stokes.StokesOperator(ambient_dim, side)[source]
ambient_dim
side
__init__(ambient_dim, side)[source]
Parameters:
  • ambient_dim – dimension of the ambient space.

  • side\(+1\) for exterior or \(-1\) for interior.

get_density_var(name='sigma')[source]
Returns:

a symbolic vector corresponding to the density.

prepare_rhs(b, *, mu)[source]
Returns:

a (potentially) modified right-hand side b that matches requirements of the representation.

operator(sigma)[source]
Returns:

the integral operator that should be solved to obtain the density sigma.

velocity(sigma, *, normal, mu, qbx_forced_limit=None)[source]
Returns:

a representation of the velocity field in the Stokes flow.

pressure(sigma, *, normal, mu, qbx_forced_limit=None)[source]
Returns:

a representation of the pressure in the Stokes flow.

class pytential.symbolic.stokes.HsiaoKressExteriorStokesOperator(*, omega, alpha=None, eta=None)[source]

Representation for 2D Stokes Flow based on [HsiaoKress1985].

Inherits from StokesOperator.

[HsiaoKress1985] (1,2)

G. C. Hsiao and R. Kress, On an Integral Equation for the Two-Dimensional Exterior Stokes Problem, Applied Numerical Mathematics, Vol. 1, 1985, DOI.

__init__(*, omega, alpha=None, eta=None)[source]
Parameters:
  • omega – farfield behaviour of the velocity field, as defined by \(A\) in [HsiaoKress1985] Equation 2.3.

  • alpha – real parameter \(\alpha > 0\).

  • eta – real parameter \(\eta > 0\). Choosing this parameter well can have a non-trivial effect on the conditioning.

class pytential.symbolic.stokes.HebekerExteriorStokesOperator(*, eta=None)[source]

Representation for 3D Stokes Flow based on [Hebeker1986].

Inherits from StokesOperator.

[Hebeker1986]

F. C. Hebeker, Efficient Boundary Element Methods for Three-Dimensional Exterior Viscous Flow, Numerical Methods for Partial Differential Equations, Vol. 2, 1986, DOI.

__init__(*, eta=None)[source]
Parameters:

eta – a parameter \(\eta > 0\). Choosing this parameter well can have a non-trivial effect on the conditioning of the operator.

Scalar Beltrami equations

class pytential.symbolic.pde.beltrami.BeltramiOperator(kernel: Kernel, *, dim: int | None = None, precond: str = 'left', kernel_arguments: dict[str, Any] | None = None)[source]

Beltrami-type operators on closed surfaces.

The construction of the operators is based on [ONeil2018] and takes any scalar PDE kernel. However, specific kernels may require additional work to allow for unique solutions. For example, the Laplace-Beltrami equation is only unique up to a constant, so using LaplaceBeltramiOperator is recommended to take this into account. In general, the boundary integral equation can be constructed as

beltrami = BeltramiOperator({...})
sigma = beltrami.get_density_var("sigma")
bc = beltrami.get_density_var("bc")

rhs = beltrami.prepare_rhs(bc)
solution = beltrami.prepare_solution(sigma)
op = beltrami.operator(sigma)

where prepare_solution() is required to recover the solution from the density due to the different types of preconditioning.

Note that a naive implementation of these operators is inefficient and can even be inaccurate (due to a subtraction of hypersingular operators).

[ONeil2018]

M. O’Neil, Second-Kind Integral Equations for the Laplace-Beltrami Problem on Surfaces in Three Dimensions, Advances in Computational Mathematics, Vol. 44, pp. 1385-1409, 2018, DOI.

dim
__init__(kernel: Kernel, *, dim: int | None = None, precond: str = 'left', kernel_arguments: dict[str, Any] | None = None) None[source]
get_density_var(name: str = 'sigma') Variable[source]
Returns:

a symbolic expression for the density.

prepare_solution(sigma: Variable) Variable[source]
Returns:

an expression for the solution to the original Beltrami equation based on the density sigma and the type of preconditioning used in the operator.

prepare_rhs(b: Variable) Variable[source]
Returns:

a modified expression for the right-hands ide b based on the preconditiong used in the operator.

operator(sigma: Variable, mean_curvature: Variable | None = None, **kwargs) Variable[source]
Parameters:

mean_curvature – an expression for the mean curvature that can be used in the construction of the operator.

Returns:

a Fredholm integral equation of the second kind for the Beltrami PDE with the unknown density sigma.

class pytential.symbolic.pde.beltrami.LaplaceBeltramiOperator(ambient_dim, *, dim: int | None = None, precond: str = 'left')[source]

Laplace-Beltrami operator on a closed surface \(\Sigma\)

\[-\Delta_\Sigma u = b\]

Inherits from BeltramiOperator.

__init__(ambient_dim, *, dim: int | None = None, precond: str = 'left') None[source]
class pytential.symbolic.pde.beltrami.YukawaBeltramiOperator(ambient_dim: int, *, dim: int | None = None, precond: str = 'left', yukawa_k_name: str = 'k')[source]

Yukawa-Beltrami operator on a closed surface \(\Sigma\).

\[-\Delta_\Sigma u + k^2 u = b.\]

Inherits from BeltramiOperator.

__init__(ambient_dim: int, *, dim: int | None = None, precond: str = 'left', yukawa_k_name: str = 'k') None[source]
class pytential.symbolic.pde.beltrami.HelmholtzBeltramiOperator(ambient_dim: int, *, dim: int | None = None, precond: str = 'left', helmholtz_k_name: str = 'k')[source]

Helmholtz-Beltrami operator on a closed surface \(\Sigma\)

\[-\Delta_\Sigma u - k^2 u = b\]

Inherits from BeltramiOperator.

__init__(ambient_dim: int, *, dim: int | None = None, precond: str = 'left', helmholtz_k_name: str = 'k') None[source]

Internal affairs

Mappers

How a symbolic operator gets executed

class pytential.symbolic.execution.BoundExpression(places: GeometryCollection, sym_op_expr: OperandTc)[source]

An expression readied for evaluation by binding it to a GeometryCollection.

cost_per_stage(calibration_params, **kwargs)[source]
Parameters:

calibration_params – either a dict returned by estimate_kernel_specific_calibration_params, or a str “constant_one”.

Returns:

a dict mapping from statement to per-stage cost. Each per-stage cost is represented by a dict mapping from the stage name to the predicted time.

cost_per_box(calibration_params, **kwargs)[source]
Parameters:

calibration_params – either a dict returned by estimate_kernel_specific_calibration_params, or a str “constant_one”.

Returns:

a dict mapping from statement to per-box cost. Each per-box cost is represented by a numpy.ndarray or pyopencl.array.Array of shape (nboxes,), where the ith entry represents the cost of all stages for box i.

scipy_op(actx: PyOpenCLArrayContext, arg_name, dtype, domains=None, **extra_args)[source]
Parameters:

domains – a list of discretization identifiers or None values indicating the domains on which each component of the solution vector lives. None values indicate that the component is a scalar. If the value of domains is None, the default target from places is used.

Returns:

An object that (mostly) satisfies the scipy.sparse.linalg.LinearOperator protocol, except for accepting and returning pyopencl.array.Array arrays.

eval(context=None, timing_data=None, array_context: PyOpenCLArrayContext | None = None)[source]

Evaluate the expression in self, using the input variables given in the dictionary context.

Parameters:
  • timing_data – A dictionary into which timing data will be inserted during evaluation. (experimental)

  • array_context – only needs to be supplied if no instances of DOFArray with a PyOpenCLArrayContext are supplied as part of context.

Returns:

the value of the expression, as a scalar, array or an arraycontext.ArrayContainer of these.

__call__(actx: ArrayContext | None = None, /, **kwargs: Array | ObjectArray[tuple[int, ...], ArrayOrContainerOrScalar] | _UserDefinedArrayContainer | int | integer | float | complex | inexact | bool | bool) DOFArray | int | integer | float | complex | inexact | bool | bool[source]
__call__(actx: ArrayContext | None = None, /, **kwargs: Array | ObjectArray[tuple[int, ...], ArrayOrContainerOrScalar] | _UserDefinedArrayContainer | int | integer | float | complex | inexact | bool | bool) MultiVector[DOFArray | ScalarLike]
__call__(actx: ArrayContext | None = None, /, **kwargs: Array | ObjectArray[tuple[int, ...], ArrayOrContainerOrScalar] | _UserDefinedArrayContainer | int | integer | float | complex | inexact | bool | bool) ObjectArrayND[DOFArray | ScalarLike]

Evaluate the expression in self, using the input variables given in the dictionary context.

Returns:

the value of the expression, as a scalar, meshmode.dof_array.DOFArray, or an object array of these.

places

Created by calling pytential.bind().