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

Average surface and volume derived quantities in Cylindrical and Spherical #820

Merged
merged 31 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fb31540
first effort
jhdark Jul 25, 2024
5fb6032
add new classes
jhdark Jul 25, 2024
cd7c930
remove comments and testing
jhdark Jul 26, 2024
e82da3a
fix
jhdark Jul 26, 2024
740e8e6
surface not volume
jhdark Jul 26, 2024
b0ce4fb
fix documentation
jhdark Jul 26, 2024
a12c519
fix azimuth range spherical and doc strings
jhdark Jul 26, 2024
307b95b
no more than pi in range
jhdark Jul 26, 2024
a83849e
ranges cancel out surface
jhdark Jul 26, 2024
4bc4a6a
remove angles
jhdark Jul 26, 2024
861fb61
Merge branch 'main' into cyl_and_sph_avg_quantities
jhdark Aug 5, 2024
bb1a023
update doc strings
jhdark Aug 5, 2024
d4fe1c2
remove print and quit
jhdark Aug 5, 2024
d904cb3
added tests
jhdark Aug 5, 2024
b96300d
rename tests
jhdark Aug 5, 2024
41b90f1
change export name
jhdark Aug 5, 2024
5944b68
test average volume
jhdark Aug 5, 2024
e747d52
dont need haevy mesh
jhdark Aug 5, 2024
1c22343
Merge branch 'main' into cyl_and_sph_avg_quantities
jhdark Jan 10, 2025
636a322
show units now default
jhdark Jan 10, 2025
87c4312
simplify avg sph surf class
jhdark Jan 10, 2025
d4b6979
fix tests
jhdark Jan 10, 2025
152a9c5
remove unused imports
jhdark Jan 10, 2025
1e5f411
Update festim/exports/derived_quantities/average_surface.py
jhdark Jan 10, 2025
296d744
test avg surface on a rhombus
jhdark Jan 10, 2025
58bd814
add allowed meshes property
jhdark Jan 14, 2025
e5f0a6a
accept spherical meshes for evg surface
jhdark Jan 14, 2025
ce2f148
remove uneccesary test
jhdark Jan 14, 2025
47f853e
test allowed meshes
jhdark Jan 14, 2025
301cba0
additional tests for avg volume
jhdark Jan 14, 2025
10fc3b0
simplify tests
jhdark Jan 14, 2025
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
12 changes: 10 additions & 2 deletions festim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,22 @@
)
from .exports.derived_quantities.hydrogen_flux import HydrogenFlux
from .exports.derived_quantities.thermal_flux import ThermalFlux
from .exports.derived_quantities.average_volume import AverageVolume
from .exports.derived_quantities.average_volume import (
AverageVolume,
AverageVolumeCylindrical,
AverageVolumeSpherical,
)
from .exports.derived_quantities.maximum_volume import MaximumVolume
from .exports.derived_quantities.minimum_volume import MinimumVolume
from .exports.derived_quantities.minimum_surface import MinimumSurface
from .exports.derived_quantities.maximum_surface import MaximumSurface
from .exports.derived_quantities.total_surface import TotalSurface
from .exports.derived_quantities.total_volume import TotalVolume
from .exports.derived_quantities.average_surface import AverageSurface
from .exports.derived_quantities.average_surface import (
AverageSurface,
AverageSurfaceCylindrical,
AverageSurfaceSpherical,
)
from .exports.derived_quantities.point_value import PointValue
from .exports.derived_quantities.adsorbed_hydrogen import AdsorbedHydrogen

Expand Down
58 changes: 57 additions & 1 deletion festim/exports/derived_quantities/average_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, field, surface) -> None:

@property
def allowed_meshes(self):
return ["cartesian"]
return ["cartesian", "spherical"]

@property
def export_unit(self):
Expand All @@ -51,3 +51,59 @@ def compute(self):
return f.assemble(self.function * self.ds(self.surface)) / f.assemble(
1 * self.ds(self.surface)
)


