Пример #1
0
    def __init__(self, link):
        # Load hdf5 data file
        sample = utils.load(link)

        inp = sample["inp"]
        # Read the inputs
        self._bin_num = int(inp["bin_num"])  # number of bins z-direction
        self._frame_num = int(inp["num_frame"])  # number of frames
        self._len_step = inp["len_step"]  # step length
        self._dt = float(inp["len_frame"]) * 10**12  # frame length [ps]
        self._bins = inp["bins"]  # bins [nm]
        self._bin_width = self._bins[1] - self._bins[0]  # bin width [nm]
        self._direction = int(inp["direction"])
        self._trans_mat = sample["data"]  # transition matrix
        self._pbc = inp["is_pbc"]  # pbc or nopbc
        self._type = sample["type"]

        self._sys_props = {}
        if "pore" in sample:
            self._sys_props["type"] = sample["pore"]["type"]
            self._sys_props["res"] = float(sample["pore"]["res"])
            self._sys_props["focal"] = sample["pore"]["focal"]
            self._sys_props["box"] = sample["pore"]["box"]
            self._sys_props["diam"] = float(sample["pore"]["diam"])
            self._system = "pore"
        if "box" in sample:
            self._system = "box"
            self._sys_props["length"] = sample["box"]["length"]

        # Initialize units of diffusion and free energy unit
        self._df_unit = 1.  # in kBT
        self._diff_unit = np.log(self._bin_width**2 / 1.)  # in m^2/s
Пример #2
0
def mc_profile(link, len_step=[], is_plot=True, kwargs={}):
    """This function plots the free energy profile over the box for the
    calculated lag times. In contrast to the diffusion profile the diffusion
    profile has not a dependency on the lag time. If the free energy profiles
    are not close to equal the calculation is incorrect.

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5, obj or yml data file generated by the :func:`poreana.mc.MC.do_mc_cycles`
    len_step: integer list, optional
        List of the different step length, if it is [] all free energy profiles
        depending on the lag time are shown
    is_plot : bool, optional
        Show free energy profile
    kwargs: dict, optional
        Dictionary with plotting parameters

    Returns
    -------
    df_bin: dictionary
        free energy profile for every calculated lag time
    bins : list
        bins over the box length
    """
    # Load Results from the output object file
    data = utils.load(link)

    # Load results
    results = data["output"]
    df_bin = results["df_profile"]

    # Load model inputs
    model = data["model"]
    dt = model["len_frame"]
    bins = model["bins"]

    # If no specific step length is chosen take the step length from the object file
    if not len_step:
        len_step = model["len_step"]

    # Set legend
    legend = [
        "$\\Delta t_{\\alpha}$ = " + str(len_step[i] * dt) + " ps"
        for i in range(len(len_step))
    ]

    # Plot the free energy profiles
    if is_plot:
        for i in len_step:
            sns.lineplot(x=bins, y=(df_bin[i]), **kwargs)

        # Plot options
        plt.xlabel("Box length (nm)")
        plt.ylabel("Free energy (-)")
        plt.legend(legend)
        plt.xlim([0, max(bins)])

    return df_bin, bins
Пример #3
0
def mc_model(link, print_con=False):
    """This function prints the model inputs of the calculation.

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5, obj or yml data file generated by the MC Algorithm function
        :func:`poreana.mc.MC.run`
    print_con: bool, optional
        True for printing in console

    Returns
    -------
    df_model: obj
        Data frame of the model inputs
    """
    # Load Results from the output object file
    data = utils.load(link)

    # Load model inputs
    model = data["model"]
    bin_number = model["bin number"]
    len_step = model["len_step"]
    len_frame = model["len_frame"]
    frame_num = model["num_frame"]
    nD = model["nD"]
    nF = model["nF"]
    nDrad = model["nDrad"]
    d = model["guess"]
    model_string = model["model"]
    pbc = model["pbc"]
    direction = int(model["direction"])

    # String for pbc
    pbc = pbc == 1

    # String for system
    system = "pore" if "pore" in data else "box"

    # Len step string
    len_step_string = ', '.join(str(step) for step in len_step)

    # Dictionary for model inputs
    data = [str("%.i" % bin_number), len_step_string, str("%.2e" % (len_frame * 10**(-12))), str("%.i" % frame_num), str("%.i" % nD), str("%.i" % nF), str("%.i" % nDrad), model_string, str("%.2e" % (d * 10**(-6))), system, pbc, str("%.i" % direction)]
    df_model = pd.DataFrame(data, index=list(['Bin number', 'step length', 'frame length (s)', 'frame number', 'nD', 'nF', 'nDrad', 'model', 'guess diffusion (m2/s-1)', 'system',"pbc", "direction"]), columns=list(['Input']))

    # If the table has to print in console and not in a jupyter notebook
    if print_con:
        print('\nModel Inputs')
        print(df_model)

    # Set styler for pandas table in jupyter
    #styler = df_model.style.set_caption('Model Inputs')
    #df_model = styler.set_properties(**{'text-align': 'right'})
    #df_model = df_model.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

    return df_model
Пример #4
0
def mc_statistics(link, print_con=False):
    """This function prints the statistic of the MC Run for every lag time.

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5, obj or yml data file generated by the MC Algorithm function
        :func:`poreana.mc.MC.run`
    print_con: bool, optional
        True for printing in console

    Returns
    -------
    df_results: obj
        Data frame of the MC Alogrithm statistics
    """
    # Load Results from the output object file
    data = utils.load(link)

    # Load results
    results = data["output"]

    # Load model inputs
    model = data["model"]
    len_step = model["len_step"]
    inp = data["inp"]
    nmc_eq = inp["MC steps eq"]
    nmc = inp["MC steps"]


    nacc_df_mean = results["nacc_df"]
    nacc_diff_mean = results["nacc_diff"]
    list_diff_fluc = results["fluc_diff"]
    list_df_fluc = results["fluc_df"]

    # Table for MC Statistics
    data = [[str("%.4e" % list_df_fluc[i]) for i in len_step],[str("%.4e" % list_diff_fluc[i]) for i in len_step],[str("%.0f" % nacc_df_mean[i]) for i in len_step],[str("%.0f" % nacc_diff_mean[i]) for i in len_step],[str("%.2f" % (nacc_df_mean[i]*100/(nmc+nmc_eq))) for i in len_step],[str("%.2f" % (nacc_diff_mean[i]*100/(nmc+nmc_eq))) for i in len_step]]

    df_results = pd.DataFrame(data,index=list(['fluctuation df','fluctuation diff','acc df steps','acc diff steps','acc df steps (%)','acc diff steps (%)']),columns=list(len_step))

    # If the table has to print in console
    if print_con:
        print('\nStatistics of the MC Algorithm')
        print(df_results)

    # Set styler for pandas table in jupyter
    df_results = pd.DataFrame(df_results.rename_axis('Step Length', axis=1))
    styler = df_results.style.set_caption('Statistics of the MC Algorithm')
    df_results = styler.set_properties(**{'text-align': 'right'})
    df_results = df_results.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

    return df_results
Пример #5
0
def mc_inputs(link, print_con=False):
    """This function prints the MC Algorithm inputs of the calculation.

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5, obj or yml data file generated by the MC Algorithm function
        :func:`poreana.mc.MC.run`
    print_con : bool, optional
        True for printing in console

    Returns
    -------
    df_mc : obj
        Data frame of the MC Alogrithm inputs
    """
    # Load Results from the output object file
    data = utils.load(link)


    # Read MC inputs
    inp = data["inp"]
    nmc_eq = inp["MC steps eq"]
    nmc = inp["MC steps"]
    num_mc_update = inp["step width update"]
    print_freq = inp["print freq"]
    temp = inp["temperature"]

    # Table for MC Inputs
    data = [str("%.i" % nmc_eq), str("%.i" % nmc), temp, str("%.i" % num_mc_update), str("%.i" % print_freq)]
    df_mc = pd.DataFrame(data, index=list(['MC steps (Equilibrium)', 'MC steps (Production)','temperature (MC)', 'movewidth update frequency', 'print frequency']), columns=list(['Input']))

    # If the table has to print in console and not in a jupyter notebook
    if print_con:
        print('\nMC Inputs')
        print(df_mc)

    # Set style for the pandas table
    #styler = df_mc.style.set_caption('MC Inputs')
    #df_mc = styler.set_properties(**{'text-align': 'right'})
    #df_mc = df_mc.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

    return df_mc
