Skip to content

Commit

Permalink
Rename to Value, work with temperature only classes
Browse files Browse the repository at this point in the history
  • Loading branch information
jhdark committed Jan 22, 2025
1 parent 06b1dab commit ac60724
Show file tree
Hide file tree
Showing 11 changed files with 447 additions and 374 deletions.
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, ConvertToFenicsObject
from .helpers import (
as_fenics_constant,
as_mapped_function,
as_fenics_interpolation_expression,
as_fenics_interp_expr_and_function,
Value,
)
from .hydrogen_transport_problem import (
HTransportProblemDiscontinuous,
HydrogenTransportProblem,
Expand Down
205 changes: 48 additions & 157 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 = helpers.ConvertToFenicsObject(value)
# self.bc_expr = None

# @property
# def value_fenics(self):
# return self._value_fenics

# @value_fenics.setter
# def value_fenics(self, value: None | fem.Function | fem.Constant | np.ndarray):
# 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?
# raise TypeError(
# "Value must be a dolfinx.fem.Function, dolfinx.fem.Constant, or a np.ndarray not"
# + f"{type(value)}"
# )
# 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
@property
def value(self):
return self._value

@value.setter
def value(self, value):
if value is None:
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 float, int, fem.Constant, fem.Function, or callable"
)

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"
)
22 changes: 19 additions & 3 deletions src/festim/boundary_conditions/flux_bc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import numpy as np
import ufl
from dolfinx import fem

import festim as F
Expand Down Expand Up @@ -34,7 +32,22 @@ def __init__(self, subdomain, value):
self.subdomain = subdomain
self.value = value

self.value_fenics = F.ConvertToFenicsObject(value)
@property
def value(self):
return self._value

@value.setter
def value(self, value):
if value is None:
self._value = value
elif isinstance(value, (float, int, fem.Constant, fem.Function)):
self._value = F.Value(value)
elif callable(value):
self._value = F.Value(value)
else:
raise TypeError(
"Value must be a float, int, fem.Constant, fem.Function, or callable"
)


class ParticleFluxBC(FluxBCBase):
Expand Down Expand Up @@ -118,3 +131,6 @@ class HeatFluxBC(FluxBCBase):

def __init__(self, subdomain, value):
super().__init__(subdomain=subdomain, value=value)

if self.value.temperature_dependent:
raise ValueError("Heat flux cannot be temperature dependent")
Loading

0 comments on commit ac60724

Please sign in to comment.