Module SPHtools

Expand source code
import numpy as np


# %% packSPH
def packSPH(siz=5, r=0.5, typ='HCP'):
    """
    PACKSPH returns the HCP or FCC packing of siz spheres of radius r

    Args:
    siz: array_like, [5, 5, 5] number of spheres along x,y,z
         if siz is a scalar, the same siz is applied to all dimensions [siz, siz, siz]
    r: float, bead radius
    typ: str, optional, 'HCP' (default, period 2) or 'FCC' (period 3)

    Returns:
    X: array_like, [size(1) x size(2) x size(3)] x 3 centers


    Example:

        import matplotlib.pyplot as plt
        from utils.SPHtools import packSPH

        X = packSPH(5)

        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        ax.scatter(X[:, 0], X[:, 1], X[:, 2], s=10, c='b', alpha=0.5)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        plt.show()

    """
    # 2023-02-20 | INRAE\Olivier Vitrac, Han Chen | rev. 2023-02-21

    # arg check
    rdefault = 0.5
    typdefault = 'HCP'
    sizedefault = 5

    if siz is None: siz = sizedefault
    if r is None: r = rdefault
    if typ == '':typ = typdefault
    if np.size(siz) == 1:siz = np.array([siz, siz, siz])
    if len(siz) != 3: raise ValueError('siz must be 1x3 or 3x1 vector')
    if not isinstance(typ, str):raise TypeError('typ must be a char array')

    # flag
    forceFCC = 0 if typ.upper() == 'HCP' else 1

    # Lattice
    i, j, k = np.mgrid[0:siz[0], 0:siz[1], 0:siz[2]]
    i, j, k = i.flatten(), j.flatten(), k.flatten()

    X = np.zeros((np.size(i), 3))
    X[:, 0] = 2 * i + np.mod(j + k, 2)
    X[:, 1] = np.sqrt(3) * (j + np.mod(k, 2) / 3) + (np.mod(k, 3) == 2) * forceFCC
    X[:, 2] = 2 * np.sqrt(6) * k / 3

    return r * X

# %% kernelSPH
def kernelSPH(h=None, typ='lucy', d=3):
    """
    KERNELSPH return a SPH kernel

    Syntax:
        W = kernelSPH(h,typ,d)
    Inputs:
            h : cutoff
          typ : kernel name (default = Lucy)
            d : dimension
    Output:
            W : kernel function lambda r: ...

    Example:
        from utils.SPHtools import kernelSPH
        W = kernelSPH(1,'lucy',3)

    See also: interp3SPH, interp2SPH, packSPH
    """
    # 2023-02-20 | INRAE\Olivier Vitrac, Han Chen | rev. 2023-02-21

    # arg check
    if h is None:
        raise ValueError('Supply a value for h')
    if typ is None:
        typ = 'lucy'
    if not isinstance(typ, str):
        raise ValueError('typ must be a string')
    if d is None:
        d = 3
    if d < 2 or d > 3:
        raise ValueError('d must be equal to 1, 2 or 3')

    # main
    if typ.lower() == 'lucy':

        if d == 3:
            # W = @(r) (r<h) .* (1.0./h.^3.*(r./h-1.0).^3.*((r.*3.0)./h+1.0).*(-1.05e+2./1.6e+1))./pi;
            W = lambda r, h=h: np.double(r < h) * \
            (1.0/h**3 * (r/h - 1.0)**3 * ((r*3.0)/h + 1.0) * (-1.05e+2/1.6e+1) / np.pi)
        elif d == 2:
            #  W = @(r) (r<h) .* (1.0./h.^2.*(r./h-1.0).^3.*((r.*3.0)./h+1.0).*-5.0)./pi;
            W = lambda r, h=h: np.double(r < h) * \
            (1.0/h**2 * (r/h - 1.0)**3 * ((r*3.0)/h + 1.0) * (-5.0) / np.pi)

    elif typ.lower() == 'lucyder':
        if d == 3:
            # W = @(r) (r<h) .* (1.0./h.^4.*(r./h-1.0).^3.*(-3.15e+2./1.6e+1))./pi-(1.0./h.^4.*(r./h-1.0).^2.*((r.*3.0)./h+1.0).*(3.15e+2./1.6e+1))./pi;
            W = lambda r,h=h: np.double(r < h) * \
                (\
                1.0/h**4 * (r/h - 1.0)**3 * (-3.15e+2/1.6e+1) / np.pi \
                - 1.0/h**4 * (r/h - 1.0)**2 * ((r*3.0)/h + 1.0) * (3.15e+2/1.6e+1) / np.pi \
                )
        elif d == 2:
            # W = @(r) (r<h) .* (1.0./h.^3.*(r./h-1.0).^3.*-1.5e+1)./pi-(1.0./h.^3.*(r./h-1.0).^2.*((r.*3.0)./h+1.0).*1.5e+1)./pi;
            W = lambda r,h=h: np.double(r < h) * \
                ( \
                1.0/h**3 * (r/h - 1.0)**3 * (-1.5e+1) / np.pi
                - 1.0/h**3 * (r/h - 1.0)**2 * ((r*3.0)/h + 1.0) * (1.5e+1) / np.pi \
                )
        else:
            raise NotImplementedError(f"the kernel '{typ}' is not implemented")

    return W