Пример #6
0
    def __init__(self, data_link, d0=1e-8):

        # Load data object
        sample = utils.load(data_link)
        inp = sample["inp"]
        self._model_inp = sample

        # Read the inputs
        self._bin_num = inp["bin_num"]                                       # number of bins z-direction
        self._frame_num= inp["num_frame"]                                    # number of bins z-direction
        self._len_step = inp["len_step"]                                     # step length
        self._dt = inp["len_frame"] * 10**12                                 # frame length [ps]
        self._bins = inp["bins"]                                             # bins [nm]
        self._bin_width = self._bins[1] - self._bins[0]                      # bin width [nm]
        self._trans_mat = sample["data"]                                     # transition matrix
        self._pbc = inp["pbc"]                                               # pbc or nopbc
        self._d0 = d0 * (10**18)/(10**12)                                    # guess init profile [A^2/ps]

        # Initialize units of w and v
        self._df_unit = 1.                                                   # in kBT
        self._diff_unit = np.log(self._bin_width**2 / 1.)                    # in m^2/s

        return
Пример #7
0
def mc_profile(link, len_step=[], section=[], infty_profile=True,  is_plot=True, kwargs={}):
    """This function plots the diffusion profile for an infinity
    lag time (:math:`\\Delta t_{\\alpha} \\rightarrow \\infty`) over the box
    fitted with the specified :math:`\\mathrm{len}\_\\mathrm{step}` list.
    Additionally, it is possible to display the diffusion profiles for the
    calculated lag times. Therefore, the
    :math:`\\mathrm{infty}\_\\mathrm{profile}` has to be false.
    The list :math:`\\mathrm{len}\_\\mathrm{step}`
    contains the calculated step length which should be
    displayed.

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5 data file generated by the MC Algorithm
        :func:`poreana.mc.MC.run`
    len_step : integer list, optional
        List of the different step length, if it is [] all diffusion profiles
        depending on the lag time are used to calculate the diffusion profile
        for an infinite lag time or the individual diffusion profiles for these
        lag times are shown
    section : list, string, optional
        If :math:`\\mathrm{section} = \\mathrm{"pore"}` the pore section is
        fitted.
        If :math:`\\mathrm{section} = \\mathrm{"reservoir"}` the left reservoir
        area is fitted.
        If :math:`\\mathrm{section} = \\mathrm{[a,b]}` the box area between a
        and b is fitted.
    infty_profile : bool, optional
        Set to false to display all individual diffusion profiles for
        the selected lag times
    is_plot : bool, optional
        Show diffusion profile
    kwargs: dict, optional
        Dictionary with plotting parameters

    Returns
    -------
    diff_profile_fit : list
        Diffusion profile :math:`\\left(10^{-9} \\frac{m^2}{s}\\right)` for an infinite
        lag time
    diff_profiles : dict
        Diffusion profiles :math:`\\left(10^{-9} \\frac{m^2}{s}\\right)` for every
        calculated lag time
    bins : list
        bins over the box length
    res : float
        residual :math:`\\left(10^{-9} \\frac{m^2}{s}\\right)` for fitting the infinite diffusion profile
    """
    # Load data
    data = utils.load(link)

    # Load results
    results = data["output"]
    diff_bin = results["diff_profile"]

    # Load model inputs
    model = data["model"]
    diff_unit = model["diffusion unit"]
    bins = model["bins"]
    dt = model["len_frame"]

    # If no specific step length is chosen take the step length from the object file
    if not len_step:
        len_step = model["len_step"]

    # If a pore system is considered
    if "pore" in data:
        pore = data["pore"]
        res = pore["res"]
        box = pore["box"]


    # Set dictionaries
    legend = []
    diff_bin_vec = {}
    diff_profile_fit = []
    res_list = []


    # Pore
    if isinstance(section, str) and section== "pore":
        # If only the pore area should be considered
        # Load pore system
        if "pore" in data:
            # Set section
            area = [res, box[2]-res-2*(bins[1]-bins[0])]


        # If only the pore area should be considered
        else:
            print("obj.-file includes results of a simple box")
            return;

    # Reservoir
    elif isinstance(section, str) and section== "reservoir":
        # If only the pore area should be considered
        # Load pore system
        if "pore" in data:
            # Set section
            area = [0,res]

        # If only the pore area should be considered
        else:
            print("obj.-file includes results of a simple box")
            return;

    elif isinstance(section, str) and section not in ["reservoir", "pore"]:
        print("Wrong input for section! Check documentation for available options")
        return;

    elif isinstance(section, list) and len(section)>=3:
        print("Wrong input for section! Check documentation for available options")
        return;

    #Area section
    elif isinstance(section, list):
        area = section

    # If section is not defined -> entire system
    if not section:
        # Set section
        area = [bins[0], bins[-1]]

    # Cut profile
    # Calculated start and end bin index of the pore area
    index_start = np.digitize(area[0], bins)
    index_end = np.digitize(area[1], bins)

    # Save for all lag times the cutted profile
    for i in len_step:
        diff_bin_vec[i] = [diff_bin[i][j] for j in range(index_start, index_end)]

    # Set bin list
    bins = [bins[i] for i in range(index_start, index_end)]


    # Calculate the inverse lag time (1/s) for the linear fit
    lagtime_inverse = [1 / (len_step[i] * dt * 10**-12) for i in range(len(len_step))]

    # Calculate diffusion profiles
    diff_profiles = {}
    for i in len_step:
        diff_profiles[i] = [np.exp(diff_bin_vec[i][j] + diff_unit) * 10 ** 3 for j in range(len(bins))]

    # If infty_profile is false the profiles for the different lag times are plotted

    if not infty_profile:
        # Plot the profiles for the
        if is_plot:
            for i in len_step:
                sns.lineplot(x=bins, y=(diff_profiles[i]), **kwargs)       # Diffusion in m^2/s

        # Plot the diffusion profiles for the different lag times
        legend = ["$\\Delta t_{\\alpha}$ = " + str(len_step[i] * dt) + " ps" for i in range(len(len_step))]

    # If infty_profile is True the diffusion profile for a infinity lag times is shwon
    if len(len_step) >= 2 and infty_profile:
        # Initialize fit vector

        # Calculate the mean diffusion over all bins
        for i in range(len(bins)):
            diff = [np.exp(diff_bin_vec[step][i] + diff_unit) * 10 ** 3 for step in len_step]   # Diffusion in m^2/s

            # fit a linear line
            fit = np.poly1d(np.polyfit(lagtime_inverse, diff, 1))

            # Append diffusion at t-> infty
            diff_profile_fit.append(fit(0))

            # Calculate residuals for fitting
            res = np.polyfit(lagtime_inverse, diff, 1, full=True)

            # Append residual
            res_list.append(float(res[1]))

        # Plot fitted diffusion profile
        if is_plot:
            sns.lineplot(x=bins, y=diff_profile_fit, **kwargs)       # Diffusion in m^2/s

    # Set legend for lag times
    if is_plot:
        if not infty_profile and len(len_step) >= 2:
            legend.append("$\\Delta t_{\\alpha} \\rightarrow \\infty$ ps")


        # Set plot properties
        # Plot axis title for a entire system
        plt.ylabel(r"Diff. coeff. ($10^{-9} \ \mathrm{m^2s^{-1}}$)")
        plt.xlabel(r"Box length (nm)")
        plt.xlim([min(bins),max(bins)])
        if legend:
            plt.legend(legend)

    # Check if profile was fitted
    if not res_list:
        res_list = 0
    else:
        np.mean(res_list)

    return diff_profile_fit, diff_profiles, bins, res_list
