Пример #1
0
class PathToField(Solver):
    """PathToField is a solver that solves the control fields for a 
    given path of dipole moment projection.

    Class PathToField is used to solve a set of control fields that
    drive the dipole moment projection of the system of interest to 
    follow a given path. The system of interest by default is a rotor 
    (molecule.Rotor.)  

    Parameters
    ----------
    path_desired: numpy.array, shape=(n,2)
        A desired path of molecule dipole moment projection.

    dt: float, optional (default=1000)
        Difference of time between two adjacent time points. This is 
        path-specific and is currently calculated when a 
        dataContainer.DataContainer object is instantiated with a
        desired path.

    molecule: Molecule object, optional (default=Rotor)
        System of interest. Default to a Rotor molecule with a system 
        dimension of m=8 specified in constants.py.

    Attributes
    ----------
    molecule: Molecule object
        System of interest.

    path: numpy.array, shape=(n,2)
        Path specified.

    n: int
        Number of time points.

    dt: float
        Delta t between two adjacent time points.

    time: numpy.array, shape=(n,)
        Time vector in atomic units.

    """
    def __init__(self, path_desired, dt=1000, molecule=None):
        # Create a Rotor object as the system of interest if not
        # provided by the user
        if molecule is None:
            ## System of interest.
            ## Default value is a rotor (solver.molecule.Rotor)
            self.molecule = Rotor(const.m)
        else:
            self.molecule = rotor

        ## Path specified
        self.path = path_desired
        self._path_predicted = np.zeros_like(path_desired)
        ## Number of time points
        self.n = path_desired.shape[0]
        ## Delta t between two adjacent time points.
        self.dt = dt
        self._t_final = self.n * self.dt
        ## Time vector in unit of ?
        self.time = np.arange(self._t_final, step=self.dt, dtype=float)
        self._ddpath = np.stack((f.d2dt2(
            self.path[:, 0], self.dt), f.d2dt2(self.path[:, 1], self.dt)),
                                axis=1)

        # operators used only by private methods within class instance
        m = self.molecule.m
        self._op1 = (f.cosphi(m) + 4 * f.sinphi(m) @ f.ddphi(m) -
                     4 * f.cosphi(m) @ f.d2dphi2(m))
        self._op2 = (f.sinphi(m) - 4 * f.cosphi(m) @ f.ddphi(m) -
                     4 * f.sinphi(m) @ f.d2dphi2(m))
        self._cosphi2 = f.cosphi(m) @ f.cosphi(m)
        self._sinphi2 = f.sinphi(m) @ f.sinphi(m)
        self._cosphi_sinphi = f.cosphi(m) @ f.sinphi(m)
        self._sinphi_cosphi = f.sinphi(m) @ f.cosphi(m)

        #calc and set initial field, but not using molecule.update_field()
        field = self._get_field(0, real=True)
        self.molecule.set_field(field)

    def solve(self):
        """Calculate the control field required for each time step.
        """

        for j in tqdm.tqdm(range(1, self.n)):
            self.molecule.evolve(self.dt)
            field = self._get_field(j, real=True)
            self.molecule.update_field(field)

        # self._velidate()

    def export(self):
        """Export calculated time vector, fields, path, and states 
        as np.ndarray.

        Returns
        -------
        time: numpy.array, shape=(n,)
            Time vector based on dt. In unit of picoseconds.

        fields: numpy.array, shape=(n,2) 
            Control fields required for the path of interest. In 
            unit of V/angstrom.

        path: numpy.array, shape=(n,2)
            Resulting path based on the calculated fields.

        states: numpy.array, shape=(2m+1,n)
            State amplitudes of the system at every time point.

        """

        time = self.molecule.get_time_asarray()
        time = time * 2.418 * 10**(-17) * 10**12  #time in picoseconds

        states = self.molecule.get_states_asarray()

        fields = self.molecule.get_fields_asarray()
        field_const = 5.142 * 10**11 * 10**(-10)  #amplitude in V/angstrom
        fields = fields * field_const

        path = np.zeros((self.n, 2))
        oper_x = self.molecule.dipole_x
        oper_y = self.molecule.dipole_y
        states_list = self.molecule.history['state']
        for i in range(self.n):
            path[i, 0] = states_list[i].get_expt(oper_x).real
            path[i, 1] = states_list[i].get_expt(oper_y).real

        return time, fields, path, states

    def _get_field(self, j, real=False):
        """Calculate the required field for the next step.

        Parameters
        ----------
        j: int
            System is at the j-th time point.

        real: bool, optional (default=False)
            If True, force the returned value to be only the real 
            part of a complex number.

        Returns
        -------
        field: numpy.array, shape=(2,)
            An array of size 2 containing x- and y-component of the
            control field for the next step.

        """

        field = self._get_Ainv() @ self._get_b(j)
        if real:
            field = field.real
        return field.flatten()

    def _get_det(self):
        """Calculate determinant of matrix A"""
        state = self.molecule.state
        c = 4 * const.B**2 * const.mu**2 / const.hbar**4
        det = (c *
               (state.get_expt(self._sinphi2) * state.get_expt(self._cosphi2) -
                state.get_expt(self._sinphi_cosphi)**2))
        return det

    def _get_Ainv(self):
        """Calculate inverse of matrix A"""
        state = self.molecule.state
        c = 2 * const.B * const.mu / const.hbar**2
        a11 = c * state.get_expt(self._sinphi2)
        a12 = -c * state.get_expt(self._cosphi_sinphi)
        a21 = -c * state.get_expt(self._sinphi_cosphi)
        a22 = c * state.get_expt(self._cosphi2)
        det = self._get_det()
        A_inv = 1 / det * np.array([[a22, -a12], [-a21, a11]])
        return A_inv

    def _get_b(self, i):
        """Calculate b vector"""
        state = self.molecule.state
        c = const.B**2 / const.hbar**2
        b1 = self._ddpath[i, 0] + np.real(c * state.get_expt(self._op1))
        b2 = self._ddpath[i, 1] + np.real(c * state.get_expt(self._op2))
        return np.array([b1, b2])
