Surface Evolution Equations

Contents

Surface Evolution Equations#

Symbolic Surface Operators#

class pystopt.evolution.StokesEvolutionOperator(force_normal_velocity: bool = True, force_tangential_velocity: bool = False, force_volume_conservation: bool = False)[source]#

Bases: object

Evolves an interface in Stokes flow.

The evolution equation is given by

\[\frac{\mathrm{d} \mathbf{X}}{\mathrm{d} t} = (\mathbf{u} \cdot \mathbf{n}) \mathbf{n} + (I - \mathbf{n} \otimes \mathbf{n}) \mathbf{u}_\tau + \mathbf{s},\]

where the first term is purely normal, the second term is purely tangential and \(\mathbf{s}\) is an additional user-provided source term (usually \(0\)). If force_normal_velocity is False, the first term is replaces by \(\mathbf{u}\). If force_tangential_velocity is False, the second term is omitted, even if it is provided to get_sym_operator().

When binding the operator from get_sym_operator(), the user must provide the Stokes velocity field \(\mathbf{u}\) and the tangential velocity field \(\mathbf{u}_\tau\).

force_normal_velocity#
force_tangential_velocity#
__init__(force_normal_velocity: bool = True, force_tangential_velocity: bool = False, force_volume_conservation: bool = False) None#
get_sym_operator(u: ndarray, w: ndarray | None = None, dim: int | None = None, dofdesc: pytential.symbolic.primitives.DOFDescriptorLike | None = None) ndarray[source]#

Time Stepping#

class pystopt.evolution.TimeWrangler(time_step_name: str, maxit: int | None = None, tmax: float | None = None, dt: float | None = None, theta: float | None = None, estimate_time_step: Callable[[float, Any], float] | bool | None = None, state_filter: Callable[[Any], Any] | None = None)[source]#

Bases: object

A class that holds all the parameters required to construct a TimestepMethodWrapper.

time_step_name#
maxit#
tmax#
dt#

Initial guess for the time step.

theta#

Courant number that can be used for time step estimation.

estimate_time_step#

A callable used to estimate the time step. Setting it to False can be used to disable adaptive time stepping.

make_stepper(state0: T, source: Callable[[float, T], T], estimate: Callable[[float, T], float] | None = None, **kwargs: Any) TimestepMethodWrapper[source]#

Construct a time stepper starting from the initial condition state0 and the right-hand side source.

Parameters:

estimate – a callable used for time step estimation that can be used to overwrite the default one in estimate_time_step.

class pystopt.evolution.TimestepMethodWrapper(leap: Any, estimate_time_step: Callable[[float, Any], float] | None = None, dt_max: float | None = None)[source]#

Bases: object

leap#

Underlying time stepper that is wrapped.

estimate_time_step#

A callbable that takes the state variable and returns an estimated time step. Note that, if the callable is provided, update_time_step_from_estimate() is used to additionally ensure the time step is not varying too quickly and remains within bounds at the final time.

dt_max#

If estimate_time_step is provided, this value can be used to additionally limit the time step size. In general, it is recommended that estimate_time_step does the preprocessing itself.

run(*, tmax: float | None = None, maxit: int | None = None, verbose: bool = True) Iterator[TimestepEvent][source]#

Run time stepper for a given period of time.

Parameters:
  • tmax – final time for the time stepping.

  • maxit – maximum number of iterations.

pystopt.evolution.make_adjoint_time_stepper(method: str, component_id: str, component: T, rhs: Callable[[float, T], T], *, dt_start: float, t_start: float = 0.0, state_filter: Callable[[T], T] | None = None, checkpoint: CheckpointManager | None = None, **kwargs: Any) TimestepMethodWrapper[source]#

Generate a time integrator with adjoint capabilities.

Currently supported methods are:

  • "aeuler": classic first-order forward Euler method.

  • "assprk22": classic two-stage second-order SSP RK method.

  • "assprk33": classic three-stage third-order SSP RK method.

  • "assprk34": classic four-stage third-order SSP RK method.

Parameters:
  • component_id – a string name for the variable.

  • component – an initial condition for the time stepping.

  • rhs – a Callable taking (t, component).

  • dt – fixed time step.

class pystopt.evolution.adjoint.T#
class pystopt.evolution.TimestepEvent(n: int, t: float, dt: float)[source]#

Bases: object

A generic event that can be emitted by TimeStepMethodBase.

n#

Time step iteration count.

t#

Time at iteration n.

dt#

