Source code for WaveBlocksND.ObservablesHAWP

"""The WaveBlocks Project

Compute some observables like norm, kinetic and potential energy
of Hagedorn wavepackets.

@author: R. Bourquin
@copyright: Copyright (C) 2013, 2014, 2016 R. Bourquin
@license: Modified BSD License
"""

from functools import partial
from numpy import conjugate, squeeze, sum

from WaveBlocksND.Observables import Observables

__all__ = ["ObservablesHAWP"]


[docs]class ObservablesHAWP(Observables): r"""This class implements observable computation for Hagedorn wavepackets :math:`\Psi`. """ def __init__(self): r"""Initialize a new :py:class:`ObservablesHAWP` instance for observable computation of Hagedorn wavepackets. """ self._innerproduct = None self._gradient = None
[docs] def set_innerproduct(self, innerproduct): r"""Set the innerproduct. :param innerproduct: An innerproduct for computing the integrals. The inner product is only used for the computation of the potential energy :math:`\langle \Psi | V(x) | \Psi \rangle` but not for the kinetic energy. :type innerproduct: A :py:class:`InnerProduct` subclass instance. """ self._innerproduct = innerproduct
[docs] def set_gradient(self, gradient): r"""Set the gradient. :param gradient: A gradient operator. The gradient is only used for the computation of the kinetic energy :math:`\langle \Psi | T | \Psi \rangle`. :type gradient: A :py:class:`Gradient` subclass instance. """ self._gradient = gradient
[docs] def norm(self, wavepacket, *, component=None, summed=False): r"""Calculate the :math:`L^2` norm :math:`\langle \Psi | \Psi \rangle` of the wavepacket :math:`\Psi`. .. note:: This method is just a shortcut and calls the :py:meth:`HagedornWavepacketBase.norm` method of the given wavepacket. :param wavepacket: The wavepacket :math:`\Psi` of which we compute the norm. :type wavepacket: A :py:class:`HagedornWavepacketBase` subclass instance. :param component: The index :math:`i` of the component :math:`\Phi_i` whose norm is calculated. The default value is ``None`` which means to compute the norms of all :math:`N` components. :type component: int or ``None``. :param summed: Whether to sum up the norms :math:`\langle \Phi_i | \Phi_i \rangle` of the individual components :math:`\Phi_i`. :type summed: Boolean, default is ``False``. :return: The norm of :math:`\Psi` or the norm of :math:`\Phi_i` or a list with the :math:`N` norms of all components. Depending on the values of ``component`` and ``summed``. """ return wavepacket.norm(component=component, summed=summed)
[docs] def kinetic_energy(self, wavepacket, *, component=None, summed=False): r"""Compute the kinetic energy :math:`E_{\text{kin}} := \langle \Psi | T | \Psi \rangle` of the different components :math:`\Phi_i` of the wavepacket :math:`\Psi`. :param wavepacket: The wavepacket :math:`\Psi` of which we compute the kinetic energy. :type wavepacket: A :py:class:`HagedornWavepacketBase` subclass instance. :param component: The index :math:`i` of the component :math:`\Phi_i` whose kinetic energy we want to compute. If set to ``None`` the computation is performed for all :math:`N` components. :type component: Integer or ``None``. :param summed: Whether to sum up the kinetic energies :math:`E_i` of the individual components :math:`\Phi_i`. :type summed: Boolean, default is ``False``. :return: A list with the kinetic energies of the individual components or the overall kinetic energy of the wavepacket. (Depending on the optional arguments.) """ if component is None: N = wavepacket.get_number_components() components = range(N) else: components = [component] ekin = [] for n in components: Kprime, cnew = self._gradient.apply_gradient(wavepacket, component=n, as_packet=False) ekin.append(0.5 * sum(sum(conjugate(cnew) * cnew, axis=1), axis=0)) if summed is True: ekin = sum(ekin) elif component is not None: # Do not return a list for specific single components ekin = ekin[0] return ekin
[docs] def potential_energy(self, wavepacket, potential, *, component=None, summed=False): r"""Compute the potential energy :math:`E_{\text{pot}} := \langle \Psi | V(x) | \Psi \rangle` of the different components :math:`\Phi_i` of the wavepacket :math:`\Psi`. :param wavepacket: The wavepacket :math:`\Psi` of which we compute the potential energy. :type wavepacket: A :py:class:`HagedornWavepacketBase` subclass instance. :param potential: The potential :math:`V(x)`. (Actually, not the potential object itself but one of its ``V.evaluate_*`` methods.) :param component: The index :math:`i` of the component :math:`\Phi_i` whose potential energy we want to compute. If set to ``None`` the computation is performed for all :math:`N` components. :type component: Integer or ``None``. :param summed: Whether to sum up the potential energies :math:`E_i` of the individual components :math:`\Phi_i`. :type summed: Boolean, default is ``False``. :return: A list with the potential energies of the individual components or the overall potential energy of the wavepacket. (Depending on the optional arguments.) """ N = wavepacket.get_number_components() # TODO: Better take 'V' instead of 'V.evaluate_at' as argument? # f = partial(potential.evaluate_at, as_matrix=True) f = partial(potential, as_matrix=True) # Compute the brakets for each component if component is not None: epot = self._innerproduct.quadrature(wavepacket, operator=f, diag_component=component, eval_at_once=True) else: Q = self._innerproduct.quadrature(wavepacket, operator=f, eval_at_once=True) # And don't forget the summation in the matrix multiplication of 'operator' and 'ket' # TODO: Should this go inside the innerproduct? tmp = list(map(squeeze, Q)) epot = [sum(tmp[i * N:(i + 1) * N]) for i in range(N)] if summed is True: epot = sum(epot) return epot