Example #1
0
    def initialize_for_fourier(self, grid):
        # NOTE: This code is only temporary until we get the
        #       more versatile initial value specifications.

        # TODO: Make a specification of the IV setup in configurations
        Psi = []

        for packet_descr in self._parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)

            # Evaluate the
            X = grid.get_nodes(flat=True)
            values = packet.evaluate_at(X, prefactor=True)

            # Reshape values into hypercubic shape
            values = [val.reshape(grid.get_number_nodes()) for val in values]
            Psi.append(values)

        # TODO: Maybe sum up immediately instead of at the end to reduce memory usage
        Psi = reduce(add, Psi)

        # Pack the values in a WaveFunction instance
        WF = WaveFunction(self._parameters)
        WF.set_grid(grid)
        WF.set_values(Psi)

        return WF
Example #2
0
    def initialize_for_fourier(self, grid):
        # NOTE: This code is only temporary until we get the
        #       more versatile initial value specifications.

        # TODO: Make a specification of the IV setup in configurations
        Psi = []

        for packet_descr in self._parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)

            # Evaluate the
            X = grid.get_nodes(flat=True)
            values = packet.evaluate_at(X, prefactor=True)

            # Reshape values into hypercubic shape
            values = [val.reshape(grid.get_number_nodes()) for val in values]
            Psi.append(values)

        # TODO: Maybe sum up immediately instead of at the end to reduce memory usage
        Psi = reduce(add, Psi)

        # Pack the values in a WaveFunction instance
        WF = WaveFunction(self._parameters)
        WF.set_grid(grid)
        WF.set_values(Psi)

        return WF
def load_wavepacket(self, timestep, blockid=0, key=("q", "p", "Q", "P", "S", "adQ")):
    r"""Load a wavepacket at a given timestep and return a fully configured instance.
    This method just calls some other :py:class:`IOManager` methods in the correct
    order. It is included only for convenience and is not particularly efficient.

    :param timestep: The timestep :math:`n` at which we load the wavepacket.
    :param key: Specify which parameters to load. All are independent.
    :type key: Tuple of valid identifier strings that are ``q``, ``p``, ``Q``, ``P``, ``S`` and ``adQ``.
               Default is ``("q", "p", "Q", "P", "S", "adQ")``.
    :param blockid: The ID of the data block to operate on.
    :return: A :py:class:`HagedornWavepacket` instance.
    """
    from WaveBlocksND.BlockFactory import BlockFactory
    BF = BlockFactory()

    descr = self.load_wavepacket_description(blockid=blockid)
    HAWP = BF.create_wavepacket(descr)

    # Parameters and coefficients
    Pi = self.load_wavepacket_parameters(timestep=timestep, blockid=blockid, key=key)
    hashes, coeffs = self.load_wavepacket_coefficients(timestep=timestep, get_hashes=True, blockid=blockid)

    # Basis shapes
    Ks = []
    for ahash in hashes:
        K_descr = self.load_wavepacket_basisshapes(the_hash=ahash, blockid=blockid)
        Ks.append(BF.create_basis_shape(K_descr))

    # Configure the wavepacket
    HAWP.set_parameters(Pi, key=key)
    HAWP.set_basis_shapes(Ks)
    HAWP.set_coefficients(coeffs)

    return HAWP
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        BF = BlockFactory()

        # The potential instance
        potential = BF.create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instance
        # TODO: Attach the "leading_component to the hawp as codata
        self.propagator = BF.create_propagator(self.parameters, potential)

        # Create suitable wavepackets
        for packet_descr in self.parameters["initvals"]:
            packet = BF.create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_innerproduct())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, ))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_events()
        key = ("q", "p", "Q", "P", "S", "adQ")

        for i in range(npackets):
            bid = self.IOManager.create_block(
                dt=self.parameters.get("dt", 0.0))
            self.IOManager.add_inhomogwavepacket(self.parameters,
                                                 timeslots=slots,
                                                 blockid=bid,
                                                 key=key)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_inhomogwavepacket_description(
                packet.get_description())

        if self._tm.is_event(0):
            for packet in self.propagator.get_wavepackets():
                # Pi
                self.IOManager.save_inhomogwavepacket_parameters(
                    packet.get_parameters(key=key), timestep=0, key=key)
                # Basis shapes
                for shape in packet.get_basis_shapes():
                    self.IOManager.save_inhomogwavepacket_basisshapes(shape)
                # Coefficients
                self.IOManager.save_inhomogwavepacket_coefficients(
                    packet.get_coefficients(),
                    packet.get_basis_shapes(),
                    timestep=0)