Пример #8
0
def mc_fit(link, len_step=[], section=[], is_std=True, is_print=True, is_plot=True, kwargs_scatter={}, kwargs_line={}):
    """This function uses the diffusion profiles over box length which are
    calculated in the function :func:`poreana.mc.MC.run` to estimate
    the final diffusion coefficient. For that a line is fitted of the averaged
    diffusion profiles :math:`D_{\\mathrm{mean}}({\\Delta t_{\\alpha}})` for the
    different lag times as a function of :math:`1/\\Delta t_{\\alpha}`. The
    function plots all mean diffusion
    :math:`D_\\mathrm{mean}({\\Delta t_{\\alpha}})` over the
    inverse lag times and the linear fit. Additionally, the final diffusion
    coefficient :math:`D` for a lag time
    :math:`\\Delta t_{\\alpha} \\rightarrow \infty` is printed. Also, a table
    of the selected step length and the belonging
    :math:`D_{\\mathrm{mean}}({\\Delta t_{\\alpha}})` can be displayed.

    In order to be able to estimate how the choice of lag times affects the
    result, an additional error estimation is possible. For this all possible
    fourth tuples of the entire calculated lag times are used to estimate a
    mean diffusion coefficient :math:`\\langle D \\rangle` which is the mean
    value of all fourth tuples fitting results. Also, the standard deviation of
    all fitting results is printed. If there is no big difference between the
    diffusion coefficient :math:`D` and the mean diffusion coefficient
    :math:`\\langle D \\rangle`, all :math:`D_\\mathrm{mean}({\\Delta t_{\\alpha}})`
    are on a straight line. The standard deviation also can be used to check
    if the result fluctuates strongly at the choice of other lag times. This
    error estimate is calculated if :math:`\\mathrm{is}\_{\\mathrm{std}}=True`.

    Parameters
    ----------
    link: string
        Link to the diffusion hdf5 data file generated by the MC Alogrithm
        :func:`poreana.mc.MC.run`
    len_step: integer, list, optional
        List of the different step length, if it is [] all calculated lag times
        will be used to fit the diffusion coefficent
    section : list, string, optional
        If :math:`\\mathrm{section} = \\mathrm{"pore"}` the pore section is
        fitted.
        If :math:`\\mathrm{section} = \\mathrm{"reservoir"}` the left reservoir
        area is fitted.
        If :math:`\\mathrm{section} = \\mathrm{[a,b]}` the box area between a
        and b is fitted.
    is_std : bool, optional
        True to calculate a mean diffusion coefficient
        to assess the dependency of the result on the selected lag time
    is_print : bool, optional
        Print diffusion coefficient
    is_plot : bool, optional
        Show the fitting plot
    kwargs_scatter: dict, optional
        Dictionary with plotting parameters for the points
    kwargs_line: dict, optional
        Dictionary with plotting parameters for the fitting line


    Returns
    -------
    diffusion: float
        Diffusion coefficient :math:`D \ \\left( 10^{-9} \\frac{m^2}{s}\\right)` for the
        calculated system
    diffusion_mean: float
        Mean diffusion coefficient :math:`\\langle D \\rangle \ \\left(10^{-9} \\frac{m^2}{s}\\right)`
        for the calculated system
    diff_table : obj
        Table of used lag times with the associated
        :math:`D_{\\mathrm{mean}}({\\Delta t_{\\alpha}}) \ \\left(10^{-9} \\frac{m^2}{s}\\right)`
    res : float
        residual :math:`\\left(10^{-9} \\frac{m^2}{s}\\right)` for fitting the selected lag times
    """
    # Load data
    data = utils.load(link)


    results = data["output"]
    diff_bin = results["diff_profile"]
    model = data["model"]
    bins = model["bins"]
    dt = model["len_frame"]
    diff_unit = model["diffusion unit"]

    # If no specific step length is chosen take the step length from the object file
    if not len_step:
        len_step = model["len_step"]

    # If a pore system is considered
    if "pore" in data:
        pore = data["pore"]
        res = float(pore["res"])
        box = pore["box"]

    # Set vector
    diff_bin_vec = {}

    # Pore
    if isinstance(section, str) and section== "pore":
        # If only the pore area should be considered
        if "pore" in data:
            # Set section
            area = [res, box[2]-res]

        # If only the pore area should be considered
        else:
            print("obj.-file includes results of a simple box")
            return;

    # Reservoir
    elif isinstance(section, str) and section == "reservoir":
        # If only the pore area should be considered
        # Load pore system
        if "pore" in data:
            # Set section
            area = [0,res]

        # If only the pore area should be considered
        else:
            print("obj.-file includes results of a simple box")
            return;

    elif isinstance(section, str) and section not in ["reservoir", "pore"]:
        print("Wrong input for section! Check documentation for available options")
        return;

    elif isinstance(section, list) and len(section)>=3:
        print("Wrong input for section! Check documentation for available options")
        return;

    #Area section
    elif isinstance(section, list):
        area = section

    # If section is not defined -> entire system
    if not section:
        # Set section
        area = [bins[0], bins[-1]]

    # Cut profile
    # Calculated start and end bin index of the pore area
    index_start = np.digitize(area[0], bins)
    index_end = np.digitize(area[1], bins)


    for i in len_step:
        diff_bin_vec[i] = [diff_bin[i][j] for j in range(index_start, index_end)]

    # Calculate mean diffusion coefficient and standard deviation
    # Mean diffusion means the averaged of all possible fourth tuple fitting results
    if is_std:
        len_step_all = len_step

        # Determine all possible combinations
        a = list(itertools.combinations(len_step_all, 2))

        # Allocate the result vector
        res = np.zeros(len(a))

        # Fit to infinity lag time for all combinations
        for i in range(len(a)):

            # Set the current tuple
            rand = list(a[i])

            # Calculate the mean diffusion (m^2/s) over all bins
            D_mean = [np.mean(np.exp([diff_bin_vec[i][j] + diff_unit for j in range(len(diff_bin_vec[i]))])) * 10**3 for i in rand]


            # Calculate the inverse lag time for the linear fit
            lagtime_inverse = [1 / (i * dt * 10**-12) for i in rand]

            # Fit a linear line
            fit = np.poly1d(np.polyfit(lagtime_inverse, D_mean, 1))

            # Set the fitted diffusion coefficient on the results list
            res[i] = fit(0)

        # Determine standard deviation and mean diffusion of fourth touple method
        diffusion_mean = np.mean(res)
        std = res.std()

    # Calculate the mean diffusion (m^2/s) over all bins
    D_mean = [np.mean(np.exp([diff_bin_vec[i][j] + diff_unit for j in range(len(diff_bin_vec[i]))])) * 10**3 for i in len_step]

    # Calculate the inverse lag time (1/s) for the linear fit
    lagtime_inverse = [1 / (len_step[i] * dt * 10**-12) for i in range(len(len_step))]

    # Fit a linear line
    fit = np.poly1d(np.polyfit(lagtime_inverse, D_mean, 1))

    # Calculate residuals
    res = np.polyfit(lagtime_inverse, D_mean, 1, full=True)

    # Print the diffusion coefficient for an entire system
    if is_print:
        if not section:
            print("\nDiffusion axial: "+"%.4e" % (fit(0) * 10 **-9) + " m^2/s\n")

            # Print resudial for fitting
            print("Residual: "+"%.4e" % (float(res[1]) * 10 **-9) + " m^2/s\n")

            # If is_std true print the results of the calculations
            if is_std:
                print("Mean Diffusion axial: "+"%.4e" % (diffusion_mean * 10 **-9)  + " m^2/s\n")

        # Print the diffusion coefficient in the pore area
        if section=="pore":
            print("\nDiffusion axial (Pore): "+"%.4e" % (fit(0) * 10 **-9) + " m^2/s\n")


            # Print resudial for fitting
            print("Residual: "+"%.4e" % (float(res[1]) * 10 **-9) + " m^2/s\n")

            # If is_std true print the results of the calculations
            if is_std:
                print("Mean Diffusion axial (Pore): "+"%.4e" % (diffusion_mean * 10 **-9)  + " m^2/s\n")

        # Print the diffusion coefficient in the reservoir area
        if section=="reservoir":
            print("\nDiffusion axial (Reservoir): "+"%.4e" % (fit(0) * 10 **-9) + " m^2/s\n")

            # Print resudial for fitting
            print("Residual: "+"%.4e" % (float(res[1]) * 10 **-9) + " m^2/s\n")

            # If is_std true print the results of the calculations
            if is_std:
                print("Mean Diffusion axial (Peservoir): "+"%.4e" % (diffusion_mean * 10 **-9) + " m^2/s\n")

        # Print the diffusion coefficient in a selected section
        if (isinstance(section, list)) and len(section)==2:
            print("\nDiffusion axial ([" + "%.2f" % (area[0]) + ", " + "%.2f" % (area[1]) + "]): "+"%.4e" % (fit(0) * 10 **-9) + " m^2/s\n")

            # Print resudial for fitting
            print("Residual: "+"%.4e" % (float(res[1]) * 10 **-9) + " m^2/s\n")

            # If is_std true print the results of the calculations
            if is_std:
                print("Mean Diffusion axial (["+ "%.2f" % (area[0]) + ", " + "%.2f" % (area[1]) +"]): "+"%.4e" % (diffusion_mean * 10 **-9)  + " m^2/s\n")

        # Print std deviation
        if is_std:
            print("Standard deviation: "+"%.4e" % (std * 10 **-9) + " m^2/s\n")

    # Set data frame for the used lag times
    data = [str("%.2f" % D_mean[i] ) for i in range(len(len_step))]
    diff_table = pd.DataFrame(data, index=list(len_step), columns=list(['$D_\mathrm{mean} \ (10^{-9} \mathrm{m^2s^{-1}})$']))
    diff_table = pd.DataFrame(diff_table.rename_axis('Step Length', axis=1))
    styler = diff_table.style.set_caption('Selected step length')
    diff_table = styler.set_properties(**{'text-align': 'right'})
    diff_table = diff_table.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

    # Set vectors for plotting
    D_mean_vec = [D_mean[i] for i in range(len(lagtime_inverse))]
    lag_time_vec = [1 / (len_step[i] * dt) for i in range(len(len_step))]
    x_vec = np.arange(0, max(lag_time_vec) * 2, (max(lag_time_vec) * 2) / 5)

    # Fit a linear line and calculated diffusion coefficent
    fit = np.poly1d(np.polyfit(lag_time_vec, D_mean_vec, 1))
    diffusion = fit(0)

    if is_plot:
        # Plot the results
        plt.xlim(0, 1.5*max(lag_time_vec))
        plt.ylim(0, 1.5*max(fit(x_vec)))
        sns.scatterplot(x=lag_time_vec, y=D_mean_vec, **kwargs_scatter)
        sns.lineplot(x=x_vec, y=fit(x_vec), **kwargs_line)
        legend = ["$D_{\mathrm{fit}}$", "$D_{\mathrm{mean}}(\\Delta t_{\\alpha})$"]
        plt.legend(legend)
        plt.xlabel(r"Inverse lag time ($10^{12} \ \mathrm{s^{-1}})$")
        plt.ylabel(r"Diff. coeff. ($10^{-9} \ \mathrm{m^2s^{-1}}$)")

    return diffusion, diffusion_mean, diff_table, float(res[1][0])
