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
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
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
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
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
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
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
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])
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)")
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)
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}
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}
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
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()
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 }
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
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
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