Example #5
0
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        BF = BlockFactory()

        # The potential instance
        potential = BF.create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instance
        # TODO: Attach the "leading_component to the hawp as codata
        self.propagator = BF.create_propagator(self.parameters, potential)

        # Create suitable wavepackets
        chi = self.parameters["leading_component"]

        for packet_descr in self.parameters["initvals"]:
            packet = BF.create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_innerproduct())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, chi))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_events()
        key = ("q", "p", "Q", "P", "S", "adQ")

        for i in range(npackets):
            bid = self.IOManager.create_block(dt=self.parameters.get("dt", 0.0))
            self.IOManager.add_wavepacket(self.parameters, timeslots=slots, blockid=bid, key=key)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_wavepacket_description(packet.get_description())

        if self._tm.is_event(0):
            for packet in self.propagator.get_wavepackets():
                # Pi
                self.IOManager.save_wavepacket_parameters(packet.get_parameters(key=key), timestep=0, key=key)
                # Basis shapes
                for shape in packet.get_basis_shapes():
                    self.IOManager.save_wavepacket_basisshapes(shape)
                # Coefficients
                self.IOManager.save_wavepacket_coefficients(packet.get_coefficients(), packet.get_basis_shapes(), timestep=0)
Example #6
0
    def compute_parameters(self):
        """Compute some further parameters from the given ones.
        """
        # Perform the computation only if the basic values are available.
        # This is necessary to add flexibility and essentially read in *any*
        # parameter file with heavily incomplete value sets. (F.e. spawn configs)
        try:
            # The number of time steps we will perform.
            tm = TimeManager(self)
            self._params["nsteps"] = tm.compute_number_timesteps()
        except:
            pass

        if "potential" in self._params:
            # Ugly hack. Should improve handling of potential libraries
            Potential = BlockFactory().create_potential(self)
            # Number of components of $\Psi$
            self._params["ncomponents"] = Potential.get_number_components()
Example #7
0
    def compute_parameters(self):
        """Compute some further parameters from the given ones.
        """
        # Perform the computation only if the basic values are available.
        # This is necessary to add flexibility and essentially read in *any*
        # parameter file with heavily incomplete value sets. (F.e. spawn configs)
        try:
            # The number of time steps we will perform.
            tm = TimeManager(self)
            self._params["nsteps"] = tm.compute_number_timesteps()
        except:
            pass

        if "potential" in self._params:
            # Ugly hack. Should improve handling of potential libraries
            Potential = BlockFactory().create_potential(self)
            # Number of components of $\Psi$
            self._params["ncomponents"] = Potential.get_number_components()
