Non-cartesian meshes

Contents

Non-cartesian meshes#

Objectives

  • Tips and tricks to better handle high concentration gradients or discontinuities

FESTIM can run simulations in cylindrical or spherical coordinates.

This is extremely useful to simulate axisymmetric systems. Here we show two examples with a pipe and a sphere.

For instance, on the geometry below, instead of simulating the full 3D domain, one can simply simulate a 2D cross section and assume axial symmetry. If the hollow cylinder is infinitely high, then it is even possible to only simulate a 1D domain.

image

The cylindrical system of coordinates can be applied to 1D and 2D domains. The spherical system of coordinates can only be applied to 1D domains.

Pipe#

The first example is a 1D axisymmetric simulation representing a pipe.

import festim as F
import numpy as np

my_model = F.Simulation()

Cylindrical coordinates can be set in the type argument of Mesh:

inner_radius = 0.1e-2
thickness = 0.2e-2
my_model.mesh = F.MeshFromVertices(
    np.linspace(inner_radius, inner_radius + thickness, num=100),
    type="cylindrical",
)

We’ll use HTM data for the properties of steel:

import h_transport_materials as htm

steel_D = htm.diffusivities.filter(material=htm.Steel).mean()
steel_S = htm.solubilities.filter(material=htm.Steel).mean()

steel = F.Material(
    id=1,
    D_0=steel_D.pre_exp.magnitude,
    E_D=steel_D.act_energy.magnitude,
    S_0=steel_S.pre_exp.magnitude,
    E_S=steel_S.act_energy.magnitude,
)

my_model.materials = steel
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[3], line 1
----> 1 import h_transport_materials as htm
      2 
      3 steel_D = htm.diffusivities.filter(material=htm.Steel).mean()
      4 steel_S = htm.solubilities.filter(material=htm.Steel).mean()

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/h_transport_materials/__init__.py:25
     22 Rg = 8.314 * ureg.Pa * ureg.m**3 * ureg.mol**-1 * ureg.K**-1
     23 avogadro_nb = 6.022e23 * ureg.particle * ureg.mol**-1
---> 25 from pybtex.database import parse_file
     26 from pathlib import Path
     28 bib_database = parse_file(str(Path(__file__).parent) + "/references.bib")

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/pybtex/database/__init__.py:44
     42 from pybtex.errors import report_error
     43 from pybtex.py3compat import fix_unicode_literals_in_doctest, python_2_unicode_compatible
---> 44 from pybtex.plugin import find_plugin
     47 # for python2 compatibility
     48 def indent(text, prefix):

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/pybtex/plugin/__init__.py:26
      2 # Copyright (c) 2006-2021  Andrey Golovizin
      3 # Copyright (c) 2014  Matthias C. M. Troffaes
      4 #
   (...)     21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25 import os.path  # splitext
---> 26 import pkg_resources
     28 from pybtex.exceptions import PybtexError
     31 class Plugin(object):

ModuleNotFoundError: No module named 'pkg_resources'

We set a Sievert’s boundary condition at the inner surface and a \(c=0\) boundary condition at the outer surface.

my_model.boundary_conditions = [
    F.SievertsBC(surfaces=[1], S_0=steel.S_0, E_S=steel.E_S, pressure=1e5),
    F.DirichletBC(surfaces=[2], value=0, field="solute"),
]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 2
      1 my_model.boundary_conditions = [
----> 2     F.SievertsBC(surfaces=[1], S_0=steel.S_0, E_S=steel.E_S, pressure=1e5),
      3     F.DirichletBC(surfaces=[2], value=0, field="solute"),
      4 ]

NameError: name 'steel' is not defined

