From ac896f2cfe5fde906d2c4c8ba23dab9039c9db87 Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Tue, 24 Feb 2026 09:37:52 -0500 Subject: [PATCH 1/5] initial example --- examples/real_function_space_nonlinear.ipynb | 418 +++++++++++++++++++ 1 file changed, 418 insertions(+) create mode 100644 examples/real_function_space_nonlinear.ipynb diff --git a/examples/real_function_space_nonlinear.ipynb b/examples/real_function_space_nonlinear.ipynb new file mode 100644 index 0000000..3567567 --- /dev/null +++ b/examples/real_function_space_nonlinear.ipynb @@ -0,0 +1,418 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2424af5e", + "metadata": {}, + "source": [ + "# Real function spaces (non linear)\n", + "\n", + "Author: Remi Delaporte-Mathurin, Jørgen S. Dokken\n", + "\n", + "License: MIT\n", + "\n", + "In this example we will show how to use the \"real\" function space to solve\n", + "a non linear problem.\n", + "\n", + "## Mathematical formulation\n", + " The problem at hand is:\n", + "Find $u \\in H^1(\\Omega)$ such that\n", + "\n", + "$$\n", + "\\begin{align}\n", + "\\Delta u &= 0 \\quad \\text{in } \\Omega, \\\\\n", + "u &= 0 \\quad \\text{on } \\Gamma_1, \\\\\n", + "u &= K P_b \\quad \\text{on } \\Gamma_b, \\\\\n", + "\\end{align}\n", + "$$\n", + "\n", + "where $P_b$ is the partial pressure inside the cavity region. The temporal evolution of $P_b$ is governed by:\n", + "\n", + "$$\n", + "\\frac{dP_b}{dt} = \\frac{e}{V} \\int_{\\Gamma_b} -D \\nabla c \\cdot \\mathbf{n} dS\n", + "$$\n", + "\n", + "\n", + "We write the weak formulation:\n", + "\n", + "$$\n", + "\\begin{align}\n", + "\\int_\\Omega \\frac{u - u_n}{\\Delta t} \\cdot v~\\mathrm{d}x + \\int_\\Omega \\nabla u \\cdot \\nabla v~\\mathrm{d}x &= 0\\\\\n", + "\\int_\\Omega \\frac{P_b - P_{b_n}}{\\Delta t} \\cdot w~\\mathrm{d}x &= \\frac{e}{V} \\int_{\\Gamma_b} -D \\nabla c \\cdot \\mathbf{n} ~dS ~w.\n", + "\\end{align}\n", + "$$\n" + ] + }, + { + "cell_type": "markdown", + "id": "f658dceb", + "metadata": {}, + "source": [ + "## Implementation\n", + "We start by import the necessary modules\n", + "```{admonition} Clickable functions/classes\n", + "Note that for the modules imported in this example, you can click on the function/class\n", + "name to be redirected to the corresponding documentation page.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "06e16e63", + "metadata": {}, + "outputs": [], + "source": [ + "from mpi4py import MPI\n", + "import dolfinx\n", + "from dolfinx import log\n", + "from dolfinx.fem.petsc import NonlinearProblem\n", + "from dolfinx.io import VTXWriter, gmsh as gmshio\n", + "import numpy as np\n", + "from scifem import create_real_functionspace\n", + "import ufl\n", + "\n", + "import gmsh" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "027d8dc8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Info : Meshing 1D... \n", + "Info : [ 0%] Meshing curve 5 (Ellipse)\n", + "Info : [ 30%] Meshing curve 6 (Line)\n", + "Info : [ 50%] Meshing curve 7 (Line)\n", + "Info : [ 70%] Meshing curve 8 (Line)\n", + "Info : [ 90%] Meshing curve 9 (Line)\n", + "Info : Done meshing 1D (Wall 0.00303789s, CPU 0.002975s)\n", + "Info : Meshing 2D...\n", + "Info : Meshing surface 1 (Plane, Frontal-Delaunay for Quads)\n", + "Info : Simple recombination completed (Wall 0.000968826s, CPU 0.000586s): 79 quads, 12 triangles, 0 invalid quads, 0 quads with Q < 0.1, avg Q = 0.791132, min Q = 0.503081\n", + "Info : Simple recombination completed (Wall 0.00135738s, CPU 0.001233s): 352 quads, 0 triangles, 0 invalid quads, 0 quads with Q < 0.1, avg Q = 0.844698, min Q = 0.493962\n", + "Info : Done meshing 2D (Wall 0.00381131s, CPU 0.003215s)\n", + "Info : Refining mesh...\n", + "Info : Meshing order 2 (curvilinear on)...\n", + "Info : [ 0%] Meshing curve 5 order 2\n", + "Info : [ 20%] Meshing curve 6 order 2\n", + "Info : [ 40%] Meshing curve 7 order 2\n", + "Info : [ 60%] Meshing curve 8 order 2\n", + "Info : [ 70%] Meshing curve 9 order 2\n", + "Info : [ 90%] Meshing surface 1 order 2\n", + "Info : Done meshing order 2 (Wall 0.0010111s, CPU 0.001076s)\n", + "Info : Done refining mesh (Wall 0.0011461s, CPU 0.00123s)\n", + "Info : 1484 nodes 1565 elements\n", + "Info : Meshing order 2 (curvilinear on)...\n", + "Info : [ 0%] Meshing curve 5 order 2\n", + "Info : [ 20%] Meshing curve 6 order 2\n", + "Info : [ 40%] Meshing curve 7 order 2\n", + "Info : [ 60%] Meshing curve 8 order 2\n", + "Info : [ 70%] Meshing curve 9 order 2\n", + "Info : [ 90%] Meshing surface 1 order 2\n", + "Info : Done meshing order 2 (Wall 0.00412756s, CPU 0.003862s)\n", + "Info : Optimizing mesh (Netgen)...\n", + "Info : Done optimizing mesh (Wall 5.89003e-07s, CPU 1e-06s)\n" + ] + } + ], + "source": [ + "gmsh.initialize()\n", + "\n", + "L = 0.5\n", + "H = 0.41\n", + "c_x = c_y = 0.2\n", + "r = 0.05\n", + "gdim = 2\n", + "mesh_comm = MPI.COMM_WORLD\n", + "model_rank = 0\n", + "if mesh_comm.rank == model_rank:\n", + " rectangle = gmsh.model.occ.addRectangle(0, 0, 0, L, H, tag=1)\n", + " obstacle = gmsh.model.occ.addDisk(c_x, c_y, 0, r, r)\n", + "\n", + "if mesh_comm.rank == model_rank:\n", + " fluid = gmsh.model.occ.cut([(gdim, rectangle)], [(gdim, obstacle)])\n", + " gmsh.model.occ.synchronize()\n", + "\n", + "solid_marker = 1\n", + "if mesh_comm.rank == model_rank:\n", + " volumes = gmsh.model.getEntities(dim=gdim)\n", + " assert len(volumes) == 1\n", + " gmsh.model.addPhysicalGroup(volumes[0][0], [volumes[0][1]], solid_marker)\n", + " gmsh.model.setPhysicalName(volumes[0][0], solid_marker, \"Solid\")\n", + "\n", + "wall_marker, obstacle_marker = 2, 3\n", + "walls, obstacle = [], []\n", + "if mesh_comm.rank == model_rank:\n", + " boundaries = gmsh.model.getBoundary(volumes, oriented=False)\n", + " for boundary in boundaries:\n", + " center_of_mass = gmsh.model.occ.getCenterOfMass(boundary[0], boundary[1])\n", + " if np.allclose(center_of_mass, [0, H / 2, 0]):\n", + " walls.append(boundary[1])\n", + " elif np.allclose(center_of_mass, [L, H / 2, 0]):\n", + " walls.append(boundary[1])\n", + " elif np.allclose(center_of_mass, [L / 2, H, 0]) or np.allclose(\n", + " center_of_mass, [L / 2, 0, 0]\n", + " ):\n", + " walls.append(boundary[1])\n", + " else:\n", + " obstacle.append(boundary[1])\n", + " gmsh.model.addPhysicalGroup(1, walls, wall_marker)\n", + " gmsh.model.setPhysicalName(1, wall_marker, \"Walls\")\n", + " gmsh.model.addPhysicalGroup(1, obstacle, obstacle_marker)\n", + " gmsh.model.setPhysicalName(1, obstacle_marker, \"Obstacle\")\n", + "\n", + "\n", + "res_min = r / 3\n", + "if mesh_comm.rank == model_rank:\n", + " distance_field = gmsh.model.mesh.field.add(\"Distance\")\n", + " gmsh.model.mesh.field.setNumbers(distance_field, \"EdgesList\", obstacle)\n", + " threshold_field = gmsh.model.mesh.field.add(\"Threshold\")\n", + " gmsh.model.mesh.field.setNumber(threshold_field, \"IField\", distance_field)\n", + " gmsh.model.mesh.field.setNumber(threshold_field, \"LcMin\", res_min)\n", + " gmsh.model.mesh.field.setNumber(threshold_field, \"LcMax\", 0.25 * H)\n", + " gmsh.model.mesh.field.setNumber(threshold_field, \"DistMin\", r)\n", + " gmsh.model.mesh.field.setNumber(threshold_field, \"DistMax\", 2 * H)\n", + " min_field = gmsh.model.mesh.field.add(\"Min\")\n", + " gmsh.model.mesh.field.setNumbers(min_field, \"FieldsList\", [threshold_field])\n", + " gmsh.model.mesh.field.setAsBackgroundMesh(min_field)\n", + "\n", + "\n", + "if mesh_comm.rank == model_rank:\n", + " gmsh.option.setNumber(\"Mesh.Algorithm\", 8)\n", + " gmsh.option.setNumber(\"Mesh.RecombinationAlgorithm\", 2)\n", + " gmsh.option.setNumber(\"Mesh.RecombineAll\", 1)\n", + " gmsh.option.setNumber(\"Mesh.SubdivisionAlgorithm\", 1)\n", + " gmsh.model.mesh.generate(gdim)\n", + " gmsh.model.mesh.setOrder(2)\n", + " gmsh.model.mesh.optimize(\"Netgen\")\n", + "\n", + "mesh_data = gmshio.model_to_mesh(gmsh.model, mesh_comm, model_rank, gdim=gdim)\n", + "mesh = mesh_data.mesh\n", + "assert mesh_data.facet_tags is not None\n", + "ft = mesh_data.facet_tags\n", + "ft.name = \"Facet markers\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "a5039e02", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "06b10b43b4034fedb3a0312c5264d6ca", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "EmbeddableWidget(value='