gpe.interfaces
==============

.. py:module:: gpe.interfaces

.. autoapi-nested-parse::

   Interface Definitions.

   To help keep the code modular and predictable, we provide several
   interfaces.  The idea is that generalized code should only these
   methods and attributes defined in the interface so that it is easy for
   one to extend the code by implementing a different version of the same
   interface.



Classes
-------

.. autoapisummary::

   gpe.interfaces.IMinimizeState
   gpe.interfaces.IExperimentMinimal
   gpe.interfaces.IExperiment
   gpe.interfaces.IStateDFT
   gpe.interfaces.IStateMinimizeDFT


Module Contents
---------------

.. py:class:: IMinimizeState(real=False, fix_N=True)

   Bases: :py:obj:`zope.interface.Interface`


   Interface provided by state minimizers.


   .. py:method:: minimize(E_tol=1e-12, callback=None, **kw)

      Return the `state` minimizing the energy.

      :param psi_tol: Desired relative tolerance for the wavefunction.
      :type psi_tol: float
      :param E_tol: Desired relative tolerance for the energy.
      :type E_tol: float
      :param callback: Optional callback function called as `callback(state)`
                       during iterations for debugging.
      :type callback: function
      :param kw: Additional arguments to pass to the solver.
      :type kw: dict



.. py:class:: IExperimentMinimal(name, bases=(), attrs=None, __doc__=None, __module__=None)

   Bases: :py:obj:`zope.interface.Interface`


   Minimal Interface for Experiments.

   The full IExperiment interface can be defined from these minimal
   attributes by the `ExperimentBase` class.


   .. py:attribute:: State


   .. py:attribute:: t_unit


   .. py:attribute:: t_name


   .. py:attribute:: image_ts_


   .. py:method:: init()

      Performs any complex initialization other that assigning
      parameters (which is done by the constructor).  Subclasses
      should not overload the constructor.



   .. py:method:: get_Vext(fiducial=False, expt=False)

      Return the external potential(s) as relevant for the
      experiment.

      The potential should return three different types of
      potential depending on the arguments:

      * If `expt==False`, then return the potential appropriate for
        the simulation.  This may differ from the actual physical
        potential if `cells_x is not None` since then only a portion
        at the center of the trap will be simulation.  In this case,
        the potential should be a periodic extension of the
        experimental potential matching the experiment in the
        middle, but becoming periodic at the boundaries so as not to
        introduce cusps.

        The simulation may also have different parameters such as
        the trapping frequency along x since one might like to
        consider a homogeneous state.
      * If `expt==True`, then return the full experimental
        potential with the experimental parameters.  This potential
        should not be a periodic modification nor should it use
        modified parameters such as the trapping frequencies.  The
        idea here is to return the potential that should be used to
        defined the initial state by determining the chemical
        potential at the center of the system.

        This potential may depend on one more flag:

        * If `fiducial==True`, then return a potential such that
          `V_ext(x_TF) = V_TF` at the edge of the cloud `x_TF` in
          the Thomas Fermi approximation.  This is generally how the
          initial state is specified, however, this potential might
          not be the one actually used to initialize the simulation
          as described below.
        * If `fiducial==False`, then return the actual potential
          used to initialize the simulation.  This may differ from
          the previous case if the initial experimental state is
          obtained after adiabatic evolution from
          `V_ext(fiducial=True, expt=True)` to
          `V_ext(fiducial=False, expt=True)`.  Strictly speaking,
          our simulation should also perform this adiabatic
          evolution, but this can be very costly.  Instead, it is
          often better to minimize into the initial state directly,
          but to do this, one needs to specify and fix the
          appropriate particle number or chemical potential from the
          fiducial state.

      :param state: Current state.  Use this to get the time `state.t` and
                    abscissa `state.basis.xyz`.
      :type state: IState
      :param fiducial: If `True`, then return the potential that should be used to
                       define the initial state in terms of the Thomas Fermi
                       radius of the cloud `x_TF`.
      :type fiducial: bool
      :param expt: If `True`, then return the proper experimental potential
                   rather than the potential used in the simulation.
      :type expt: bool



   .. py:method:: get_state()

      Quickly return a valid `State` object.

      Should not set `state.initializing = False` unless `initialize == True`.

      :param initialize: If `False`, then the initial data need not be provided.
                         (Used by simulations when data is loaded from a file.)

                         If returning a fully initialized state, then may set
                         `state.initializing=True`, but users should not generally rely on this flag,
                         and should still call `pre_evolve_hook()` before evolving.
      :type initialize: bool



   .. py:method:: get_initial_state()

      Return the valid `t=0` state to initialize the simulations.

      The returned state should have `state.initializing == False`.



   .. py:method:: get_initialized_state()

      Return a valid state initialized from `state`.

      This is used in chained simulations where a specified state of one
      simulation is used to initialize a state for further use.  For example,
      for expansion.



.. py:class:: IExperiment(**kw)

   Bases: :py:obj:`IExperimentMinimal`


   Full interface for Experiment classes.

   States should delegate to the experiment allowing the experiment
   to control external potentials etc.

   Note: All times are expressed in terms of `t_unit` such that `t_ =
   t/t_unit`.  We try to consistently use the name `t_` for such dimensionless
   quantities except in class variables which are assumed to be in the
   specified `t_unit`.

   Simulations and Imaging
   =======================
   The idea of an "Experiment" is some sort of simulation run defined by a set
   of parameters (attributes of this class) that is evolved through a set of
   `image_ts_` under a set of "normal" experimental conditions.  These states would be
   what is observed in "in situ imaging".  Typically, however, from these states, one
   evolves for an additional time `t__image` without any interactions or traps to allow
   the clouds to "expand", after which an "expansion image" is taken, usually resolving
   better details like vortices and domain walls.

   The assumption is that one experiment object is shared between all states
   belonging to a simulation.  For example, this means that
   `experiment.t__image` will be the same for each state (along with every
   other parameter).


   .. py:attribute:: image_ts_


   .. py:attribute:: t__image


   .. py:attribute:: max_key_length


   .. py:attribute:: directory_per_key


   .. py:attribute:: dir_name


   .. py:method:: items()

      Provides support for Archivable.



   .. py:method:: copy()

      Return a copy of the experiment.



