Source code for SpawnNonAdiabaticPropagator

"""The WaveBlocks Project

This file contains a simple spawning propagator class
for wavepackets and spawning in the non-adiabatic case.

@author: R. Bourquin
@copyright: Copyright (C) 2010, 2011, 2012 R. Bourquin
@license: Modified BSD License
"""

from Propagator import Propagator
from HagedornWavepacket import HagedornWavepacket
from MatrixExponentialFactory import MatrixExponentialFactory
from NonAdiabaticSpawner import NonAdiabaticSpawner
from SpawnConditionFactory import SpawnConditionFactory as SCF


[docs]class SpawnNonAdiabaticPropagator(Propagator): r""" This class can numerically propagate given initial values :math:`\Psi` in a potential :math:`V(x)`. The propagation is done for several given homogeneous Hagedorn wavepackets neglecting interaction. """ def __init__(self, propagator, potential, packet, leading_component, parameters): r""" Initialize a new :py:class:SpawnNonAdiabaticPropagator instance. :param propagator: The propagator used for time propagation. :param potential: The potential the wavepacket :math:`\Psi` feels during the time propagation. :param packet: The initial homogeneous Hagedorn wavepacket we propagate in time. :param leading_component: The leading component index :math:`\chi`. :raises ValueError: If the number of components of :math:`\Psi` does not match the number of energy levels :math:`\lambda_i` of the potential. """ if packet.get_number_components() != potential.get_number_components(): raise ValueError("Wave packet does not match to the given potential!") #: The potential :math:`V(x)` the packet(s) feel. self.potential = potential #: The number :math:`N` of components the wavepacket :math:`\Psi` has got. self.number_components = self.potential.get_number_components() #: The list of :py:class:`HagedornWavepacket` taking part in the simulation. self.packets = [ (packet,leading_component) ] # Cache some parameter values for efficiency self.parameters = parameters # The propagator used for time propagation self.propagator = propagator #: The condition which determines when to spawn. oracle = SCF().get_condition(parameters) # Setup the environment for the spawning condition. self.spawn_condition = oracle(self.parameters, self) # Decide about the matrix exponential algorithm to use self.__dict__["matrix_exponential"] = MatrixExponentialFactory().get_matrixexponential(parameters) # Precalculate the potential splitting self.potential.calculate_local_quadratic(diagonal_component=leading_component) self.potential.calculate_local_remainder(diagonal_component=leading_component) def __str__(self): r""" Prepare a printable string representing the :py:class:`SpawnNonAdiabaticPropagator` instance. """ return "Prapagation and spawning in the adabatic case."
[docs] def get_number_components(self): r""" :return: The number :math:`N` of components :math:`\Phi_i` of :math:`\Psi`. """ return self.number_components
[docs] def get_potential(self): r""" :return: The :py:class:`MatrixPotential` instance used for time propagation. """ return self.potential
[docs] def get_number_packets(self): r""" Get the number of packets :math:`\Psi_n` taking part in the simulation. :return: The number of packets currently taking part in the simulation. """ return len(self.packets)
[docs] def get_wavepackets(self, packet=None): r""" Retrieve the wavepackets taking part in the simulation. :param packet: The number of a single packet that is to be returned. :type packet: Integer :return: A list of :py:class:`HagedornWavepacket` instances that represents the current wavepackets. """ if packet is None: return [ p[0] for p in self.packets ] else: return self.packets[packet][0]
[docs] def propagate(self, time): r""" Given the wavepacket :math:`\Psi` at time :math:`t` compute the propagated wavepacket at time :math:`t + \tau`. We perform exactly one timestep :math:`\tau` here. At every timestep we check the spawning condition. """ # Make time accessible for spawn condition testers self.time = time # Perform spawning in necessary todo = self.should_spwan() for info in todo: self.spawn(info) # Propagate all packets self.propagator.set_wavepackets(self.packets) self.propagator.propagate()
[docs] def should_spwan(self): r""" Check if there is a reason to spawn a new wavepacket. """ components = range(self.number_components) spawn_todo = [] # What do we have to do now? # For each packet in the simulation # For all its components except the leading one # Check if we should spawn on this component for packet, leading_chi in self.packets: P = packet.clone(keepid=True) P.project_to_eigen(self.potential) for component in [ c for c in components if c != leading_chi ]: # Spawn condition fulfilled? should_spawn = self.spawn_condition.check_condition(P, component, self) if should_spawn: print(" Spawn condition fulfilled for component "+str(component)+" of packet with ID "+str(packet.get_id())+".") spawn_todo.append((packet, component)) # return structure is [ (packet, component), ...] return spawn_todo
[docs] def spawn(self, info): r""" Really spawn the new wavepackets :math:`\tilde{\Psi}`. This method appends the new :py:class:`HagedornWavepacket` instances to the list :py:attr:`packets` of packets. """ # Transform the packet to the eigenbasis where spawning has to happen WP, component = info WP.project_to_eigen(self.potential) # Prepare the potential (this functions are idempotent) self.potential.calculate_local_quadratic(diagonal_component=component) self.potential.calculate_local_remainder(diagonal_component=component) # Initialize an empty wavepacket for spawning SWP = HagedornWavepacket(self.parameters) SWP.set_quadrature(None) # Initialize a Spawner NAS = NonAdiabaticSpawner(self.parameters) # Estimate the parameter set Pi Pi = NAS.estimate_parameters(WP, component=component) # Spawn a new packet if Pi is not None: SWP.set_parameters(Pi) # Project the coefficients from mother to child NAS.project_coefficients(WP, SWP, component=component) # Transform both packets back to the canonical basis where propagation happens WP.project_to_canonical(self.potential) SWP.project_to_canonical(self.potential) print(" Spawned a new wavepacket with ID "+str(SWP.get_id())+".") # Append the spawned packet to the world self.packets.append((SWP,component))