예제 #1
0
def step(dis, matano, diffsys, Xlim=[], name=''):
    """
    Create a step profile at the Matano Plane.

    Parameters
    ----------
    dis : numpy.array
        Distance information.
    matano : float
        Matano plane location (micron).
    diffsys : DiffSystem
        The diffusion system information for initial setups before simulation.
    Xlim : list (float), optional
        Indicates the left and right concentration limits for step profile.
        Default value = [diffsys.Xr[0][0], diffsys.Xr[-1][1]].
        For a decreasing step, Xlim must be provided.
    name : str, optional
        Name the output DiffProfile

    Returns
    -------
    profile : DiffProfile
        Step profile can be used for input of pydiffusion.simulation.mphSim

    Examples
    --------
    Create a step profile on a meshed grid (ascending):

    >>> dis = mesh()
    >>> init_profile = step(dis, 200, dsys, Xlim=[0, 1])

    Create a reversed step profile (descending):

    >>> init_profile = step(dis, 200, dsys, Xlim=[1, 0])

    """
    Np = diffsys.Np
    if Xlim == []:
        XL, XR = diffsys.Xr[0][0], diffsys.Xr[-1][1]
    else:
        [XL, XR] = Xlim
    X = np.ones(len(dis)) * XL
    X[dis > matano] = XR
    if name == '':
        name = diffsys.name + '_step'
    if Np == 1:
        return DiffProfile(dis, X, name=name)
    else:
        If = [0] * (Np - 1)
        Ip = np.where(X == XR)[0][0]
        d = dis[Ip] - dis[Ip - 1]
        for i in range(Np - 1):
            If[i] = dis[Ip - 1] + d / (Np + 1) * (i + 1)
        return DiffProfile(dis, X, If, name=name)
예제 #2
0
def test_profileplot():
    """
    profileplot should return an axes object when a DiffProfile is passed
    """
    dis = np.linspace(0, 100, 101)
    X = np.linspace(0, 1, 101)
    If = [30.5, 60.5]
    profile = DiffProfile(dis=dis, X=X, If=If)
    ax = profileplot(profile, label='test')

    assert isinstance(ax, Axes)
예제 #3
0
def sphSim(profile, diffsys, time, output=True, name=''):
    """
    Single-Phase Diffusion Simulation

    Parameters
    ----------
    profile : DiffProfile
        Initial diffusion profile before simulation.
    diffsys : DiffSystem
        Diffusion coefficients.
    time : float
        time in seconds.
    output : boolean, optional
        Print simulation progress, default = True.
    name : str, optional
        Name the output DiffProfile.

    Returns
    -------
    profile : DiffProfile
        Simulated diffusion profile

    """
    if name == '':
        name = diffsys.name + '_%.1fh' % (time / 3600)

    dis, Xs = profile.dis.copy() / 1e6, profile.X.copy()
    fD = diffsys.Dfunc
    d = dis[1:] - dis[:-1]
    dj = 0.5 * (d[1:] + d[:-1])
    t, m = 0.0, 0
    while t < time:
        Xm = 0.5 * (Xs[1:] + Xs[:-1])
        DCs = np.exp(splev(Xm, fD[0]))
        dt = min(d**2 / DCs / 2)
        dt = time - t if t + dt > time else dt * 0.95
        t += dt
        m += 1
        Jf = -DCs * (Xs[1:] - Xs[:-1]) / d
        Xs[1:-1] = Xs[1:-1] - dt * (Jf[1:] - Jf[:-1]) / dj
        Xs[0] -= Jf[0] / d[0] * dt * 2
        Xs[-1] += Jf[-1] / d[-1] * dt * 2
        if output and np.mod(m, 3e4) == 0:
            print('%.3f/%.3f hrs simulated' % (t / 3600, time / 3600))
    if output:
        print('Simulation Complete')
    return DiffProfile(dis * 1e6, Xs, name=name)
예제 #4
0
def test_profile():
    """
    DiffProfile constructor
    """
    dis = np.linspace(100, 0, 101)
    X = np.linspace(0, 1, 101)
    If = [30.5, 60.5]
    profile = DiffProfile(dis=dis, X=X, If=If)

    assert len(profile.dis) == len(profile.X)
    assert isinstance(profile.name, str)
    assert np.all(profile.dis[1:] >= profile.dis[:-1])
    assert len(profile.If) == len(If) + 2
    assert len(profile.Ip) == len(If) + 2
    assert profile.Ip[-1] == len(profile.dis)
    assert profile.If[0] < profile.dis[profile.Ip[0]]
    assert profile.If[-1] > profile.dis[profile.Ip[-1] - 1]
    for i in range(1, len(If)):
        assert profile.If[i] > profile.dis[profile.Ip[i] - 1]
        assert profile.If[i] < profile.dis[profile.Ip[i]]
