Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add advection-diffusion as another usage example #1138

Merged
merged 4 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions docs/examples/ex50.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright (C) 2024 Radost Waszkiewicz and Jan Turczynowicz
# This software is published under BSD-3-clause license

# Folowing code solves advection-diffusion problem for
# temperature distribution around a cold sphere in warm
# liquid. Liquid flow is modelled using Stokes flow field.
# Thermal difusivity to advection ratio is controlled by
# Peclet number.

import numpy as np

from pathlib import Path
from skfem import MeshTri, Basis, ElementTriP1, BilinearForm
from skfem import asm, solve, condense
from skfem.helpers import grad, dot

# Define the Peclet number
peclet = 30

#
# Code for creating mesh which we load
#

# floor_depth = 5.0
# floor_width = 5.0
# ball_size = 1.0
# ball_segments = 100
# mesh_size = 0.01
# far_mesh = 0.5

# box_points = [
# ([0, -floor_depth], far_mesh),
# ([floor_width, -floor_depth], far_mesh),
# ([floor_width, floor_depth], far_mesh),
# ([0, floor_depth], mesh_size),
# ]

# phi_values = np.linspace(0, np.pi, ball_segments)
# ball_points = ball_size * np.column_stack((np.sin(phi_values), np.cos(phi_values)))
# mesh_boundary = np.vstack((
# np.array([p for p,s in box_points])
# , ball_points))

# # Create the geometry and mesh using pygmsh
# with pygmsh.geo.Geometry() as geom:
# poly = geom.add_polygon(
# mesh_boundary,
# mesh_size=([s for p,s in box_points]) + ([mesh_size] * len(ball_points)),
# )

# raw_mesh = geom.generate_mesh()

# # Convert the mesh to a skfem MeshTri object and define boundaries
# mesh = MeshTri(
# raw_mesh.points[:, :2].T, raw_mesh.cells_dict["triangle"].T
# ).with_boundaries(
# {
# "left": lambda x: np.isclose(x[0], 0), # Left boundary condition
# "bottom": lambda x: np.isclose(x[1], -floor_depth), # Bottom boundary condition
# "ball": lambda x: x[0] ** 2 + x[1] ** 2 < 1.1 * ball_size**2,
# }
# )

mesh = MeshTri.load(Path(__file__).parent / 'meshes' / 'cylinder_stokes.msh')

# Define the basis for the finite element method
basis = Basis(mesh, ElementTriP1())


@BilinearForm
def advection(k, l, m):
"""Advection bilinear form."""

# Coordinate fields
r, z = m.x

u = 1 # velocity scale
a = 1 # ball size

# Stokes flow around a sphere of size `a`
w = r ** 2 + z ** 2
v_r = ((3 * a * r * z * u) / (4 * w**0.5)) * ((a / w) ** 2 - (1 / w))
v_z = u + ((3 * a * u) / (4 * w**0.5)) * (
(2 * a**2 + 3 * r**2) / (3 * w) - ((a * r) / w) ** 2 - 2
)

return (l * v_r * grad(k)[0] + l * v_z * grad(k)[1]) * 2 * np.pi * r


@BilinearForm
def claplace(u, v, w):
"""Laplace operator in cylindrical coordinates."""
r = abs(w.x[1])
return dot(grad(u), grad(v)) * 2 * np.pi * r


# Identify the interior degrees of freedom
interior = basis.complement_dofs(basis.get_dofs({"bottom", "ball"}))

# Assemble the system matrix
A = asm(claplace, basis) + peclet * asm(advection, basis)

# Boundary condition
u = basis.zeros()
u[basis.get_dofs("bottom")] = 1.0
u[basis.get_dofs("ball")] = 0.0

u = solve(*condense(A, x=u, I=interior))

if __name__ == "__main__":

mesh.draw(boundaries=True).show()
basis.plot(u, shading='gouraud', cmap='viridis').show()
Binary file added docs/examples/meshes/cylinder_stokes.msh
Binary file not shown.
18 changes: 18 additions & 0 deletions docs/listofexamples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,24 @@ This example solves the advection equation on a periodic square mesh.
See the source code of :exlink:`42`
for more information.

Example 50: Advection-diffusion in non-uniform flow
---------------------------------------------------

This example solves the advection-diffusion problem
for the temperature distribution around a cold sphere
in a warm liquid. The liquid flow is modeled using
the Stokes flow field. The thermal diffusivity to
advection ratio is controlled by the Peclet number.
The problem is solved in a cylindrical coordinate
system (hence a different definition of the Laplacian).

.. figure:: https://github.com/turczyneq/fem_advection_diffusion/assets/51670923/271b5b86-71f1-49fb-b32e-3c9d012a29ee

Temperature distribution of Example 50. Liquid behind the sphere is colder than the incoming one.

See the source code of :exlink:`50` for more information.


Heat transfer
=============

Expand Down
8 changes: 8 additions & 0 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,3 +408,11 @@ class TestEx49(TestCase):
def runTest(self):
import docs.examples.ex49 as ex49
self.assertLess(np.abs(ex49.l2), 0.0012)


class TestEx50(TestCase):

def runTest(self):
import docs.examples.ex50 as ex50
self.assertAlmostEqual(ex50.u.max(),
1.0026836382652844)
Loading