# %%interp3SPH

def interp3SPH(centers = packSPH(5),
               y = np.array([]),
               Xq =None,
               Yq =None,
               Zq =None,
               W = kernelSPH(1,'lucy',3),
               V = np.array([])):

    """
    INTERP3SPH interpolates y at Xq,Yq,Zq using the 3D kernel W centered on centers

   Syntax:
       Vq = interp3SPH(X,y,Xq,Yq,Zq [,W,V])

   Inputs:
     centers : kx3 coordinates of the kernel centers
           y : kxny values at X (m is the number of values associated with the same center)
               [] (empty matrix) forces a uniform density calculatoin
          Xq : array or matrix coordinates along X
          Yq : array or matrix coordinates along Y
          Zq : array or matrix coordinates along Z
           W : kernel function @(r) <-- use kernelSPH() to supply a vectorized kernel
           V : kx1 volume of the kernels (default=1)
               [] (empty matrix) or scalar value forces uniform volumes (default =1)

   Output:
           Vq : same size as Xq, with an additional dimension if y was an array

    Example:  arbitrary field to be interpolated x+2*y-3*z

        import numpy as np
        import matplotlib.pyplot as plt
        from mpl_toolkits.mplot3d import Axes3D
        from scipy.interpolate import griddata

        from utils.SPHtools import interp3SPH


        r = 0.5
        h = 2*r
        XYZ = packSPH(5,r)
        W = kernelSPH(h,'lucy',3)
        y = XYZ @ np.array([1,2,-3]).transpose()
        nresolution = 50
        xg = np.linspace(np.min(XYZ[:,0])-h,np.max(XYZ[:,0])+h,num=nresolution)
        yg = np.linspace(np.min(XYZ[:,1])-h,np.max(XYZ[:,1])+h,num=nresolution)
        zg = np.linspace(np.min(XYZ[:,2])-h,np.max(XYZ[:,2])+h,num=nresolution)
        Xg,Yg,Zg = np.meshgrid(xg,yg,zg)
        Vg = interp3SPH(XYZ,y,Xg,Yg,Zg,W)

        # projection along y,z
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot(Xg[1,:,1],np.mean(Vg,axis=(0,2),keepdims=False),color='red',marker='o')

        # unvalidated code for voxels
        fig = plt.figure()
        cmap = plt.cm.get_cmap("RdPu_r", 64)
        ax = fig.add_subplot(111, projection='3d')
        ax.set_box_aspect([1,1,1]) # set aspect ratio of the 3D plot
        hs = ax.contour(Xg, Yg, Zg, Vg, 10, cmap=cmap) # not working
        fig.colorbar(hs, ax=ax)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        plt.show()

   See also: interp2SPH, kernelSPH, packSPH

    """

    # 2023-02-20 | INRAE\Olivier Vitrac, Han Chen | rev. 2023-02-21

    # arg check
    if isinstance(centers,list): centers = np.array(centers)
    k,d = centers.shape
    ky,ny = np.shape(y) if np.ndim(y)>1 else (np.size(y),1)
    kv = np.size(V)
    if k==0: raise ValueError('please supply some centers')
    if d != 3: raise ValueError('3 dimensions (columns) are required')
    if ky*ny==0: y = np.ones(k,1); ky=k; ny=1;
    if ky != k: raise ValueError(f'the number of y values ({ky}) does not match the number of kernels ({k})')
    if Xq.shape != Yq.shape  or Yq.shape != Zq.shape: raise ValueError('Xq,Yq and Zq do not have compatible sizes')
    if kv==0:  V=1;  kv=1;
    if kv==1: V = np.ones([k,1])*V; kv=k;
    if kv!=k: raise ValueError(f'the number of V values ({kv}) does not match the number of kernels ({k})')

    # main
    sumW = [];
    verbosity = np.size(Xq)>1e4;
    for i in range(k):
        # initialization if needed
        if i==0:
            for iy in range(ny):
                sumW.append(np.zeros(np.shape(Xq),dtype=Xq.dtype))
        # interpolation
        if verbosity:print(f'interpolate respectively to kernel {i} of {k}')
        R = np.sqrt( (Xq-centers[i,0])**2 + \
                     (Yq-centers[i,1])**2 + \
                     (Zq-centers[i,2])**2 )
        if np.ndim(y)==1:
            sumW[0] = sumW[0] + y[i] * V[i] * W(R);
        else:
            for iy in range(ny):
                sumW[iy] = sumW[iy] + y[i,iy] * V[i] * W(R);

    # output (data unfolding)
    if ny==1:
        return sumW[0];
    else:
        return np.stack(sumW,axis=Xq.ndim)

