Beispiel #1
0
def _create_table(directory,
                  intcor="average.ic",
                  parmfile="fluctmatch.dist.prm",
                  tbltype="Kb",
                  verbose=False):
    if path.isdir(directory):
        if verbose:
            print("Reading directory {}".format(directory))
        with reader(path.join(directory, intcor)) as ic_file:
            if verbose:
                print("    Processing {}...".format(
                    path.join(directory, intcor)))
            ic_table = ic_file.read()
            ic_table.set_index(_header, inplace=True)
        with reader(path.join(directory, parmfile)) as prm_file:
            if verbose:
                print("    Processing {}...".format(
                    path.join(directory, parmfile)))
            prm_table = prm_file.read()["BONDS"].set_index(_header)
        table = pd.concat([ic_table, prm_table], axis=1)
        table.reset_index(inplace=True)
        table = table.set_index(_index["general"])[tbltype].to_frame()
        table.columns = [
            path.basename(directory),
        ]
        return table
    def trim_get_new_bondlist_write_prm(self, atomfluc_diff, **kwargs):

        # READ fixed and dynamic parameter files and initial ic files
        params = dict()
        with reader(self.filenames["fixed_prm"]) as fixed:
            params.update(fixed.read())

        dyn_params = dict()
        with reader(self.filenames["dynamic_prm"]) as dynamic:
            dyn_params.update(dynamic.read())

        with reader(self.filenames["init_fluct_ic"]) as icfile:
            std_bonds = icfile.read().set_index(self.pair_idx)

        with reader(self.filenames["init_avg_ic"]) as avgfile:
            avg_bonds = avgfile.read().set_index(self.pair_idx)

        # Get the bonds to be trimmed from fixed parameter file
        params["BONDS"].set_index(self.pair_idx, inplace=True)
        logger.info(f"Before trimming NBONDS: {params['BONDS'].shape}")
        count = 0
        bonds2trim = []
        for i in params["BONDS"].index.values:
            if atomfluc_diff.loc[
                    i[0]] > self.rmsf_cutoff and atomfluc_diff.loc[
                        i[1]] > self.rmsf_cutoff:
                if params["BONDS"].loc[i, 'Kb'] > self.kb_cutoff and params[
                        "BONDS"].loc[i, 'b0'] > self.dist_cutoff:
                    resI = self.universe.atoms.select_atoms(
                        f"type {i[0]}").resids[0]
                    resJ = self.universe.atoms.select_atoms(
                        f"type {i[1]}").resids[0]
                    if resI > resJ + 2 or resJ > resI + 2:
                        bonds2trim.append(i)
                        count += 1
        logger.info(f"Totally {count} bonds found")
        params["BONDS"].drop(bonds2trim, inplace=True)
        logger.info(f"After trimming NBONDS: {params['BONDS'].shape}")
        params["BONDS"].reset_index(inplace=True)

        # Write both fixed and dynamics
        with mda.Writer(self.write_filenames["fixed_prm"],
                        **kwargs) as fixed_param:
            fixed_param.write(params)
        new_bondlist = params["BONDS"].set_index(self.pair_idx).index.values

        # Make NBONDS in dynamic parameters to match fixed parameter NBONDS
        # as the # of bonds selected from parameters and dynamic_parameter
        # could be diffrent due to its update average bond details in every iteration of FM

        dyn_params["BONDS"].set_index(self.pair_idx, inplace=True)
        dyn_params["BONDS"] = dyn_params["BONDS"].loc[new_bondlist]
        dyn_params["BONDS"].reset_index(inplace=True)
        assert dyn_params['BONDS'].shape[0] == params['BONDS'].shape[
            0], "Shape Mismatch."

        with mda.Writer(self.write_filenames["dynamic_prm"],
                        **kwargs) as dynamic_param:
            dynamic_param.write(dyn_params)
        """ for matching the shape of initial and current parameter files for force constant calculation, 
            the atom pairs trimmed in parameter files should also be trimmed in ic files """

        cols = np.asarray([
            "segidI", "resI", "I", "segidJ", "resJ", "J", "segidK", "resK",
            "K", "segidL", "resL", "L", "r_IJ", "T_IJK", "P_IJKL", "T_JKL",
            "r_KL"
        ])
        std_bonds = std_bonds.loc[new_bondlist]
        std_bonds.reset_index(inplace=True)
        std_bonds = std_bonds[cols]
        avg_bonds = avg_bonds.loc[new_bondlist]
        avg_bonds.reset_index(inplace=True)
        avg_bonds = avg_bonds[cols]
        with mda.Writer(self.write_filenames["init_fluct_ic"],
                        **kwargs) as fluct_init:
            fluct_init.write(std_bonds)
        with mda.Writer(self.write_filenames["init_avg_ic"],
                        **kwargs) as avg_init:
            avg_init.write(avg_bonds)

        return new_bondlist, bonds2trim
    def run(self, nma_exec=None, tol=1.e-3, n_cycles=300, low_bound=0.):
        """Perform a self-consistent fluctuation matching.

        Parameters
        ----------
        nma_exec : str
            executable file for normal mode analysis
        tol : float, optional
            fluct difference tolerance
        n_cycles : int, optional
            number of fluctuation matching cycles
        low_bound  : float, optional
            lowest Kb values to reduce noise
        """
        # Find CHARMM executable
        charmm_exec = (os.environ.get("CHARMMEXEC", util.which("charmm"))
                       if nma_exec is None else nma_exec)
        if charmm_exec is None:
            logger.exception(
                "Please set CHARMMEXEC with the location of your CHARMM "
                "executable file or add the charmm path to your PATH "
                "environment.")
            raise_with_traceback(
                OSError(
                    "Please set CHARMMEXEC with the location of your CHARMM "
                    "executable file or add the charmm path to your PATH "
                    "environment."))

        # Read the parameters
        if not self.parameters:
            try:
                self.initialize(nma_exec, restart=True)
            except IOError:
                raise_with_traceback(
                    (IOError("Some files are missing. Unable to restart.")))

        # Write CHARMM input file.
        if not path.exists(self.filenames["charmm_input"]):
            version = self.kwargs.get("charmm_version", 41)
            dimension = ("dimension chsize 1000000" if version >= 36 else "")
            with open(self.filenames["charmm_input"],
                      mode="wb") as charmm_file:
                logger.info("Writing CHARMM input file.")
                charmm_inp = charmm_nma.nma.format(
                    temperature=self.temperature,
                    flex="flex" if version else "",
                    version=version,
                    dimension=dimension,
                    **self.filenames)
                charmm_inp = textwrap.dedent(charmm_inp[1:])
                charmm_file.write(charmm_inp.encode())

        # Set the indices for the parameter tables.
        self.target["BONDS"].set_index(self.bond_def, inplace=True)
        bond_values = self.target["BONDS"].columns

        # Check for restart.
        try:
            if os.stat(self.filenames["error_data"]).st_size > 0:
                with open(self.filenames["error_data"], "rb") as data:
                    error_info = pd.read_csv(data,
                                             header=0,
                                             skipinitialspace=True,
                                             delim_whitespace=True)
                    if not error_info.empty:
                        self.error["step"] = error_info["step"].values[-1]
            else:
                raise FileNotFoundError
        except (FileNotFoundError, OSError):
            with open(self.filenames["error_data"], "wb") as data:
                np.savetxt(
                    data,
                    [
                        self.error_hdr,
                    ],
                    fmt=native_str("%15s"),  # Nix
                    delimiter=native_str(""))
        self.error["step"] += 1

        # Initiate an all true index data, for preserving bond convergence
        if not self.restart:
            temp = ~self.target["BONDS"]["Kb"].isna()
            temp = temp.reset_index()
            self.converge_bnd_list = temp.iloc[:, 2]

        # Start self-consistent iteration for Fluctuation Matching
        # Run simulation
        logger.info(
            f"Starting fluctuation matching--{n_cycles} iterations to run")
        if low_bound != 0.:
            logger.info(
                f"Lower bound after 75% iteration is set to {low_bound}")
        st = time.time()
        fdiff = []
        for i in range(n_cycles):
            ct = time.time()
            self.error["step"] = i + 1
            with open(self.filenames["charmm_log"], "w") as log_file:
                subprocess.check_call(
                    [charmm_exec, "-i", self.filenames["charmm_input"]],
                    stdout=log_file,
                    stderr=subprocess.STDOUT,
                )
            self.dynamic_params["BONDS"].set_index(self.bond_def, inplace=True)
            self.parameters["BONDS"].set_index(self.bond_def, inplace=True)

            # Read the average bond distance.
            with reader(self.filenames["avg_ic"]) as icavg:
                avg_ic = icavg.read().set_index(self.bond_def)["r_IJ"]

            # Read the bond fluctuations.
            with reader(self.filenames["fluct_ic"]) as icfluct:
                fluct_ic = icfluct.read().set_index(self.bond_def)["r_IJ"]

            vib_ic = pd.concat([fluct_ic, avg_ic], axis=1)
            vib_ic.columns = bond_values
            logger.info(f"Checking for bondlist convergence")
            fluct_diff = np.abs(vib_ic[bond_values[0]] -
                                self.target["BONDS"][bond_values[0]])
            fdiff.append(fluct_diff)
            fluct_diff = fluct_diff.reset_index()
            tmp = self.parameters["BONDS"][bond_values[0]].reset_index()

            if not self.restart:
                self.converge_bnd_list &= ((fluct_diff.iloc[:, 2] > tol) &
                                           (tmp.iloc[:, 2] > 0))
            else:
                if i == 0:
                    self.converge_bnd_list = ((fluct_diff.iloc[:, 2] > tol) &
                                              (tmp.iloc[:, 2] > 0))
                else:
                    self.converge_bnd_list &= ((fluct_diff.iloc[:, 2] > tol) &
                                               (tmp.iloc[:, 2] > 0))

            # Calculate the r.m.s.d. between fluctuation and distances
            # compared with the target values.
            vib_error = self.target["BONDS"] - vib_ic
            vib_error = vib_error.apply(np.square).mean(axis=0)
            vib_error = np.sqrt(vib_error)
            self.error[self.error.columns[-2:]] = vib_error.T.values

            # Calculate the new force constant.
            optimized = vib_ic.apply(np.reciprocal).apply(np.square)
            target = self.target["BONDS"].apply(np.reciprocal).apply(np.square)
            optimized -= target
            optimized *= self.BOLTZ * self.KFACTOR

            # update  bond list
            vib_ic[bond_values[0]] = (
                self.parameters["BONDS"][bond_values[0]] -
                optimized[bond_values[0]])
            vib_ic[bond_values[0]] = (vib_ic[bond_values[0]].where(
                vib_ic[bond_values[0]] >= 0., 0.))  # set negative to zero

            if low_bound > 0. and i > int(n_cycles * 0.75):
                logger.info(
                    f"Fluctuation matching cycle {i}: low bound is {low_bound}"
                )
                vib_ic[bond_values[0]] = (vib_ic[bond_values[0]].where(
                    vib_ic[bond_values[0]] >= low_bound, 0.))

            # r.m.s.d. between previous and current force constant
            diff = self.dynamic_params["BONDS"] - vib_ic
            diff = diff.apply(np.square).mean(axis=0)
            diff = np.sqrt(diff)
            self.error[self.error.columns[1]] = diff.values[0]

            # Update the parameters and write to file.
            self.parameters["BONDS"][bond_values[0]] = vib_ic[bond_values[0]]
            self.dynamic_params["BONDS"][bond_values[0]] = vib_ic[
                bond_values[0]]
            self.dynamic_params["BONDS"][bond_values[1]] = vib_ic[
                bond_values[1]]

            self.parameters["BONDS"].reset_index(inplace=True)
            self.dynamic_params["BONDS"].reset_index(inplace=True)
            with mda.Writer(self.filenames["fixed_prm"], **self.kwargs) as prm:
                prm.write(self.parameters)
            with mda.Writer(self.filenames["dynamic_prm"],
                            **self.kwargs) as prm:
                prm.write(self.dynamic_params)

            # Update the error values.
            with open(self.filenames["error_data"], "ab") as error_file:
                np.savetxt(
                    error_file,
                    self.error,
                    fmt=native_str("%15d%15.6f%15.6f%15.6f", ),  # Nix
                    delimiter=native_str(""),
                )
            logger.info(
                "Fluctuation matching cycle {} completed in {:.6f}".format(
                    i,
                    time.time() - ct))
            logger.info(
                f"{self.converge_bnd_list.sum()} not converged out of {len(self.converge_bnd_list)}"
            )

            if self.converge_bnd_list.sum() <= len(
                    self.converge_bnd_list.values.tolist()) * 0.003:
                # if bonds to converge is less than 0.3% of total bonds, use relative difference as criteria
                # as it takes more than 100 iterations for these 0.3%  bonds to converge.
                relative_diff = (fluct_diff.iloc[:, 2] - tol) / tol

                ### To know the late converged bonds uncomment the below 5 lines ###

                # late_converged = pd.DataFrame()
                # indx = self.converge_bnd_list[self.converge_bnd_list].index.values
                # late_converged = pd.concat([fluct_diff.loc[indx], relative_diff.loc[indx]], axis=1)
                # late_converged.columns = ["I", "J", "fluct_diff_Kb", "relative_diff_kb"]
                # print(late_converged)

                self.converge_bnd_list = self.converge_bnd_list & (
                    relative_diff > 5)
                if self.converge_bnd_list.sum() == 0:
                    logger.info(
                        "Checking relative difference: All bonds converged, exiting"
                    )
                    break
        fluct_conv = pd.concat(fdiff, axis=1).round(6)
        fluct_conv.columns = [j for j in range(1, i + 2)]
        fluct_conv.to_csv(self.filenames["bond_convergence"])
        logger.info(
            "Fluctuation matching completed in {:.6f}".format(time.time() -
                                                              st))
        self.target["BONDS"].reset_index(inplace=True)
    def initialize(self, nma_exec=None, restart=False):
        """Create an elastic network model from a basic coarse-grain model.

        Parameters
        ----------
        nma_exec : str
            executable file for normal mode analysis
        restart : bool, optional
            Reinitialize the object by reading files instead of doing initial
            calculations.
        """
        self.restart = restart
        if not self.restart:
            # Write CHARMM input file.
            if not path.exists(self.filenames["init_input"]):
                version = self.kwargs.get("charmm_version", 41)
                dimension = ("dimension chsize 1000000"
                             if version >= 36 else "")
                with open(self.filenames["init_input"],
                          mode="wb") as charmm_file:
                    logger.info("Writing CHARMM input file.")
                    charmm_inp = charmm_init.init.format(
                        flex="flex" if version else "",
                        version=version,
                        dimension=dimension,
                        **self.filenames)
                    charmm_inp = textwrap.dedent(charmm_inp[1:])
                    charmm_file.write(charmm_inp.encode())

            charmm_exec = (os.environ.get("CHARMMEXEC", util.which("charmm"))
                           if nma_exec is None else nma_exec)
            with open(self.filenames["init_log"], "w") as log_file:
                subprocess.check_call(
                    [charmm_exec, "-i", self.filenames["init_input"]],
                    stdout=log_file,
                    stderr=subprocess.STDOUT,
                )

            # Write the parameter files.
            with reader(self.filenames["init_fluct_ic"]) as icfile:
                std_bonds = icfile.read().set_index(self.bond_def)
            with reader(self.filenames["init_avg_ic"]) as icfile:
                avg_bonds = icfile.read().set_index(self.bond_def)
            target = pd.concat([std_bonds["r_IJ"], avg_bonds["r_IJ"]], axis=1)
            target.reset_index(inplace=True)
            logger.info("Calculating the initial CHARMM parameters...")
            universe = mda.Universe(self.filenames["xplor_psf_file"],
                                    self.filenames["crd_file"])
            self.target = prmutils.create_empty_parameters(
                universe, **self.kwargs)
            target.columns = self.target["BONDS"].columns
            self.target["BONDS"] = target.copy(deep=True)
            self.parameters = copy.deepcopy(self.target)
            self.parameters["BONDS"]["Kb"] = (
                self.BOLTZ / self.parameters["BONDS"]["Kb"].apply(np.square))
            self.dynamic_params = copy.deepcopy(self.parameters)
            with mda.Writer(self.filenames["fixed_prm"], **self.kwargs) as prm:
                logger.info("Writing {}...".format(
                    self.filenames["fixed_prm"]))
                prm.write(self.parameters)
            with mda.Writer(self.filenames["dynamic_prm"],
                            **self.kwargs) as prm:
                logger.info("Writing {}...".format(
                    self.filenames["dynamic_prm"]))
                prm.write(self.dynamic_params)
        else:
            print("FM Restarted")
            if not path.exists(self.filenames["fixed_prm"]):
                self.initialize(nma_exec, restart=False)
            try:
                # Read the parameter files.
                logger.info("Loading parameter and internal coordinate files.")
                with reader(self.filenames["fixed_prm"]) as fixed:
                    self.parameters.update(fixed.read())
                with reader(self.filenames["dynamic_prm"]) as dynamic:
                    self.dynamic_params.update(dynamic.read())

                # Read the initial internal coordinate files.
                with reader(self.filenames["init_avg_ic"]) as init_avg:
                    avg_table = init_avg.read().set_index(
                        self.bond_def)["r_IJ"]

                with reader(self.filenames["init_fluct_ic"]) as init_fluct:
                    fluct_table = (init_fluct.read().set_index(
                        self.bond_def)["r_IJ"])
                table = pd.concat([fluct_table, avg_table], axis=1)

                # Set the target fluctuation values.
                logger.info("Files loaded successfully...")
                self.target = copy.deepcopy(self.parameters)
                self.target["BONDS"].set_index(self.bond_def, inplace=True)
                cols = self.target["BONDS"].columns
                table.columns = cols
                self.target["BONDS"] = table.copy(deep=True).reset_index()

            except (FileNotFoundError, IOError):
                raise_with_traceback(
                    (IOError("Some files are missing. Unable to restart.")))