.. py:class:: IStateDFT(name, bases=(), attrs=None, __doc__=None, __module__=None)

   Bases: :py:obj:`pytimeode.interfaces.IStateFlat`, :py:obj:`pytimeode.interfaces.IStateWithBraket`


   Extension to the IState interface for orbital-free DFT equations.

   The specialization here is to provide a basis capable of applying
   a Schrodinger-like Hamiltonian to a wavefunction, including a
   Laplacian.  Various extensions can be supported, including using
   changing basis to account for expansion, rotation, and motion, as
   well as to multiple components.

   This basic interface should be provided by all such classes, and
   will be relied upon by various utilities.

   .. attribute:: experiment

      All states should delegate to an Experiment object for control.  Experiment
      objects can be shared, and help provide mechanisms for archiving to disk etc.
      This is an additional requirement of this project above the requirements of
      :mod:`pytimeode`.

      :type: IExperiment

   .. attribute:: t_final

      This is a special parameter used to signify how a state should be run.
      From `t=0` to `t==t_final` the experiment should be run under "normal"
      conditions, while for `t_final < t` the experiment should be run under
      "imaging" conditions, which might include letting a trapped gas expand, or
      ramping the coupling constants etc.
      
      In general, the state could have many different stages of evolution, but the
      need for expansion images is so common that we make a special case here --
      storing a single "normal" evolution up to different `t_final` times followed by
      chains of imaging evolution.  In these cases, the values of these parameters
      (which should be considered volatile) will be manipulated by the `Simulation`
      class.
      
      States often also use `t_ < 0` to prepare the initial state, which might be
      prepared (minimized) under different conditions than the run.

      :type: float, None


   .. py:attribute:: xyz


   .. py:attribute:: t__scale


   .. py:attribute:: t_final


   .. py:attribute:: experiment


   .. py:attribute:: initializing


   .. py:method:: set_psi()

      Set the state from a physical wavefunction.

      This should perform any corrections required such as un-applying
      Bloch twists so that the underlying computational function is
      periodic, or performing any adjustments to account for
      changing coordinates.

      This is used by the Simulation class to restore data from disk,
      so this should accept a contiguous numpy array as input.



   .. py:method:: get_psi()

      Return the physical wavefunction.

      This should perform any corrections required such as applying
      Bloch twists so that the underlying computational function is
      periodic, or performing any adjustments to account for
      changing coordinates.

      This is used by the Simulation class to archive data to disk,
      so this should return a contiguous numpy array as input.



   .. py:method:: apply_V(exp=False)

      Apply V as a potential to the state and return self.

      :param V: Diagonal potential to be applied by multiplication to the
                state.
      :type V: array-like
      :param exp: If `True` then `exp(V)` will be applied instead.
      :type exp: bool



   .. py:method:: apply_laplacian(exp=False)

      Apply the laplacian multiplied by `factor` to the state.

      :param factor: The result will be multiplied by this factor.
      :type factor: array-like
      :param exp: If `True` then `exp(factor*laplacian)(y)` will be computed instead.
      :type exp: bool



.. py:class:: IStateMinimizeDFT(name, bases=(), attrs=None, __doc__=None, __module__=None)

   Bases: :py:obj:`IStateDFT`, :py:obj:`pytimeode.interfaces.IStateWithNormalize`


   Extension of IStateDFT that permits minimization.

   These extensions are not needed for direct evolution, but helpful
   for ground-state preparation.


   .. py:attribute:: asnumpy


   .. py:attribute:: xp


   .. py:attribute:: metric


   .. py:method:: pre_minimize_hook()

      Called like `pre_evolve_hook()` but should not set `initializing=False`.



   .. py:method:: fill()

      Fill state will constant value.



   .. py:method:: normalize()

      Normalize the state and return `(s, N)`self.

      This is a generalization of the method required by the
      IStateWithNormalize interface which returns the scale-factor
      `s` effecting the normalization through multiplication::

          state *= s

      :param s: The scale factor to scale the state by such that `y*s` is
                normalized.  If `None`, then the scale factor should be
                computed and returned along with the final normalization.
      :type s: float or array-like, None

      :returns: * **s** (*float or array-like*) -- The scale factor such that `y*s` is normalized.
                * **N** (*float or array-like*) -- Final normalization.  This is used in two cases: 1) if some
                  of the normalization values are zero `np.any(N == 0)`, then
                  if any values are zero, then the minimizers expect the
                  non-zero entries of `state*N` to be zero and will not
                  optimize over these values. 2) When fixing the constraints,
                  `N` is used to remove the singular directions from the Hamiltonian.



   .. py:method:: get_energy()

      Return the energy of the state.

      Used by minimizers.



   .. py:method:: get_Hy()

      Return `H*psi = dE/dpsi.T.conj()`.

      This determines the descent direction to minimize the energy.
      Usually it is implemented simply as `1j*hbar*dy_dt` and the
      default implementation in `mixins` does this.

      :param subtract_mu: If `True`, then the appropriate chemical potentials should
                          be subtracted from the Hamiltonian so that real-time
                          evolution would fix the particle number.
      :type subtract_mu: bool