Functions

def interp3SPH(centers=array([[0. , 0. , 0. ], [0.5 , 0.28867513, 0.81649658], [0. , 0. , 1.63299316], [0.5 , 0.28867513, 2.44948974], [0. , 0. , 3.26598632], [0.5 , 0.8660254 , 0. ], [0. , 1.15470054, 0.81649658], [0.5 , 0.8660254 , 1.63299316], [0. , 1.15470054, 2.44948974], [0.5 , 0.8660254 , 3.26598632], [0. , 1.73205081, 0. ], [0.5 , 2.02072594, 0.81649658], [0. , 1.73205081, 1.63299316], [0.5 , 2.02072594, 2.44948974], [0. , 1.73205081, 3.26598632], [0.5 , 2.59807621, 0. ], [0. , 2.88675135, 0.81649658], [0.5 , 2.59807621, 1.63299316], [0. , 2.88675135, 2.44948974], [0.5 , 2.59807621, 3.26598632], [0. , 3.46410162, 0. ], [0.5 , 3.75277675, 0.81649658], [0. , 3.46410162, 1.63299316], [0.5 , 3.75277675, 2.44948974], [0. , 3.46410162, 3.26598632], [1. , 0. , 0. ], [1.5 , 0.28867513, 0.81649658], [1. , 0. , 1.63299316], [1.5 , 0.28867513, 2.44948974], [1. , 0. , 3.26598632], [1.5 , 0.8660254 , 0. ], [1. , 1.15470054, 0.81649658], [1.5 , 0.8660254 , 1.63299316], [1. , 1.15470054, 2.44948974], [1.5 , 0.8660254 , 3.26598632], [1. , 1.73205081, 0. ], [1.5 , 2.02072594, 0.81649658], [1. , 1.73205081, 1.63299316], [1.5 , 2.02072594, 2.44948974], [1. , 1.73205081, 3.26598632], [1.5 , 2.59807621, 0. ], [1. , 2.88675135, 0.81649658], [1.5 , 2.59807621, 1.63299316], [1. , 2.88675135, 2.44948974], [1.5 , 2.59807621, 3.26598632], [1. , 3.46410162, 0. ], [1.5 , 3.75277675, 0.81649658], [1. , 3.46410162, 1.63299316], [1.5 , 3.75277675, 2.44948974], [1. , 3.46410162, 3.26598632], [2. , 0. , 0. ], [2.5 , 0.28867513, 0.81649658], [2. , 0. , 1.63299316], [2.5 , 0.28867513, 2.44948974], [2. , 0. , 3.26598632], [2.5 , 0.8660254 , 0. ], [2. , 1.15470054, 0.81649658], [2.5 , 0.8660254 , 1.63299316], [2. , 1.15470054, 2.44948974], [2.5 , 0.8660254 , 3.26598632], [2. , 1.73205081, 0. ], [2.5 , 2.02072594, 0.81649658], [2. , 1.73205081, 1.63299316], [2.5 , 2.02072594, 2.44948974], [2. , 1.73205081, 3.26598632], [2.5 , 2.59807621, 0. ], [2. , 2.88675135, 0.81649658], [2.5 , 2.59807621, 1.63299316], [2. , 2.88675135, 2.44948974], [2.5 , 2.59807621, 3.26598632], [2. , 3.46410162, 0. ], [2.5 , 3.75277675, 0.81649658], [2. , 3.46410162, 1.63299316], [2.5 , 3.75277675, 2.44948974], [2. , 3.46410162, 3.26598632], [3. , 0. , 0. ], [3.5 , 0.28867513, 0.81649658], [3. , 0. , 1.63299316], [3.5 , 0.28867513, 2.44948974], [3. , 0. , 3.26598632], [3.5 , 0.8660254 , 0. ], [3. , 1.15470054, 0.81649658], [3.5 , 0.8660254 , 1.63299316], [3. , 1.15470054, 2.44948974], [3.5 , 0.8660254 , 3.26598632], [3. , 1.73205081, 0. ], [3.5 , 2.02072594, 0.81649658], [3. , 1.73205081, 1.63299316], [3.5 , 2.02072594, 2.44948974], [3. , 1.73205081, 3.26598632], [3.5 , 2.59807621, 0. ], [3. , 2.88675135, 0.81649658], [3.5 , 2.59807621, 1.63299316], [3. , 2.88675135, 2.44948974], [3.5 , 2.59807621, 3.26598632], [3. , 3.46410162, 0. ], [3.5 , 3.75277675, 0.81649658], [3. , 3.46410162, 1.63299316], [3.5 , 3.75277675, 2.44948974], [3. , 3.46410162, 3.26598632], [4. , 0. , 0. ], [4.5 , 0.28867513, 0.81649658], [4. , 0. , 1.63299316], [4.5 , 0.28867513, 2.44948974], [4. , 0. , 3.26598632], [4.5 , 0.8660254 , 0. ], [4. , 1.15470054, 0.81649658], [4.5 , 0.8660254 , 1.63299316], [4. , 1.15470054, 2.44948974], [4.5 , 0.8660254 , 3.26598632], [4. , 1.73205081, 0. ], [4.5 , 2.02072594, 0.81649658], [4. , 1.73205081, 1.63299316], [4.5 , 2.02072594, 2.44948974], [4. , 1.73205081, 3.26598632], [4.5 , 2.59807621, 0. ], [4. , 2.88675135, 0.81649658], [4.5 , 2.59807621, 1.63299316], [4. , 2.88675135, 2.44948974], [4.5 , 2.59807621, 3.26598632], [4. , 3.46410162, 0. ], [4.5 , 3.75277675, 0.81649658], [4. , 3.46410162, 1.63299316], [4.5 , 3.75277675, 2.44948974], [4. , 3.46410162, 3.26598632]]), y=array([], dtype=float64), Xq=None, Yq=None, Zq=None, W=<function kernelSPH.<locals>.<lambda>>, V=array([], dtype=float64))

