gpe.utils
=========

.. py:module:: gpe.utils

.. autoapi-nested-parse::

   Various utilities used throughout the project



Exceptions
----------

.. autoapisummary::

   gpe.utils.PerformanceWarning


Classes
-------

.. autoapisummary::

   gpe.utils.IStateDFT
   gpe.utils.Frames
   gpe.utils.GPUHelper
   gpe.utils.AsNumpyMixin
   gpe.utils.StateWithExperimentMixin


Functions
---------

.. autoapisummary::

   gpe.utils.use_wisdom
   gpe.utils.step
   gpe.utils.x2_2
   gpe.utils.good_Ns
   gpe.utils.get_good_N
   gpe.utils.get_smooth_transition


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

.. 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:exception:: PerformanceWarning

   Bases: :py:obj:`Warning`


   Warning for potential performance issues.


.. py:function:: use_wisdom(**kw)

   Load the fftw_wisdom file (if it exists) and save on exit.


.. py:function:: step(t, t1, alpha=3.0)

   Smooth step function that goes from 0 at time ``t=0`` to 1 at time
   ``t=t1``.  This step function is $C_\infty$:


.. py:function:: x2_2(x, order=6)

   Return a periodic approximation for $x^2/2$ with period $2\pi$.

   order == 0: 1-cos(x)
   order == 2: cos(x)**2/6 - 4*cos(x)/3 + 7/6
   ...


.. py:function:: good_Ns(Nmax=2**15)

   Return a list of good N's for the FFT (powers of 2, 3, and 5).


.. py:function:: get_good_N(N)

   Get the lowest good size N greater than or equal to N for the FFT.

   .. rubric:: Examples

   >>> get_good_N(600)
   600
   >>> get_good_N(601)
   625


.. py:function:: get_smooth_transition(fs, durations, transitions, alphas=None)

   Return a C(inf) smooth transition as a function of t.

   Smoothly transition from fs[0] to fs[1] to fs[2] etc. and hold these for
   times `ts[0]`, `ts[1]`, respectively starting from `t=0`.  The transitions
   take time `dts[0]` etc.

   :param fs: List of `N` function values.
   :type fs: [float]
   :param durations: List of `N-1` durations for each function values  (the last value will
                     be held indefinitely).
   :type durations: [float]
   :param transitions: List of `N-1` transition durations.
   :type transitions: [float]
   :param alphas: List of `N-1` alpha values for each transition.
   :type alphas: [float]