Note: at the moment, AverageVolume and TotalVolume are not implemented for non-cartesian coordinates (see Issue #723)

my_model.T = 500

my_model.settings = F.Settings(
    absolute_tolerance=1e10,
    relative_tolerance=1e-10,
    final_time=10000,
)

my_model.dt = F.Stepsize(10, stepsize_change_ratio=1.1)

derived_quantities = F.DerivedQuantities(
    [
        F.SurfaceFluxCylindrical(field="solute", surface=1),
        F.SurfaceFluxCylindrical(field="solute", surface=2),
    ],
    show_units=True,
)

txt_export = F.TXTExport(field="solute", filename="task14/solute.txt")

my_model.exports = [derived_quantities, txt_export]

my_model.initialise()
my_model.run()

Hide code cell output

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[5], line 23
     19 txt_export = F.TXTExport(field="solute", filename="task14/solute.txt")
     20 
     21 my_model.exports = [derived_quantities, txt_export]
     22 
---> 23 my_model.initialise()
     24 my_model.run()

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/festim/generic_simulation.py:356, in Simulation.initialise(self)
    354 self.check_mesh_dim_coords()
    355 if isinstance(self.mesh, festim.Mesh1D):
--> 356     self.mesh.define_measures(self.materials)
    357 else:
    358     self.mesh.define_measures()

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/festim/meshing/mesh_1d.py:76, in Mesh1D.define_measures(self, materials)
     74 def define_measures(self, materials):
     75     """Creates the fenics.Measure objects for self.dx and self.ds"""
---> 76     if len(materials) > 1 and any(m.borders is None for m in materials):
     77         raise ValueError(
     78             "borders attributes need to be set for multiple 1D domains"
     79         )
     80     if materials[0].borders is not None:

TypeError: object of type 'NoneType' has no len()
import matplotlib.pyplot as plt

cmap = plt.get_cmap("viridis")

data = np.genfromtxt("task14/solute.txt", skip_header=1, delimiter=",")
for i in range(1, data.shape[1], 3):
    plt.plot(data[:, 0] / 1e-3, data[:, i], color=cmap(i/data.shape[1]))

plt.xlabel("x [mm]")
plt.ylabel("c [H/m³]")
plt.show()
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[6], line 5
      1 import matplotlib.pyplot as plt
      2 
      3 cmap = plt.get_cmap("viridis")
      4 
----> 5 data = np.genfromtxt("task14/solute.txt", skip_header=1, delimiter=",")
      6 for i in range(1, data.shape[1], 3):
      7     plt.plot(data[:, 0] / 1e-3, data[:, i], color=cmap(i/data.shape[1]))
      8 

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/numpy/lib/_npyio_impl.py:1978, in genfromtxt(fname, dtype, comments, delimiter, skip_header, skip_footer, converters, missing_values, filling_values, usecols, names, excludelist, deletechars, replace_space, autostrip, case_sensitive, defaultfmt, unpack, usemask, loose, invalid_raise, max_rows, encoding, ndmin, like)
   1976     fname = os.fspath(fname)
   1977 if isinstance(fname, str):
-> 1978     fid = np.lib._datasource.open(fname, 'rt', encoding=encoding)
   1979     fid_ctx = contextlib.closing(fid)
   1980 else:

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/numpy/lib/_datasource.py:192, in open(path, mode, destpath, encoding, newline)
    155 """
    156 Open `path` with `mode` and return the file object.
    157 
   (...)    188 
    189 """
    191 ds = DataSource(destpath)
--> 192 return ds.open(path, mode, encoding=encoding, newline=newline)

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/numpy/lib/_datasource.py:529, in DataSource.open(self, path, mode, encoding, newline)
    526     return _file_openers[ext](found, mode=mode,
    527                               encoding=encoding, newline=newline)
    528 else:
--> 529     raise FileNotFoundError(f"{path} not found.")

FileNotFoundError: task14/solute.txt not found.

The surface flux is computed for a full angular coverage by default. The units of the surface flux is H/m/s (per unit height).

plt.plot(
    derived_quantities[0].t,
    np.abs(derived_quantities[0].data),
    linewidth=2,
    label="Inner surface",
)

plt.plot(
    derived_quantities[1].t,
    np.abs(derived_quantities[1].data),
    linewidth=2,
    label="Outer surface",
)

plt.xlabel("Time [s]")
plt.ylabel("Absolute flux [H/m s]")
plt.legend()
plt.show()
../_images/5c67559efac0eaff64053d71a017c58cc9d541b0850e325a4400f77bc8555f53.png

Sphere#

The second example is a plain tungsten sphere of radius 0.1 mm. Similarly we can set the system of coordinates to spherical.

my_model = F.Simulation()

radius = 1e-4
my_model.mesh = F.MeshFromVertices(
    np.linspace(0, radius, num=100),
    type="spherical",
)

tungsten_D = htm.diffusivities.filter(material=htm.Tungsten).mean()
tungsten_S = htm.solubilities.filter(material=htm.Tungsten).mean()
tungsten = F.Material(
    id=1,
    D_0=tungsten_D.pre_exp.magnitude,
    E_D=tungsten_D.act_energy.magnitude,
    S_0=tungsten_S.pre_exp.magnitude,
    E_S=tungsten_S.act_energy.magnitude,
)

my_model.materials = tungsten

my_model.boundary_conditions = [
    F.SievertsBC(surfaces=[2], S_0=tungsten.S_0, E_S=tungsten.E_S, pressure=1e5),
]

my_model.T = 800

my_model.settings = F.Settings(
    absolute_tolerance=1e10,
    relative_tolerance=1e-10,
    final_time=100,
)

my_model.dt = F.Stepsize(1, stepsize_change_ratio=1.1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 9
      5     np.linspace(0, radius, num=100),
      6     type="spherical",
      7 )
      8 
----> 9 tungsten_D = htm.diffusivities.filter(material=htm.Tungsten).mean()
     10 tungsten_S = htm.solubilities.filter(material=htm.Tungsten).mean()
     11 tungsten = F.Material(
     12     id=1,

NameError: name 'htm' is not defined

The spherical equivalent class of SurfaceFlux is SurfaceFluxSpherical:

derived_quantities = F.DerivedQuantities(
    [
        F.SurfaceFluxSpherical(field="solute", surface=2),
    ],
    show_units=True,
)

txt_export = F.TXTExport(field="solute", filename="task14/solute_spherical.txt")

my_model.exports = [derived_quantities, txt_export]
my_model.initialise()
my_model.run()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[10], line 1
----> 1 my_model.initialise()
      2 my_model.run()

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/festim/generic_simulation.py:328, in Simulation.initialise(self)
    324 set_log_level(self.log_level)
    326 self.t = 0  # reinitialise t to zero
--> 328 if self.settings.chemical_pot:
    329     self.mobile = festim.Theta()
    330 else:

AttributeError: 'NoneType' object has no attribute 'chemical_pot'
import matplotlib.pyplot as plt

data = np.genfromtxt("task14/solute_spherical.txt", skip_header=1, delimiter=",")
cmap = plt.get_cmap("viridis")
for i in range(1, data.shape[1], 3):
    plt.plot(
        data[:, 0] / 1e-3, data[:, i], color=cmap(i / data.shape[1])
    )

plt.xlabel("x [mm]")
plt.ylabel("c [H/m³]")
plt.show()
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[11], line 3
      1 import matplotlib.pyplot as plt
      2 
----> 3 data = np.genfromtxt("task14/solute_spherical.txt", skip_header=1, delimiter=",")
      4 cmap = plt.get_cmap("viridis")
      5 for i in range(1, data.shape[1], 3):
      6     plt.plot(

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/numpy/lib/_npyio_impl.py:1978, in genfromtxt(fname, dtype, comments, delimiter, skip_header, skip_footer, converters, missing_values, filling_values, usecols, names, excludelist, deletechars, replace_space, autostrip, case_sensitive, defaultfmt, unpack, usemask, loose, invalid_raise, max_rows, encoding, ndmin, like)
   1976     fname = os.fspath(fname)
   1977 if isinstance(fname, str):
-> 1978     fid = np.lib._datasource.open(fname, 'rt', encoding=encoding)
   1979     fid_ctx = contextlib.closing(fid)
   1980 else:

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/numpy/lib/_datasource.py:192, in open(path, mode, destpath, encoding, newline)
    155 """
    156 Open `path` with `mode` and return the file object.
    157 
   (...)    188 
    189 """
    191 ds = DataSource(destpath)
--> 192 return ds.open(path, mode, encoding=encoding, newline=newline)

File ~/checkouts/readthedocs.org/user_builds/festim-workshop/conda/festim1/lib/python3.11/site-packages/numpy/lib/_datasource.py:529, in DataSource.open(self, path, mode, encoding, newline)
    526     return _file_openers[ext](found, mode=mode,
    527                               encoding=encoding, newline=newline)
    528 else:
--> 529     raise FileNotFoundError(f"{path} not found.")

FileNotFoundError: task14/solute_spherical.txt not found.
plt.plot(
    derived_quantities[0].t,
    np.abs(derived_quantities[0].data),
    label="Outer surface flux",
)

plt.xlabel("Time [s]")
plt.ylabel("Absolute flux [H/s]")
plt.legend()
plt.show()
../_images/c865d67acc9781d8b6bb61426cc0dbc7084ed63ba2ffd6b12c21350ce649dfd6.png