예제 #5
0
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
예제 #6
0
import pandas as pd
from pydiffusion.core import DiffProfile
from pydiffusion.io import read_csv, save_csv
from pydiffusion.plot import profileplot
from pydiffusion.smooth import datasmooth

# Read raw data
data = pd.read_csv('examples/data/NiMo_exp.csv')
dis, X = data['dis'], data['X']
NiMo_exp = DiffProfile(dis=dis, X=X, name='NiMo_exp')

# Another way to read profile data, .csv must be created by pydiffusion.io.save_csv
NiMo_exp, _ = read_csv('examples/data/NiMo_exp.csv')

ax = profileplot(NiMo_exp, c='b', marker='o', ls='none', fillstyle='none')

# Data smoothing
NiMo_sm = datasmooth(NiMo_exp, [311.5, 340.5], n=500)

# Plot result
profileplot(NiMo_sm, ax, c='r')

# Save result
save_csv('examples/data/NiMo_sm.csv', profile=NiMo_sm)
예제 #7
0
def datasmooth(profile, interface=[], n=2000, name=''):
    """
    Data smooth of diffusion profile. The functions use moving radius method
    on each phase. Please do not close any plot window during the process.

    datasmooth() is the first step of the forward simulation analysis (FSA).

    Parameters
    ----------
    profile: DiffProfile
        Diffusion profile data.
    interface : list of float
        Np-1 locations of interfaces for a Np-phase system
    n : int
        Interpolation number of the smoothed profile
    name : string, optional
        Name the output DiffProfile

    Returns
    -------
    profile : pydiffusion.diffusion.DiffProfile

    Examples
    --------
    Smooth the experimental profile (exp) with known interfaces [200, 300]:

    >>> ds = datasmooth(exp, [200, 300])

    """
    dis, X = profile.dis, profile.X
    if len(dis) != len(X):
        raise ValueError('Nonequal length of distance and composition data')
    try:
        If = np.array(interface)
    except TypeError:
        print('interface must be array_like')
    if If.ndim != 1:
        raise ValueError('interface must be 1d-array')

    fig = plt.figure()
    ax = fig.add_subplot(111)

    If = [dis[0]-0.5] + list(If) + [dis[-1]+0.5]
    Np = len(If)-1
    Ip = [0]*(Np+1)
    disn, Xn = dis.copy(), X.copy()

    # Same distance values
    for i in range(len(disn)-1):
        for j in range(i+1, len(disn)):
            if disn[i] == disn[j]:
                disn[j] += 1e-5*(j-i)
            elif disn[j] > disn[i]:
                break

    ita_start()

    # Apply phasesmooth to each phase
    for i in range(Np):
        pid = np.where((disn > If[i]) & (disn < If[i+1]))[0]
        try:
            Xn[pid] = phasesmooth(disn[pid], Xn[pid], ax, i+1)
        except (ValueError, TypeError) as error:
            ita_finish()
            raise error
    plt.close()

    ita_finish()

    # Create a sharp interface at interface locations
    # Solubility of each phase will be extended a little
    for i in range(1, Np):
        pid = np.where(disn > If[i])[0][0]
        start = max(pid-5, np.where(disn > If[i-1])[0][0])
        end = min(pid+5, np.where(disn < If[i+1])[0][-1])
        if start+2 < pid:
            fX1 = splrep(disn[start:pid], Xn[start:pid], k=2)
        else:
            fX1 = splrep(disn[start:pid], Xn[start:pid], k=1)
        if pid+1 < end:
            fX2 = splrep(disn[pid:end+1], Xn[pid:end+1], k=2)
        else:
            fX2 = splrep(disn[pid:end+1], Xn[pid:end+1], k=1)
        disn = np.insert(disn, pid, [If[i], If[i]])
        Xn = np.insert(Xn, pid, [splev(If[i], fX1), splev(If[i], fX2)])
        Ip[i] = pid+1
    Ip[-1] = len(Xn)
    disni, Xni = disn.copy(), Xn.copy()

    # Interpolation
    if n > 0:
        ni = [int(n*(If[i]-If[0])//(If[-1]-If[0])) for i in range(Np)]+[n]
        disni, Xni = np.zeros(n), np.zeros(n)
        for i in range(Np):
            fX = splrep(disn[Ip[i]:Ip[i+1]], Xn[Ip[i]:Ip[i+1]], k=1)
            disni[ni[i]:ni[i+1]] = np.linspace(disn[Ip[i]], disn[Ip[i+1]-1], ni[i+1]-ni[i])
            Xni[ni[i]:ni[i+1]] = splev(disni[ni[i]:ni[i+1]], fX)

    print('Smooth finished')

    if name == '':
        name = profile.name+'_smoothed'

    return DiffProfile(disni, Xni, If[1:-1], name=name)
예제 #8
0
def mphSim(profile, diffsys, time, liquid=0, output=True, name=''):
    """
    Single/Multiple-Phase Diffusion Simulation. Liquid phase can be attached
    at left or right end. For thin film simulation, please set up the
    interface nearby the end in the initial profile.

    Parameters
    ----------
    profile : DiffProfile
        Initial profile before simulation.
    diffsys : DiffSystem
        Diffusion coefficients.
    time : float
        time in seconds.
    liquid : 1, 0 or -1, optional
        Liquid phase provide infinite mass flux during simulation, can only be
        attached at left or right end.
        0 : No liquid phase attached.
        -1 : Liquid phase attached at left.
        1 : Liquid phase attached at right.
    output : boolean, optional
        Print simulation progress, default = True.
    name : str, optional
        Name the output DiffProfile.

    Returns
    -------
    profile : DiffProfile
        Simulated diffusion profile

    Examples
    --------
    With known diffusion coefficients (dsys), simulate profile of 100 hours of diffusion
    for a diffusion couple experiment (initial profile is a step) on a 600 micron grids:

    >>> dis = mesh(0, 600)
    >>> init_profile = step(dis, 300, dsys)
    >>> final_profile = mphSim(init_profile, dsys, 3600*100)

    If liquid phase is attached to the left:

    >>> final_profile = mphSim(init_profile, dsys, 3600*100, liquid=-1)

    """
    dis, Xs = profile.dis.copy() / 1e6, profile.X.copy()
    Ip, If = profile.Ip.copy(), profile.If.copy() / 1e6
    Np, Xr = diffsys.Np, diffsys.Xr.copy()
    fD = [f for f in diffsys.Dfunc]

    # Ascending or descending profile
    if (Xs[-1] - Xs[0]) * (Xr[-1, 1] - Xr[0, 0]) < 0:
        Xr = Xr.flatten()[::-1].reshape((Np, 2))
        fD = fD[::-1]

    if name == '':
        name = diffsys.name + '_%.1fh' % (time / 3600)

    if len(If) != Np + 1:
        raise ValueError(
            'Number of phases mismatches between profile and diffusion system')
    if liquid not in [-1, 0, 1]:
        raise ValueError('liquid can only be 0 1 or -1')
    try:
        time = float(time)
    except TypeError:
        print('Wrong Type of time')

    d = dis[1:] - dis[:-1]
    dj = 0.5 * (d[1:] + d[:-1])
    Jf, DCs = np.zeros(len(dis) - 1), np.zeros(len(dis) - 1)
    JIf = np.zeros([Np + 1, 2])
    vIf = np.zeros(Np + 1)
    t, m = 0.0, 0
    dt0 = time / 1e3

    while t < time:

        Xm = (Xs[:-1] + Xs[1:]) / 2
        dt = dt0

        # Jf JIf calculation
        # dt limited by simulation stability inside each phases
        for i in range(Np):
            if Ip[i + 1] > Ip[i] + 1:
                Ipr = np.arange(Ip[i], Ip[i + 1] - 1)
                DCs[Ipr] = np.exp(splev(Xm[Ipr], fD[i]))
                Jf[Ipr] = -DCs[Ipr] * (Xs[Ipr + 1] - Xs[Ipr]) / d[Ipr]
                dtn = np.abs(d[Ipr]**2 / DCs[Ipr] / 2).min()
                dt = dtn if dtn < dt else dt
            if Ip[i + 1] == Ip[i]:
                X0 = np.mean(Xr[i])
                JIf[i, 1] = JIf[i + 1, 0] = -(Xr[i, 1] - Xr[i, 0]) / (
                    If[i + 1] - If[i]) * np.exp(splev(X0, fD[i]))
            else:
                if i < Np - 1:
                    X0 = 0.5 * (Xr[i, 1] + Xs[Ip[i + 1] - 1])
                    JIf[i + 1, 0] = -(Xr[i, 1] - Xs[Ip[i + 1] - 1]) / (
                        If[i + 1] - dis[Ip[i + 1] - 1]) * np.exp(
                            splev(X0, fD[i]))
                if i > 0:
                    X0 = 0.5 * (Xr[i, 0] + Xs[Ip[i]])
                    JIf[i, 1] = -(Xs[Ip[i]] - Xr[i, 0]) / (
                        dis[Ip[i]] - If[i]) * np.exp(splev(X0, fD[i]))

        # vIf calculation, dt limited by 'No interface passing by'
        for i in range(1, Np):
            vIf[i] = (JIf[i, 1] - JIf[i, 0]) / (Xr[i, 0] - Xr[i - 1, 1])
            vid = [Ip[i] - 2] if Ip[i] > 1 else []
            vid += [Ip[i]] if Ip[i] < len(dis) else []
            dtn = np.abs(d[vid] / vIf[i]).min()
            dt = dtn if dtn < dt else dt
            if i > 1 and vIf[i - 1] > vIf[i]:
                dtn = (If[i] - If[i - 1]) / (vIf[i - 1] - vIf[i]) / 2
                dt = dtn if dtn < dt else dt

        # dt limited by grid nearby interfaces cannot exceed solubility limit
        if Xr[0, 0] < Xr[-1, 1]:
            for i in range(Np):
                if Ip[i + 1] == Ip[i]:
                    continue
                elif Ip[i + 1] == Ip[i] + 1:
                    if JIf[i, 1] > JIf[i + 1, 0]:
                        dtn = (Xr[i, 1] - Xs[Ip[i]]) * dj[Ip[i] - 1] / (
                            JIf[i, 1] - JIf[i + 1, 0])
                        dt = dtn if dtn < dt else dt
                    elif JIf[i, 1] < JIf[i + 1, 0]:
                        dtn = (Xs[Ip[i]] - Xr[i, 0]) * dj[Ip[i] - 1] / (
                            JIf[i + 1, 0] - JIf[i, 1])
                        dt = dtn if dtn < dt else dt
                else:
                    if i < Np - 1 and JIf[i + 1, 0] < Jf[Ip[i + 1] - 2]:
                        dtn = -(Xr[i, 1] - Xs[Ip[i + 1] - 1]) / (
                            JIf[i + 1, 0] - Jf[Ip[i + 1] - 2]) * dj[Ip[i + 1] -
                                                                    2]
                        dt = dtn if dtn < dt else dt
                    if i > 0 and Jf[Ip[i]] > JIf[i, 1]:
                        dtn = (Xs[Ip[i]] - Xr[i, 0]) / (
                            Jf[Ip[i]] - JIf[i, 1]) * dj[Ip[i] - 1]
                        dt = dtn if dtn < dt else dt
        else:
            for i in range(Np):
                if Ip[i + 1] == Ip[i]:
                    continue
                elif Ip[i + 1] == Ip[i] + 1:
                    if JIf[i, 1] < JIf[i + 1, 0]:
                        dtn = (Xr[i, 1] - Xs[Ip[i]]) * dj[Ip[i] - 1] / (
                            JIf[i, 1] - JIf[i + 1, 0])
                        dt = dtn if dtn < dt else dt
                    elif JIf[i, 1] > JIf[i + 1, 0]:
                        dtn = (Xs[Ip[i]] - Xr[i, 0]) * dj[Ip[i] - 1] / (
                            JIf[i + 1, 0] - JIf[i, 1])
                        dt = dtn if dtn < dt else dt
                else:
                    if i < Np - 1 and JIf[i + 1, 0] > Jf[Ip[i + 1] - 2]:
                        dtn = -(Xr[i, 1] - Xs[Ip[i + 1] - 1]) / (
                            JIf[i + 1, 0] - Jf[Ip[i + 1] - 2]) * dj[Ip[i + 1] -
                                                                    2]
                        dt = dtn if dtn < dt else dt
                    if i > 0 and Jf[Ip[i]] < JIf[i, 1]:
                        dtn = (Xs[Ip[i]] - Xr[i, 0]) / (
                            Jf[Ip[i]] - JIf[i, 1]) * dj[Ip[i] - 1]
                        dt = dtn if dtn < dt else dt

        dt = time - t if t + dt > time else dt * 0.95

        # If first or last phase will be consumed
        if If[1] < dis[1] and vIf[1] < 0:
            dtn = (dis[0] - If[1]) / vIf[1]
            dt = dtn if dtn < dt else dt
        elif If[-2] > dis[-2] and vIf[-2] > 0:
            dtn = (dis[-1] - If[-2]) / vIf[-2]
            dt = dtn if dtn < dt else dt

        t += dt
        m += 1

        # Ficks 2nd law inside each phase
        for i in range(Np):
            if Ip[i + 1] == Ip[i]:
                continue
            elif Ip[i + 1] == Ip[i] + 1:
                Xs[Ip[i]] -= dt * (JIf[i + 1, 0] - JIf[i, 1]) / dj[Ip[i] - 1]
            else:
                if i > 0:
                    Xs[Ip[i]] -= dt * (Jf[Ip[i]] - JIf[i, 1]) / dj[Ip[i] - 1]
                if i < Np - 1:
                    Xs[Ip[i + 1] -
                       1] -= dt * (JIf[i + 1, 0] -
                                   Jf[Ip[i + 1] - 2]) / dj[Ip[i + 1] - 2]
                if Ip[i + 1] > Ip[i] + 2:
                    Ipr = np.arange(Ip[i] + 1, Ip[i + 1] - 1)
                    Xs[Ipr] -= dt * (Jf[Ipr] - Jf[Ipr - 1]) / dj[Ipr - 1]

        # Composition changes at first & last grid
        # If there is liquid phase attached, composition unchanged.
        if liquid != -1:
            Xs[0] -= Jf[0] / d[0] * dt
        if liquid != 1:
            Xs[-1] += Jf[-1] / d[-1] * dt

        # If one phase consumed, delete this phase
        if If[1] + vIf[1] * dt <= dis[0]:
            Xs[0] = Xr[1, 0]
            Np -= 1
            Xr, If, Ip, fD = Xr[1:], If[1:], Ip[1:], fD[1:]
            Ip[0] = 0
            JIf = np.zeros([Np + 1, 2])
            vIf = np.zeros(Np + 1)
            if output:
                print(
                    'First phase consumed, %i phase(s) left, time = %.3f hrs' %
                    (Np, t / 3600))
        elif If[-2] + vIf[-2] * dt >= dis[-1]:
            Xs[-1] = Xr[-2, 1]
            Np -= 1
            Xr, If, Ip, fD = Xr[:-1], If[:-1], Ip[:-1], fD[:-1]
            Ip[-1] = len(dis)
            JIf = np.zeros([Np + 1, 2])
            vIf = np.zeros(Np + 1)
            if output:
                print(
                    'Last phase consumed, %i phase(s) left, time = %.3f hrs' %
                    (Np, t / 3600))

        # Interface move across one grid, passed grid has value change
        for i in range(1, Np):
            If[i] += vIf[i] * dt
            if If[i] < dis[Ip[i] - 1]:
                Ip[i] -= 1
                if If[i + 1] < dis[Ip[i] + 1]:
                    Xs[Ip[i]] = splev(dis[Ip[i]],
                                      splrep([If[i], If[i + 1]], Xr[i], k=1))
                else:
                    Xs[Ip[i]] = splev(
                        dis[Ip[i]],
                        splrep([If[i], dis[Ip[i] + 1]],
                               [Xr[i, 0], Xs[Ip[i] + 1]],
                               k=1))
            elif If[i] > dis[Ip[i]]:
                Ip[i] += 1
                if If[i - 1] > dis[Ip[i] - 2]:
                    Xs[Ip[i] - 1] = splev(
                        dis[Ip[i] - 1],
                        splrep([If[i - 1], If[i]], Xr[i - 1], k=1))
                else:
                    Xs[Ip[i] - 1] = splev(
                        dis[Ip[i] - 1],
                        splrep([dis[Ip[i] - 2], If[i]],
                               [Xs[Ip[i] - 2], Xr[i - 1, 1]],
                               k=1))

        if output and np.mod(m, 3e4) == 0:
            print('%.3f/%.3f hrs simulated' % (t / 3600, time / 3600))
    if output:
        print('Simulation Complete')

    # Insert interface informations
    for i in list(range(Np - 1, 0, -1)):
        dis = np.insert(dis, Ip[i], [If[i], If[i]])
        Xs = np.insert(Xs, Ip[i], [Xr[i - 1, 1], Xr[i, 0]])

    return DiffProfile(dis * 1e6, Xs, If[1:-1] * 1e6, name=name)