def test_DCplot(): """ DCplot should return an axes object when a DiffSystem is passed """ Xr = [[0, .4], [.6, 1]] X = np.linspace(0, 1, 101) DC = np.linspace(1, 10, 101) * 1e-14 diffsys = DiffSystem(Xr=Xr, X=X, DC=DC) ax = DCplot(diffsys, label='test') assert isinstance(ax, Axes)
def test_SFplot(): """ SFplot should return an axes object when a DiffProfile and time is passed """ diffsys = DiffSystem(Xr=[0, 1], X=[0, 1], DC=[1e-14, 1e-13]) dis = mesh(0, 1000, 201) profile_init = step(dis, 500, diffsys) time = 200 * 3600 profile = sphSim(profile_init, diffsys, time) ax = SFplot(profile, time, Xlim=[0, 1], label='test') assert isinstance(ax, Axes)
def test_system(): """ DiffSystem constructor """ # Construct with X, DC Xr = [[0, .4], [.6, 1]] X = np.linspace(0, 1, 101) DC = np.linspace(1, 10, 101) * 1e-14 diffsys = DiffSystem(Xr=Xr, X=X, DC=DC) assert diffsys.Xr.shape == (diffsys.Np, 2) assert isinstance(diffsys.name, str) assert len(diffsys.Dfunc) == diffsys.Np for i in range(diffsys.Np): assert len(diffsys.Dfunc[i][0]) == len(diffsys.Dfunc[i][1]) # Construct with Dfunc diffsys1 = DiffSystem(Xr=Xr, Dfunc=diffsys.Dfunc) assert diffsys1.Xr.shape == (diffsys1.Np, 2) assert len(diffsys1.Dfunc) == diffsys1.Np
def test_automesh(): """ automesh should return a meshed array whose length is within its range. """ diffsys = DiffSystem(Xr=[0, 1], X=[0, 1], DC=[1e-14, 1e-13]) dis = mesh(0, 1000, 201) profile_init = step(dis, 500, diffsys) time = 200 * 3600 profile = sphSim(profile_init, diffsys, time) dism = automesh(profile, diffsys, n=[300, 400]) assert len(dism) >= 300 and len(dism) <= 400
def test_dispfunc(): """ disfunc and profilefunc should give functions to copy profile data. """ diffsys = DiffSystem(Xr=[0, 1], X=[0, 1], DC=[1e-14, 1e-13]) dis = mesh(0, 1000, 201) profile_init = step(dis, 500, diffsys) time = 200 * 3600 profile = sphSim(profile_init, diffsys, time) fX = profilefunc(profile) fdis = disfunc(profile.dis, profile.X) assert np.all(abs(splev(dis, fX) - profile.X) < 0.01) assert np.all(abs(splev(profile.X, fdis) - dis) < 0.1)
def test_sphsim(): """ Single-phase system simulation. Offset of the simulated matano plane should be very small. """ diffsys = DiffSystem(Xr=[0, 1], X=[0, 1], DC=[1e-14, 1e-14]) dis = mesh(0, 1000, 201) profile_init = step(dis, 500, diffsys) time = 200 * 3600 profile_final = sphSim(profile_init, diffsys, time) mpi = matanocalc(profile_init, [0, 1]) mpf = matanocalc(profile_final, [0, 1]) assert isinstance(profile_final, DiffProfile) assert len(profile_final.If) == diffsys.Np + 1 assert abs(mpi - mpf) < 1
def test_mphsim(): """ Multiple-phase system simulation. Offset of the simulated matano plane should be very small. """ Xr = [[0, .4], [.6, 1]] X = np.linspace(0, 1, 101) DC = np.linspace(1, 2, 101) * 1e-14 diffsys = DiffSystem(Xr=Xr, X=X, DC=DC) dis = mesh(0, 1000, 201) profile_init = step(dis, 500, diffsys) time = 200 * 3600 profile_final = mphSim(profile_init, diffsys, time) mpi = matanocalc(profile_init, [0, 1]) mpf = matanocalc(profile_final, [0, 1]) assert isinstance(profile_final, DiffProfile) assert len(profile_final.If) == diffsys.Np + 1 assert abs(mpi - mpf) < 1
def DCbias(diffsys, X, deltaD, r=0.3, efunc=None): """ This function creates bias for a diffusion coefficients profile Parameters ---------- diffsys : DiffSystem Original diffusion coefficients. X : float Concentration location which has largest bias. deltaD : float Scale of the bias. D *= 10**deltaD is the maximum of bias. r : float, optional Bias at X will create smaller bias in range of (X-r, X+r) efunc : function, optional Function to create bias on diffusion coefficients. Default = pydiffusion.utils.efunc efunc(X, Xf, r) efunc should return 1 as the maximum when X == Xf, and return 0 when abs(X-Xf) == r. Returns ------- fDbias : DiffSystem Diffusion coefficients with bias. """ Xr, Np, fD = diffsys.Xr, diffsys.Np, diffsys.Dfunc efunc = efunc_default if efunc is None else efunc fDbias = [] for i in range(Np): if X >= Xr[i, 0] and X <= Xr[i, 1]: Xf = np.linspace(Xr[i, 0], Xr[i, 1], 30) Df = np.exp(splev(Xf, fD[i])) eid = np.where((Xf >= X - r) & (Xf <= X + r))[0] for j in eid: Df[j] *= 10**(deltaD * efunc(X, Xf[j], r)) fDbias += [splrep(Xf, np.log(Df))] else: fDbias += [fD[i]] return DiffSystem(Xr, fDbias)
def FSA(profile_exp, profile_sm, diffsys, time, Xlim=[], n=[400, 500], w=None, f=None, alpha=0.3, name=''): """ Forward Simulation Analysis Extract diffusion coefficients based on a diffusion profile. Please do not close any plot window during the FSA process. This is the final step of FSA. Parameters ---------- profile_exp : DiffProfile Experimental diffusion profile, used for comparison with simulation results. profile_sm : DiffProfile Diffusion profile after data smooth on experimental profile. diffsys : DiffSystem Diffusion coefficients time : float Diffusion time in seconds Xlim : list (float), optional Passed to 'pydiffusion.Dtools.SF', 'pydiffusion.utils.step'. Indicates the left and right concentration limits for calculation. Default value = [profile.X[0], profile.X[-1]]. n : list. optional Passed to 'pydiffusion.utils.automesh'. Meshing number range, default = [400, 500]. w : list, optional Weights of each phase to calculate error. Passed to 'pydiffusion.utils.error_profile'. f : function of Meshing Keyword argument of automesh() alpha : float Keyword argument of automesh() name : str, optional Name the output DiffProfile Returns ------- profile_sim : DiffProfile Simulated diffusion profile after FSA. diffsys_sim : DiffSystem Calculated diffusion efficients by FSA. Examples -------- After datasmooth() and Dmodel(), FSA can be performed to calculate accurate diffusion coefficients: >>> ds = datasmooth(exp) >>> dsys = Dmodel(ds, time) >>> fsa = FSA(exp, ds, dsys, time) """ # Create step profile on meshed grids dism = automesh(profile=profile_sm, diffsys=diffsys, n=n, f=f, alpha=alpha) matano = matanocalc(profile_sm, Xlim) if Xlim == [] and profile_sm.X[-1] < profile_sm.X[0]: profile_init = step(dism, matano, diffsys, [diffsys.Xr[-1, 1], diffsys.Xr[0, 0]]) else: profile_init = step(dism, matano, diffsys, Xlim) # Determine the stop criteria of forward simulations error_sm = error_profile(profile_sm, profile_exp) ipt = input( 'Default error = %.6f\nInput the stop criteria of error: [%.6f]\n' % (error_sm, error_sm * 2)) error_stop = error_sm * 2 if ipt == '' else float(ipt) # If there is no Xspl info in diffsys, use Phase Mode # else: ask if use Phase or Point Mode if diffsys.Xspl is not None: ipt = input( 'Use Phase Mode? [n]\n(The shape of diffusivity curve does not change)\n' ) pp = False if 'y' in ipt or 'Y' in ipt else True else: pp = False if name == '': name = profile_exp.name + '_FSA' # Diffusion coefficients used for forward simulations diffsys_sim = DiffSystem(diffsys.Xr, diffsys.Dfunc, Xspl=diffsys.Xspl, name=name) # Plot FSA status fig = plt.figure('FSA', figsize=(16, 6)) ax1, ax2 = fig.add_subplot(121), fig.add_subplot(122) profileplot(profile_exp, ax1, ls='none', marker='o', c='b', fillstyle='none') profileplot(profile_sm, ax1, ls='-', c='g', lw=1) SFplot(profile_sm, time, Xlim, ax2, ls='none', c='b', marker='.') DCplot(diffsys_sim, ax2, ls='-', c='r', lw=2) plt.draw() plt.tight_layout() plt.pause(0.1) n_sim = 0 while True: # Simulation n_sim += 1 profile_sim = mphSim(profile_init, diffsys_sim, time, name=name) error_sim = error_profile(profile_sim, profile_exp, w) print('Simulation %i, error = %f(%f)' % (n_sim, error_sim, error_stop)) # Plot simulation results ax1.cla() ax2.cla() profileplot(profile_exp, ax1, ls='none', marker='o', c='b', fillstyle='none') profileplot(profile_sm, ax1, ls='-', c='g', lw=1) profileplot(profile_sim, ax1, ls='-', c='r', lw=2) SFplot(profile_sm, time, Xlim, ax2, ls='none', c='b', marker='.') DCplot(diffsys_sim, ax2, ls='-', c='r', lw=2) plt.draw() plt.tight_layout() # DC adjust Dfunc_adjust = [0] * diffsys_sim.Np # If error > stop criteria, continue simulation by auto DC adjustment if error_sim > error_stop: for ph in range(diffsys_sim.Np): try: Dfunc_adjust[ph] = Dadjust(profile_sm, profile_sim, diffsys_sim, ph, pp) except (ValueError, TypeError) as error: ita_finish() raise error diffsys_sim.Dfunc = Dfunc_adjust # If error < stop criteria or simulate too many times if error_sim <= error_stop or n_sim > 9: ita_start() # Ask if exit ipt = ask_input('Satisfied with FSA? [n]') if 'y' in ipt or 'Y' in ipt: ita_finish() break # If use Point Mode if diffsys_sim.Xspl is not None: ipt = ask_input('Use Point Mode (y) or Phase Mode (n)? [y]') pp = False if 'n' in ipt or 'N' in ipt else True if pp: for ph in range(diffsys_sim.Np): try: Dfunc_adjust[ph] = Dadjust(profile_sm, profile_sim, diffsys_sim, ph, pp) except (ValueError, TypeError) as error: ita_finish() raise error diffsys_sim.Dfunc = Dfunc_adjust DCplot(diffsys_sim, ax2, ls='-', c='m', lw=2) plt.draw() plt.pause(0.1) ita_finish() continue # Phase Mode, ask if use manual input for each phase pp = False ipt = input('Phase Mode\nManually input for each phase? [n]') manual = True if 'y' in ipt or 'Y' in ipt else False for ph in range(diffsys_sim.Np): if manual: ipt = input( 'Input deltaD for phase # %i:\n(DC = DC * 10^deltaD, default deltaD = auto)\n' % (ph + 1)) deltaD = float(ipt) if ipt != '' else None else: deltaD = None try: Dfunc_adjust[ph] = Dadjust(profile_sm, profile_sim, diffsys_sim, ph, pp, deltaD) except (ValueError, TypeError) as error: ita_finish() raise error # Apply the adjustment to diffsys_sim diffsys_sim.Dfunc = Dfunc_adjust DCplot(diffsys_sim, ax2, ls='-', c='m', lw=2) plt.draw() plt.pause(0.1) ita_finish() return profile_sim, diffsys_sim
def Dmodel(profile, time, Xspl=None, Xlim=[], output=True, name=''): """ Given the diffusion profile and diffusion time, modeling the diffusion coefficients for each phase. Please do not close any plot window during the modeling process. Dmodel() is the second step of the forward simulation analysis (FSA). Parameters ---------- profile : DiffProfile Diffusion profile. Multiple phase profile must be after datasmooth to identify phase boundaries. time : float Diffusion time in seconds. Xspl : list, optional If Xspl is given, Dmodel will be done automatically. Xlim : list, optional Left and Right limit of diffusion coefficients. Xlim is also passed to SF function to calculate diffusion coefficients initially. output : bool, optional Plot Dmodel result or not. Can be False only if Xspl is given. name : str, optional Name of the output DiffSystem Examples -------- After datasmooth(), a initial diffusion coefficients will be established before FSA(): >>> ds = datasmooth(exp) >>> dsys = Dmodel(ds, time) """ if not isinstance(Xlim, list): raise TypeError('Xlim must be a list') if len(Xlim) != 2 and Xlim != []: raise ValueError( 'Xlim must be an empty list or a list with length = 2') dis, X = profile.dis, profile.X # If input Xlim doesn't follow trend of X, correct it if Xlim != [] and (X[-1] - X[0]) * (Xlim[1] - Xlim[0]) < 0: Xlim = Xlim[::-1] # Initial set-up of Xr (phase boundaries) Xlim = [X[0], X[-1]] if Xlim == [] else Xlim DC = SF(profile, time, Xlim) Xr = np.array(Xlim, dtype=float) for i in range(len(dis) - 1): if dis[i] == dis[i + 1]: Xr = np.insert(Xr, -1, [X[i], X[i + 1]]) Np = len(Xr) // 2 Xr.sort() Xr = Xr.reshape(Np, 2) fD = [0] * Np ita_start() # Choose Spline or UnivariateSpline if Xspl is None or output: ax = plt.figure().gca() ax.semilogy(X, DC, 'b.') ax.set_title('Sauer-Freise result') ax.set_xlabel('Mole fraction') ax.set_ylabel('Diffusion Coefficients ' + '$(m^2/s)$') plt.tight_layout() ipt = ask_input( 'Use Spline (y) or UnivariateSpline (n) to model diffusion coefficients? [y]\n' ) choice = False if 'N' in ipt or 'n' in ipt else True # Xspl provided, no need for manually picking Xspl if Xspl is not None: if len(Xspl) != Np: raise ValueError('Xspl must has a length of phase number') for i in range(Np): if Xr[i, 1] > Xr[i, 0]: pid = np.where((X >= Xr[i, 0]) & (X <= Xr[i, 1]))[0] else: pid = np.where((X <= Xr[i, 0]) & (X >= Xr[i, 1]))[0] # Spline if choice: try: Dp = Dpcalc(X, DC, Xspl[i]) fD[i] = Dfunc_spl(Xspl[i], Dp) except (ValueError, TypeError) as error: ita_finish() raise error # UnivariateSpline else: try: fD[i] = Dfunc_uspl(X, DC, Xspl[i], Xr[i]) except (ValueError, TypeError) as error: ita_finish() raise error print('DC modeling finished, Xspl info:') print(Xspl) if output: ax.cla() ax.set_title('DC Modeling Result') ax.semilogy(X, DC, 'b.') for i in range(Np): Xf = np.linspace(Xr[i, 0], Xr[i, 1], 30) ax.semilogy(Xf, np.exp(splev(Xf, fD[i])), 'r-') ax.set_xlabel('Mole fraction') ax.set_ylabel('Diffusion Coefficients ' + '$(m^2/s)$') plt.tight_layout() plt.pause(0.1) plt.show() ita_finish() return DiffSystem(Xr, Dfunc=fD, Xspl=Xspl) Xspl = [0] * Np if choice else None for i in range(Np): if Xr[i, 1] > Xr[i, 0]: pid = np.where((X >= Xr[i, 0]) & (X <= Xr[i, 1]))[0] else: pid = np.where((X <= Xr[i, 0]) & (X >= Xr[i, 1]))[0] # Spline if choice: while True: DC_real = [ k for k in DC[pid] if not np.isnan(k) and not np.isinf(k) ] DCmean = np.mean(DC_real) for k in pid: if np.isnan(DC[k]) or np.isinf( DC[k]) or abs(np.log10(DC[k] / DCmean)) > 5: DC[k] = DCmean ax.cla() ax.semilogy(X[pid], DC[pid], 'b.') ax.set_xlabel('Mole fraction') ax.set_ylabel('Diffusion Coefficients ' + '$(m^2/s)$') ax.set_title('Phase %i' % (i + 1)) plt.draw() msg = '# of spline points: 1 (constant), 2 (linear), >2 (spline)\n' ipt = ask_input(msg + 'input # of spline points\n') ax.set_title('Phase %i: Select %i points of Spline' % (i + 1, int(ipt))) plt.pause(0.1) Xp = np.array(plt.ginput(int(ipt)))[:, 0] try: Dp = Dpcalc(X, DC, Xp) fD[i] = Dfunc_spl(Xp, Dp) except (ValueError, TypeError) as error: ita_finish() raise error Xspl[i] = list(Xp) Xf = np.linspace(Xr[i, 0], Xr[i, 1], 30) ax.semilogy(Xf, np.exp(splev(Xf, fD[i])), 'r-', lw=2) plt.draw() ipt = ask_input('Continue to next phase? [y]') redo = False if 'N' in ipt or 'n' in ipt else True if redo: break # UnivariateSpline else: while True: ax.cla() ax.semilogy(X[pid], DC[pid], 'b.') ax.set_xlabel('Mole fraction') ax.set_ylabel('Diffusion Coefficients ' + '$(m^2/s)$') #ax.set_title('Phase %i' % (i+1)) ax.set_title( 'Phase %i: Select 2 boundaries for UnivariateSpline' % (i + 1)) plt.draw() plt.pause(0.1) Xp = np.array(plt.ginput(2))[:, 0] #ipt = ask_input('input 2 boundaries for UnivariateSpline\n') #Xp = np.array([float(x) for x in ipt.split()]) try: fD[i] = Dfunc_uspl(X, DC, Xp, Xr[i]) except (ValueError, TypeError) as error: ita_finish() raise error Xf = np.linspace(Xr[i, 0], Xr[i, 1], 30) ax.semilogy(Xf, np.exp(splev(Xf, fD[i])), 'r-', lw=2) plt.draw() ipt = ask_input('Continue to next phase? [y]') redo = False if 'N' in ipt or 'n' in ipt else True if redo: break ita_finish() print('DC modeling finished, Xspl info:') print(Xspl) ax.cla() ax.set_title('DC Modeling Result') ax.semilogy(X, DC, 'b.') for i in range(Np): Xf = np.linspace(Xr[i, 0], Xr[i, 1], 30) ax.semilogy(Xf, np.exp(splev(Xf, fD[i])), 'r-') ax.set_xlabel('Mole fraction') ax.set_ylabel('Diffusion Coefficients ' + '$(m^2/s)$') plt.tight_layout() plt.pause(0.1) plt.show() if name == '': name = profile.name + '_%.1fh_Dmodeled' % (time / 3600) return DiffSystem(Xr, Dfunc=fD, Xspl=Xspl, name=name)
import numpy as np import matplotlib.pyplot as plt import pandas as pd from pydiffusion.core import DiffSystem from pydiffusion.utils import step, mesh from pydiffusion.simulation import mphSim from pydiffusion.plot import profileplot, DCplot from pydiffusion.io import read_csv, save_csv # Create diffusion system with constant DC diffsys = DiffSystem(Xr=[0, 1], X=[0, 1], DC=[1e-14, 1e-14], name='Constant D') # Create initial step profile dis = mesh(0, 1000, 501) profile_init = step(dis, 500, diffsys, name='Intitial step profile') fig = plt.figure(figsize=(16, 6)) ax1, ax2 = fig.add_subplot(121), fig.add_subplot(122) ax1.set_title('Diffusion Coefficients', fontsize=15) ax2.set_title('Initial Step Profile', fontsize=15) DCplot(diffsys, ax1) profileplot(profile_init, ax2) # Diffusion simulation using the setups time = 200 * 3600 profile_final = mphSim(profile_init, diffsys, time) ax = profileplot(profile_init, ls='--') profileplot(profile_final, ax, c='r') # Read diffusion coefficients data of Ni-Mo system
def read_csv(filename, Xlim=None, name=''): """ Read diffusion data from csv file. Parameters ---------- filename : str csv file path. Xlim : list (length of 2), optional A list to determine the two ends solubilities. name : str, optional Name the output DiffProfile and DiffSystem Returns ------- profile : DiffProfile output DiffProfile object. diffsys : DiffSystem output DiffSystem object. Examples -------- Both profile and diffusivity data: >>> profile, dsys = read_csv('data.csv') Profile data only: >>> profile, _ = read_csv('data.csv') """ data = pd.read_csv(filename) if 'X' not in data.columns: raise ValueError('No column X in csv file') X = np.array(data['X']) # Auto rename if name == '': if '/' in filename: r = filename.rfind('/') elif '\\' in filename: r = filename.rfind('\\') else: r = -1 if filename.endswith('.csv'): name = filename[r + 1:-4] else: name = filename[r + 1:] if 'dis' in data.columns: dis = np.array(data['dis']) If = [] XIf = [] for i in range(len(dis) - 1): if dis[i] == dis[i + 1]: If += [dis[i]] XIf += [X[i], X[i + 1]] profile = DiffProfile(dis, X, If, name=name) if Xlim is None: XIf = np.array([X[0]] + XIf + [X[-1]]) else: XIf = np.array([Xlim[0]] + XIf + [Xlim[-1]]) Xr = XIf.reshape((len(XIf) // 2, 2)) if 'DC' in data.columns: DC = np.array(data['DC']) else: X = DC = None if Xr is not None: diffsys = DiffSystem(Xr, X=X, DC=DC, name=name) return profile, diffsys