"""Library of and framework for making boundary conditions in wepy simulations.

In many simulations you would like to apply external constraints upon
the dynamics of a simulation. For example in ligand unbinding
simulations you may want to recognize and act on the fact that a
ligand has reached some sort of threshold distance away from it's
originally bound protein. The `LigandUnbinding` boundary condition
class does exactly this and modifies the walker state to restart it in
the original starting position instead of continuing the dynamics of
the runner. Another possible way to implement this would be to only
change a single data field in the walker state that tells you the
"color" of the walker which indicates the last boundary it crossed,
without interrupting the dynamical continuity of the
simulation. However, the analysis routines in `wepy` are adapted to
deal with such discontinuities if you desire such non-equilibrium
simulations.

Use of boundary conditions is so common that the `wepy` simulation
manager and `WepyHDF5` storage backend provide an interface for
applying and saving results of boundary conditions.

Boundary conditions need not be used in a simulation at all however
and are strictly optional.

This module provides a small library of common boundary conditions and
a reference for the implementation of your own boundary
conditions. The approach to have a microframework for making boundary
conditions was chosen because the actual details of them are highly
domain dependent and it is unlikely that `wepy` could begin to cover
all of them.

A boundary condition is simply a duck typed object that follows the
interface called by the simulation manager. This means that you need
not use this microframework at all and can implement one from the
ground up. I.e. no explicit class inheritance checks are done at any
point.

However, to make things easy just inherit from the `BoundaryCondition`
base class and override the necessary functions.

These methods must be implemented to work correctly in the reference
simulation manager:
- warp_walkers
- progress
- update_bc


A non-trivial boundary condition (BC for short) will override at least
the `warp_walkers` method. "Warping" in `wepy` terminology means for a
walker to have met the constraints of the BC and have it's state be
modified in response (think warping through space via a wormhole like
in star trek). Warps can be 'continuous' or 'discontinuous' depending
on whether the continuity of dynamics is broken due to this
effect. For example, resetting the positions of a molecular dynamics
to its starting point is discontinuous, and the change of a string
label or some other field not used by the runner to compute dynamics
is continuous.

For each warping event a single warping record is generated, that
should detail the event.

The simulation manager will also make calls to `progress` and
`update_bc`, but these are accessory to the `warp_walkers` method.

The `progress` call returns a continual record (one per cycle) and is
intended to report on some metrics of progress the walkers have
achieved. For example in the ligand unbinding example this can be the
minimum distance to the protein. The progress record can contain
multiple features per walker. It can also be used to make economical
use of calculations used in the BC when determining the warpings of
the walkers.

Additionally for routines in `wepy.analysis` to work properly the
`warping_discontinuity` classmethod (can be called from the class
without an object instance) must be implemented correctly.

The `update_bc` call is used to get changes to the BC object
itself. To use the ligand unbinding example one might lower the
threshold for unbinding if so many cycles go by without an event
occuring with perhaps introspection on the progress values.

Any changes in the state of the BC object should be happen in the call
stack of `update_bc` and reflected in the 'bc_record' returned to the
simulation manager.

Record groups generated by a BC class are:
- warping
- boundary_conditions
- progress

It is recommended (but optional) to have all the field names, shapes,
and dtypes for the records in the class definition for these
where possible.

Notes
-----

Boundary conditions in wepy simulations are optional.

Inherit from the
`wepy.boundary_conditions.boundary.BoundaryConditions` class to get
started developing your own boundary conditions.

Boundary conditions must implement methods: `warp_walkers`,
`progress`, and `update_bc` for simulation.

It is the programmers responsibility to handle discontinuous warping
events by implementing the `warping_discontinuity` class method.


References
----------

Examples
--------

"""