INTERP3SPH interpolates y at Xq,Yq,Zq using the 3D kernel W centered on centers

Syntax

Vq = interp3SPH(X,y,Xq,Yq,Zq [,W,V])

Inputs

centers : kx3 coordinates of the kernel centers y : kxny values at X (m is the number of values associated with the same center) [] (empty matrix) forces a uniform density calculatoin Xq : array or matrix coordinates along X Yq : array or matrix coordinates along Y Zq : array or matrix coordinates along Z W : kernel function @(r) <– use kernelSPH() to supply a vectorized kernel V : kx1 volume of the kernels (default=1) [] (empty matrix) or scalar value forces uniform volumes (default =1)

Output

Vq : same size as Xq, with an additional dimension if y was an array

Example: arbitrary field to be interpolated x+2y-3z

 import numpy as np
 import matplotlib.pyplot as plt
 from mpl_toolkits.mplot3d import Axes3D
 from scipy.interpolate import griddata

 from utils.SPHtools import interp3SPH


 r = 0.5
 h = 2*r
 XYZ = packSPH(5,r)
 W = kernelSPH(h,'lucy',3)
 y = XYZ @ np.array([1,2,-3]).transpose()
 nresolution = 50
 xg = np.linspace(np.min(XYZ[:,0])-h,np.max(XYZ[:,0])+h,num=nresolution)
 yg = np.linspace(np.min(XYZ[:,1])-h,np.max(XYZ[:,1])+h,num=nresolution)
 zg = np.linspace(np.min(XYZ[:,2])-h,np.max(XYZ[:,2])+h,num=nresolution)
 Xg,Yg,Zg = np.meshgrid(xg,yg,zg)
 Vg = interp3SPH(XYZ,y,Xg,Yg,Zg,W)

 # projection along y,z
 fig = plt.figure()
 ax = fig.add_subplot(111)
 ax.plot(Xg[1,:,1],np.mean(Vg,axis=(0,2),keepdims=False),color='red',marker='o')

 # unvalidated code for voxels
 fig = plt.figure()
 cmap = plt.cm.get_cmap("RdPu_r", 64)
 ax = fig.add_subplot(111, projection='3d')
 ax.set_box_aspect([1,1,1]) # set aspect ratio of the 3D plot
 hs = ax.contour(Xg, Yg, Zg, Vg, 10, cmap=cmap) # not working
 fig.colorbar(hs, ax=ax)
 ax.set_xlabel('X')
 ax.set_ylabel('Y')
 ax.set_zlabel('Z')
 plt.show()

