matto.design#

Design-variable parameterization as a chain of operators.

Every design variable is stored twice: a raw field that the optimizer controls and a physical field that enters the material model. The map between them is a composition of operators,

raw –T_1–> … –T_k–> phys,

and the sensitivity of anything with respect to raw is obtained by applying the adjoints in the opposite order,

dJ/draw = T_1^T … T_k^T dJ/dphys.

Each operator implements its forward map and its adjoint, and may carry a continuation schedule (the Heaviside sharpness, for instance). DesignVariable only walks the chain; nothing in it is specific to any operator.

matto.design.volume_constraint(field, upper_bound, dx)[source]#

A volume constraint on a physical design field, as the dictionary build_constraints() returns: the field’s integral over the domain, normalized by the domain volume, no larger than upper_bound.

class matto.design.Operator(input_field, output_field)[source]#

Bases: ABC

One map in the parameterization chain, together with its adjoint.

An operator reads self.input and writes self.output. The two may be the same Function when the map acts in place.

forward() must run before backward() within an iteration. Operators are free to cache the linearization at the current point during forward() and use it in backward().

Operators that can appear in an input file’s operators list implement from_spec() and are listed in OPERATOR_TYPES.

classmethod from_spec(spec, input_field, output_field, context)[source]#

Build the operator from one entry of an input file’s operators list.

input_field is the field written by the previous operator (or the raw field). output_field is the variable’s phys field when this operator is last in the chain and None otherwise, in which case the operator allocates its own intermediate. context holds name, index, comm, petsc_options and physical_space.

abstractmethod forward()[source]#

Apply the map: output <- T(input).

abstractmethod backward(gradients)[source]#

Apply the adjoint to a list of PETSc vectors living on the output side and return the corresponding list on the input side.

Entries may be None and are passed through untouched. The returned vectors may alias or overwrite the given ones, or be work vectors owned by the operator that stay valid only until its next backward() call. Callers that need to keep a result copy it.

update(iteration)[source]#

Advance any continuation schedule at the start of an iteration.

Returns True when the operator changed, so the caller can avoid declaring convergence on a step where the map itself moved.

property continuation_complete#

True once the continuation schedule, if any, has run out.

describe()[source]#

One short phrase for the startup log.

class matto.design.Identity(input_field, output_field)[source]#

Bases: Operator

Copy input to output. Used when a variable has no operators.

forward()[source]#

Apply the map: output <- T(input).

backward(gradients)[source]#

Apply the adjoint to a list of PETSc vectors living on the output side and return the corresponding list on the input side.

Entries may be None and are passed through untouched. The returned vectors may alias or overwrite the given ones, or be work vectors owned by the operator that stay valid only until its next backward() call. Callers that need to keep a result copy it.

class matto.design.HelmholtzKernel(comm, input_space, output_space, radius, petsc_options=None)[source]#

Bases: object

The matrices and solver of a Helmholtz filter for one space pair and radius.

Every design field filtered with the same spaces and radius on the same mesh can share one of these; the operators hold only their own bound fields. The work vectors are shared too, which is fine as the operators run one after another.

static key(input_space, output_space, radius, petsc_options)[source]#

Cache key: same mesh, same elements, same radius, same options.

class matto.design.HelmholtzFilter(kernel, input_field, output_field)[source]#

Bases: Operator

PDE filter: -R^2 lap(phys) + phys = raw with natural boundary conditions.

The raw and filtered fields may live in different spaces. Written as a linear map phys = Kf^{-1} T raw, the adjoint is T^T Kf^{-1} since Kf is symmetric.

classmethod from_spec(spec, input_field, output_field, context)[source]#

Build the operator from one entry of an input file’s operators list.

input_field is the field written by the previous operator (or the raw field). output_field is the variable’s phys field when this operator is last in the chain and None otherwise, in which case the operator allocates its own intermediate. context holds name, index, comm, petsc_options and physical_space.

forward()[source]#

Apply the map: output <- T(input).

backward(gradients)[source]#

Apply the adjoint to a list of PETSc vectors living on the output side and return the corresponding list on the input side.

Entries may be None and are passed through untouched. The returned vectors may alias or overwrite the given ones, or be work vectors owned by the operator that stay valid only until its next backward() call. Callers that need to keep a result copy it.

describe()[source]#

One short phrase for the startup log.

class matto.design.HeavisideProjection(input_field, output_field, beta, beta_max, update_interval, eta=0.5)[source]#

Bases: Operator

Smoothed Heaviside projection about the threshold eta.

phys = [tanh(beta eta) + tanh(beta (x - eta))]

/ [tanh(beta eta) + tanh(beta (1 - eta))],

with the sharpness beta doubled every update_interval iterations up to beta_max. Acts pointwise, so input and output share a space and the adjoint is multiplication by dphys/dx at the current point.

classmethod from_spec(spec, input_field, output_field, context)[source]#

Build the operator from one entry of an input file’s operators list.

input_field is the field written by the previous operator (or the raw field). output_field is the variable’s phys field when this operator is last in the chain and None otherwise, in which case the operator allocates its own intermediate. context holds name, index, comm, petsc_options and physical_space.

update(iteration)[source]#

Advance any continuation schedule at the start of an iteration.

Returns True when the operator changed, so the caller can avoid declaring convergence on a step where the map itself moved.

property continuation_complete#

True once the continuation schedule, if any, has run out.

forward()[source]#

Apply the map: output <- T(input).

backward(gradients)[source]#

Apply the adjoint to a list of PETSc vectors living on the output side and return the corresponding list on the input side.

Entries may be None and are passed through untouched. The returned vectors may alias or overwrite the given ones, or be work vectors owned by the operator that stay valid only until its next backward() call. Callers that need to keep a result copy it.

describe()[source]#

One short phrase for the startup log.

matto.design.build_operator_chain(name, specs, raw, phys, comm, petsc_options, kernels=None)[source]#

Turn the operators list of an input file into Operator objects.

The chain is threaded from raw to phys: each operator reads the field the previous one wrote, and the last one writes phys. An operator that is not last allocates its own intermediate field, so raw is never overwritten.

kernels is a dict shared across the design variables of one problem so operators with the same matrices, such as a filter with the same spaces and radius, assemble them once.

class matto.design.DesignVariable(name, settings, mesh, petsc_options=None, kernels=None)[source]#

Bases: object

Manage one optimization design variable.

The class owns:

raw - the variable controlled by MMA phys - the variable after the operator chain

It walks the chain forward to update phys and backward to carry sensitivities from phys to raw.

apply_fixed_regions()[source]#

Reapply prescribed raw values in fixed regions.

describe()[source]#

The chain as one line, for the startup log.

continuation_complete()[source]#

True once every operator has finished its continuation.

forward(iteration)[source]#

Map raw values to physical values.

Returns True when any operator advanced its continuation this iteration.

backward(physical_gradients)[source]#

Map sensitivity vectors from phys back to raw.

physical_gradients must be a list of PETSc vectors. The returned values are NumPy arrays in raw-variable space.

get_values()[source]#

Return the current raw MMA values.

get_lower_bounds()[source]#

Return the raw lower-bound array.

get_upper_bounds()[source]#

Return the raw upper-bound array.

set_values(values)[source]#

Assign an updated MMA array to the raw field.