Пример #9
0
def mc_trans_mat(link, step, kwargs={}, is_norm=False, is_diagonal=False, is_length=True, limit = 0):
    """This function plots the occupation of a normalized transition matrix as a
    heatmap. To normalize the transition matrix the number of the frame are used.
    This means that all entries in the transition matrix are divided by the
    total number of frames which are used to estimate the matrix.

    Parameters
    ----------
    link: string
        Link to the diffusion hdf5, obj or yml data file generated by the sample routine
        :func:`poreana.sample.Sample.init_diffusion_mc` or the MC run
        :func:`poreana.mc.MC.run`
    step: integer
        Step length for which the transition matrix is to be displayed
    kwargs: dict, optional
        Dictionary with plotting parameters
    is_norm: bool,optional
        Normalized the transition with the number for frames
    is_diagonal: bool, optional
        Set the matrix diagonal to zero
    is_length: bool, optional
        x axis is output in box length
    limit: float, optional
        Set the lowest occupancy value for calculating the width of occupancy
    """
    # Load results from the output object file
    data = utils.load(link)

    # Sample obj file is loaded
    if "data" in data:
        trans_mat = data["data"][step]
        inp = data["inp"]
        frame_num = inp["num_frame"]
        frame_length = float(inp["len_frame"])
        bins = inp["bins"]

    # MC obj file is loaded
    else:
        model = data["model"]
        trans_mat = model["data"][step]
        frame_num = model["num_frame"]
        frame_length = float(model["len_frame"])
        bins = model["bins"]

    # Calculation of the width of the diagonal occupation of the transition matrix
    results=[]
    # Loop over the rows of the transition matrix
    for i in range(len(trans_mat[:])-1):
        # Right side of the matrix (row)
        row_right= trans_mat[i][i:]/frame_num
        # Left side of the matrix (row)
        row_left = trans_mat[i][:i]/frame_num
        # If a one element is zero call index
        if np.where(row_right<=limit)[0].size != 0:
            results.append(np.min(np.where(row_right<=limit)))
        if np.where(row_left<=limit)[0].size != 0:
            results.append(i-np.max(np.where(row_left<=limit)))

    # Get average of index
    idx_avg = np.mean(results)
    print("Width of occupancy: " + str(idx_avg))


    # Normalized transition matrix with frame number
    if is_norm:
        trans_mat = trans_mat/frame_num

    # Set diagonal elements of the transition matrix to zero
    if is_diagonal:
        trans_mat[np.diag_indices_from(trans_mat)] = 0



    # Set x axis from bins to box length
    if is_length:
        trans_new={}
        for i in range(len(bins)-1):
            trans_new[str(round(bins[i],2))] = trans_mat[i]
        trans_mat = pd.DataFrame(trans_new)


    # Set title with selected lag time
    plt.title("Lagtime: "+ str(step * frame_length * 10**12) + " ps", fontsize=10)

    # Plot the normalized transition matrix in a heatmap
    sns.heatmap(trans_mat, **kwargs)
    if is_length:
        plt.xlabel("Box length (nm)")
