Exemplo n.º 1
0
    def displace(self):
        """
        Displaces the center of the distribution towards the optimized limit.
        """

        # Conditionally, before performing the optimization checks if
        # the forces are statistically significant by checking if the error
        # is smaller than the forces. When sobol sequences are used, since
        # the true error is not known, we use the "Monte Carlo standrard error"
        #  as a proxy.

        if self.precheck:

            # Calculates the force, the error and the
            # batch weights.
            f, f_err, batch_w = self.weighted_force()
            fnm = np.dot(self.dm.V.T, f)

            # Assumes error = standard error.
            fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2))
            fnm[self.z] = 0.0

            if np.all(np.abs(fnm) < fnm_err):
                info(
                    " @SCP: All the average force components are statistically insignificant.",
                    verbosity.medium,
                )
                info(" @SCP: Skipping the optimization step.",
                     verbosity.medium)
                return

        # Uses the displacement correction and a used defined scaling
        # factor to move in the direction of the force.

        elif self.dm.displace_mode == "sd":

            # Calculates the force, the error and the
            # batch weights.
            f, f_err, batch_w = self.weighted_force()

            # Calculates the Hessian..
            k = self.weighted_hessian()

            # Displaces.
            self.dm.beads.q += np.dot(self.dm.D, f) * self.dm.tau

            # Calculates the new dynamical matrix.
            self.dm.dynmatrix = np.dot(self.dm.isqM,
                                       np.dot((k + k.T) / 2.0, self.dm.isqM))

            # Applies acoustic sum rule to project out zero modes.
            # Applies a high pass filter to zero out low frequency modes.
            # Calculates the displacement correction matrix.
            self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix,
                                          self.dm.beads)
            self.apply_hpf()
            self.get_KnD()

            info(" @SCP: <f> =  %10.8f +/-  %10.8f: " % (f, f_err),
                 verbosity.medium)
            info(
                " @SCP: OPTMODE = sd : Using the displacement correlation as a preconditioner.",
                verbosity.medium,
            )

        # Uses the inverse Hessian to determine how much
        # to move in the direction of the force.

        if self.dm.displace_mode == "ik":

            # Calculates the force, the error and the
            # batch weights.
            f, f_err, batch_w = self.weighted_force()
            self.dm.beads.q += np.dot(self.dm.iK, f)

            # Calculates the new dynamical matrix.
            k = self.weighted_hessian()
            self.dm.dynmatrix = np.dot(self.dm.isqM,
                                       np.dot((k + k.T) / 2.0, self.dm.isqM))

            # Applies acoustic sum rule to project out zero modes.
            # Applies a high pass filter to zero out low frequency modes.
            # Calculates the displacement correction matrix.
            self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix,
                                          self.dm.beads)
            self.apply_hpf()
            self.get_KnD()

            info(" @SCP: <f> =  %10.8f +/-  %10.8f: " % (f, f_err),
                 verbosity.medium)
            info(
                " @SCP: OPTMODE = iK : Using the inverse Hessian as a preconditioner.",
                verbosity.medium,
            )

        # Same as iK but only moves along normal modes which have statistically
        # significant force components.

        elif self.dm.displace_mode == "nmik":

            # Calculates the force, the error and the
            # batch weights.
            f, f_err, batch_w = self.weighted_force()

            # Calculates the new dynamical matrix.
            k = self.weighted_hessian()
            self.dm.dynmatrix = np.dot(self.dm.isqM,
                                       np.dot((k + k.T) / 2.0, self.dm.isqM))

            # Applies acoustic sum rule to project out zero modes.
            # Applies a high pass filter to zero out low frequency modes.
            # Calculates the displacement correction matrix.
            self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix,
                                          self.dm.beads)
            self.apply_hpf()
            self.get_KnD()

            # Calculates the force along normal moces.
            # Zeros the forces along the known "zero modes".
            # Estimates the error.
            fnm = np.dot(self.dm.V.T, f)
            fnm[self.z] = 0.0
            fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2))

            # Zeros the displacement along "insignificant" normal modes.
            iKfnm = self.dm.iw2 * fnm
            iKfnm[np.abs(fnm) < fnm_err] = 0.0
            dqnm = iKfnm
            dqnm[self.z] = 0.0

            self.dm.beads.q += np.dot(self.dm.V, dqnm)

            # Calculates the new forces.
            f, f_err, batch_w = self.weighted_force()

            info(
                " @SCP: <f> =  %10.8f +/-  %10.8f: : number of batch weights > %10.8f =  %8d / %8d"
                % (
                    np.linalg.norm(f),
                    np.linalg.norm(f_err),
                    self.wthreshold,
                    sum(batch_w > self.wthreshold),
                    len(batch_w),
                ),
                verbosity.medium,
            )
            info(
                " @SCP: OPTMODE = nmiK : Using the inverse Hessian along statistically significant normal modes as a preconditioner.",
                verbosity.medium,
            )

        elif self.dm.displace_mode == "rnmik":

            scale_forces = 1.0 * self.dm.tau

            # Outer Optimization Loop
            while True:

                # Inner Optimization Loop
                w = 1.0

                while True:

                    # Calculates the force, the error and the
                    # batch weights.
                    f, f_err, batch_w = self.weighted_force()

                    # Saves the change in the weight associated with
                    # last batch.
                    w_old = w
                    w = batch_w[-1]
                    if np.absolute(w - w_old) < 1e-4:
                        scale_forces *= 1.1
                        # info(" @SCP: Increasing displacement by 10 %.")
                    elif np.absolute(w - w_old) > 5e-2:
                        scale_forces /= 1.1

                    # Computes the force along the normal modes.
                    fnm = np.dot(self.dm.V.T, f)
                    fnm[self.z] = 0.0
                    fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2))

                    # Breaks if all the forces are statistically insignificant.
                    if np.all(np.abs(fnm) < fnm_err):
                        info(
                            " @SCP: All forces are statistically insignificant.",
                            verbosity.medium,
                        )
                        info(
                            " @SCP: Exiting the inner optimization loop.",
                            verbosity.medium,
                        )
                        break
                    # or if the batch weights go to shit.
                    elif np.max(batch_w) < self.wthreshold:
                        info(" @SCP: Batch weights are small.",
                             verbosity.medium)
                        info(
                            " @SCP: Exiting the inner optimization loop.",
                            verbosity.medium,
                        )
                        break

                    # Calculates the displacement along the normal modes.
                    # Moves only if forces are statistically insignificant.
                    # Does not move along zero modes.
                    dqnm = self.dm.iw2 * fnm * scale_forces / self.dm.dof
                    dqnm[np.abs(fnm) < fnm_err] = 0.0
                    dqnm[self.z] = 0.0

                    self.dm.beads.q += np.dot(self.dm.V, dqnm)
                    info(
                        " @SCP: <f> =  %10.8f +/-  %10.8f : number of batch weights > %10.8f =  %8d / %8d : largest batch weight = %10.8e"
                        % (
                            np.linalg.norm(f),
                            np.linalg.norm(f_err),
                            self.wthreshold,
                            sum(batch_w > self.wthreshold),
                            len(batch_w),
                            batch_w[-1],
                        ),
                        verbosity.medium,
                    )

                # Calculates the hessian.
                # Applies acoustic sum rule to project out zero modes.
                # Applies a high pass filter to zero out low frequency modes.
                # Calculates the displacement correction matrix.
                k = self.weighted_hessian()
                self.dm.dynmatrix = np.dot(
                    self.dm.isqM, np.dot((k + k.T) / 2.0, self.dm.isqM))
                self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix,
                                              self.dm.beads)
                self.apply_hpf()
                self.get_KnD()

                # Stores the old forces.
                fnm_old = fnm

                # Recalculates the new forces in normal mode coordinates..
                f, f_err, batch_w = self.weighted_force()
                f_err = f_err
                fnm = np.dot(self.dm.V.T, f)
                fnm[self.z] = 0.0
                fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2))

                # Breaks if all the forces are statistically insignificant.
                if np.all(np.abs(fnm) < fnm_err):
                    info(
                        " @SCP: All forces are statistically insignificant.",
                        verbosity.medium,
                    )
                    info(" @SCP: Skipping the optimization step.",
                         verbosity.medium)
                    break
                # or if the batch weights have gone to shit.
                elif np.max(batch_w) < self.wthreshold:
                    info(" @SCP: Batch weights are small..", verbosity.medium)
                    info(" @SCP: Skipping the optimization step.",
                         verbosity.medium)
                    break

                if np.all(np.abs(fnm_old - fnm) < fnm_err):
                    info(" @SCP: All forces are converged.", verbosity.medium)
                    break
