Source code for PyFinitDiff.finite_difference_1D.boundaries
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy
from typing import Optional, List, Tuple
from pydantic.dataclasses import dataclass
from pydantic import ConfigDict
from PyFinitDiff.boundary_values import BoundaryValue
config_dict = ConfigDict(
extra='forbid',
strict=True,
arbitrary_types_allowed=True,
kw_only=True,
frozen=False
)
[docs]
class Boundary:
"""
Class representing a boundary with a specific name, value, and mesh information.
Parameters
----------
name : str
The name of the boundary.
value : Optional[BoundaryValue]
The value associated with the boundary, such as BoundaryValue.SYMMETRIC, BoundaryValue.ANTI_SYMMETRIC, BoundaryValue.ZERO, or BoundaryValue.NONE.
mesh_info : object
The mesh information object containing information about the mesh size and structure.
"""
def __init__(self, name: str, value: Optional[BoundaryValue], mesh_info: object) -> None:
self.name = name
self.value = value
self.mesh_info = mesh_info
[docs]
def get_factor(self) -> float:
"""
Get the factor associated with the boundary value.
Returns
-------
float
The factor corresponding to the boundary value.
Raises
------
ValueError
If the boundary value is unexpected.
"""
match self.value:
case BoundaryValue.SYMMETRIC:
return 1.0
case BoundaryValue.ANTI_SYMMETRIC:
return -1.0
case BoundaryValue.ZERO:
return 0.0
case BoundaryValue.NONE:
return numpy.nan
case _:
raise ValueError(f"Unexpected boundary value: {self.value}")
[docs]
def get_shift_vector(self, offset: int) -> Optional[numpy.ndarray]:
"""
Calculate the shift vector based on the boundary name and offset.
Parameters
----------
offset : int
The offset value to be used in the shift vector calculation.
Returns
-------
Optional[numpy.ndarray]
The shift vector as a numpy array, or None if the boundary name is 'center'.
Raises
------
ValueError
If the boundary name is unexpected.
"""
offset = abs(offset)
match self.name.lower():
case 'center':
return None
case 'left':
shift_vector = numpy.zeros(self.mesh_info.size)
shift_vector[:offset] = numpy.arange(offset)[::-1] + 1
return shift_vector
case 'right':
shift_vector = numpy.zeros(self.mesh_info.size)
shift_vector[-offset - 1:] = -numpy.arange(offset + 1)
return shift_vector
case _:
raise ValueError(f"Unexpected boundary name: {self.name}")
@dataclass(config=config_dict)
class Boundaries:
"""
Class representing the boundaries with left and right values.
Parameters
----------
left : Optional[BoundaryValue]
Value of the left boundary. Defaults to BoundaryValue.ZERO.
right : Optional[BoundaryValue]
Value of the right boundary. Defaults to BoundaryValue.ZERO.
all_boundaries : List[str]
List of all boundary names.
"""
left: Optional[BoundaryValue] = BoundaryValue.ZERO
right: Optional[BoundaryValue] = BoundaryValue.ZERO
all_boundaries = ['left', 'right']
def assert_both_boundaries_not_same(self, boundary_0: BoundaryValue, boundary_1: BoundaryValue) -> None:
"""
Assert that both boundaries are not the same axis symmetries if they are not BoundaryValue.ZERO.
Parameters
----------
boundary_0 : BoundaryValue
The first boundary value.
boundary_1 : BoundaryValue
The second boundary value.
Raises
------
ValueError
If both boundaries are set to the same axis symmetries.
"""
if boundary_0 != BoundaryValue.ZERO and boundary_1 != BoundaryValue.ZERO:
raise ValueError("Same-axis symmetries shouldn't be set on both ends")
def get_boundary_pairs(self) -> List[Tuple[BoundaryValue, BoundaryValue]]:
"""
Get the pairs of boundaries.
Returns
-------
List[Tuple[BoundaryValue, BoundaryValue]]
A list of tuples containing boundary pairs.
"""
return [(self.left, self.right)]
def get_boundary(self, name: str) -> Boundary:
"""
Return a specific instance of the boundary.
Parameters
----------
name : str
The name of the boundary.
Returns
-------
Boundary
The boundary instance.
Raises
------
AttributeError
If the boundary name is not an attribute of the instance.
"""
if not hasattr(self, name):
value = None
else:
value = getattr(self, name)
boundary = Boundary(
name=name,
value=value,
mesh_info=self.mesh_info
)
return boundary
def offset_to_boundary(self, offset: int) -> str:
"""
Determine the boundary based on the offset.
Parameters
----------
offset : int
The offset value.
Returns
-------
str
The name of the boundary corresponding to the offset.
Raises
------
ValueError
If the offset does not correspond to a valid boundary.
"""
if offset == 0:
return self.get_boundary('center')
if offset > 0:
if offset < self.mesh_info.n_x:
return self.get_boundary('right')
if offset < 0:
if offset > -self.mesh_info.n_x:
return self.get_boundary('left')
raise ValueError("Offset does not correspond to a valid boundary.")