Пример #10
0
def cui(link, z_dist=0, ax_area=[0.2, 0.8], intent="", is_fit=False, is_plot=True):
    """This function samples and calculates the diffusion coefficient of a
    molecule group in a pore in both axial and radial direction, as described
    in the paper of `Cui <https://doi.org/10.1063/1.1989314>`_.

    The mean square displacement is sampled in function
    :func:`poreana.sample.Sample._diffusion_bin`.

    The axial diffusion is given by the Einstein relation

    .. math::

        \\langle\\left[z(0)-z(t)\\right]^2\\rangle=2D_\\text{axial}t

    with axial diffusion coefficient :math:`D_\\text{axial}`. Thus the
    coefficient corresponds to the slope of the axial msd

    .. math::

        D_\\text{axial}=\\frac12\\frac{\\text{msd}_\\text{axial}[i]-\\text{msd}_\\text{axial}[j]}{t_i-t_j}

    with bin :math:`i>j`. The radial diffusion is given by

    .. math::

        \\langle\\left[r(0)-r(t)\\right]^2\\rangle=R^2\\left[1-\\sum_{n=1}^\\infty\\frac{8}{\\lambda_{1n}^2(\\lambda_{1n}^2-1)}\\exp\\left(-\\frac{\\lambda_{1n}^2}{R^2}D_\\text{radial}t\\right)\\right].

    with radial diffusion coefficient :math:`D_\\text{radial}`, the maximal
    accessible radial position :math:`R` by an atom

    .. math::

        R = \\frac12d-0.2

    with pore diameter :math:`d`, and the zeros :math:`\\lambda_{1n}^2` of
    the derivative

    .. math::

        \\frac{dJ_1}{dx}

    of the first order Bessel function :math:`J_1`. Therefore the coefficient
    has to be fitted to the sampled radial msd. The author observed that
    the first 20 Bessel function zeros were sufficient for the function fit.

    The final unit transformation is done by

    .. math::

        \\frac{\\text{nm}^2}{\\text{ps}}=10^{-2}\\frac{\\text{cm}^2}{\\text{s}}.

    **Note that the function for the radial diffusion is obtained under the
    assumption that the density inside the pore is uniform.**

    Parameters
    ----------
    link : string
        Link to hdf5 data file generated by the sample routine
        :func:`poreana.sample.Sample._diffusion_bin`
    z_dist : float, optional
        Distance from pore centre to calculate the mean
    ax_area : list, optional
        Bin area percentage to calculate the axial diffusion coefficient
    intent : string, optional
        axial, radial or empty for both
    is_fit : bool, optional
        True to plot the fitted function
    is_plot : bool, optional
        True to create plot in this function
    """
    # Load data object
    sample = utils.load(link)

    # Load data
    pore = sample["pore"]
    inp = sample["inp"]
    len_window = int(inp["len_window"])
    len_step = inp["len_step"]
    len_frame = inp["len_frame"]
    bins = int(inp["bin_num"]) if not z_dist else math.floor(z_dist/sample["data"]["width"])
    msd_z = [0 for x in range(len_window)]
    msd_r = [0 for x in range(len_window)]
    norm_z = [0 for x in range(len_window)]
    norm_r = [0 for x in range(len_window)]

    # Sum up all bins
    for i in range(bins):
        for j in range(len_window):
            msd_z[j] += sample["data"]["z_tot"][i][j]
            norm_z[j] += sample["data"]["n_tot"][i][j]

    for i in range(int(inp["bin_num"])):
        for j in range(len_window):
            msd_r[j] += sample["data"]["r_tot"][i][j]
            norm_r[j] += sample["data"]["n_tot"][i][j]

    # Normalize
    msd_z_n = [msd_z[i]/norm_z[i] if norm_z[i] > 0 else 0 for i in range(len_window)]
    msd_r_n = [msd_r[i]/norm_r[i] if norm_r[i] > 0 else 0 for i in range(len_window)]

    # Define time axis and range
    time_ax = [x*len_step*len_frame for x in range(len_window)]
    t_range = (len_window-1)*len_step*len_frame

    # Calculate axial coefficient
    if not intent or intent == "axial":
        dz = (msd_z_n[int(ax_area[1]*len_window)]-msd_z_n[int(ax_area[0]*len_window)])*1e-9**2/((ax_area[1]-ax_area[0])*t_range)/2*1e2**2*1e5  # 10^-9 m^2s^-1

        print("Diffusion axial:  "+"%.3f" % dz+" 10^-9 m^2s^-1")

    # Calculate radial coefficient
    if not intent or intent == "radial":
        def diff_rad(x, a, b, c):
            # Process input
            x = x if isinstance(x, list) or isinstance(x, np.ndarray) else [x]

            # Get bessel function zeros
            jz = sp.special.jnp_zeros(1, math.ceil(b))
            # Calculate sum
            sm = [[8/(z**2*(z**2-1))*math.exp(-(z/c)**2*a*t) for z in jz] for t in x]
            # Final equation
            return [c**2*(1-sum(s)) for s in sm]

        # Fit function
        popt, pcov = sp.optimize.curve_fit(diff_rad, [x*1e12 for x in time_ax], msd_r_n, p0=[1, 20, float(pore["diam"])/2-0.2], bounds=(0, np.inf))

        print("Diffusion radial: "+"%.3f" % (popt[0]*1e3)+" 10^-9 m^2 s^-1; Number of zeros: "+"%2i" % (math.ceil(popt[1]))+"; Radius: "+"%5.2f" % popt[2])

    # Plot
    if is_plot:
        legend = []

    if not intent or intent == "axial":
        sns.lineplot(x=[x*1e12 for x in time_ax], y=msd_z_n)
        if is_plot:
            legend += ["Axial"]
        if is_fit:
            sns.lineplot(x=[x*1e12 for x in time_ax], y=[dz*2*time_ax[x]/1e5/1e-7**2 for x in range(len_window)])
            legend += ["Fitted Axial"]

    if not intent or intent == "radial":
        sns.lineplot(x=[x*1e12 for x in time_ax], y=msd_r_n)
        if is_plot:
            legend += ["Radial"]
        if is_fit:
            sns.lineplot(x=[x*1e12 for x in time_ax], y=diff_rad([x*1e12 for x in time_ax], *popt))
            legend += ["Fitted Radial"]

    if is_plot:
        plt.xlabel("Time (ps)")
        plt.ylabel(r"Mean square displacement (nm$^2$)")
        plt.legend(legend)
Пример #11
0
def bins(link, ax_area=[0.2, 0.8], is_norm=False):
    """This function calculates the axial (z-axis) diffusion coefficient as a
    function of the radial distance. This is done by sampling the mean square
    displacement for all molecules in a radial sub volume.

    The mean square displacement is sampled in function
    :func:`poreana.sample.Sample._diffusion_bin`.

    For each bin, the msd is summed up, resulting into a msd slope for each
    bin. Thus, the axial diffusion coefficient can be calculated using

    .. math::

        D_\\text{axial}=\\frac12\\frac{\\text{msd}_\\text{axial}[i]-\\text{msd}_\\text{axial}[j]}{t_i-t_j}.

    Note that the msd is evaluated in the area, where the slope is uniform,
    which means that the first twenty and last twenty percent should be
    neglected.

    If ``is_norm`` is set to **True**, the radius will be normalized in respect
    to the effective radius which means, the last radius that has a
    Diffusion greater than zero is taken

    .. math::

        r_\\text{norm}=\\frac{1}{r_\\text{eff}}r.

    Parameters
    ----------
    link : string
        Link to hdf5 data file generated by the sample routine :func:`poreana.sample.Sample._diffusion_bin`
    ax_area : list, optional
        Bin area percentage to calculate the axial diffusion coefficient
    intent : string, optional
        Set to **plot**, for plotting or set to **line** to only return the
        lineplot, leave empty for nothing
    is_norm : bool, optional
        True to normalize x-axis

    Returns
    -------
    diffusion : list
        List of the slope of the non-normalized diffusion coefficient
    """
    # Load data object
    sample = utils.load(link)

    # Load data
    inp = sample["inp"]
    width = sample["data"]["width"]
    msd_z = sample["data"]["z"]
    norm = sample["data"]["n"]
    len_window = int(inp["len_window"])
    len_step = inp["len_step"]
    len_frame = inp["len_frame"]


    # Normalize
    msd_norm = [[msd_z[i][j]/norm[i][j] if norm[i][j] > 0 else 0 for j in range(len_window)] for i in range(int(inp["bin_num"])+1)]

    # Calculate slope
    f_start = int(ax_area[0]*len_window)
    f_end = int(ax_area[1]*len_window)
    time_ax = [x*len_step*len_frame for x in range(len_window)]
    slope = [(msd_norm[i][f_end]-msd_norm[i][f_start])/(time_ax[f_end]-time_ax[f_start]) for i in range(int(inp["bin_num"])+1)]

    # Calculate diffusion coefficient
    diff = [msd*1e-9**2/2*1e2**2*1e5 for msd in slope]  # 10^-9 m^2s^-1

    return {"width": width, "diff": diff, "is_norm": is_norm}