Exemplo n.º 2
0
    def reset(self):
        """
        Resets the variables for a new round of phonon evaluation.
        """
        self.dm.w = np.zeros(self.dm.dof)
        self.dm.iw = np.zeros(self.dm.dof)
        self.dm.iw2 = np.zeros(self.dm.dof)
        self.dm.avgPot = 0.0
        self.dm.avgForce = np.zeros((1, self.dm.dof))
        self.dm.avgHessian = np.zeros((self.dm.dof, self.dm.dof))
        self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix,
                                      self.dm.beads)
        self.apply_hpf()
        self.get_KnD()
        self.iD[self.dm.isc] = self.dm.iD.copy()
        self.q[self.dm.isc] = self.dm.beads.q.copy()
        self.dm.oldK = self.dm.K
        self.dm.imc = 1

        info("\n @SCP: Beginning SCP step no. %8d" % (self.dm.isc, ),
             verbosity.medium)

        # prints the force constant matrix.
        outfile = self.dm.output_maker.get_output(self.dm.prefix + ".K." +
                                                  str(self.dm.isc))
        np.savetxt(outfile, self.dm.K)
        outfile.close_stream()
        info(" @SCP: Saving the force constant matrix.", verbosity.medium)

        # prints the inverse displacement correlation matrix.
        outfile = self.dm.output_maker.get_output(self.dm.prefix + ".iD." +
                                                  str(self.dm.isc))
        np.savetxt(outfile, self.dm.iD)
        outfile.close_stream()
        info(
            " @SCP: Saving the inverse displacement correlation matrix.",
            verbosity.medium,
        )

        # prints the mean position.
        outfile = self.dm.output_maker.get_output(self.dm.prefix + ".q." +
                                                  str(self.dm.isc))
        np.savetxt(outfile, self.dm.beads.q)
        outfile.close_stream()
        info(" @SCP: Saving the mean position.", verbosity.medium)

        # prints the frequencies.
        outfile = self.dm.output_maker.get_output(self.dm.prefix + ".w." +
                                                  str(self.dm.isc))
        np.savetxt(outfile, self.dm.w)
        outfile.close_stream()
        info(" @SCP: Saving the frequencies.", verbosity.medium)

        # prints the potential energy at the mean position.
        outfile = self.dm.output_maker.get_output(self.dm.prefix + ".V0." +
                                                  str(self.dm.isc))
        np.savetxt(outfile, self.dm.forces.pots)
        outfile.close_stream()
        info(" @SCP: Saving the minimum potential.", verbosity.medium)

        if os.path.exists(self.dm.output_maker.prefix + "." + self.dm.prefix +
                          ".x." + str(self.dm.isc)):
            info(
                " @SCP: Loading %8d configurations from file." %
                (self.dm.max_steps, ),
                verbosity.medium,
            )
            self.x[self.dm.isc] = np.loadtxt(self.dm.output_maker.prefix +
                                             "." + self.dm.prefix + ".x." +
                                             str(self.dm.isc))
        else:
            info(
                " @SCP: Generating %8d new configurations to be sampled." %
                (self.dm.max_steps, ),
                verbosity.medium,
            )
            # Creates a list of configurations that are to be sampled.
            while self.dm.imc <= self.dm.max_steps:

                irng = (self.dm.isc) * self.dm.max_steps // 2 + (self.dm.imc +
                                                                 1) // 2
                x = self.dm.fginv(self.dm.random_sequence[irng])

                # picks the elements of the vector in a random order.
                # this introduces a degree of randomness in the sobol-like PRNGs
                x = x[self.dm.random_shuffle]

                # Transforms the "normal" random number and stores it.
                x = np.dot(self.dm.isqM, np.dot(self.dm.sqtD, x))
                self.x[self.dm.isc,
                       self.dm.imc - 1] = (self.dm.beads.q + x.T)[-1]
                self.dm.imc += 1

                # Performs an inversion to the displacement and samples another configuration.
                x = -x
                self.x[self.dm.isc,
                       self.dm.imc - 1] = (self.dm.beads.q + x.T)[-1]
                self.dm.imc += 1

        # Resets the number of MC steps to 1.
        self.dm.imc = 1
        info(
            " @SCP: Performing %8d new force evaluations." %
            (self.dm.max_steps, ),
            verbosity.medium,
        )