def load_lincombhawp(self, timestep, blockid=0, key=("q", "p", "Q", "P", "S")):
    r"""Load a linear combination at a given timestep and return a fully configured
    :py:class:`LinearCombinationOfHAWPs` instance. This method just calls some other
    :py:class:`IOManager` methods in the correct order. It is included only for
    convenience and is not particularly efficient.

    :param timestep: The timestep :math:`n` we load the wavepacket.
    :param blockid: The ID of the data block to operate on.
    :return: A :py:class:`LinearCombinationOfHAWPs` instance.
    """
    from WaveBlocksND.LinearCombinationOfHAWPs import LinearCombinationOfHAWPs
    from WaveBlocksND.BlockFactory import BlockFactory
    BF = BlockFactory()

    descr = self.load_lincombhawp_description(blockid=blockid)

    # Empty linear combination
    J = self.load_lincombhawp_size(timestep=timestep, blockid=blockid)
    if J == 0:
        return None

    # A new and empty linear combination
    LC = LinearCombinationOfHAWPs(descr["dimension"],
                                  descr["ncomponents"],
                                  descr["eps"],
                                  number_packets=J)
    # Basis shapes
    K_descrs = self.load_lincombhawp_wavepacket_basisshapes(blockid=blockid)
    K = {ha: BF.create_basis_shape(de) for ha, de in K_descrs.items()}
    # Coefficients and basis shape hashes
    hashes, coeffs = self.load_lincombhawp_wavepacket_coefficients(
        timestep=timestep, get_hashes=True, blockid=blockid)
    Ks = [K[ha] for ha in np.squeeze(hashes)]
    LC.set_wavepacket_coefficients(coeffs, Ks)
    # Parameters
    Pi = self.load_lincombhawp_wavepacket_parameters(timestep=timestep,
                                                     blockid=blockid,
                                                     key=key)
    LC.set_wavepacket_parameters(Pi)
    # Cj
    Cj = self.load_lincombhawp_coefficients(timestep=timestep, blockid=blockid)
    LC.set_coefficients(Cj)

    return LC
Example #9
0
def load_wavepacket_inhomogeneous(iom, timestep, blockid=0):
    r"""Utility function to load an inhomogeneous
    wavepacket from an :py:class:`IOManager` instance.

    :param iom: The :py:class:`IOManager` instance from which to load data.
    :param timestep: Load the data corresponding to the given `timestep`.
    :param blockid: The `datablock` from which to read the data.
                    Default is the block with `blockid=0`.

    Note: This function is a pure utility function and is not efficient. It is
          built for interactive use only and should not be use in scripts.
    """
    if not iom.has_inhomogwavepacket(blockid=blockid):
        print("There is no (inhomogeneous) wavepacket to load in block {}".
              format(blockid))
        return

    BF = BlockFactory()

    wpd = iom.load_inhomogwavepacket_description(blockid=blockid)
    HAWP = BF.create_wavepacket(wpd)

    # Basis shapes
    BS_descr = iom.load_inhomogwavepacket_basisshapes(blockid=blockid)
    BS = {}
    for ahash, descr in BS_descr.items():
        BS[ahash] = BF.create_basis_shape(descr)

    KEY = ("q", "p", "Q", "P", "S", "adQ")
    # Retrieve simulation data
    params = iom.load_inhomogwavepacket_parameters(timestep=timestep,
                                                   blockid=blockid,
                                                   key=KEY)
    hashes, coeffs = iom.load_inhomogwavepacket_coefficients(timestep=timestep,
                                                             get_hashes=True,
                                                             blockid=blockid)
    # Configure the wavepacket
    HAWP.set_parameters(params, key=KEY)
    HAWP.set_basis_shapes([BS[int(ha)] for ha in hashes])
    HAWP.set_coefficients(coeffs)

    return HAWP