class AverageSurfaceCylindrical(AverageSurface):
RemDelaporteMathurin marked this conversation as resolved.
Show resolved Hide resolved
"""
Computes the average value of a field on a given surface
int(f ds) / int (1 * ds)
ds is the surface measure in cylindrical coordinates.
ds = r dr dtheta

Args:
field (str, int): the field ("solute", 0, 1, "T", "retention")
surface (int): the surface id

Attributes:
field (str, int): the field ("solute", 0, 1, "T", "retention")
surface (int): the surface id
title (str): the title of the derived quantity
show_units (bool): show the units in the title in the derived quantities
file
function (dolfin.function.function.Function): the solution function of
RemDelaporteMathurin marked this conversation as resolved.
Show resolved Hide resolved
the field
r (ufl.indexed.Indexed): the radius of the cylinder

Notes:
Units are in H/m3 for hydrogen concentration and K for temperature
"""

def __init__(self, field, surface) -> None:
super().__init__(field=field, surface=surface)
self.r = None

@property
def allowed_meshes(self):
return ["cylindrical"]

def compute(self):

if self.r is None:
mesh = (
self.function.function_space().mesh()
) # get the mesh from the function
rthetaz = f.SpatialCoordinate(mesh) # get the coordinates from the mesh
self.r = rthetaz[0] # only care about r here

# dS_z = r dr dtheta , assuming axisymmetry dS_z = theta r dr
# dS_r = r dz dtheta , assuming axisymmetry dS_r = theta r dz
# in both cases the expression with self.dx is the same

avg_surf = f.assemble(
self.function * self.r * self.ds(self.surface)
) / f.assemble(1 * self.r * self.ds(self.surface))

return avg_surf


AverageSurfaceSpherical = AverageSurface
92 changes: 92 additions & 0 deletions festim/exports/derived_quantities/average_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
file
function (dolfin.function.function.Function): the solution function of
the field
r (ufl.indexed.Indexed): the radius of the cylinder

.. note::
Units are in H/m3 for hydrogen concentration and K for temperature
Expand Down Expand Up @@ -50,3 +51,94 @@
return f.assemble(self.function * self.dx(self.volume)) / f.assemble(
1 * self.dx(self.volume)
)


class AverageVolumeCylindrical(AverageVolume):
"""
Computes the average value of a field in a given volume
int(f dx) / int (1 * dx)
dx is the volume measure in cylindrical coordinates.
dx = r dr dz dtheta

Note: for particle fluxes J is given in H/s, for heat fluxes J is given in W

Args:
field (str, int): the field ("solute", 0, 1, "T", "retention")
volume (int): the volume id

Attributes:
field (str, int): the field ("solute", 0, 1, "T", "retention")
volume (int): the volume id
title (str): the title of the derived quantity
show_units (bool): show the units in the title in the derived quantities
file
function (dolfin.function.function.Function): the solution function of
the field
r (ufl.indexed.Indexed): the radius of the sphere
"""

def __init__(self, field, volume) -> None:
super().__init__(field=field, volume=volume)
self.r = None

@property
def allowed_meshes(self):
return ["cylindrical"]

Check warning on line 86 in festim/exports/derived_quantities/average_volume.py

View check run for this annotation

Codecov / codecov/patch

festim/exports/derived_quantities/average_volume.py#L86

Added line #L86 was not covered by tests

def compute(self):

if self.r is None:
mesh = (
self.function.function_space().mesh()
) # get the mesh from the function
rthetaz = f.SpatialCoordinate(mesh) # get the coordinates from the mesh
self.r = rthetaz[0] # only care about r here

avg_vol = f.assemble(
self.function * self.r * self.dx(self.volume)
) / f.assemble(1 * self.r * self.dx(self.volume))

return avg_vol


