Surface normal

Overview

I assume that I know the profile of \(\phi\) at step \(n\), e.g.

../_images/normal0.png

Discrete phase distribution (volume fraction) in a two-dimensioinal domain.

Note

In this project, uniform grid spacings are adopted in the homogeneous (\(y\)) direction(s), while non-uniform grids can be used in the wall-normal (\(x\)) direction.

In the above figure, the left-bottom and the right-top cells are fully occupied by one of the two phases, while two liquids coexist in the other cells.

To start the surface reconstruction, I first compute the local surface normal. In particular, I would like to compute the local normal vector defined at each cell center \(n_{\pic,\pjc}\).

There are many options, depending on the accuracy required and the computational cost. Here, I adopt one of the most simple approaches using the fact that the surface normal is given by

\[n_i = \frac{m_i}{\left| m_i \right|},\]

where \(m_i\) is the local gradient of the indicator function and is approximated as

\[m_i \approx \der{\phi}{x_i}.\]

To compute this quantity at the cell center \(\left( \pic, \pjc \right)\), one of the easiest ways would be to use the second-order-accurate central-difference scheme:

\[\der{\phi}{x} \approx \frac{\phi_{\pipp, \pjc} - \phi_{\pimm, \pjc}}{2 \Delta x},\]

which tends to be unstable since this skips \(\phi_{\pic, \pjc}\) to evaluate the derivative there (not diagonally-dominant).

Instead, I adopt the Youngs approach (introduced by Youngs, Numer. Methods Fluid Dyn., 1982, extensively explained by Ii et al., J. Comput. Phys. (231), 2012), which is discussed in the following section.