Пример #2
0
class FieldToPath(Solver):
    """Calculate the resulting path from a given set of control fields.
    
    Class FieldToPath is used to solve the trajectory of molecule's 
    dipole moment projection for a given set of control fields in 
    time. The system of interest by default is a rotor 
    (molecule.Rotor.) 

    Parameters
    ----------
    fields: numpy.array, shape=(n,2)
        A prescribed set of control fields to apply to the molecule.

    dt: float, optional (default=1000)
        Difference of time between two adjacent time points. This is 
        path-specific and is currently calculated when a 
        dataContainer.DataContainer object is instantiated with a
        desired path.

    molecule: Molecule object, optional (default=Rotor)
        System of interest. Default to a Rotor molecule with a system 
        dimension of m=8 specified in constants.py.

    Attributes
    ----------
    molecule: Molecule object
        System of interest.

    n: int
        Number of time points.

    fields: numpy.array, shape=(n,2)
        Time-series of control field prescribed.

    dt: float
        Delta t between two adjacent time points.

    time: numpy.array, shape=(n,)
        Time vector in atomic units.

    path: numpy.array, shape=(n,2)
        Resulting path of molecule's dipole moment projection.

    """
    def __init__(self, fields, dt=1000, molecule=None):
        # Create a Rotor object as the system of interest if not
        # provided by the user
        if molecule is None:
            ## System of interest
            ## Default: a Rotor object with quantum number = const.m
            self.molecule = Rotor(const.m)
        else:
            self.molecule = rotor
        ## Number of time points
        self.n = fields.shape[0]
        ## An nx2 np.ndarray containing the given field.
        ## Each row is the x- and y-component of such field at a time point.
        self.fields = fields
        field_const = 5.142 * 10**11 * 10**(-10)  #amplitude in V/angstrom
        self.fields = self.fields / field_const  #back to atomic units
        self._fields_list = [
            self.fields[i, :].flatten() for i in range(self.n)
        ]
        ## The resulting path from the given fields.
        self.path = np.zeros((self.n, 2))
        ## Time difference between two adjacent time points.
        self.dt = dt
        self._t_final = self.dt * self.n
        ## Time vector containing all time points.
        self.time = np.arange(self._t_final, step=self.dt, dtype=float)
        self.time_in_ps = self.time * 2.418e-5  #time in picoseconds

        #get and set initial field, but not using molecule.update_field()
        field = self._fields_list[0]
        self.molecule.set_field(field)

    def solve(self):
        """Calculate path of rotor dipole moment projection from 
        given fields.

        """

        for i in tqdm.tqdm(range(1, self.n)):
            self.molecule.evolve(self.dt)
            self.molecule.set_field(self._fields_list[i])

    def export(self):
        """Export calculated time vector, fields, and states as 
        np.ndarray.

        Returns
        -------
        time: numpy.array, shape=(n,)
            Time vector based on dt. In unit of picoseconds.

        path: numpy.array, shape=(n,2)
            Resulting path of molecule's dipole moment projection.

        states: numpy.array, shape=(2m+1,n)
            State amplitudes of the system at every time point.
        
        """

        time = self.molecule.get_time_asarray()
        # time = self.time
        states = self.molecule.get_states_asarray()
        states_list = self.molecule.history['state']
        path = np.zeros((self.n, 2))
        oper_x = self.molecule.dipole_x
        oper_y = self.molecule.dipole_y
        for i in range(self.n):
            path[i, 0] = states_list[i].get_expt(oper_x).real
            path[i, 1] = states_list[i].get_expt(oper_y).real

        return time, path, states