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

New method to handle and convert user inputs to FEniCS objects #940

Merged
merged 42 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
06b1dab
new Convert to fenics class
jhdark Jan 22, 2025
ac60724
Rename to Value, work with temperature only classes
jhdark Jan 22, 2025
76ab530
addtional argument for convert value
jhdark Jan 27, 2025
19d6b28
add repr dunder for when users print the value
jhdark Jan 27, 2025
0bf64c4
better naming
jhdark Jan 28, 2025
7ef4791
back to as mapped function, typehinting
jhdark Jan 31, 2025
808286e
revert dirichet bc file
jhdark Feb 20, 2025
a8ef536
add spaces
jhdark Feb 20, 2025
61e2ab4
revert flux bc
jhdark Feb 20, 2025
595ec79
add spaces
jhdark Feb 20, 2025
fa27411
revert initial condition
jhdark Feb 20, 2025
d57e3b0
remove import festim
jhdark Feb 20, 2025
4f52b04
revert heat transfer problem
jhdark Feb 20, 2025
abb58a4
revert temperature
jhdark Feb 20, 2025
dac66e1
revert hydrogen transport problem
jhdark Feb 20, 2025
eb7d1a0
revert h transport problem and tests
jhdark Feb 20, 2025
cf7cecb
revert changes to just source
jhdark Feb 20, 2025
41396f3
use fenics object
jhdark Feb 20, 2025
77a8422
accept None as input for value
jhdark Feb 20, 2025
83dfca5
fix tests
jhdark Feb 20, 2025
5cc8841
Merge branch 'fenicsx' into new_value_fenics
jhdark Feb 20, 2025
584b20d
format ruff
jhdark Feb 20, 2025
5af11cc
use fenics object
jhdark Feb 20, 2025
2cca0b1
format ruff
jhdark Feb 20, 2025
9e4e8db
formatted ruff
jhdark Feb 20, 2025
aac0a52
add tests for coverage
jhdark Feb 20, 2025
3354072
setter already in Value
jhdark Feb 20, 2025
6bc191b
remove bloat, accept expression
jhdark Feb 20, 2025
f4f86d6
more tests for coverage
jhdark Feb 20, 2025
c712c0e
Apply suggestions from code review
jhdark Feb 21, 2025
ae2763c
use mesh from functionspace
jhdark Feb 21, 2025
a0a10a6
format ruff
jhdark Feb 21, 2025
1022bef
refactor source: better docs, typehinting
jhdark Feb 21, 2025
d800685
format ruff
jhdark Feb 24, 2025
f0a640b
use functionspace to get mesh
jhdark Feb 24, 2025
3afc0c4
Update src/festim/helpers.py
jhdark Feb 25, 2025
dc76b64
create fucntion spaces first, use function space from subdomain
jhdark Feb 25, 2025
422e4f3
time dependent propety changed to explicitly time dependent
jhdark Feb 25, 2025
d004c62
rename function processing sources
jhdark Feb 25, 2025
5f672bf
Merge branch 'fenicsx' into new_value_fenics
jhdark Feb 28, 2025
9c9ef4d
support nightly
jhdark Feb 28, 2025
4e04da8
new method for interpolation points
jhdark Feb 28, 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
8 changes: 7 additions & 1 deletion src/festim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@
from .exports.vtx import VTXSpeciesExport, VTXTemperatureExport
from .exports.xdmf import XDMFExport
from .heat_transfer_problem import HeatTransferProblem
from .helpers import as_fenics_constant
from .helpers import (
as_fenics_constant,
as_ufl_expression,
as_fenics_interpolation_expression,
as_fenics_interp_expr_and_function,
Value,
)
from .hydrogen_transport_problem import (
HTransportProblemDiscontinuous,
HydrogenTransportProblem,
Expand Down
195 changes: 43 additions & 152 deletions src/festim/boundary_conditions/dirichlet_bc.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,38 +49,22 @@ def __init__(
self.subdomain = subdomain
self.value = value

self.value_fenics = None
self.bc_expr = None

@property
def value_fenics(self):
return self._value_fenics
def value(self):
return self._value

@value_fenics.setter
def value_fenics(self, value: None | fem.Function | fem.Constant | np.ndarray):
@value.setter
def value(self, value):
if value is None:
self._value_fenics = value
return
if not isinstance(value, (fem.Function, fem.Constant, np.ndarray)):
# FIXME: Should we allow sending in a callable here?
self._value = value
elif isinstance(value, (float, int, fem.Constant, fem.Function)):
self._value = helpers.Value(value)
elif callable(value):
self._value = helpers.Value(value)
else:
raise TypeError(
"Value must be a dolfinx.fem.Function, dolfinx.fem.Constant, or a np.ndarray not"
+ f"{type(value)}"
"Value must be a float, int, fem.Constant, fem.Function, or callable"
)
self._value_fenics = value

@property
def time_dependent(self) -> bool:
"""Returns true if the value of the boundary condition is time dependent"""
if self.value is None:
return False
if isinstance(self.value, fem.Constant):
return False
if callable(self.value):
arguments = self.value.__code__.co_varnames
return "t" in arguments
else:
return False

def define_surface_subdomain_dofs(
self,
Expand Down Expand Up @@ -120,19 +104,6 @@ def define_surface_subdomain_dofs(

return bc_dofs

def update(self, t: float):
"""Updates the boundary condition value

Args:
t (float): the time
"""
if callable(self.value):
arguments = self.value.__code__.co_varnames
if isinstance(self.value_fenics, fem.Constant) and "t" in arguments:
self.value_fenics.value = self.value(t=t)
else:
self.value_fenics.interpolate(self.bc_expr)


class FixedConcentrationBC(DirichletBCBase):
"""
Expand Down Expand Up @@ -174,122 +145,42 @@ def __init__(
self.species = species
super().__init__(subdomain, value)

@property
def temperature_dependent(self):
if self.value is None:
return False
if isinstance(self.value, fem.Constant):
return False
if callable(self.value):
arguments = self.value.__code__.co_varnames
return "T" in arguments
else:
return False

def create_value(
self,
function_space: fem.FunctionSpace,
temperature: float | fem.Constant,
t: float | fem.Constant,
):
"""Creates the value of the boundary condition as a fenics object and sets it to
self.value_fenics.
If the value is a constant, it is converted to a `dolfinx.fem.Constant`.
If the value is a function of t, it is converted to `dolfinx.fem.Constant`.
Otherwise, it is converted to a `dolfinx.fem.Function`.Function and the
expression of the function is stored in `bc_expr`.

Args:
function_space (dolfinx.fem.FunctionSpace): the function space
temperature: The temperature
t (dolfinx.fem.Constant): the time
"""
mesh = function_space.mesh
x = ufl.SpatialCoordinate(mesh)

if isinstance(self.value, (int, float)):
self.value_fenics = helpers.as_fenics_constant(mesh=mesh, value=self.value)

elif callable(self.value):
arguments = self.value.__code__.co_varnames

if "t" in arguments and "x" not in arguments and "T" not in arguments:
# only t is an argument
if not isinstance(self.value(t=float(t)), (float, int)):
raise ValueError(
"self.value should return a float or an int, not "
+ f"{type(self.value(t=float(t)))} "
)
self.value_fenics = helpers.as_fenics_constant(
mesh=mesh, value=self.value(t=float(t))
)
else:
self.value_fenics = fem.Function(function_space)
kwargs = {}
if "t" in arguments:
kwargs["t"] = t
if "x" in arguments:
kwargs["x"] = x
if "T" in arguments:
kwargs["T"] = temperature

# store the expression of the boundary condition
# to update the value_fenics later
self.bc_expr = fem.Expression(
self.value(**kwargs),
function_space.element.interpolation_points(),
)
self.value_fenics.interpolate(self.bc_expr)


# alias for FixedConcentrationBC
DirichletBC = FixedConcentrationBC


class FixedTemperatureBC(DirichletBCBase):
def create_value(self, function_space: fem.FunctionSpace, t: fem.Constant):
"""Creates the value of the boundary condition as a fenics object and sets it to
self.value_fenics.
If the value is a constant, it is converted to a `dolfinx.fem.Constant`.
If the value is a function of t, it is converted to a `dolfinx.fem.Constant`.
Otherwise, it is converted to a` dolfinx.fem.Function` and the
expression of the function is stored in `bc_expr`.
"""
Args:
subdomain (festim.Subdomain): the surface subdomain where the boundary
condition is applied
value: The value of the boundary condition. It can be a function of space and/or time

Args:
function_space: the function space
t: the time
"""
mesh = function_space.mesh
x = ufl.SpatialCoordinate(mesh)

if isinstance(self.value, (int, float)):
self.value_fenics = helpers.as_fenics_constant(mesh=mesh, value=self.value)

elif callable(self.value):
arguments = self.value.__code__.co_varnames

if "t" in arguments and "x" not in arguments:
# only t is an argument
if not isinstance(self.value(t=float(t)), (float, int)):
raise ValueError(
"self.value should return a float or an int, not "
+ f"{type(self.value(t=float(t)))} "
)
self.value_fenics = helpers.as_fenics_constant(
mesh=mesh, value=self.value(t=float(t))
)
else:
self.value_fenics = fem.Function(function_space)
kwargs = {}
if "t" in arguments:
kwargs["t"] = t
if "x" in arguments:
kwargs["x"] = x

# store the expression of the boundary condition
# to update the value_fenics later
self.bc_expr = fem.Expression(
self.value(**kwargs),
function_space.element.interpolation_points(),
)
self.value_fenics.interpolate(self.bc_expr)
Examples:

.. highlight:: python
.. code-block:: python

from festim import FixedTemperatureBC
FixedTemperatureBC(subdomain=my_subdomain, value=1)
FixedTemperatureBC(subdomain=my_subdomain,
value=lambda x: 1 + x[0])
FixedTemperatureBC(subdomain=my_subdomain,
value=lambda t: 1 + t)
FixedTemperatureBC(subdomain=my_subdomain,
value=lambda x, t: 1 + x[0] + t)

"""

def __init__(
self,
subdomain: _subdomain.SurfaceSubdomain,
value: np.ndarray | fem.Constant | int | float | Callable,
):
super().__init__(subdomain, value)

if self.value.temperature_dependent:
raise ValueError(
"Temperature dependent boundary conditions are not supported for FixedTemperatureBC"
)
Loading
Loading