Example #10
0
def load_wavepacket(self,
                    timestep,
                    blockid=0,
                    key=("q", "p", "Q", "P", "S", "adQ")):
    r"""Load a wavepacket at a given timestep and return a fully configured instance.
    This method just calls some other :py:class:`IOManager` methods in the correct
    order. It is included only for convenience and is not particularly efficient.

    :param timestep: The timestep :math:`n` at which we load the wavepacket.
    :param key: Specify which parameters to load. All are independent.
    :type key: Tuple of valid identifier strings that are ``q``, ``p``, ``Q``, ``P``, ``S`` and ``adQ``.
               Default is ``("q", "p", "Q", "P", "S", "adQ")``.
    :param blockid: The ID of the data block to operate on.
    :return: A :py:class:`HagedornWavepacket` instance.
    """
    from WaveBlocksND.BlockFactory import BlockFactory
    BF = BlockFactory()

    descr = self.load_wavepacket_description(blockid=blockid)
    HAWP = BF.create_wavepacket(descr)

    # Parameters and coefficients
    Pi = self.load_wavepacket_parameters(timestep=timestep,
                                         blockid=blockid,
                                         key=key)
    hashes, coeffs = self.load_wavepacket_coefficients(timestep=timestep,
                                                       get_hashes=True,
                                                       blockid=blockid)

    # Basis shapes
    Ks = []
    for ahash in hashes:
        K_descr = self.load_wavepacket_basisshapes(the_hash=ahash,
                                                   blockid=blockid)
        Ks.append(BF.create_basis_shape(K_descr))

    # Configure the wavepacket
    HAWP.set_parameters(Pi, key=key)
    HAWP.set_basis_shapes(Ks)
    HAWP.set_coefficients(coeffs)

    return HAWP