See also: interp2SPH, kernelSPH, packSPH

Expand source code
def interp3SPH(centers = packSPH(5),
               y = np.array([]),
               Xq =None,
               Yq =None,
               Zq =None,
               W = kernelSPH(1,'lucy',3),
               V = np.array([])):

    """
    INTERP3SPH interpolates y at Xq,Yq,Zq using the 3D kernel W centered on centers

   Syntax:
       Vq = interp3SPH(X,y,Xq,Yq,Zq [,W,V])

   Inputs:
     centers : kx3 coordinates of the kernel centers
           y : kxny values at X (m is the number of values associated with the same center)
               [] (empty matrix) forces a uniform density calculatoin
          Xq : array or matrix coordinates along X
          Yq : array or matrix coordinates along Y
          Zq : array or matrix coordinates along Z
           W : kernel function @(r) <-- use kernelSPH() to supply a vectorized kernel
           V : kx1 volume of the kernels (default=1)
               [] (empty matrix) or scalar value forces uniform volumes (default =1)

   Output:
           Vq : same size as Xq, with an additional dimension if y was an array

    Example:  arbitrary field to be interpolated x+2*y-3*z

        import numpy as np
        import matplotlib.pyplot as plt
        from mpl_toolkits.mplot3d import Axes3D
        from scipy.interpolate import griddata

        from utils.SPHtools import interp3SPH


        r = 0.5
        h = 2*r
        XYZ = packSPH(5,r)
        W = kernelSPH(h,'lucy',3)
        y = XYZ @ np.array([1,2,-3]).transpose()
        nresolution = 50
        xg = np.linspace(np.min(XYZ[:,0])-h,np.max(XYZ[:,0])+h,num=nresolution)
        yg = np.linspace(np.min(XYZ[:,1])-h,np.max(XYZ[:,1])+h,num=nresolution)
        zg = np.linspace(np.min(XYZ[:,2])-h,np.max(XYZ[:,2])+h,num=nresolution)
        Xg,Yg,Zg = np.meshgrid(xg,yg,zg)
        Vg = interp3SPH(XYZ,y,Xg,Yg,Zg,W)

        # projection along y,z
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot(Xg[1,:,1],np.mean(Vg,axis=(0,2),keepdims=False),color='red',marker='o')

        # unvalidated code for voxels
        fig = plt.figure()
        cmap = plt.cm.get_cmap("RdPu_r", 64)
        ax = fig.add_subplot(111, projection='3d')
        ax.set_box_aspect([1,1,1]) # set aspect ratio of the 3D plot
        hs = ax.contour(Xg, Yg, Zg, Vg, 10, cmap=cmap) # not working
        fig.colorbar(hs, ax=ax)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        plt.show()

   See also: interp2SPH, kernelSPH, packSPH

    """

    # 2023-02-20 | INRAE\Olivier Vitrac, Han Chen | rev. 2023-02-21

    # arg check
    if isinstance(centers,list): centers = np.array(centers)
    k,d = centers.shape
    ky,ny = np.shape(y) if np.ndim(y)>1 else (np.size(y),1)
    kv = np.size(V)
    if k==0: raise ValueError('please supply some centers')
    if d != 3: raise ValueError('3 dimensions (columns) are required')
    if ky*ny==0: y = np.ones(k,1); ky=k; ny=1;
    if ky != k: raise ValueError(f'the number of y values ({ky}) does not match the number of kernels ({k})')
    if Xq.shape != Yq.shape  or Yq.shape != Zq.shape: raise ValueError('Xq,Yq and Zq do not have compatible sizes')
    if kv==0:  V=1;  kv=1;
    if kv==1: V = np.ones([k,1])*V; kv=k;
    if kv!=k: raise ValueError(f'the number of V values ({kv}) does not match the number of kernels ({k})')

    # main
    sumW = [];
    verbosity = np.size(Xq)>1e4;
    for i in range(k):
        # initialization if needed
        if i==0:
            for iy in range(ny):
                sumW.append(np.zeros(np.shape(Xq),dtype=Xq.dtype))
        # interpolation
        if verbosity:print(f'interpolate respectively to kernel {i} of {k}')
        R = np.sqrt( (Xq-centers[i,0])**2 + \
                     (Yq-centers[i,1])**2 + \
                     (Zq-centers[i,2])**2 )
        if np.ndim(y)==1:
            sumW[0] = sumW[0] + y[i] * V[i] * W(R);
        else:
            for iy in range(ny):
                sumW[iy] = sumW[iy] + y[i,iy] * V[i] * W(R);

    # output (data unfolding)
    if ny==1:
        return sumW[0];
    else:
        return np.stack(sumW,axis=Xq.ndim)