Beispiel #5
0
    def run(self, nma_exec=None, tol=1.e-4, n_cycles=250):
        """Perform a self-consistent fluctuation matching.

        Parameters
        ----------
        nma_exec : str
            executable file for normal mode analysis
        tol : float, optional
            error tolerance
        n_cycles : int, optional
            number of fluctuation matching cycles
        """
        # Find CHARMM executable
        charmm_exec = (os.environ.get("CHARMMEXEC", util.which("charmm"))
                       if nma_exec is None else nma_exec)
        if charmm_exec is None:
            logger.exception(
                "Please set CHARMMEXEC with the location of your CHARMM "
                "executable file or add the charmm path to your PATH "
                "environment.")
            raise_with_traceback(
                OSError(
                    "Please set CHARMMEXEC with the location of your CHARMM "
                    "executable file or add the charmm path to your PATH "
                    "environment."))

        # Read the parameters
        if not self.parameters:
            try:
                self.initialize(nma_exec, restart=True)
            except IOError:
                raise_with_traceback(
                    (IOError("Some files are missing. Unable to restart.")))

        # Write CHARMM input file.
        if not path.exists(self.filenames["charmm_input"]):
            version = self.kwargs.get("charmm_version", 41)
            dimension = ("dimension chsize 1000000" if version >= 36 else "")
            with open(
                    self.filenames["charmm_input"], mode="wb") as charmm_file:
                logger.info("Writing CHARMM input file.")
                charmm_inp = charmm_nma.nma.format(
                    temperature=self.temperature,
                    flex="flex" if version else "",
                    version=version,
                    dimension=dimension,
                    **self.filenames)
                charmm_inp = textwrap.dedent(charmm_inp[1:])
                charmm_file.write(charmm_inp.encode())

        # Set the indices for the parameter tables.
        self.target["BONDS"].set_index(self.bond_def, inplace=True)
        bond_values = self.target["BONDS"].columns

        # Check for restart.
        try:
            if os.stat(self.filenames["error_data"]).st_size > 0:
                with open(self.filenames["error_data"], "rb") as data:
                    error_info = pd.read_csv(
                        data,
                        header=0,
                        skipinitialspace=True,
                        delim_whitespace=True)
                    if not error_info.empty:
                        self.error["step"] = error_info["step"].values[-1]
            else:
                raise FileNotFoundError
        except (FileNotFoundError, OSError):
            with open(self.filenames["error_data"], "wb") as data:
                np.savetxt(
                    data, [
                        self.error_hdr,
                    ],
                    fmt=native_str("%10s"),
                    delimiter=native_str(""))
        self.error["step"] += 1

        # Run simulation
        logger.info("Starting fluctuation matching")
        st = time.time()

        for i in range(n_cycles):
            self.error["step"] = i + 1
            with open(self.filenames["charmm_log"], "w") as log_file:
                subprocess.check_call(
                    [charmm_exec, "-i", self.filenames["charmm_input"]],
                    stdout=log_file,
                    stderr=subprocess.STDOUT,
                )
            self.dynamic_params["BONDS"].set_index(self.bond_def, inplace=True)
            self.parameters["BONDS"].set_index(self.bond_def, inplace=True)

            # Read the average bond distance.
            with reader(self.filenames["avg_ic"]) as intcor:
                avg_ic = intcor.read().set_index(self.bond_def)["r_IJ"]

            # Read the bond fluctuations.
            with reader(self.filenames["fluct_ic"]) as intcor:
                fluct_ic = intcor.read().set_index(self.bond_def)["r_IJ"]

            vib_ic = pd.concat([fluct_ic, avg_ic], axis=1)
            vib_ic.columns = bond_values

            # Calculate the r.m.s.d. between fluctuation and distances
            # compared with the target values.
            vib_error = self.target["BONDS"] - vib_ic
            vib_error = vib_error.apply(np.square).mean(axis=0)
            vib_error = np.sqrt(vib_error)
            self.error[self.error.columns[-2:]] = vib_error.T.values

            # Calculate the new force constant.
            optimized = vib_ic.apply(np.reciprocal).apply(np.square)
            target = self.target["BONDS"].apply(np.reciprocal).apply(np.square)
            optimized -= target
            optimized *= self.BOLTZ * self.KFACTOR
            vib_ic[bond_values[0]] = (self.parameters["BONDS"][bond_values[0]]
                                      - optimized[bond_values[0]])
            vib_ic[bond_values[0]] = (vib_ic[bond_values[0]].where(
                vib_ic[bond_values[0]] >= 0., 0.))

            # r.m.s.d. between previous and current force constant
            diff = self.dynamic_params["BONDS"] - vib_ic
            diff = diff.apply(np.square).mean(axis=0)
            diff = np.sqrt(diff)
            self.error[self.error.columns[1]] = diff.values[0]

            # Update the parameters and write to file.
            self.parameters["BONDS"][bond_values[0]] = (
                vib_ic[bond_values[0]].copy(deep=True))
            self.dynamic_params["BONDS"] = vib_ic.copy(deep=True)
            self.parameters["BONDS"].reset_index(inplace=True)
            self.dynamic_params["BONDS"].reset_index(inplace=True)
            with mda.Writer(self.filenames["fixed_prm"], **self.kwargs) as prm:
                prm.write(self.parameters)
            with mda.Writer(self.filenames["dynamic_prm"],
                            **self.kwargs) as prm:
                prm.write(self.dynamic_params)

            # Update the error values.
            with open(self.filenames["error_data"], "ab") as error_file:
                np.savetxt(
                    error_file,
                    self.error,
                    fmt=native_str("%10d%10.6f%10.6f%10.6f", ),
                    delimiter=native_str(""),
                )

            if (self.error[self.error.columns[1]] < tol).bool():
                break

        logger.info("Fluctuation matching completed in {:.6f}".format(
            time.time() - st))
        self.target["BONDS"].reset_index(inplace=True)