Example #11
0
    def __init__(self, parameters, potential, packets=[]):
        r"""Initialize a new :py:class:`SemiclassicalPropagator` instance.

        :param parameters: A :py:class:`ParameterProvider` instance containing at least
                           the key ``dt`` for providing the timestep :math:`\tau`.
        :type parameters: A :py:class:`ParameterProvider` instance

        :param potential: The potential :math:`V(x)` the wavepacket :math:`\Psi` feels during the time propagation.
        :param packet: The initial homogeneous Hagedorn wavepacket :math:`\Psi` we propagate in time.
        :raises ValueError: If the number of components of :math:`\Psi` does not match
                            the number of energy levels :math:`\lambda_i` of the potential.
        """
        # The potential :math:`V(x)` the packet(s) feel.
        self._potential = potential

        # Number :math:`N` of components the wavepacket :math:`\Psi` has got.
        self._number_components = self._potential.get_number_components()
        self._dimension = self._potential.get_dimension()

        # A list of Hagedorn wavepackets :math:`\Psi` together with some codata
        # like the leading component :math:`\chi` which is the index of the eigenvalue
        # :math:`\lambda_\chi` of the potential :math:`V` that is responsible for
        # propagating the Hagedorn parameters.
        # TODO: We assume a list of (packet, leading_component) tuples here. Generalize tuples to dicts!
        # TODO: Do not use a list but better use a hashtable by packet IDs?
        self._packets = packets[:]

        # Keep a reference to the parameter provider instance
        self._parameters = parameters

        self._dt = self._parameters["dt"]

        # The relative mass scaling matrix M
        if "mass_scaling" in self._parameters:
            self._M = atleast_2d(self._parameters["mass_scaling"])
            assert self._M.shape == (self._dimension, self._dimension)
            self._Minv = inv(self._M)
        else:
            # No mass matrix given. Scale all masses equally
            self._M = eye(self._dimension)
            self._Minv = self._M

        # Decide about the matrix exponential algorithm to use
        self.__dict__["_matrix_exponential"] = BlockFactory(
        ).create_matrixexponential(parameters)

        # Precalculate the potential splittings needed
        self._prepare_potential()
        self._a, self._b = SplittingParameters().build(
            parameters["splitting_method"])
        self._innerorder = SplittingParameters().order(
            parameters["splitting_method"])
        self._A, self._B, self._Y, self._Z = ProcessingSplittingParameters(
        ).build("BCR764")
    def prepare_simulation(self):
        r"""Set up a Fourier propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        BF = BlockFactory()

        # The potential instance
        potential = BF.create_potential(self.parameters)

        # Compute the position space grid points
        grid = BF.create_grid(self.parameters)

        # Construct initial values
        I = Initializer(self.parameters)
        initialvalues = I.initialize_for_fourier(grid)

        # Transform the initial values to the canonical basis
        BT = BasisTransformationWF(potential)
        BT.set_grid(grid)
        BT.transform_to_canonical(initialvalues)

        # Finally create and initialize the propagator instance
        self.propagator = BF.create_propagator(self.parameters, potential,
                                               initialvalues)

        # Write some initial values to disk
        slots = self._tm.compute_number_events()

        self.IOManager.add_grid(self.parameters, blockid="global")
        self.IOManager.add_fourieroperators(self.parameters)
        self.IOManager.add_wavefunction(self.parameters, timeslots=slots)

        self.IOManager.save_grid(grid.get_nodes(flat=True), blockid="global")
        self.IOManager.save_fourieroperators(self.propagator.get_operators())
        if self._tm.is_event(0):
            self.IOManager.save_wavefunction(initialvalues.get_values(),
                                             timestep=0)
def load_lincombhawp(self, timestep, blockid=0, key=("q", "p", "Q", "P", "S")):
    r"""Load a linear combination at a given timestep and return a fully configured
    :py:class:`LinearCombinationOfHAWPs` instance. This method just calls some other
    :py:class:`IOManager` methods in the correct order. It is included only for
    convenience and is not particularly efficient.

    :param timestep: The timestep :math:`n` we load the wavepacket.
    :param blockid: The ID of the data block to operate on.
    :return: A :py:class:`LinearCombinationOfHAWPs` instance.
    """
    from WaveBlocksND.LinearCombinationOfHAWPs import LinearCombinationOfHAWPs
    from WaveBlocksND.BlockFactory import BlockFactory
    BF = BlockFactory()

    descr = self.load_lincombhawp_description(blockid=blockid)

    # Empty linear combination
    J = self.load_lincombhawp_size(timestep=timestep, blockid=blockid)
    if J == 0:
        return None

    # A new and empty linear combination
    LC = LinearCombinationOfHAWPs(descr["dimension"], descr["ncomponents"], descr["eps"], number_packets=J)
    # Basis shapes
    K_descrs = self.load_lincombhawp_wavepacket_basisshapes(blockid=blockid)
    K = {ha: BF.create_basis_shape(de) for ha, de in K_descrs.items()}
    # Coefficients and basis shape hashes
    hashes, coeffs = self.load_lincombhawp_wavepacket_coefficients(timestep=timestep, get_hashes=True, blockid=blockid)
    Ks = [K[ha] for ha in np.squeeze(hashes)]
    LC.set_wavepacket_coefficients(coeffs, Ks)
    # Parameters
    Pi = self.load_lincombhawp_wavepacket_parameters(timestep=timestep, blockid=blockid, key=key)
    LC.set_wavepacket_parameters(Pi)
    # Cj
    Cj = self.load_lincombhawp_coefficients(timestep=timestep, blockid=blockid)
    LC.set_coefficients(Cj)

    return LC
    def prepare_simulation(self):
        r"""Set up a Fourier propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        BF = BlockFactory()

        # The potential instance
        potential = BF.create_potential(self.parameters)

        # Compute the position space grid points
        grid = BF.create_grid(self.parameters)

        # Construct initial values
        I = Initializer(self.parameters)
        initialvalues = I.initialize_for_fourier(grid)

        # Transform the initial values to the canonical basis
        BT = BasisTransformationWF(potential)
        BT.set_grid(grid)
        BT.transform_to_canonical(initialvalues)

        # Finally create and initialize the propagator instance
        self.propagator = BF.create_propagator(self.parameters, potential, initialvalues)

        # Write some initial values to disk
        slots = self._tm.compute_number_events()

        self.IOManager.add_grid(self.parameters, blockid="global")
        self.IOManager.add_fourieroperators(self.parameters)
        self.IOManager.add_wavefunction(self.parameters, timeslots=slots)

        self.IOManager.save_grid(grid.get_nodes(flat=True), blockid="global")
        self.IOManager.save_fourieroperators(self.propagator.get_operators())
        if self._tm.is_event(0):
            self.IOManager.save_wavefunction(initialvalues.get_values(), timestep=0)
