Steady diffusion-reaction with mesh refinement

Steady diffusion-reaction with mesh refinement#

\[\begin{split} \mathbb{S}_u \begin{cases} \Omega = [0, L_x] \times [0, L_y] \\ u_{\text{D}}(x,y=L_y)=1 \\ u_{\text{D}}(x,y=0)=0 \\ u_{\text{D}}(x=0,y) \\ u_{\text{D}}(x=L_x,y)=0 \\ \mathsf{D}=D\mathsf{I} \\ R = -1 \\ J = 0 \end{cases} \end{split}\]
from typing import Callable
import numpy as np
from lucifex.fem import Function, Constant, cross_section_grid
from lucifex.mesh import rectangle_mesh, refine_mesh, deform_mesh
from lucifex.utils.fenicsx_utils import cell_sizes, number_of_entities
from lucifex.solver import bvp, BoundaryConditions, BoundaryValueProblem
from lucifex.pde.diffusion import steady_diffusion_reaction
from lucifex.plt import plot_mesh, plot_colormap, plot_line, create_multifigure, save_figure

from dolfinx.mesh import create_mesh


def stretch_transform(
    s: float,
    Lx: float | tuple[float, float],
) -> Callable[[float], float]:
    """
    `(x - x₋) / (x₊ - x₋) –> (x - x₋)ˢ / (x₊ - x₋)ˢ`
    """
    if isinstance(Lx, float):
        Lx = (0.0, Lx)
    x_min, x_max = Lx
    Lx = x_max - x_min
    return lambda x: x_min + Lx * ((x - x_min) / Lx) ** s


def shishkin_transform(
    xi: float,
    x0: float,
    Lx: float | tuple[float, float],
) -> Callable[[float], float]:
    """
    `xi ≷ x0` to refine `x ≷ x0`
    """
    if isinstance(Lx, float):
        Lx = (0.0, Lx)
    x_min, x_max = Lx
    x_step = x_min + x0
    m_lt = xi / x0
    m_gt = (x_max - xi) / (x_max - x0)
    return lambda x: (
        (x_min + m_lt * x) * (x <= x_step) 
        +  (x_min + m_lt * x0  + m_gt * (x - x_step)) * (x > x_step)
    )


def create_solver(
    Lx: float,
    Ly: float,
    Nx: int,
    Ny: int,
    cell: str,
    d: float, 
    r: float,
    j: float,
    refinement: str | int | None,
) -> BoundaryValueProblem:
    mesh = rectangle_mesh(Lx, Ly, Nx, Ny, cell)
    bcs = BoundaryConditions(
        ("dirichlet", lambda x: x[1] - Ly, 1.0),
        ("dirichlet", lambda x: x[1], 0.0),  
        ("dirichlet", lambda x: x[0] - Lx, 0.0),  
        ("dirichlet", lambda x: x[0], 0.0),  
    )
    degree = 1
    if refinement is None:
        pass
    elif isinstance(refinement, int):
        degree = refinement
    elif refinement == 'refine':
        marker = lambda x: x[1] > 0.75 * Ly
        mesh = refine_mesh(mesh, marker, n_stop=2)
    elif refinement == 'stretch':
        mesh = deform_mesh(mesh, stretch_transform(0.5, Ly), axis=1)
    elif refinement == 'shishkin':
        mesh = deform_mesh(mesh, shishkin_transform(0.75 * Ly, 0.5 * Ly, Ly), axis=1)
    else:
        raise ValueError(refinement)

    d = Constant(mesh, d)
    r = Constant(mesh, r)
    j = Constant(mesh, j)
    u = Function((mesh, 'P', degree), name='u')
    return bvp(steady_diffusion_reaction, bcs)(u, d, r, j)


Lx = 1.0
Ly = 1.0
Nx = 10
Ny = 10
cell = 'right'
elem = ('P', 1)
d = 1e-3
r = -1.0
j = 0.0

refinement_opts = (None, 'shishkin', 'refine', 'stretch', 2)
solutions: dict[str, Function] = {}

for rfn in refinement_opts:
    u_solver = create_solver(Lx, Ly, Nx, Ny, cell, d, r, j, rfn)
    u_solver.solve()
    solutions[rfn] = u_solver.solution
dy_bl = np.sqrt(d)
y_lims = (-0.3, 1.0)

for rfn, u in solutions.items():
    mesh = u.function_space.mesh
    n_cells = number_of_entities(mesh, 2)
    hmin = np.min(cell_sizes(mesh, 'hmin'))
    hmax = np.max(cell_sizes(mesh, 'hmax'))
    print(f"Refinement option '{rfn}':\n hmin, hmax, ncells = {hmin:.2f}, {hmax:.2f}, {n_cells}")
    u_cs, x_value = cross_section_grid(u, 'x', 0.5)
    deg = u.function_space.ufl_element().degree()
    mfig, axs, _ = create_multifigure(n_cols=2)
    plot_colormap(mfig, axs[0], u, title='$u$')
    plot_mesh(mfig, axs[0], mesh, color='cyan', linewidth=0.75)
    plot_line(mfig, axs[1], u_cs, x_label='$y$', y_label=f'$u(x={x_value})$', title=f'P$_{deg}$')
    axs[1].vlines([Ly - dy_bl], *y_lims, color='gray', ls='dashed', lw=0.75),
    axs[1].set_ylim(*y_lims)
    save_figure(f'u(x,y)_{rfn}', thumbnail=(rfn == 'shishkin'))(mfig)
Refinement option 'None':
 hmin, hmax, ncells = 0.10, 0.14, 200
Refinement option 'shishkin':
 hmin, hmax, ncells = 0.05, 0.18, 200
Refinement option 'refine':
 hmin, hmax, ncells = 0.02, 0.14, 910
Refinement option 'stretch':
 hmin, hmax, ncells = 0.05, 0.33, 200
Refinement option '2':
 hmin, hmax, ncells = 0.10, 0.14, 200
../../_images/b37ea16e6cea609a3240cb0626aa62d3c00ca3eede563ffa5e342b04fd3989ed.png ../../_images/b1949fd8d591cc55df590a5b1576675def00eb84141c1cdb641ac1dc73f538df.png ../../_images/f1f9855e2c2629b41093b5be25d21cb3375bc7b8e5aab904607232d5fc568e4f.png ../../_images/32c0550e26df431e041d317ad3c723f814c78ec2f56ae9d8d84d290b71eda8f6.png ../../_images/5e465d4a0790c6dffdf97ac16560d5b9859b337846f0eb42982ae29cfb800837.png
The Kernel crashed while executing code in the current cell or a previous cell. 

Please review the code in the cell(s) to identify a possible cause of the failure. 

Click <a href='https://aka.ms/vscodeJupyterKernelCrash'>here</a> for more info. 

View Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details.