class AverageVolumeSpherical(AverageVolume):
"""
Computes the average value of a field in a given volume
int(f dx) / int (1 * dx)
dx is the volume measure in cylindrical coordinates.
dx = rho dtheta dphi

Note: for particle fluxes J is given in H/s, for heat fluxes J is given in W

Args:
field (str, int): the field ("solute", 0, 1, "T", "retention")
volume (int): the volume id
title (str): the title of the derived quantity
show_units (bool): show the units in the title in the derived quantities
file
function (dolfin.function.function.Function): the solution function of
the field
"""

def __init__(self, field, volume) -> None:
super().__init__(field=field, volume=volume)
self.r = None

@property
def allowed_meshes(self):
return ["spherical"]

Check warning on line 129 in festim/exports/derived_quantities/average_volume.py

View check run for this annotation

Codecov / codecov/patch

festim/exports/derived_quantities/average_volume.py#L129

Added line #L129 was not covered by tests

def compute(self):

if self.r is None:
mesh = (
self.function.function_space().mesh()
) # get the mesh from the function
rthetaphi = f.SpatialCoordinate(mesh) # get the coordinates from the mesh
self.r = rthetaphi[0] # only care about r here

avg_vol = f.assemble(
self.function * self.r**2 * self.dx(self.volume)
) / f.assemble(1 * self.r**2 * self.dx(self.volume))

return avg_vol
57 changes: 56 additions & 1 deletion test/simulation/test_initialise.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def test_TXTExport_times_added_to_milestones(tmpdir):
F.SurfaceFlux(field="solute", surface=1),
F.TotalVolume(field="solute", volume=1),
F.TotalSurface(field="solute", surface=1),
F.AverageSurface(field="solute", surface=1),
F.AverageVolume(field="solute", volume=1),
F.HydrogenFlux(surface=1),
F.ThermalFlux(surface=1),
Expand Down Expand Up @@ -151,6 +150,62 @@ def test_cartesian_and_surface_flux_warning(quantity, sys):
my_model.initialise()


@pytest.mark.parametrize("sys", ["cartesian", "spherical"])
def test_cylindrical_quantities_warning(sys):
"""
Creates a Simulation object and checks that, if either a cartesian
or spherical meshes are given with a AverageSurfaceCylindrical, a warning is raised.

Args:
sys (str): type of the coordinate system
"""
# build
my_model = F.Simulation()
my_model.mesh = F.MeshFromVertices([1, 2, 3], type=sys)
my_model.materials = F.Material(id=1, D_0=1, E_D=0)
my_model.T = F.Temperature(100)
my_model.dt = F.Stepsize(initial_value=3)
my_model.settings = F.Settings(
absolute_tolerance=1e-10, relative_tolerance=1e-10, final_time=4
)

derived_quantities = F.DerivedQuantities(
[F.AverageSurfaceCylindrical(field="solute", surface=1)]
)
my_model.exports = [derived_quantities]

# test
with pytest.warns(UserWarning, match=f"may not work as intended for {sys} meshes"):
my_model.initialise()


def test_spherical_quantities_warning():
"""
Creates a Simulation object and checks that, if a cylindrical
mesh is given with a AverageSurfaceSpherical, a warning is raised.
"""
# build
my_model = F.Simulation()
my_model.mesh = F.MeshFromVertices([1, 2, 3], type="cylindrical")
my_model.materials = F.Material(id=1, D_0=1, E_D=0)
my_model.T = F.Temperature(100)
my_model.dt = F.Stepsize(initial_value=3)
my_model.settings = F.Settings(
absolute_tolerance=1e-10, relative_tolerance=1e-10, final_time=4
)

derived_quantities = F.DerivedQuantities(
[F.AverageSurfaceSpherical(field="solute", surface=1)]
)
my_model.exports = [derived_quantities]

# test
with pytest.warns(
UserWarning, match=f"may not work as intended for cylindrical meshes"
):
my_model.initialise()


@pytest.mark.parametrize(
"value",
[
Expand Down
Loading
Loading