Example #15
0
def load_wavepacket_inhomogeneous(iom, timestep, blockid=0):
    r"""Utility function to load an inhomogeneous
    wavepacket from an :py:class:`IOManager` instance.

    :param iom: The :py:class:`IOManager` instance from which to load data.
    :param timestep: Load the data corresponding to the given `timestep`.
    :param blockid: The `datablock` from which to read the data.
                    Default is the block with `blockid=0`.

    Note: This function is a pure utility function and is not efficient. It is
          built for interactive use only and should not be use in scripts.
    """
    if not iom.has_inhomogwavepacket(blockid=blockid):
        print("There is no (inhomogeneous) wavepacket to load in block {}".format(blockid))
        return

    BF = BlockFactory()

    wpd = iom.load_inhomogwavepacket_description(blockid=blockid)
    HAWP = BF.create_wavepacket(wpd)

    # Basis shapes
    BS_descr = iom.load_inhomogwavepacket_basisshapes(blockid=blockid)
    BS = {}
    for ahash, descr in BS_descr.items():
        BS[ahash] = BF.create_basis_shape(descr)

    KEY = ("q", "p", "Q", "P", "S", "adQ")
    # Retrieve simulation data
    params = iom.load_inhomogwavepacket_parameters(timestep=timestep, blockid=blockid, key=KEY)
    hashes, coeffs = iom.load_inhomogwavepacket_coefficients(timestep=timestep, get_hashes=True, blockid=blockid)
    # Configure the wavepacket
    HAWP.set_parameters(params, key=KEY)
    HAWP.set_basis_shapes([BS[int(ha)] for ha in hashes])
    HAWP.set_coefficients(coeffs)

    return HAWP
Example #16
0
    def __init__(self, parameters, potential, packets=[]):
        r"""Initialize a new :py:class:`HagedornPropagatorInhomogeneous` instance.

        :param parameters: A :py:class:`ParameterProvider` instance containing at least
                           the key ``dt`` for providing the timestep :math:`\tau`.
        :type parameters: A :py:class:`ParameterProvider` instance

        :param potential: The potential :math:`V(x)` the wavepacket :math:`\Psi` feels during the time propagation.
        :param packet: The initial homogeneous Hagedorn wavepacket :math:`\Psi` we propagate in time.
        :raises ValueError: If the number of components of :math:`\Psi` does not match
                            the number of energy levels :math:`\lambda_i` of the potential.
        """
        # The potential :math:`V(x)` the packet(s) feel.
        self._potential = potential

        # Number :math:`N` of components the wavepacket :math:`\Psi` has got.
        self._number_components = self._potential.get_number_components()
        self._dimension = self._potential.get_dimension()

        # A list of Hagedorn wavepackets :math:`\Psi` together with some codata
        # At the moment we do not use any codata here.
        # TODO: Do not use a list but better use a hashtable by packet IDs?
        self._packets = packets[:]

        # Keep a reference to the parameter provider instance
        self._parameters = parameters

        self._dt = self._parameters["dt"]

        # The relative mass scaling matrix M
        if "mass_scaling" in self._parameters:
            self._M = atleast_2d(self._parameters["mass_scaling"])
            assert self._M.shape == (self._dimension, self._dimension)
            self._Minv = inv(self._M)
        else:
            # No mass matrix given. Scale all masses equally
            self._M = eye(self._dimension)
            self._Minv = self._M

        # Decide about the matrix exponential algorithm to use
        self.__dict__["_matrix_exponential"] = BlockFactory().create_matrixexponential(parameters)

        # Precalculate the potential splittings needed
        self._potential.calculate_local_quadratic()
        self._potential.calculate_local_remainder()
