pymor.operators.mpi

Module Contents

Classes

MPIOperator

MPI distributed Operator.

Functions

_MPIOperator_get_local_spaces

_MPIOperator_assemble_lincomb

_MPIOperator_restriced

_MPIOperator_assemble

mpi_wrap_operator

Wrap MPI distributed local Operators to a global Operator on rank 0.

_mpi_wrap_operator_LincombOperator_manage_operators

_mpi_wrap_operator_VectorArrayOperator_manage_array

class pymor.operators.mpi.MPIOperator(obj_id, mpi_range, mpi_source, with_apply2=False, pickle_local_spaces=True, space_type=MPIVectorSpace)[source]

Bases: pymor.operators.interface.Operator

MPI distributed Operator.

Given a single-rank implementation of an Operator, this wrapper class uses the event loop from pymor.tools.mpi to allow an MPI distributed usage of the Operator.

Instances of MPIOperator can be used on rank 0 like any other (non-distributed) Operator.

Note, however, that the underlying Operator implementation needs to be MPI aware. For instance, the operator’s apply method has to perform the necessary MPI communication to obtain all DOFs hosted on other MPI ranks which are required for the local operator evaluation.

Instead of instantiating MPIOperator directly, it is usually preferable to use mpi_wrap_operator instead.

Parameters

obj_id

ObjectId of the local Operators on each rank.

mpi_range

Set to True if the range of the Operator is MPI distributed.

mpi_source

Set to True if the source of the Operator is MPI distributed.

with_apply2

Set to True if the operator implementation has its own MPI aware implementation of apply2 and pairwise_apply2. Otherwise, the default implementations using apply and inner will be used.

pickle_local_spaces

If pickle_local_spaces is False, a unique identifier is computed for each local source/range VectorSpace, which is then transferred to rank 0 instead of the true VectorSpace. This allows the usage of MPIVectorArray even when the local VectorSpaces are not picklable.

space_type

This class will be used to wrap the local VectorArrays returned by the local operators into an MPI distributed VectorArray managed from rank 0. By default, MPIVectorSpace will be used, other options are MPIVectorSpaceAutoComm and MPIVectorSpaceNoComm.

apply(self, U, mu=None)[source]

Apply the operator to a VectorArray.

Parameters

U

VectorArray of vectors to which the operator is applied.

mu

The parameter values for which to evaluate the operator.

Returns

VectorArray of the operator evaluations.

as_range_array(self, mu=None)[source]

Return a VectorArray representation of the operator in its range space.

In the case of a linear operator with NumpyVectorSpace as source, this method returns for given parameter values mu a VectorArray V in the operator’s range, such that

V.lincomb(U.to_numpy()) == self.apply(U, mu)

for all VectorArrays U.

Parameters

mu

The parameter values for which to return the VectorArray representation.

Returns

V

The VectorArray defined above.

as_source_array(self, mu=None)[source]

Return a VectorArray representation of the operator in its source space.

In the case of a linear operator with NumpyVectorSpace as range, this method returns for given parameter values mu a VectorArray V in the operator’s source, such that

self.range.make_array(V.inner(U).T) == self.apply(U, mu)

for all VectorArrays U.

Parameters

mu

The parameter values for which to return the VectorArray representation.

Returns

V

The VectorArray defined above.

apply2(self, V, U, mu=None)[source]

Treat the operator as a 2-form and apply it to V and U.

This method is usually implemented as V.inner(self.apply(U)). In particular, if the operator is a linear operator given by multiplication with a matrix M, then apply2 is given as:

op.apply2(V, U) = V^T*M*U.

In the case of complex numbers, note that apply2 is anti-linear in the first variable by definition of inner.

Parameters

V

VectorArray of the left arguments V.

U

VectorArray of the right arguments U.

mu

The parameter values for which to evaluate the operator.

Returns

A NumPy array with shape (len(V), len(U)) containing the 2-form evaluations.

pairwise_apply2(self, V, U, mu=None)[source]

Treat the operator as a 2-form and apply it to V and U in pairs.

This method is usually implemented as V.pairwise_inner(self.apply(U)). In particular, if the operator is a linear operator given by multiplication with a matrix M, then apply2 is given as:

op.apply2(V, U)[i] = V[i]^T*M*U[i].

In the case of complex numbers, note that pairwise_apply2 is anti-linear in the first variable by definition of pairwise_inner.

Parameters

V

VectorArray of the left arguments V.

U

VectorArray of the right arguments U.

mu

The parameter values for which to evaluate the operator.

Returns

A NumPy array with shape (len(V),) == (len(U),) containing the 2-form evaluations.

apply_adjoint(self, V, mu=None)[source]

Apply the adjoint operator.

For any given linear Operator op, parameter values mu and VectorArrays U, V in the source resp. range we have:

op.apply_adjoint(V, mu).dot(U) == V.inner(op.apply(U, mu))

Thus, when op is represented by a matrix M, apply_adjoint is given by left-multplication of (the complex conjugate of) M with V.

Parameters

V

VectorArray of vectors to which the adjoint operator is applied.

mu

The parameter values for which to apply the adjoint operator.

Returns

VectorArray of the adjoint operator evaluations.

apply_inverse(self, V, mu=None, initial_guess=None, least_squares=False)[source]

Apply the inverse operator.

Parameters

V

VectorArray of vectors to which the inverse operator is applied.

mu

The parameter values for which to evaluate the inverse operator.