Пример #12
0
def calculate(link_data, res_cutoff=1, is_normalize=True):
    """This function calculates the values for the adsorption isotherms. This is
    done by counting the number of molecules inside the reservoir and within the
    pore over the whole simulation.

    By normalizing the summation by the number of frames, the resulting value
    is converted to a surface specific concentration inside the pore and volume
    specific concentration within the reservoir.

    The resulting value pair is a point in the adsorption isotherm.

    Parameters
    ----------
    link_data : string
        Link to hdf5, obj or yml file generated by the density sample routine
        :func:`poreana.sample.Sample.init_density`
    res_cutoff : float, optional
        Area of the reservoir to remove from counting on both sides of the
        reservoir
    is_normalize : bool, optional
        True to normalize the number of atoms with the number of frames

    Returns
    -------
    adsorption : dictionary
        Normalized number of molecules outside and insidem and value pair of a
        point on the adsorption isotherm
        :math:`\\left[\\frac{\\text{mmol}}{\\text{l}}\\ ,\\frac{\\mu\\text{mol}}{\\text{m}^2}\\right]`
    """
    # Load data object
    sample = utils.load(link_data)

    # Load pore properties
    pore = sample["pore"]
    res = pore["res"]
    diam = pore["diam"]
    box = pore["box"]
    box[2] -= 2 * res

    # Load bins
    bins = {}
    bins["in"] = sample["data"]["in"]
    bins["ex"] = sample["data"]["ex"]

    # Load Width
    width = {}
    width["in"] = sample["data"]["in_width"]
    width["ex"] = sample["data"]["ex_width"]

    # Load input data
    inp = sample["inp"]
    num_frames = inp["num_frame"]
    entry = inp["entry"]

    # Calculate number of molecules
    num = {}
    num["in"] = sum(bins["in"])
    num["ex"] = sum([
        num_mol for i, num_mol in enumerate(bins["ex"])
        if width["ex"][i] <= res - res_cutoff and width["ex"][i] >= res_cutoff
    ])

    # Normalize number of instances by the number of frames
    num["in"] /= num_frames if is_normalize else 1
    num["ex"] /= num_frames if is_normalize else 1

    # Calculate surface and volume
    surface = 2 * np.pi * (diam) / 2 * (box[2] - 2 * entry)
    volume = 2 * (res - 2 * res_cutoff) * box[0] * box[1]

    # Convert to concentrations
    conc = {}
    conc["mumol_m2"] = utils.mols_to_mumol_m2(num["in"], surface)
    conc["mmol_l"] = utils.mols_to_mmol_l(num["ex"], volume)

    return {"conc": conc, "num": num}
Пример #13
0
def mc_results(link, print_con=False, sections={"box": [], "pore": [], "res": []}, len_step = []):
    """This function prints the MC results of the diffusion calculation.
    Different areas are defined in the **sections** dectionary. Standard areas
    are calculated by leaving the value list empty

    * **box** - For the entire system
    * **pore** - Pore section (only for pore systems)
    * **res** - Resercoir section (only for pore systems)

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5, obj or yml data file generated by the MC Algorithm
        function :func:`poreana.mc.MC.run`
    print_con : bool, optional
        True for printing in console
    sections : dictionary, optinal
        Sections for diffusion calculation

    Returns
    -------
    df_mc_results : DataFrame
        Data frame of the MC Alogrithm inputs
    """
    # Load data
    data = utils.load(link)
    model = data["model"]

    # Process input
    len_step = list(model["len_step"]) if not len_step else len_step

    # Extract system data
    box_z = round(data["pore"]["box"][2], 2) if "pore" in data else round(data["box"]["length"][2], 2)
    res = round(data["pore"]["res"], 2) if "pore" in data else 0

    # Run through sections
    diff_dict = {}
    for key, section in sections.items():
        # Process special cases
        if not section:
            if key=="box":
                area = [0, box_z]
            elif key=="pore":
                area = [res, round(box_z-res, 2)] if "pore" in data else []
            elif key=="res":
                area = [0, res] if "pore" in data else []
            else:
                print("The area of \""+key+"\" needs to be specified")
                return
        else:
            area = section

        # Calculate diffusion in area
        if area:
            diff = diffusion.mc_fit(link, section=area, is_print=False, is_plot=False, len_step=len_step)
            diff_dict[key] = {"Area (nm)": area, "Diffusion (10^-9 m^2s^-1)": diff[0], "Residual (10^-9 m2s^-1)": diff[3]}

    # Create pandas table
    df_mc_results = pd.DataFrame.from_dict(diff_dict, orient="index")

    # If the table has to print in console and not in a jupyter notebook
    if print_con:
        print('\nMC Results')
        print(df_mc_results)

    return df_mc_results
Пример #14
0
    def __init__(self, system, link_traj, mol, atoms=[], masses=[], entry=0.5):
        # Initialize
        self._pore = utils.load(system) if isinstance(system, str) else None
        self._box = system if isinstance(system, list) else []
        self._traj = link_traj
        self._mol = mol
        self._atoms = atoms
        self._masses = masses
        self._entry = entry

        # Set analysis routines
        self._is_density = False
        self._is_gyration = False
        self._is_diffusion_bin = False
        self._is_diffusion_mc = False

        # Get molecule ids
        self._atoms = [atom.get_name() for atom in mol.get_atom_list()] if not self._atoms else self._atoms
        self._atoms = [atom_id for atom_id in range(mol.get_num()) if mol.get_atom_list()[atom_id].get_name() in self._atoms]

        # Check masses
        if not self._masses:
            if len(self._atoms)==mol.get_num():
                self._masses = mol.get_masses()
            elif len(self._atoms) == 1:
                self._masses = [1]
        self._sum_masses = sum(self._masses)

        # Check atom mass consistency
        if self._atoms and not len(self._masses) == len(self._atoms):
            print("Length of variables *atoms* and *masses* do not match!")
            return

        # Get number of frames
        traj = cf.Trajectory(self._traj)
        self._num_frame = traj.nsteps

        # Get numer of residues
        frame = traj.read()
        num_res = len(frame.topology.atoms)/mol.get_num()

        # Check number of residues
        if abs(int(num_res)-num_res) >= 1e-5:
            print("Number of atoms is inconsistent with number of residues.")
            return

        # Create residue list with all relevant atom ids
        self._res_list = {}
        for res_id in range(int(num_res)):
            self._res_list[res_id] = [res_id*mol.get_num()+atom for atom in range(mol.get_num()) if atom in self._atoms]

        # Get pore properties
        self._pore_props = {}
        if self._pore:
            if isinstance(self._pore, pms.PoreCylinder):
                self._pore_props["type"] = "CYLINDER"
            elif isinstance(self._pore, pms.PoreSlit):
                self._pore_props["type"] = "SLIT"
            self._pore_props["res"] = self._pore.reservoir()
            self._pore_props["focal"] = self._pore.centroid()
            self._pore_props["box"] = self._pore.box()
            self._pore_props["box"][2] += 2*self._pore_props["res"]

            # Get pore diameter
            if isinstance(self._pore, pms.PoreCylinder):
                self._pore_props["diam"] = self._pore.diameter()
            elif isinstance(self._pore, pms.PoreSlit):
                self._pore_props["diam"] = self._pore.height()
