Source code for WaveBlocksND.ObservablesMixedHAWP

"""The WaveBlocks Project

Compute some observables like norm, kinetic and potential energy
of Hagedorn wavepackets. This class implements the mixed case
where the bra does not equal the ket.

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

from functools import partial
from numpy import squeeze, sum

from WaveBlocksND.Observables import Observables

__all__ = ["ObservablesMixedHAWP"]


[docs]class ObservablesMixedHAWP(Observables): r"""This class implements the mixed case observable computation :math:`\langle \Psi | \cdot | \Psi^{\prime} \rangle` for Hagedorn wavepackets :math:`\Psi` where the bra :math:`\Psi` does not equal the ket :math:`\Psi^{\prime}`. """ def __init__(self, *, innerproduct=None, gradient=None): r"""Initialize a new :py:class:`ObservablesMixedHAWP` 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 inner product for computing the integrals. The inner product is used for the computation of all brakets :math:`\langle \Psi | \cdot | \Psi^{\prime} \rangle`. :type innerproduct: A :py:class:`InnerProduct` subclass instance. .. note:: Make sure to use an inhomogeneous inner product here. """ 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^{\prime} \rangle`. :type gradient: A :py:class:`Gradient` subclass instance. """ self._gradient = gradient
[docs] def overlap(self, pacbra, packet, *, component=None, summed=False): r"""Calculate the overlap :math:`\langle \Psi | \Psi^{\prime} \rangle` of the wavepackets :math:`\Psi` and :math:`\Psi^{\prime}`. :param pacbra: The wavepacket :math:`\Psi` which takes part in the overlap integral. :type pacbra: A :py:class:`HagedornWavepacketBase` subclass instance. :param packet: The wavepacket :math:`\Psi^{\prime}` which takes part in the overlap integral. :type packet: A :py:class:`HagedornWavepacketBase` subclass instance. :param component: The index :math:`i` of the components :math:`\Phi_i` of :math:`\Psi` and :math:`\Phi_i^{\prime}` of :math:`\Psi^{\prime}` whose overlap is computed. The default value is ``None`` which means to compute the overlaps with all :math:`N` components involved. :type component: Integer or ``None``. :param summed: Whether to sum up the overlaps :math:`\langle \Phi_i | \Phi_i^{\prime} \rangle` of the individual components :math:`\Phi_i` and :math:`\Phi_i^{\prime}`. :type summed: Boolean, default is ``False``. :return: The overlap of :math:`\Psi` with :math:`\Psi^{\prime}` or the overlap of :math:`\Phi_i` with :math:`\Phi_i^{\prime}` or a list with the :math:`N` overlaps of all components. (Depending on the optional arguments.) """ return self._innerproduct.quadrature(pacbra, packet, diag_component=component, diagonal=True, summed=summed)
[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`. :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 computed. 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 optional arguments.) .. note:: This method just redirects to a call to :py:meth:`HagedornWavepacketBase.norm`. """ return wavepacket.norm(component=component, summed=summed)
[docs] def kinetic_overlap_energy(self, pacbra, packet, *, component=None, summed=False): r"""Compute the kinetic energy overlap :math:`\langle \Psi | T | \Psi^{\prime} \rangle` of the different components :math:`\Phi_i` and :math:`\Phi_i^{\prime}` of the wavepackets :math:`\Psi` and :math:`\Psi^{\prime}`. :param pacbra: The wavepacket :math:`\Psi` which takes part in the kinetic energy integral. :type pacbra: A :py:class:`HagedornWavepacketBase` subclass instance. :param packet: The wavepacket :math:`\Psi^{\prime}` which takes part in the kinetic energy integral. :type packet: A :py:class:`HagedornWavepacketBase` subclass instance. :param component: The index :math:`i` of the components :math:`\Phi_i` of :math:`\Psi` and :math:`\Phi_i^{\prime}` of :math:`\Psi^{\prime}` which take part in the kinetic energy integral. If set to ``None`` the computation is performed for all :math:`N` components of :math:`\Psi` and :math:`\Psi^{\prime}`. :type component: Integer or ``None``. :param summed: Whether to sum up the kinetic energies :math:`E_i` of the individual components :math:`\Phi_i` and :math:`\Phi_i^{\prime}`. :type summed: Boolean, default is ``False``. :return: A list of the kinetic energy overlap integrals of the individual components or the overall kinetic energy overlap of the wavepackets. (Depending on the optional arguments.) """ Nbra = pacbra.get_number_components() Nket = packet.get_number_components() if not Nbra == Nket: # TODO: Drop this requirement, should be easy when zip(...) exhausts raise ValueError("Number of components in bra (%d) and ket (%d) differs!" % (Nbra, Nket)) if component is None: components = range(Nbra) else: components = [component] ekin = [] for n in components: gradpacbra = self._gradient.apply_gradient(pacbra, component=n) gradpacket = self._gradient.apply_gradient(packet, component=n) Q = [self._innerproduct.quadrature(gpb, gpk, diag_component=n) for gpb, gpk in zip(gradpacbra, gradpacket)] ekin.append(0.5 * sum(Q)) 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 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 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 of the kinetic energies of the individual components or the overall kinetic energy of the wavepacket. (Depending on the optional arguments.) .. note:: This method just expands to a call of the :py:meth:`ObservablesMixedHAWP.kinetic_overlap_energy` method. Better use :py:meth:`ObservablesHAWP.kinetic_energy`. """ return self.kinetic_overlap_energy(wavepacket, wavepacket, component=component, summed=summed)
[docs] def potential_overlap_energy(self, pacbra, packet, potential, *, component=None, summed=False): r"""Compute the potential energy overlap :math:`\langle \Psi | V(x) | \Psi^{\prime} \rangle` of the different components :math:`\Phi_i` and :math:`\Phi_i^{\prime}` of the wavepackets :math:`\Psi` and :math:`\Psi^{\prime}`. :param pacbra: The wavepacket :math:`\Psi` which takes part in the potential energy integral. :type pacbra: A :py:class:`HagedornWavepacketBase` subclass instance. :param packet: The wavepacket :math:`\Psi^{\prime}` which takes part in the potential energy integral. :type packet: 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 components :math:`\Phi_i` of :math:`\Psi` and :math:`\Phi_i^{\prime}` of :math:`\Psi^{\prime}` which take part in the potential energy integral. If set to ``None`` the computation is performed for all :math:`N` components of :math:`\Psi` and :math:`\Psi^{\prime}`. :type component: Integer or ``None``. :param summed: Whether to sum up the potential energies :math:`E_i` of the individual components :math:`\Phi_i` and :math:`\Phi_i^{\prime}`. :type summed: Boolean, default is ``False``. :return: A list of the potential energy overlap integrals of the individual components or the overall potential energy overlap of the wavepackets. (Depending on the optional arguments.) """ Nbra = pacbra.get_number_components() Nket = packet.get_number_components() if not Nbra == Nket: # TODO: Drop this requirement, should be easy when zip(...) exhausts raise ValueError("Number of components in bra (%d) and ket (%d) differs!" % (Nbra, Nket)) # 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: Q = self._innerproduct.quadrature(pacbra, packet, operator=f, diag_component=component, eval_at_once=True) Q = [squeeze(Q)] else: Q = self._innerproduct.quadrature(pacbra, packet, operator=f, eval_at_once=True) Q = list(map(squeeze, Q)) # And don't forget the summation in the matrix multiplication of 'operator' and 'ket' # TODO: Should this go inside the innerproduct? epot = [sum(Q[i * Nket:(i + 1) * Nket]) for i in range(Nbra)] if summed is True: epot = sum(epot) elif component is not None: # Do not return a list for specific single components epot = epot[0] return epot
[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 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 of the potential energies of the individual components or the overall potential energy of the wavepacket. (Depending on the optional arguments.) .. note:: This method just expands to a call of the :py:meth:`ObservablesMixedHAWP.potential_overlap_energy` method. Better use :py:meth:`ObservablesHAWP.potential_energy`. """ return self.potential_overlap_energy(wavepacket, wavepacket, potential, component=component, summed=summed)