def kernelSPH(h=None, typ='lucy', d=3)

KERNELSPH return a SPH kernel

Syntax

W = kernelSPH(h,typ,d)

Inputs

h : cutoff typ : kernel name (default = Lucy) d : dimension

Output

W : kernel function lambda r: …

Example

from utils.SPHtools import kernelSPH W = kernelSPH(1,'lucy',3)

See also: interp3SPH, interp2SPH, packSPH

Expand source code
def kernelSPH(h=None, typ='lucy', d=3):
    """
    KERNELSPH return a SPH kernel

    Syntax:
        W = kernelSPH(h,typ,d)
    Inputs:
            h : cutoff
          typ : kernel name (default = Lucy)
            d : dimension
    Output:
            W : kernel function lambda r: ...

    Example:
        from utils.SPHtools import kernelSPH
        W = kernelSPH(1,'lucy',3)

    See also: interp3SPH, interp2SPH, packSPH
    """
    # 2023-02-20 | INRAE\Olivier Vitrac, Han Chen | rev. 2023-02-21

    # arg check
    if h is None:
        raise ValueError('Supply a value for h')
    if typ is None:
        typ = 'lucy'
    if not isinstance(typ, str):
        raise ValueError('typ must be a string')
    if d is None:
        d = 3
    if d < 2 or d > 3:
        raise ValueError('d must be equal to 1, 2 or 3')

    # main
    if typ.lower() == 'lucy':

        if d == 3:
            # W = @(r) (r<h) .* (1.0./h.^3.*(r./h-1.0).^3.*((r.*3.0)./h+1.0).*(-1.05e+2./1.6e+1))./pi;
            W = lambda r, h=h: np.double(r < h) * \
            (1.0/h**3 * (r/h - 1.0)**3 * ((r*3.0)/h + 1.0) * (-1.05e+2/1.6e+1) / np.pi)
        elif d == 2:
            #  W = @(r) (r<h) .* (1.0./h.^2.*(r./h-1.0).^3.*((r.*3.0)./h+1.0).*-5.0)./pi;
            W = lambda r, h=h: np.double(r < h) * \
            (1.0/h**2 * (r/h - 1.0)**3 * ((r*3.0)/h + 1.0) * (-5.0) / np.pi)

    elif typ.lower() == 'lucyder':
        if d == 3:
            # W = @(r) (r<h) .* (1.0./h.^4.*(r./h-1.0).^3.*(-3.15e+2./1.6e+1))./pi-(1.0./h.^4.*(r./h-1.0).^2.*((r.*3.0)./h+1.0).*(3.15e+2./1.6e+1))./pi;
            W = lambda r,h=h: np.double(r < h) * \
                (\
                1.0/h**4 * (r/h - 1.0)**3 * (-3.15e+2/1.6e+1) / np.pi \
                - 1.0/h**4 * (r/h - 1.0)**2 * ((r*3.0)/h + 1.0) * (3.15e+2/1.6e+1) / np.pi \
                )
        elif d == 2:
            # W = @(r) (r<h) .* (1.0./h.^3.*(r./h-1.0).^3.*-1.5e+1)./pi-(1.0./h.^3.*(r./h-1.0).^2.*((r.*3.0)./h+1.0).*1.5e+1)./pi;
            W = lambda r,h=h: np.double(r < h) * \
                ( \
                1.0/h**3 * (r/h - 1.0)**3 * (-1.5e+1) / np.pi
                - 1.0/h**3 * (r/h - 1.0)**2 * ((r*3.0)/h + 1.0) * (1.5e+1) / np.pi \
                )
        else:
            raise NotImplementedError(f"the kernel '{typ}' is not implemented")

    return W