Youngs approach

  1. Compute gradients at the cell corners

    To begin with, I compute the gradients at the cell corners (not the center). I use 4 (or 9 in 3D) surrounding cells to compute the gradients at the cell corners.

    Gradients in the \(x\) direction:

    \[\begin{split}\vat{m_x}{\pim, \pjm} & \approx \vat{\der{\phi}{x}}{\pim, \pjm}, \\ \vat{m_x}{\pip, \pjm} & \approx \vat{\der{\phi}{x}}{\pip, \pjm}, \\ \vat{m_x}{\pim, \pjp} & \approx \vat{\der{\phi}{x}}{\pim, \pjp}, \\ \vat{m_x}{\pip, \pjp} & \approx \vat{\der{\phi}{x}}{\pip, \pjp}.\end{split}\]
    src/interface/curvature_tensor.c
    34const double dx = DXC(i  );
    35const double dvofdx = 1. / dx * (
    36    - VOF(i-1, j-1) + VOF(i  , j-1)
    37    - VOF(i-1, j  ) + VOF(i  , j  )
    38);
    
    src/interface/curvature_tensor.c
    60const double dvofdx = 1. / dx * (
    61    - VOF(i-1, j-1, k-1) + VOF(i  , j-1, k-1)
    62    - VOF(i-1, j  , k-1) + VOF(i  , j  , k-1)
    63    - VOF(i-1, j-1, k  ) + VOF(i  , j-1, k  )
    64    - VOF(i-1, j  , k  ) + VOF(i  , j  , k  )
    65);
    

    Gradients in \(y\):

    \[\begin{split}\vat{m_y}{\pim, \pjm} & \approx \vat{\der{\phi}{y}}{\pim, \pjm}, \\ \vat{m_y}{\pip, \pjm} & \approx \vat{\der{\phi}{y}}{\pip, \pjm}, \\ \vat{m_y}{\pim, \pjp} & \approx \vat{\der{\phi}{y}}{\pim, \pjp}, \\ \vat{m_y}{\pip, \pjp} & \approx \vat{\der{\phi}{y}}{\pip, \pjp}.\end{split}\]
    src/interface/curvature_tensor.c
    40const double dvofdy = 1. / dy * (
    41    - VOF(i-1, j-1) - VOF(i  , j-1)
    42    + VOF(i-1, j  ) + VOF(i  , j  )
    43);
    
    src/interface/curvature_tensor.c
    67const double dvofdy = 1. / dy * (
    68    - VOF(i-1, j-1, k-1) - VOF(i  , j-1, k-1)
    69    + VOF(i-1, j  , k-1) + VOF(i  , j  , k-1)
    70    - VOF(i-1, j-1, k  ) - VOF(i  , j-1, k  )
    71    + VOF(i-1, j  , k  ) + VOF(i  , j  , k  )
    72);
    

    Gradients in \(z\):

    src/interface/curvature_tensor.c
    74const double dvofdz = 1. / dz * (
    75    - VOF(i-1, j-1, k-1) - VOF(i  , j-1, k-1)
    76    - VOF(i-1, j  , k-1) - VOF(i  , j  , k-1)
    77    + VOF(i-1, j-1, k  ) + VOF(i  , j-1, k  )
    78    + VOF(i-1, j  , k  ) + VOF(i  , j  , k  )
    79);
    

    Note that they are gradients and not the normal vectors. To obtain normal, I normalise them and the results are assigned to the resulting array DVOF.

    src/interface/curvature_tensor.c
    45const double norm = sqrt(
    46    + pow(dvofdx, 2.)
    47    + pow(dvofdy, 2.)
    48);
    49const double norminv = 1. / fmax(norm, DBL_EPSILON);
    50DVOF(i, j)[0] = dvofdx * norminv;
    51DVOF(i, j)[1] = dvofdy * norminv;
    
    src/interface/curvature_tensor.c
    81const double norm = sqrt(
    82    + pow(dvofdx, 2.)
    83    + pow(dvofdy, 2.)
    84    + pow(dvofdz, 2.)
    85);
    86const double norminv = 1. / fmax(norm, DBL_EPSILON);
    87DVOF(i, j, k)[0] = dvofdx * norminv;
    88DVOF(i, j, k)[1] = dvofdy * norminv;
    89DVOF(i, j, k)[2] = dvofdz * norminv;
    
    ../_images/normal1.png

    Normal vectors at the surrounding cell corners.

    Note

    In this image, lengths of the arrows are adjusted for the sake of appearance and thus may be inaccurate.

  2. Compute normals at the cell centers

    The normal vector defined at each cell center is computed by averaging the normals defined at the surrounding cell corners:

    src/interface/curvature_tensor.c
    201double nx = (
    202    + DVOF(i  , j  )[0] + DVOF(i+1, j  )[0]
    203    + DVOF(i  , j+1)[0] + DVOF(i+1, j+1)[0]
    204);
    
    src/interface/curvature_tensor.c
    242double nx = (
    243    + DVOF(i  , j  , k  )[0] + DVOF(i+1, j  , k  )[0]
    244    + DVOF(i  , j+1, k  )[0] + DVOF(i+1, j+1, k  )[0]
    245    + DVOF(i  , j  , k+1)[0] + DVOF(i+1, j  , k+1)[0]
    246    + DVOF(i  , j+1, k+1)[0] + DVOF(i+1, j+1, k+1)[0]
    247);
    
    src/interface/curvature_tensor.c
    206double ny = (
    207    + DVOF(i  , j  )[1] + DVOF(i+1, j  )[1]
    208    + DVOF(i  , j+1)[1] + DVOF(i+1, j+1)[1]
    209);
    
    src/interface/curvature_tensor.c
    249double ny = (
    250    + DVOF(i  , j  , k  )[1] + DVOF(i+1, j  , k  )[1]
    251    + DVOF(i  , j+1, k  )[1] + DVOF(i+1, j+1, k  )[1]
    252    + DVOF(i  , j  , k+1)[1] + DVOF(i+1, j  , k+1)[1]
    253    + DVOF(i  , j+1, k+1)[1] + DVOF(i+1, j+1, k+1)[1]
    254);
    
    src/interface/curvature_tensor.c
    256double nz = (
    257    + DVOF(i  , j  , k  )[2] + DVOF(i+1, j  , k  )[2]
    258    + DVOF(i  , j+1, k  )[2] + DVOF(i+1, j+1, k  )[2]
    259    + DVOF(i  , j  , k+1)[2] + DVOF(i+1, j  , k+1)[2]
    260    + DVOF(i  , j+1, k+1)[2] + DVOF(i+1, j+1, k+1)[2]
    261);
    
    ../_images/normal2.png

    Normal vector at the cell center.

    Note

    In this image, lengths of the arrows are adjusted for the sake of appearance and thus may be inaccurate. In general, the norm of the new vector is not unity.

  3. Coordinate transformation

    The normal vector at each cell center is defined on the global coordinate system (e.g., coordinate system used to describe the domain), whose basis is \(\left( \underline{e}_x, \underline{e}_y \right)\). For convenience, I define a local coordinate system which is attached to each cell \(\left( i, j \right)\), whose basis is \(\left( \underline{e}_{X}, \underline{e}_{Y} \right)\). These two coordinate systems are related with

    \[\begin{split}\begin{pmatrix} \underline{e}_X \\ \underline{e}_Y \end{pmatrix} = \begin{pmatrix} \Delta x_i & 0 \\ 0 & \Delta y \end{pmatrix} \begin{pmatrix} \underline{e}_x \\ \underline{e}_y \end{pmatrix},\end{split}\]

    or equivalently

    \[\begin{split}\begin{pmatrix} \underline{e}_x \\ \underline{e}_y \end{pmatrix} = \begin{pmatrix} \frac{1}{\Delta x_i} & 0 \\ 0 & \frac{1}{\Delta y} \end{pmatrix} \begin{pmatrix} \underline{e}_X \\ \underline{e}_Y \end{pmatrix}.\end{split}\]
    ../_images/normal3.png

    (Left) global coordinate system, and (right) local coordinate system used in this project. Notice that the directions of the reddish arrows differ, which is because the grid size is scaled.

    Thus, the above normal vector defined on the globa* coordinate system

    \[n_i^{global} = \underline{e}_x n_x + \underline{e}_y n_y\]

    is written as

    \[\underline{e}_X \frac{1}{\Delta x_i} n_x + \underline{e}_Y \frac{1}{\Delta y } n_y\]

    on the local coordinate system.

    Finally, the surface normal defined at the cell center of the local coordinate system is computed as

    \[n_i^{local} \equiv \underline{e}_X n_X + \underline{e}_Y n_Y,\]

    which is

    \[\begin{split}n_X &= \frac{1}{k} \frac{n_x}{\Delta x_{i}}, \\ n_Y &= \frac{1}{k} \frac{n_y}{\Delta y },\end{split}\]

    where

    \[k \equiv \sqrt{ \left( \frac{n_x}{\Delta x_{i}} \right)^2 + \left( \frac{n_y}{\Delta y } \right)^2 }.\]
    src/interface/curvature_tensor.c
    211nx /= dx;
    212ny /= dy;
    213const double norm = sqrt(
    214    + pow(nx, 2.)
    215    + pow(ny, 2.)
    216);
    217const double norminv = 1. / fmax(norm, DBL_EPSILON);
    218nx *= norminv;
    219ny *= norminv;
    
    src/interface/curvature_tensor.c
    263nx = nx / dx;
    264ny = ny / dy;
    265nz = nz / dz;
    266const double norm = sqrt(
    267    + pow(nx, 2.)
    268    + pow(ny, 2.)
    269    + pow(nz, 2.)
    270);
    271const double norminv = 1. / fmax(norm, DBL_EPSILON);
    272nx *= norminv;
    273ny *= norminv;
    274nz *= norminv;
    

Note

On the new coordinate system, each control volume is a unit square.