Example #17
0
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instance
        # TODO: Attach the "leading_component to the hawp as codata
        # TODO: Clean up this ugly if tree
        if self.parameters["propagator"] == "magnus_split":
            from WaveBlocksND.MagnusPropagator import MagnusPropagator
            self.propagator = MagnusPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "semiclassical":
            from WaveBlocksND.SemiclassicalPropagator import SemiclassicalPropagator
            self.propagator = SemiclassicalPropagator(self.parameters,
                                                      potential)
        elif self.parameters["propagator"] == "McL42sc":
            from WaveBlocksND.McL42scPropagator import McL42scPropagator
            self.propagator = McL42scPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "Pre764sc":
            from WaveBlocksND.Pre764scPropagator import Pre764scPropagator
            self.propagator = Pre764scPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "McL84sc":
            from WaveBlocksND.McL84scPropagator import McL84scPropagator
            self.propagator = McL84scPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "hagedorn":
            from WaveBlocksND.HagedornPropagator import HagedornPropagator
            self.propagator = HagedornPropagator(self.parameters, potential)
        else:
            raise NotImplementedError("Unknown propagator type: " +
                                      self.parameters["propagator"])

        # Create suitable wavepackets
        chi = self.parameters["leading_component"]

        for packet_descr in self.parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_innerproduct())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, chi))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_events()
        key = ("q", "p", "Q", "P", "S", "adQ")

        for i in range(npackets):
            bid = self.IOManager.create_block()
            self.IOManager.add_wavepacket(self.parameters,
                                          timeslots=slots,
                                          blockid=bid,
                                          key=key)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_wavepacket_description(
                packet.get_description())

        if self._tm.is_event(0):
            for packet in self.propagator.get_wavepackets():
                # Pi
                self.IOManager.save_wavepacket_parameters(
                    packet.get_parameters(key=key), timestep=0, key=key)
                # Basis shapes
                for shape in packet.get_basis_shapes():
                    self.IOManager.save_wavepacket_basisshapes(shape)
                # Coefficients
                self.IOManager.save_wavepacket_coefficients(
                    packet.get_coefficients(),
                    packet.get_basis_shapes(),
                    timestep=0)
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instance
        # TODO: Attach the "leading_component to the hawp as codata
        # TODO: Clean up this ugly if tree
        if self.parameters["propagator"] == "magnus_split":
            from WaveBlocksND.MagnusPropagator import MagnusPropagator
            self.propagator = MagnusPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "semiclassical":
            from WaveBlocksND.SemiclassicalPropagator import SemiclassicalPropagator
            self.propagator = SemiclassicalPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "McL42sc":
            from WaveBlocksND.McL42scPropagator import McL42scPropagator
            self.propagator = McL42scPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "Pre764sc":
            from WaveBlocksND.Pre764scPropagator import Pre764scPropagator
            self.propagator = Pre764scPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "McL84sc":
            from WaveBlocksND.McL84scPropagator import McL84scPropagator
            self.propagator = McL84scPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "hagedorn":
            from WaveBlocksND.HagedornPropagator import HagedornPropagator
            self.propagator = HagedornPropagator(self.parameters, potential)
        else:
            raise NotImplementedError("Unknown propagator type: " + self.parameters["propagator"])

        # Create suitable wavepackets
        chi = self.parameters["leading_component"]

        for packet_descr in self.parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_innerproduct())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, chi))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_events()
        key = ("q", "p", "Q", "P", "S", "adQ")

        for i in range(npackets):
            bid = self.IOManager.create_block()
            self.IOManager.add_wavepacket(self.parameters, timeslots=slots, blockid=bid, key=key)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_wavepacket_description(packet.get_description())

        if self._tm.is_event(0):
            for packet in self.propagator.get_wavepackets():
                # Pi
                self.IOManager.save_wavepacket_parameters(packet.get_parameters(key=key), timestep=0, key=key)
                # Basis shapes
                for shape in packet.get_basis_shapes():
                    self.IOManager.save_wavepacket_basisshapes(shape)
                # Coefficients
                self.IOManager.save_wavepacket_coefficients(packet.get_coefficients(), packet.get_basis_shapes(), timestep=0)