# Symbolic operator representation¶

Based on pymbolic.

## Basic objects¶

### Object types¶

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

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.

### DOF Description¶

class pytential.symbolic.primitives.DEFAULT_SOURCE[source]
class pytential.symbolic.primitives.DEFAULT_TARGET[source]
class pytential.symbolic.primitives.QBX_SOURCE_STAGE1[source]

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

class pytential.symbolic.primitives.QBX_SOURCE_STAGE2[source]

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

class pytential.symbolic.primitives.QBX_SOURCE_QUAD_STAGE2[source]

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

class pytential.symbolic.primitives.GRANULARITY_NODE[source]

DOFs are per node.

class pytential.symbolic.primitives.GRANULARITY_CENTER[source]

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

class pytential.symbolic.primitives.GRANULARITY_ELEMENT[source]

DOFs per discretization element.

class pytential.symbolic.primitives.DOFDescriptor(geometry=None, discr_stage=None, granularity=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

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

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

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

pytential.symbolic.primitives.as_dofdesc(desc)[source]

### Placeholders¶

pytential.symbolic.primitives.var
pytential.symbolic.primitives.make_sym_vector(name, components, var_factory=<class 'pymbolic.primitives.Variable'>)[source]

Return an object array of components subscripted Variable (or subclass) instances.

Parameters
• components – Either a list of indices, or an integer representing the number of indices.

• var_factory – The Variable subclass to use for instantiating the scalar variables.

For example, this creates a vector with three components:

>>> make_sym_vector("vec", 3)
array([Subscript(Variable('vec'), 0), Subscript(Variable('vec'), 1),
Subscript(Variable('vec'), 2)], dtype=object)

pytential.symbolic.primitives.make_sym_mv(name, num_components)[source]
pytential.symbolic.primitives.make_sym_surface_mv(name, ambient_dim, dim, dofdesc=None)[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.asin
pytential.symbolic.primitives.acos
pytential.symbolic.primitives.atan
pytential.symbolic.primitives.atan2
pytential.symbolic.primitives.sinh
pytential.symbolic.primitives.cosh
pytential.symbolic.primitives.tanh
pytential.symbolic.primitives.asinh
pytential.symbolic.primitives.acosh
pytential.symbolic.primitives.atanh
pytential.symbolic.primitives.exp
pytential.symbolic.primitives.log

### Discretization properties¶

class pytential.symbolic.primitives.QWeight(**kwargs)[source]

Bare quadrature weights (without Jacobians).

Parameters

dofdesc – A DOFDescriptor or a symbolic name for a geometric object (such as a Discretization).

pytential.symbolic.primitives.nodes(ambient_dim, dofdesc=None)[source]

Return a pymbolic.geometric_algebra.MultiVector of node locations.

pytential.symbolic.primitives.parametrization_derivative(ambient_dim, dim, dofdesc=None)[source]

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

pytential.symbolic.primitives.parametrization_derivative_matrix(ambient_dim, dim, dofdesc=None)[source]

Return a np.array representing the derivative of the reference-to-global parametrization.

pytential.symbolic.primitives.pseudoscalar(ambient_dim, dim=None, dofdesc=None)[source]

Same as the outer product of all parametrization derivative columns.

pytential.symbolic.primitives.area_element(ambient_dim, dim=None, dofdesc=None)[source]
pytential.symbolic.primitives.sqrt_jac_q_weight(ambient_dim, dim=None, dofdesc=None)[source]
pytential.symbolic.primitives.normal(ambient_dim, dim=None, dofdesc=None)[source]

Exterior unit normals.

pytential.symbolic.primitives.mean_curvature(ambient_dim, dim=None, dofdesc=None)[source]

(Numerical) mean curvature.

pytential.symbolic.primitives.first_fundamental_form(ambient_dim, dim=None, dofdesc=None)[source]
pytential.symbolic.primitives.second_fundamental_form(ambient_dim, dim=None, dofdesc=None)[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, dim=None, dofdesc=None)[source]
pytential.symbolic.primitives.expansion_radii(ambient_dim, dim=None, granularity=None, dofdesc=None)[source]
pytential.symbolic.primitives.expansion_centers(ambient_dim, side, dim=None, dofdesc=None)[source]
pytential.symbolic.primitives.h_max(ambient_dim, dim=None, dofdesc=None)[source]

Defines a maximum element size in the discretization.

pytential.symbolic.primitives.weights_and_area_elements(ambient_dim, dim=None, dofdesc=None)[source]

Combines area_element() and QWeight.

### Elementary numerics¶

class pytential.symbolic.primitives.NumReferenceDerivative(**kwargs)[source]

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

Parameters
• ref_axes

a tuple of tuples indicating indices of coordinate axes of the reference element to the number of derivatives which will be taken. For example, the value ((0, 2), (1, 1)) indicates that Each axis must occur at most once. The tuple must be sorted by the axis index.

May also be a singile integer i, which is viewed as equivalent to ((i, 1),).

• dofdesc – A DOFDescriptor or a symbolic name for a geometric object (such as a Discretization).

class pytential.symbolic.primitives.NodeSum(operand=None)[source]

Implements a global sum over all discretization nodes.

class pytential.symbolic.primitives.NodeMax(operand=None)[source]

Implements a global maximum over all discretization nodes.

class pytential.symbolic.primitives.ElementwiseSum(**kwargs)[source]

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

class pytential.symbolic.primitives.ElementwiseMax(**kwargs)[source]

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

pytential.symbolic.primitives.integral(ambient_dim, dim, operand, dofdesc=None)[source]

A volume integral of operand.

class pytential.symbolic.primitives.Ones(**kwargs)[source]

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

pytential.symbolic.primitives.ones_vec(dim, dofdesc=None)[source]
pytential.symbolic.primitives.area(ambient_dim, dim, dofdesc=None)[source]
pytential.symbolic.primitives.mean(ambient_dim, dim, operand, dofdesc=None)[source]
class pytential.symbolic.primitives.IterativeInverse(**kwargs)[source]

### Operators¶

class pytential.symbolic.primitives.Interpolation(from_dd, to_dd, operand)[source]

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

pytential.symbolic.primitives.interp(from_dd, to_dd, operand)[source]

### Geometric Calculus (based on Geometric/Clifford Algebra)¶

class pytential.symbolic.primitives.Derivative[source]

### Conventional Calculus¶

pytential.symbolic.primitives.dd_axis(axis, ambient_dim, operand)[source]

Return 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, operand)[source]

Return the ambient_dim-dimensional gradient of operand as a pymbolic.geometric_algebra.MultiVector.

pytential.symbolic.primitives.grad(ambient_dim, operand)[source]

Return the ambient_dim-dimensional gradient of operand as a numpy.ndarray.

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

### Layer potentials¶

class pytential.symbolic.primitives.IntG(kernel=None, density=None, *args, **kwargs)[source]
$\int_\Gamma g_k(x-y) \sigma(y) dS_y$

where $$\sigma$$ is density.

target_derivatives and later arguments should be considered keyword-only.

Parameters
• kernel – a kernel as accepted by sumpy.kernel.to_kernel_and_args(), likely a sumpy.kernel.Kernel.

• qbx_forced_limit

+1 if the output is required to originate from a QBX center on the “+” side of the boundary. -1 for the other side. Evaluation at a target with a value of +/- 1 in qbx_forced_limit will fail if no QBX center is found.

+2 may be used to allow evaluation QBX center on the “+” side of the (but disallow evaluation using a center on the “-” side). Potential evaluation at the target still succeeds if no applicable QBX center is found. (-2 for the analogous behavior on the “-” side.)

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

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

• kernel_arguments – A dictionary mapping named sumpy.kernel.Kernel arguments (see sumpy.kernel.Kernel.get_args() and sumpy.kernel.Kernel.get_source_args()) to expressions that determine them

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

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

kwargs has the same meaning as kernel_arguments can be used as a more user-friendly interface.

pytential.symbolic.primitives.int_g_dsource(ambient_dim, dsource, kernel, density, qbx_forced_limit, source=None, target=None, kernel_arguments=None, **kwargs)[source]
$\int_\Gamma \operatorname{dsource} \dot \nabla_y \dot g(x-y) \sigma(y) dS_y$

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

pytential.symbolic.primitives.dsource
pytential.symbolic.primitives.S(kernel, density, qbx_forced_limit=<class 'pytential.symbolic.primitives._unspecified'>, source=None, target=None, kernel_arguments=None, **kwargs)[source]
pytential.symbolic.primitives.Sp(kernel, *args, **kwargs)[source]
pytential.symbolic.primitives.Spp(kernel, *args, **kwargs)[source]
pytential.symbolic.primitives.D(kernel, *args, **kwargs)[source]
pytential.symbolic.primitives.Dp(kernel, *args, **kwargs)[source]
pytential.symbolic.primitives.normal_derivative(ambient_dim, operand, dim=None, dofdesc=None)[source]
pytential.symbolic.primitives.tangential_derivative(ambient_dim, operand, dim=None, dofdesc=None)[source]

### “Conventional” Vector Calculus¶

pytential.symbolic.primitives.tangential_onb(ambient_dim, dim=None, dofdesc=None)[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, dofdesc=None)[source]
pytential.symbolic.primitives.tangential_to_xyz(tangential_vec, dofdesc=None)[source]
pytential.symbolic.primitives.project_to_tangential(xyz_vec, dofdesc=None)[source]
pytential.symbolic.primitives.cross(vec_a, vec_b)[source]
pytential.symbolic.primitives.n_dot(vec, dofdesc=None)[source]
pytential.symbolic.primitives.n_cross(vec, dofdesc=None)[source]
pytential.symbolic.primitives.curl(vec)[source]
pytential.symbolic.primitives.pretty(expr)[source]

### Pretty-printing expressions¶

pytential.symbolic.primitives.pretty(expr)[source]

## Binding an operator to a discretization¶

class pytential.GeometryCollection(places, auto_where=None)[source]

A mapping from symbolic identifiers (“place IDs”, typically strings) to ‘geometries’, where a geometry can be a pytential.source.PotentialSource or a pytential.target.TargetBase. This class is meant to hold a specific combination of sources and targets serve to host caches of information derived from them, e.g. FMM trees of subsets of them, as well as related common subexpressions such as metric terms.

get_geometry(geometry)[source]
get_connection(from_dd, to_dd)[source]
get_discretization(geometry, discr_stage=None)[source]
Parameters

dofdesc – a DOFDescriptor specifying the desired discretization.

Returns

a geometry object in the collection corresponding to the key dofdesc. If it is a LayerPotentialSourceBase, we look for the corresponding Discretization in its attributes instead.

copy(places=None, auto_where=None)[source]
merge(places)[source]

Merges two geometry collections and returns the new collection.

Parameters

places – A dict or GeometryCollection to merge with the current collection. If it is empty, a copy of the current collection is returned.

Refinement of QBXLayerPotentialSource entries is performed on demand, or it may be performed by explcitly calling pytential.qbx.refinement.refine_geometry_collection(), which allows more customization of the refinement process through parameters.

Parameters
pytential.bind(places, expr, auto_where=None)[source]
Parameters
• places – a 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.

• 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 BoundExpression

## PDE operators¶

### Scalar PDEs¶

class pytential.symbolic.pde.scalar.L2WeightedPDEOperator(kernel, use_l2_weighting)[source]
class pytential.symbolic.pde.scalar.DirichletOperator(kernel, loc_sign, alpha=None, use_l2_weighting=False, kernel_arguments=None)[source]

IE operator and field representation for solving Dirichlet boundary value problems with scalar kernels (e.g. sumpy.kernel.LaplaceKernel, sumpy.kernel.HelmholtzKernel, sumpy.kernel.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.)

is_unique_only_up_to_constant()[source]
representation(u, map_potentials=None, qbx_forced_limit=None)[source]
operator(u)[source]
Parameters
• loc_sign – +1 for exterior, -1 for interior

• alpha – the coefficient for the combined-field representation Set to 0 for Laplace.

class pytential.symbolic.pde.scalar.NeumannOperator(kernel, loc_sign, alpha=None, use_improved_operator=True, laplace_kernel=0, use_l2_weighting=False, kernel_arguments=None)[source]

IE operator and field representation for solving Dirichlet boundary value problems with scalar kernels (e.g. sumpy.kernel.LaplaceKernel, sumpy.kernel.HelmholtzKernel, sumpy.kernel.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.)

is_unique_only_up_to_constant()[source]
representation(u, map_potentials=None, qbx_forced_limit=None, **kwargs)[source]
operator(u)[source]
Parameters
• loc_sign – +1 for exterior, -1 for interior

• alpha – the coefficient for the combined-field representation Set to 0 for Laplace.

• use_improved_operator – Whether to use the least singular operator available

class pytential.symbolic.pde.scalar.BiharmonicClampedPlateOperator(knl, loc_sign)[source]

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

\begin{split}\begin{align*} \Delta^2 u &= 0 \text{ on } D \\ u &= g_1 \text{ on } \delta D \\ \frac{\partial u}{\partial \nu} &= g_2 \text{ on } \delta D. \end{align*}\end{split}

This operator assumes that the boundary data $$g_1, g_2$$ are represented as column vectors and vertically stacked.

Note

This operator supports only interior problem.

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

representation(sigma, map_potentials=None, qbx_forced_limit=None)[source]
operator(sigma)[source]

Returns the two second kind integral equations.

### 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=Variable('k'))[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().

## Internal affairs¶

### How a symbolic operator gets executed¶

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

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

get_modeled_cost(queue, **args)[source]
scipy_op(actx: meshmode.array_context.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 domains is None, DEFAULT_TARGET is required to be a key in places.

Returns

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

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

Evaluate the expression in self, using the pyopencl.CommandQueue queue and the input variables given in the dictionary context.

Parameters
Returns

the value of the expression, as a scalar, pyopencl.array.Array, or an object array of these.

__call__(*args, **kwargs)[source]

Evaluate the expression in self, using the pyopencl.CommandQueue queue and 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.

Created by calling bind().