def packSPH(siz=5, r=0.5, typ='HCP')

PACKSPH returns the HCP or FCC packing of siz spheres of radius r

Args: siz: array_like, [5, 5, 5] number of spheres along x,y,z if siz is a scalar, the same siz is applied to all dimensions [siz, siz, siz] r: float, bead radius typ: str, optional, 'HCP' (default, period 2) or 'FCC' (period 3)

Returns: X: array_like, [size(1) x size(2) x size(3)] x 3 centers

Example

import matplotlib.pyplot as plt from utils.SPHtools import packSPH

X = packSPH(5)

fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(X[:, 0], X[:, 1], X[:, 2], s=10, c='b', alpha=0.5) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plt.show()

Expand source code
def packSPH(siz=5, r=0.5, typ='HCP'):
    """
    PACKSPH returns the HCP or FCC packing of siz spheres of radius r

    Args:
    siz: array_like, [5, 5, 5] number of spheres along x,y,z
         if siz is a scalar, the same siz is applied to all dimensions [siz, siz, siz]
    r: float, bead radius
    typ: str, optional, 'HCP' (default, period 2) or 'FCC' (period 3)

    Returns:
    X: array_like, [size(1) x size(2) x size(3)] x 3 centers


    Example:

        import matplotlib.pyplot as plt
        from utils.SPHtools import packSPH

        X = packSPH(5)

        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        ax.scatter(X[:, 0], X[:, 1], X[:, 2], s=10, c='b', alpha=0.5)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        plt.show()

    """
    # 2023-02-20 | INRAE\Olivier Vitrac, Han Chen | rev. 2023-02-21

    # arg check
    rdefault = 0.5
    typdefault = 'HCP'
    sizedefault = 5

    if siz is None: siz = sizedefault
    if r is None: r = rdefault
    if typ == '':typ = typdefault
    if np.size(siz) == 1:siz = np.array([siz, siz, siz])
    if len(siz) != 3: raise ValueError('siz must be 1x3 or 3x1 vector')
    if not isinstance(typ, str):raise TypeError('typ must be a char array')

    # flag
    forceFCC = 0 if typ.upper() == 'HCP' else 1

    # Lattice
    i, j, k = np.mgrid[0:siz[0], 0:siz[1], 0:siz[2]]
    i, j, k = i.flatten(), j.flatten(), k.flatten()

    X = np.zeros((np.size(i), 3))
    X[:, 0] = 2 * i + np.mod(j + k, 2)
    X[:, 1] = np.sqrt(3) * (j + np.mod(k, 2) / 3) + (np.mod(k, 3) == 2) * forceFCC
    X[:, 2] = 2 * np.sqrt(6) * k / 3

    return r * X