initial_guess

VectorArray with the same length as V containing initial guesses for the solution. Some implementations of apply_inverse may ignore this parameter. If None a solver-dependent default is used.

least_squares

If True, solve the least squares problem:

u = argmin ||op(u) - v||_2.

Since for an invertible operator the least squares solution agrees with the result of the application of the inverse operator, setting this option should, in general, have no effect on the result for those operators. However, note that when no appropriate solver_options are set for the operator, most implementations will choose a least squares solver by default which may be undesirable.

Returns

VectorArray of the inverse operator evaluations.

Raises

InversionError

The operator could not be inverted.

apply_inverse_adjoint(self, U, mu=None, initial_guess=None, least_squares=False)[source]

Apply the inverse adjoint operator.

Parameters

U

VectorArray of vectors to which the inverse adjoint operator is applied.

mu

The parameter values for which to evaluate the inverse adjoint operator.

initial_guess

VectorArray with the same length as U containing initial guesses for the solution. Some implementations of apply_inverse_adjoint may ignore this parameter. If None a solver-dependent default is used.

least_squares

If True, solve the least squares problem:

v = argmin ||op^*(v) - u||_2.

Since for an invertible operator the least squares solution agrees with the result of the application of the inverse operator, setting this option should, in general, have no effect on the result for those operators. However, note that when no appropriate solver_options are set for the operator, most operator implementations will choose a least squares solver by default which may be undesirable.

Returns

VectorArray of the inverse adjoint operator evaluations.

Raises

InversionError

The operator could not be inverted.

jacobian(self, U, mu=None)[source]

Return the operator’s Jacobian as a new Operator.

Parameters

U

Length 1 VectorArray containing the vector for which to compute the Jacobian.

mu

The parameter values for which to compute the Jacobian.

Returns

Linear Operator representing the Jacobian.

assemble(self, mu=None)[source]

Assemble the operator for given parameter values.

The result of the method strongly depends on the given operator. For instance, a matrix-based operator will assemble its matrix, a LincombOperator will try to form the linear combination of its operators, whereas an arbitrary operator might simply return a FixedParameterOperator. The only assured property of the assembled operator is that it no longer depends on a Parameter.

Parameters

mu

The parameter values for which to assemble the operator.

Returns

Parameter-independent, assembled Operator.

_assemble_lincomb(self, operators, coefficients, identity_shift=0.0, solver_options=None, name=None)[source]

Try to assemble a linear combination of the given operators.

Returns a new Operator which represents the sum

c_1*O_1 + ... + c_N*O_N + s*I

where O_i are Operators, c_i, s scalar coefficients and I the identity.

This method is called in the assemble method of LincombOperator on the first of its operators. If an assembly of the given linear combination is possible, e.g. the linear combination of the system matrices of the operators can be formed, then the assembled operator is returned. Otherwise, the method returns None to indicate that assembly is not possible.

Parameters

operators

List of Operators O_i whose linear combination is formed.

coefficients

List of the corresponding linear coefficients c_i.

identity_shift

The coefficient s.

solver_options

solver_options for the assembled operator.

name

Name of the assembled operator.

Returns

The assembled Operator if assembly is possible, otherwise None.

restricted(self, dofs)[source]

Restrict the operator range to a given set of degrees of freedom.

This method returns a restricted version restricted_op of the operator along with an array source_dofs such that for any VectorArray U in self.source the following is true:

self.apply(U, mu).dofs(dofs)
    == restricted_op.apply(NumpyVectorArray(U.dofs(source_dofs)), mu))

Such an operator is mainly useful for empirical interpolation where the evaluation of the original operator only needs to be known for few selected degrees of freedom. If the operator has a small stencil, only few source_dofs will be needed to evaluate the restricted operator which can make its evaluation very fast compared to evaluating the original operator.

Parameters

dofs

One-dimensional NumPy array of degrees of freedom in the operator range to which to restrict.

Returns

restricted_op

The restricted operator as defined above. The operator will have NumpyVectorSpace (len(source_dofs)) as source and NumpyVectorSpace (len(dofs)) as range.

source_dofs

One-dimensional NumPy array of source degrees of freedom as defined above.

__del__(self)[source]
pymor.operators.mpi._MPIOperator_get_local_spaces(self, source, pickle_local_spaces)[source]
pymor.operators.mpi._MPIOperator_assemble_lincomb(operators, coefficients, identity_shift, name)[source]
pymor.operators.mpi._MPIOperator_restriced(self, dofs)[source]
pymor.operators.mpi._MPIOperator_assemble(self, mu)[source]
pymor.operators.mpi.mpi_wrap_operator(obj_id, mpi_range, mpi_source, with_apply2=False, pickle_local_spaces=True, space_type=MPIVectorSpace)[source]

Wrap MPI distributed local Operators to a global Operator on rank 0.

Given MPI distributed local Operators referred to by the ObjectId obj_id, return a new Operator which manages these distributed operators from rank 0. This is done by instantiating MPIOperator. Additionally, the structure of the wrapped operators is preserved. E.g. LincombOperators will be wrapped as a LincombOperator of MPIOperators.

Parameters

See : class:MPIOperator.

Returns

The wrapped Operator.

pymor.operators.mpi._mpi_wrap_operator_LincombOperator_manage_operators(obj_id)[source]
pymor.operators.mpi._mpi_wrap_operator_VectorArrayOperator_manage_array(obj_id, pickle_local_spaces)[source]