.. py:class:: Frames(data_dir, mode='', prefix='frame_', sep='_image_', checkpoint_prefix='check_', checkpoints_to_retain=1, immediate=None, mem_limit_bytes=None, decimal_precision=4)

   Represents a series of frames (arrays) on disk for checkpointing and
   making movies.  Each frame is stored in a file with a key that is usually
   the time at which the frame is valid.

   The main interface is through item access, i.e.::

       state.set_data(frames[key])
       with frames as frames:
           frames[key] = state.get_data()

   Checkpoints should be created explicitly::

       with frames as frames:
           frames.checkpoint(key, state.get_data())

   These will be treated as regular frames, but will be deleted when
   a new checkpoint is created.

   Context
   =======
   Frames instances should generally be used as a context if writing to disk.  This
   will suspend immediate mode until the end of the context, improving performance.

   Key Conversions
   ===============
   To ensure safe comparisons between frame keys, we convert to and
   from an ikey with the methods `key_to_ikey()` and `ikey_to_key()`.
   These should perform appropriate manipulations like rounding so
   that equality comparison between keys is meaningful.  (Direct
   comparison of floating point values is dangerous since round-off
   error could cause a key failure between keys that are practically
   the same, but obtained by slightly different orders of operations.)

   key : This is what the user supplies as a key.  In the default
      implementation this is either a floating point number or a
      tuple of floating point numbers.
   ikey : This is the internal representation of the key, use for
      comparison, indexing, etc.  In the default implementation, we
      use the Decimal() class to truncate and provide an exact
      representation of the floating point number.

   Finally, the ikey needs to be converted to a string for use in the
   filenames.  These conversions are done by the ikey_to_str() and
   str_to_ikey() methods.  All four conversion methods should be
   redefined if a different type of key is used.

   The default implementation produces filenames such as::

     frame_0.1000_image_0.0500.npy

   which would represent a frame evolved after 0.1 time units and
   then imaged after an additional 0.05 time units of expansion.

   .. attribute:: data_dir

      The frames will be stored in this directory.

      :type: str

   .. attribute:: mode

      Read ('r'), write ('w'), or in-memory ('m').  Data not written to disk unless
      'w' in mode.   Setting a frame without 'w' issues a warning that the data will
      not be stored on disk.  This can be suppressed by setting the mode to 'm' which
      indicates in-memory mode.  Setting `mode='m'` will not access the disk at all
      (even read access will be disabled).
      
      The default `mode=''` is 'w' in a context and 'r' outside a context.

      :type: str containing 'r', 'w', 'm'

   .. attribute:: prefix

      Frame filenames start with this.

      :type: str

   .. attribute:: sep

      When the key is iterable (as defined by `self.isiterable()`),
      then keys are joined by this separator.  The default is `"_image_"`.

      :type: str

   .. attribute:: immediate

      If `True`, then frames will be saved to disk upon assignment, otherwise
      they will be saved upon `flush()` (also called at the end of a context.)
      The default (`None`) is False if used in a context, but True
      otherwise.

      :type: bool, None

   .. attribute:: checkpoints_to_retain

      When saving a checkpoint, keep this many previous checkpoints
      and delete the rest.

      :type: int

   .. attribute:: mem_limit_bytes

      If not `None`, limit in bytes imposed by `self.limit_memory()` on the
      total size the frames may take up in memory.  If `self.immediate is
      None` (default), `limit_memory` is called upon exiting a context.  If
      `self.immediate`, `limit_memory` is automatically called whenever a
      frame is set or loaded.  Otherwise, `limit_memory` must be invoked
      manually.

      :type: None (default) or int

   .. attribute:: decimal_precision

      This is a specialized argument for the default version of the class.
      Internally, we use the Decimal class for keys, normalized to this
      precision.  If precision is lost (according to `np.allclose`, then a
      ValueError is raised.  When loading data from a file, this may be
      increased to prevent loss of precision.

      :type: int

   .. rubric:: Examples

   .. admonition:: Notes

      * If `del` is called, the underlying file will be removed immediately, even
        if the immediate flag is False.
      * The list of `keys()` will only be updated at the start of a context, the
        first time it is called, or after `flush()`.
      * One can only set an item if it is not already existing.  (Call del first
        if needed.)


   .. py:attribute:: data_dir


   .. py:attribute:: prefix
      :value: 'frame_'



   .. py:attribute:: checkpoint_prefix
      :value: 'check_'



   .. py:attribute:: checkpoints_to_retain
      :value: 1



   .. py:attribute:: sep
      :value: '_image_'



   .. py:property:: mode

      Return the mode, making context corrections for defaults.


   .. py:attribute:: immediate
      :value: None



   .. py:attribute:: mem_limit_bytes
      :value: None



   .. py:attribute:: decimal_precision
      :value: 4



   .. py:attribute:: _data


   .. py:attribute:: _context
      :value: False



   .. py:method:: __enter__()

      Enter the context.



   .. py:method:: __exit__(exc_type, exc_val, exc_tb)


   .. py:method:: isiterable(key)

      Return `True` if the key represents a list or tuple of subkeys.



   .. py:method:: Decimal(key, increase_precision=False)

      Return `Decimal(key)` rounded to `self.decimal_precision`.

      :param increase_precision: If `True`, then increase as needed `self.decimal_precision` to
                                 ensure accuracy of the keys, otherwise, raise `ValueError` if
                                 precision is lost.
      :type increase_precision: bool



   .. py:method:: key_to_ikey(key)

      Convert user-supplied key to internal format.



   .. py:method:: ikey_to_key(ikey)

      Convert internal key format to user format.



   .. py:method:: ikey_to_str(ikey)

      Convert internal key to string for filename.



   .. py:method:: str_to_ikey(key_string)

      Convert string to internal key, increasing `decimal_precision`
      if required.



   .. py:method:: __contains__(key)


   .. py:method:: ikeys(update=False)

      Return a list of available ikeys.

      :param update: If True, then reset the list of previously loaded files,
                     and reread data from disk.  If False, then only previously
                     detected frames and computed frames will be seen - any
                     additional frames (i.e. saved by another process) will not
                     be detected.
      :type update: bool



   .. py:method:: keys(update=False)

      Return a list of available keys.

      :param update: If True, then reset the list of previously loaded files,
                     and reread data from disk.  If False, then only previously
                     detected frames and computed frames will be seen - any
                     additional frames (i.e. saved by another process) will not
                     be detected.
      :type update: bool



   .. py:method:: __getitem__(key)


   .. py:method:: checkpoint(key, value)

      Save data to file, but as a checkpoint.



   .. py:method:: _is_checkpoint(ikey)

      Return True if key is saved as a checkpoint.



   .. py:method:: _convert_checkpoint(ikey)

      Convert a saved checkpoint to a real frame.

      Return True if if checkpoint file was converted.



   .. py:method:: __setitem__(key, value, checkpoint=False)


   .. py:method:: __delitem__(key, ikey=None)


   .. py:method:: flush()

      Write current data to disk.



   .. py:method:: get_mem_size()

      Return the size of the frames stored in memory in bytes.

      Note that this only calls `sys.getsizeof()` on each value of
      `self._data`; the actual size of a Frames instance will be slightly
      bigger.



   .. py:method:: limit_memory(update_files=True)

      Drop the oldest frames from memory to fit under the memory limit.



   .. py:property:: _prefixes

      Return a tuple of the possible filename prefixes.


   .. py:method:: _filename_to_ikey(filename)

      Extract the time key from the filename.



   .. py:property:: _files


   .. py:method:: _get_data_dir()

      Return `data_dir`, checking that it exists, is a directory, and making it if
      needed and 'w' in mode.



   .. py:method:: get_filename(key, checkpoint=False)

      Return the filename associated with key.



   .. py:method:: _get_filename(ikey, data_dir, checkpoint=False)

      Return the filename associated with key.



   .. py:method:: _save(ikey, value, data_dir=None, checkpoint=False)


   .. py:method:: _remove_checkpoints(checkpoints_to_retain=None)

      Remove old checkpoints in key order.



.. py:class:: GPUHelper

   Class for helping with GPU and other accelerator support.

   .. rubric:: Examples

   >>> class B(GPUHelper):
   ...     def asnumpy(self, result):
   ...         return result + ['from B.asnumpy']  # Simulate a custom asnumpy method

   >>> class A(GPUHelper):
   ...     def asnumpy(self, result):
   ...         return result + ['from A.asnumpy']  # Simulate a custom asnumpy method
   ...
   ...     @staticmethod
   ...     def get_list1_GPU():
   ...         "Return a list.  Non-GPU method will return an array."
   ...         return [1, 2]
   ...
   ...     def get_list2_GPU(self):
   ...         "Return a list.  Non-GPU will use self.asnumpy."
   ...         return [1, 2]
   ...
   ...     def get_list3_GPU(self, state):
   ...         "Return a list.  None-GPU will use state.asnumpy."
   ...         return [1, 2]
   >>> a = A()
   >>> a.get_list1_GPU()  # GPU method returns list (representing GPU array)
   [1, 2]
   >>> a.get_list2_GPU()
   [1, 2]
   >>> a.get_list3_GPU(state=B())
   [1, 2]
   >>> a.get_list1()  # Non GPU method returns an array.
   array([1, 2])
   >>> a.get_list2()  # Non GPU instance method uses A.asnumpy.
   [1, 2, 'from A.asnumpy']
   >>> a.get_list3(state=B())  # Non GPU instance method uses B.asnumpy.
   [1, 2, 'from B.asnumpy']
   >>> set(['get_list1', 'get_list2', 'get_list3']).issubset(dir(a))
   True


   .. py:method:: __init_subclass__()
      :classmethod:


      Prepare the class by defining all non-GPU/GPU pairs "*" and "*_GPU".



.. py:class:: AsNumpyMixin

   Bases: :py:obj:`gpe.mixins.StateMixin`, :py:obj:`GPUHelper`


   Mixin providing asnumpy method.


   .. py:attribute:: asnumpy


   .. py:method:: get_data_GPU()

      Partner to set_data().

      This allows access to the data via self.get_data() which is
      guaranteed to be a number array.



.. py:class:: StateWithExperimentMixin(experiment, **kw)

   Bases: :py:obj:`gpe.mixins.StateMixin`, :py:obj:`GPUHelper`


   Mixing to delegate to self.experiment.


   .. py:attribute:: experiment


   .. py:method:: get_Vext_GPU()


   .. py:method:: get_Vint_GPU()


   .. py:method:: get_Eint()


   .. py:method:: get_n_TF(V_TF)


   .. py:method:: get_ns_TF(Vs_TF)