Пример #15
0
def bins(link_data, area=[[10, 90], [10, 90]], target_dens=0, is_print=True):
    """This function calculates the density inside and outside of the pore.
    This is done by calculating the number density :math:`\\rho_n` and using the
    molar mass :math:`M` of the molecule to determine the mass density
    :math:`\\rho`.

    The basic idea is counting the number of molecules :math:`N_i` in volume
    slices :math:`V_i`, thus getting the number density :math:`\\rho_{n,i}` in
    these sub volumes. Inside the pore this is done by creating a radial slicing
    like the radial distribution function. These sub volumes are calculated by

    .. math::

        V_i^\\text{radial}=\\pi z_\\text{pore}(r_i^2-r_{i-1}^2).

    with pore length :math:`z_\\text{pore}` and radius :math:`r_i` of sub volume
    :math:`i`. This yields

    .. math::

        \\rho_{n,i}^\\text{radial}=\\frac{N_i}{V_i^\\text{radial}}=\\frac{N_i}{\\pi z_\\text{pore}}\\frac{1}{r_i^2-r_{i-1}^2}.

    Outside the pore, the sub volumes are given by

    .. math::

        V_j^\\text{ex}=(x_\\text{pore}\\cdot y_\\text{pore}-\\pi r^2)z_j

    with pore width :math:`x_\\text{pore}`, height :math:`y_\\text{pore}`, pore
    radius :math:`r` and slice width :math:`z_j`. Thus

    .. math::

        \\rho_{n,j}^\\text{ex}=\\frac{N_j}{V_j^\\text{ex}}=\\frac{N_j}{x_\\text{pore}\\cdot y_\\text{pore}-\\pi r^2}\\frac{1}{z_j}.

    Note that the outside refers to the reservoirs of the pore simulation.
    Therefore the slices add up to the reservoir length :math:`z_{res}`.
    Since there is a reservoir on each side, they are brought together
    by translating the atom coordinates to one of the reservoirs. Since the
    outside density refers to the density of the outside surface, it does
    not contain the cylindrical extension of the pore inside the reservoirs.

    Finally, the mass density is calculated by

    .. math::

        \\rho=\\frac M{N_A}\\rho_n

    with Avogadro constant :math:`N_A`. The units are then transformed to
    :math:`\\frac{\\text{kg}}{\\text m^3}` by

    .. math::

        [\\rho]=\\frac{[M]\\frac{\\text{g}}{\\text{mol}}}{[N_A]10^{23}\\frac{\\#}{\\text{mol}}}[\\rho_n]\\frac{\\#}{\\text{nm}^3}
               =\\frac{[M]}{[N_A]}[\\rho_n]\\cdot10\\frac{\\text{kg}}{\\text m^3}

    where the square brackets mean, that only the variables value is taken.
    Since finding full molecules in a sub volume is difficult, the atoms
    of the specified molecule are counted in the sub volumes and the result
    is then divided by the number of atoms the molecule consists of.

    Parameters
    ----------
    link_data : string
        Link to hdf5, obj or yml data file generated by the sample routine :func:`poreana.sample.Sample.init_density`
    area : list,  optional
        Bin areas to calculate the mean number density from (pore, exout)
    target_dens : float, optional
        Target density in :math:`\\frac{\\text{kg}}{\\text{m}^3}`
    is_print : bool, optional
        True to print output

    Returns
    -------
    density : dictionary
        dictonary with inputs and the calculated density profiles and mean
        density
    """
    # Load data object
    sample = utils.load(link_data)
    is_pore = "pore" in sample

    # Load bins
    bins = {}
    bins["in"] = sample["data"]["in"] if is_pore else []
    bins["ex"] = sample["data"]["ex"]

    # Load width
    width = {}
    width["in"] = sample["data"]["in_width"] if is_pore else []
    width["ex"] = sample["data"]["ex_width"]

    # Load input data
    inp = sample["inp"]
    bin_num = int(inp["bin_num"])
    num_frame = inp["num_frame"]
    entry = inp["entry"]
    mass = inp["mass"]
    remove_pore_from_res = inp["remove_pore_from_res"]

    # Load pore data
    if is_pore:
        pore = sample["pore"]
        pore_type = pore["type"]
        res = pore["res"]
        diam = pore["diam"]
        box = pore["box"]
    else:
        box = sample["box"]["length"]

    # Calculate bin volume
    volume = {}
    ## Interior
    if is_pore and pore_type == "CYLINDER":
        volume["in"] = [
            math.pi * (box[2] - 2 * res - 2 * entry) *
            (width["in"][i + 1]**2 - width["in"][i]**2)
            for i in range(0, bin_num + 1)
        ]
    elif is_pore and pore_type == "SLIT":
        volume["in"] = [
            box[0] * (box[2] - 2 * res - 2 * entry) *
            (width["in"][i + 1] - width["in"][i]) * 2
            for i in range(0, bin_num + 1)
        ]
    ## Exterior
    if is_pore:
        if remove_pore_from_res and pore_type == "CYLINDER":
            volume["ex"] = [
                2 * width["ex"][1] * (box[0] * box[1] - math.pi *
                                      (diam / 2)**2)
                for i in range(bin_num + 1)
            ]
        elif remove_pore_from_res and pore_type == "SLIT":
            volume["ex"] = [
                2 * width["ex"][1] * box[0] * (box[1] - diam)
                for i in range(bin_num + 1)
            ]
        else:
            volume["ex"] = [
                2 * width["ex"][1] * box[0] * box[1]
                for i in range(bin_num + 1)
            ]
    else:
        volume["ex"] = [
            width["ex"][1] * box[0] * box[1] for i in range(bin_num + 1)
        ]

    # Calculate the number density
    num_dens = {}
    num_dens["in"] = [
        bins["in"][i] / volume["in"][i] / num_frame for i in range(bin_num + 1)
    ] if is_pore else []
    num_dens["ex"] = [
        bins["ex"][i] / volume["ex"][i] / num_frame for i in range(bin_num + 1)
    ]

    # Calculate the mean in the selected area
    mean = {}
    mean["in"] = np.mean(
        num_dens["in"][area[0][0]:area[0][1]]) if is_pore else []
    mean["ex"] = np.mean(num_dens["ex"][area[1][0]:area[1][1]])

    # Calculate Density
    dens = {}
    dens["in"] = mass * 10 / 6.022 * mean["in"] if is_pore else []
    dens["ex"] = mass * 10 / 6.022 * mean["ex"]

    # Calculate difference to target density
    num_diff = (target_dens / mass / 10 * 6.022 -
                mean["ex"]) * box[0] * box[1] * res * 2 if target_dens else 0

    # Output
    if is_print:
        if is_pore:
            print("Density inside  Pore = " + "%5.3f" % mean["in"] +
                  " #/nm^3 ; " + "%7.3f" % dens["in"] + " kg/m^3")
            print("Density outside Pore = " + "%5.3f" % mean["ex"] +
                  " #/nm^3 ; " + "%7.3f" % dens["ex"] + " kg/m^3")
        else:
            print("Density = " + "%5.3f" % mean["ex"] + " #/nm^3 ; " +
                  "%7.3f" % dens["ex"] + " kg/m^3")
        if target_dens:
            print("Density difference   = " + "%5.3f" %
                  (target_dens - dens["ex"]) + " kg/m^3 ; " + "%4.2f" %
                  ((1 - dens["ex"] / target_dens) * 100) + " % ; " +
                  "%3i" % num_diff + " #")

    # Return output
    return {
        "sample": sample,
        "num_dens": num_dens,
        "mean": mean,
        "dens": dens,
        "diff": num_diff
    }