Time step at iteration n, such that \(t - \Delta t\) gives the previous iteration time.

class pystopt.evolution.StepFailedEvent(n: int, t: float, dt: float, state_component: Any)[source]#

Bases: TimestepEvent

An event used to signal that a step update failed.

state_component#

Value of the states components that were used to cause the failure.

class pystopt.evolution.StateComputedEvent(n: int, t: float, dt: float, state_component: Any, adjoint_component: Any)[source]#

Bases: TimestepEvent

An event used to emit the successful update of the state.

state_component#

Value of the state components at iteration TimestepEvent.n.

adjoint_component#

When performing the adjoint evolution, this will contain the adjoint state components. Otherwise, it is expected to be None.

class pystopt.evolution.InitialStateEvent(n: int, t: float, dt: float, state_component: Any, adjoint_component: Any)[source]#

Bases: StateComputedEvent

An event used to emit the initial state of the evolution.

class pystopt.evolution.TimeStepMethodBase(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None)[source]#

Bases: Generic[T]

state_id#

Name of the state variable in TimeStepMethod.function or AdjointTimeStepMethod.function.

state0#

Initial value for the time stepping variable.

dt#

Initial time step, which can be changed by overwriting.

filter#

A callable that is used to “filter” the state variables after an update.

checkpoint#

A CheckpointManager that can read and write the state0 type object.

run(t_end: float | None = None, max_steps: int | None = None) Iterator[TimestepEvent][source]#
Parameters:
  • t_end – if None, the final time is assumed to be infinite.

  • max_steps – if None, an infinite number of time steps is assumed.

class pystopt.evolution.TimeStepMethod(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, function: Callable[[float, T], T], t_start: float)[source]#

Bases: TimeStepMethodBase[T]

Family of standard forward time stepping methods.

function#

Right-hand side function used in time stepping.

t_start#

Initial time.

make_adjoint(adjoint_id: str, adjoint0: T, fun: Callable[[float, T], T]) AdjointTimeStepMethod[source]#

Construct the corresponding discrete adjoint to the time stepper.

Parameters:
  • adjoint_id – identifier of the adjoint variable.

  • adjoint0 – value of the adjoint variable at the final time.

  • fun – right-hand side in the adjoint equation.

class pystopt.evolution.AdjointTimeStepMethod(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, adjoint_id: str, function: Callable[[float, Any, T], T])[source]#

Bases: TimeStepMethodBase[T]

Family of adjoint time stepping methods.

The adjoint equation is assumed to be of the form

\[-\frac{p}{t} = f^*(t, p; y)\]

where \(y\) is the corresponding forward variable, which appears in the adjoint equation if the forward ODE was nonlinear. The adjoint equation is solved backwards in time based on the checkpointed values of the forward problem.

adjoint_id#
function#

Right-hand side for the adjoint evolution equation that takes (t, state, adjoint).

class pystopt.evolution.ForwardEulerMethod(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, function: Callable[[float, T], T], t_start: float)[source]#

Bases: TimeStepMethod[T]

Forward Euler time stepper.

class pystopt.evolution.AdjointForwardEulerMethod(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, adjoint_id: str, function: Callable[[float, Any, T], T])[source]#

Bases: AdjointTimeStepMethod[T]

Adjoint of ForwardEulerMethod.

class pystopt.evolution.SSPRK22Method(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, function: Callable[[float, T], T], t_start: float)[source]#

Bases: TimeStepMethod[T]

Classic second-order SSP Runge-Kutta method with 2 stages.

class pystopt.evolution.AdjointSSPRK22Method(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, adjoint_id: str, function: Callable[[float, Any, T], T])[source]#

Bases: AdjointTimeStepMethod[T]

Adjoint of SSPRK22Method.

class pystopt.evolution.SSPRK33Method(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, function: Callable[[float, T], T], t_start: float)[source]#

Bases: TimeStepMethod[T]

Classic third-order SSP Runge-Kutta method with 3 stages.

class pystopt.evolution.AdjointSSPRK33Method(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, adjoint_id: str, function: Callable[[float, Any, T], T])[source]#

Bases: AdjointTimeStepMethod[T]

Adjoint of SSPRK33Method.

class pystopt.evolution.SSPRK34Method(state_id: str, state0: T, dt: float, filter: Callable[[T], T] | None, checkpoint: IteratorCheckpointManager | None, function: Callable[[float, T], T], t_start: float)[source]#

Bases: TimeStepMethod[T]

