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)
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)
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
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
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 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
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()
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)