Пример #16
0
def mc_lag_time(link, print_con=False):
    """This function prints the final coefficients of the profile for
    every lag time profile.

    Parameters
    ----------
    link : string
        Link to the diffusion hdf5, obj or yml data file generated by the MC Algorithm function
        :func:`poreana.mc.MC.run`
    print_con: bool, optional
        True for printing in console

    Returns
    -------
    df_results: obj
        Data frame of profile coefficients
    """
    # Load Results from the output object file
    data = utils.load(link)

    # Load results
    results = data["output"]
    diff_coeff = results["diff_coeff"]
    df_coeff = results["df_coeff"]

    # Load model inputs
    model = data["model"]
    len_step = model["len_step"]
    nD = model["nD"]
    nF = model["nF"]

    # Initialize data dictionary for the diffusion profile coefficients
    data = {}

    # Save diffusion profile coefficients on data dictionary
    for i in len_step:
        data[i] = [str("%.4e" % diff_coeff[i][j]) for j in range(int(nD))]

    # Pandas table
    diff_coeff = pd.DataFrame(data, index=list(np.arange(1, int(nD)+1)), columns=list(len_step))
    diff_coeff = pd.DataFrame(diff_coeff.rename_axis('Step Length', axis=1))

    # If the table has to print in console
    if print_con:
        print('\nDiffusion coefficients')
        print(diff_coeff)

    # Set styler for pandas table in jupyter
    styler = diff_coeff.style.set_caption('Diffusion coefficients')
    diff_coeff = styler.set_properties(**{'text-align': 'right'})
    diff_coeff = diff_coeff.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

    # Initialize data dictionary for the diffusion profile coefficients
    data = {}

    # Save free energy profile coefficients on data dictionary
    for i in len_step:
        data[i] = [str("%.4e" % df_coeff[i][j]) for j in range(int(nF))]

    # Pandas table
    df_coeff = pd.DataFrame(data, index=list(np.arange(1, int(nF)+1)), columns=list(len_step))
    df_coeff = pd.DataFrame(df_coeff.rename_axis('Step Length', axis=1))

    # If the table has to print in console and not in a jupyter notebook
    if print_con:
        print('\nFree energy coefficients')
        print(df_coeff)

    # Set styler for pandas table in jupyter
    styler = df_coeff.style.set_caption('Free energy coefficients')
    df_coeff = styler.set_properties(**{'text-align': 'right'})
    df_coeff = df_coeff.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

    return diff_coeff, df_coeff
Пример #17
0
def plot(data_link_gyr, data_link_dens, intent="", is_mean=False):
    """This function plots the gyration radius. If an intent is
    given instead, only a plot-function will be called. Available
    options for ``intent`` are

    * empty string - Create subplots for the density inside and outside the pore
    * **in** - Create plot for the density inside pore
    * **ex** - Create plot for the density outside pore

    Parameters
    ----------
    data_link_dens : string
        Link to density data object generated by the sample rountine
        :func:`poreana.sample.density`
    data_link_gyr : string
        Link to gyration data object generated by the sample routine
        :func:`poreana.sample.gyration`
    intent : string, optional
        Intent for plotting
    is_mean : bool, optional
        True to plot mean values

    Returns
    -------
    mean : dictionary
        Mean value of the radius of gyration inside and outside the pore in nm
    """
    # Load data
    gyr = utils.load(data_link_gyr)
    dens = utils.load(data_link_dens)
    is_pore = "pore" in gyr
    width = {}
    width["in"] = gyr["data"]["in_width"][:-1] if is_pore else []
    width["ex"] = gyr["data"]["ex_width"]

    areas = ["in", "ex"] if is_pore else ["ex"]

    # Divide gyration radius by density in bins
    gyration = {
        area: [
            gyr["data"][area][i] /
            dens["data"][area][i] if dens["data"][area][i] else 0
            for i in range(len(gyr["data"][area]))
        ]
        for area in areas
    }

    # Calculate mean gyration radius
    mean = {area: sum(gyration[area]) / len(gyration[area]) for area in areas}

    # Full plot
    if not intent:
        plt.subplot(211)
        sns.lineplot(x=width["in"], y=gyration["in"])
        if is_mean:
            sns.lineplot(x=width["in"], y=[mean["in"] for x in width["in"]])

        plt.xlim([0, width["in"][-1]])
        plt.xlabel("Distance from pore center (nm)")
        plt.ylabel(r"Radius (nm)")
        plt.legend(["Gyration radius", "Mean"])

        plt.subplot(212)
        sns.lineplot(x=width["ex"], y=gyration["ex"])
        if is_mean:
            sns.lineplot(x=width["ex"], y=[mean["ex"] for x in width["ex"]])

        plt.xlim([0, width["ex"][-1]])
        plt.xlabel("Distance from reservoir end (nm)")
        plt.ylabel(r"Radius (nm)")
        plt.legend(["Gyration radius", "Mean"])

    # Intent plots
    else:
        if intent not in ["in", "ex"]:
            print("Invalid intent. Check documentation for available options.")
            return

        sns.lineplot(x=width[intent], y=gyration[intent])
        plt.xlim([0, width[intent][-1]])

    return mean
Пример #18
0
def bins_plot(data_link_angle, data_link_dens, intent="", is_mean=False, is_norm=False, kwargs={}):
    """This function plots the angle. If an intent is given instead, only a
    plot-function will be called. Available options for ``intent`` are

    * empty string - Create subplots for the density inside and outside the pore
    * **in** - Create plot for the density inside pore
    * **ex** - Create plot for the density outside pore

    Parameters
    ----------
    data_link_dens : string
        Link to density hdf5, obj or yml data file generated by the sample rountine
        :func:`poreana.sample.Sample.init_density`
    data_link_angle : string
        Link to angle data object generated by the sample routine
        :func:`poreana.sample.Sample.init_angle`
    intent : string, optional
        Intent for plotting
    is_mean : bool, optional
        True to plot mean values
    is_norm : bool, optional
        True to normalize x-axis
    kwargs: dict, optional
        Dictionary with plotting parameters (only with given intent)

    Returns
    -------
    mean : dictionary
        Mean value of the angle inside and outside the pore in nm
    """
    # Load data
    angle = utils.load(data_link_angle)
    dens = utils.load(data_link_dens)

    is_pore = "pore" in angle
    width = {}
    width["in"] = angle["data"]["in_width"][:-1] if is_pore else []
    width["ex"] = angle["data"]["ex_width"]

    if is_norm:
        for key, val in width.items():
            x_max = val[-1]
            width[key] = [x/x_max for x in val]

    areas = ["in", "ex"] if is_pore else ["ex"]

    # Divide angle by density in bins
    angle = {area: [angle["data"][area][i]/dens["data"][area][i] if dens["data"][area][i] else 0 for i in range(len(angle["data"][area]))] for area in areas}

    # Calculate mean angle
    mean = {area: sum(angle[area])/len(angle[area]) for area in areas}

    # Full plot
    if not intent:
        plt.subplot(211)
        sns.lineplot(x=width["in"], y=angle["in"])
        if is_mean:
            sns.lineplot(x=width["in"], y=[mean["in"] for x in width["in"]])

        plt.xlim([0, width["in"][-1]])
        plt.xlabel("Distance from pore center (nm)")
        plt.ylabel(r"Angle (deg)")
        plt.legend(["Angle", "Mean"])

        plt.subplot(212)
        sns.lineplot(x=width["ex"], y=angle["ex"])
        if is_mean:
            sns.lineplot(x=width["ex"], y=[mean["ex"] for x in width["ex"]])

        plt.xlim([0, width["ex"][-1]])
        plt.xlabel("Distance from reservoir end (nm)")
        plt.ylabel(r"Angle (deg)")
        plt.legend(["Angle", "Mean"])

    # Intent plots
    else:
        if intent not in ["in", "ex"]:
            print("Invalid intent. Check documentation for available options.")
            return

        sns.lineplot(x=width[intent], y=angle[intent], **kwargs)
        plt.xlim([0, width[intent][-1]])

    return mean