A third-order SSP Runge-Kutta method with 4 stages.

pystopt.evolution.update_time_step_from_estimate(dt_old: float, dt_new: float, dt_max: float | None = None) float[source]#

Smooth out time step estimates based on immediate history.

Parameters:
  • dt_old – previous time step.

  • dt_new – estimate for the next time step.

  • dt_max – maximum allowed time step.

Returns:

an improved estimate for the next time step that does not grow too fast compared to dt_old, e.g. it shouldn’t be more than twice as large as dt_old.

pystopt.evolution.fixed_time_step_from(*, maxit: int | None = None, tmax: float | None = None, timestep: float | None = None) Tuple[int, float, float][source]#

Harmonize the values of maxit, tmax and timestep.

This function can be used to ensure that the values are consistent in the sense that:

tmax = maxit * timestep

In the case all the values are given, i.e. not None, then the time step is kept as is and the other values are changed to match the formula above.

pystopt.evolution.capillary_time_step_estimation(actx: ArrayContext, places: pytential.GeometryCollection, velocity: ndarray, *, method: str, capillary_number: float, viscosity_ratio: float = 1.0, sources: Sequence[pytential.symbolic.primitives.DOFDescriptorLike] | None = None) float[source]#

Time step estimation based on capillary waves.

List of methods

Parameters:

sources – a single or list of identifiers for geometries in places. The returned time step will be a minimum value over all the sources.

pystopt.evolution.capillary_time_step_brackbill(actx: ArrayContext, places: pytential.GeometryCollection, *, capillary_number: float, viscosity_ratio: float | None = None, velocity: ndarray | None = None, dofdesc: pytential.symbolic.primitives.DOFDescriptorLike | None = None) Array[source]#

Time step estimation based on [brackbill-1992] Equation 61.

The length scale in [brackbill-1992] was based on a uniform rectangular mesh. In our case, we use h_min().

[brackbill-1992] (1,2)

J. U. Brackbill, D. B. Kothe, C. Zemach, A Continuum Method for Modeling Surface Tension, Journal of Computational Physics, Vol. 100, pp. 335–354, 1992, DOI.

pystopt.evolution.capillary_time_step_loewenberg(actx: ArrayContext, places: pytential.GeometryCollection, *, capillary_number: float, viscosity_ratio: float, velocity: ndarray | None = None, dofdesc: pytential.symbolic.primitives.DOFDescriptorLike | None = None) Array[source]#

Time step estimation based on [loewenberg-2001] Section 4.1.1.

The length scale in [loewenberg-2001] was based on the smallest edge length. In our case, we use a length scale based on the smallest stretch of an element in h_min().

[loewenberg-2001] (1,2)

V. Cristini, J. Bławzdziewicz, M. Loewenberg, An Adaptive Mesh Algorithm for Evolving Surfaces: Simulations of Drop Breakup and Coalescence, Journal of Computational Physics, Vol. 168, pp. 445–463, 2001, DOI.

pystopt.evolution.capillary_time_step_zinchenko2000(actx: ArrayContext, places: pytential.GeometryCollection, *, capillary_number: float, viscosity_ratio: float | None = None, velocity: ndarray | None = None, dofdesc: pytential.symbolic.primitives.DOFDescriptorLike | None = None) Array[source]#

Time step estimation based on [zinchenko-2000] Appendix C.

This is very similar to capillary_time_step_zinchenko2006(), but also involves the viscosity ratio.

[zinchenko-2000]

A. Z. Zinchenko, R. H. Davis, An Efficient Algorithm for Hydrodynamical Interaction of Many Deformable Drops, Journal of Computational Physics, Vol. 157, pp. 539–587, 2000, DOI.

pystopt.evolution.capillary_time_step_zinchenko2006(actx: ArrayContext, places: pytential.GeometryCollection, *, capillary_number: float, viscosity_ratio: float | None = None, velocity: ndarray | None = None, dofdesc: pytential.symbolic.primitives.DOFDescriptorLike | None = None) Array[source]#

Time step estimation based on [zinchenko-2006] Equation 3.26.

A small modification is made to the results from [zinchenko-2006], since we take the maximum of the mean curvature instead the maximum of the individual principal curvatures. This should result in a more stringent time step.

[zinchenko-2006] (1,2)

A. Z. Zinchenko, R. H. Davis, A Boundary-Integral Study of a Drop Squeezing Through Interparticle Constrictions, Journal of Fluid Mechanics, Vol. 564, pp. 227, 2006, DOI.