Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
48d95a0
fundamental functions for face properties and cyl mesh averaging.
dccowan Feb 3, 2023
aaa39f0
Revert "fundamental functions for face properties and cyl mesh averag…
dccowan Feb 3, 2023
29f0b77
Starting Mass matrix funs
dccowan Feb 3, 2023
a716702
basic face properties implementation
dccowan Mar 16, 2023
cbd8e18
Merge branch 'main' into face_props_mass_matrices
dccowan Mar 16, 2023
df15517
Merge branch 'main' into face_props_mass_matrices
dccowan Mar 19, 2023
103014c
Merge branch 'main' into face_props_mass_matrices
dccowan Jun 10, 2023
7064873
re-add property
dccowan Jun 12, 2023
6f1d107
remove average edge to face by component
dccowan Jun 16, 2023
021ba3d
Test tensor for inner products with face properties
dccowan Jun 17, 2023
6118afa
Merge branch 'main' into face_props_mass_matrices
dccowan Jun 17, 2023
e115854
face/edge properties for tree meshes
dccowan Jun 19, 2023
166358e
add 2D tensor and tree tests for face and edge property inner products
dccowan Jun 19, 2023
df9ca18
add derivative tests for face and edge properties (TENSOR and TREE)
dccowan Jun 20, 2023
f4fc514
Add analytic, order and derivative tests for cyl mesh (face properties)
dccowan Jun 21, 2023
3dae379
finalize new inner product names, black and flake8
dccowan Jun 22, 2023
60559fb
add documentation
dccowan Jun 23, 2023
1129e0b
review and bug fixes
dccowan Jun 24, 2023
a4d2d49
Merge branch 'main' into face_props_mass_matrices
dccowan Jun 24, 2023
acf55f5
Merge branch 'main' into face_props_mass_matrices
dccowan Jun 29, 2023
f894add
add ignore lambda fun for flake8 in tests
dccowan Jun 29, 2023
6ba8477
Eliminate 'fast inner products' for face and edge properties. Done na…
dccowan Jul 4, 2023
f8862fb
final style check
dccowan Jul 4, 2023
1e42b16
base mesh tests
dccowan Jul 5, 2023
9380206
code coverage and error raise tests
dccowan Jul 5, 2023
8cac8f7
post Joe comments
dccowan Jul 5, 2023
6cad723
More coverage tests
dccowan Jul 6, 2023
73d6b64
fix broken examples
dccowan Jul 6, 2023
a8dbe89
fix broken example
dccowan Jul 6, 2023
e360437
Merge branch 'main' into face_props_mass_matrices
dccowan Jul 17, 2023
51c16c0
remove main from tests
dccowan Jul 17, 2023
0246f5e
style check
dccowan Jul 17, 2023
e729631
style checks
dccowan Jul 17, 2023
df6fce6
one more format...sigh
dccowan Jul 17, 2023
990cca1
remove NOQA E731
dccowan Jul 18, 2023
76581e3
Merge branch 'main' into face_props_mass_matrices
dccowan Jul 18, 2023
861080b
Address latest round of review
dccowan Jul 18, 2023
97bc7cb
add correct exception
dccowan Jul 18, 2023
ac968f5
initial tests
dccowan Jul 21, 2023
7ee4923
Merge branch 'main' into face_props_mass_matrices
dccowan Jul 21, 2023
098cbc1
fix edge inner product surface bugs
dccowan Jul 21, 2023
96d5507
add 3d tests
dccowan Jul 21, 2023
6174069
Create and comment out simplex deriv tests for face and edge properties
dccowan Jul 21, 2023
d1b89f7
Merge branch 'main' into face_props_mass_matrices
dccowan May 12, 2025
fefa235
tree ext
dccowan Jul 7, 2025
67d7272
Merge branch 'main' into face_props_mass_matrices
dccowan Jul 7, 2025
6e02972
Merge branch 'main' into face_props_mass_matrices
dccowan Sep 18, 2025
662d1cb
Merge branch 'main' into face_props_mass_matrices
dccowan Oct 9, 2025
b8f8c34
Merge branch 'main' into face_props_mass_matrices
dccowan Feb 18, 2026
42fa3d6
uncomment inner product tests
dccowan Feb 20, 2026
7852024
simplify a bit, and start working on more tests
jcapriot Jun 11, 2026
526c3ba
tile instead of repeat
jcapriot Jun 12, 2026
6756233
move duplicate code to a new class
jcapriot Jun 12, 2026
c7bad4a
add tests for curvilinear edge surface
jcapriot Jun 15, 2026
61cb540
add another test
jcapriot Jun 15, 2026
cf4aa69
Merge branch 'main' into face_props_mass_matrices
jcapriot Jun 15, 2026
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
137 changes: 135 additions & 2 deletions discretize/curvilinear_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from discretize.base import BaseRectangularMesh
from discretize.operators import DiffOperators, InnerProducts
from discretize.mixins import InterfaceMixins
from discretize.utils import spzeros


# Some helper functions.
Expand All @@ -33,7 +34,10 @@ def _normalize3D(x):


class CurvilinearMesh(
DiffOperators, InnerProducts, BaseRectangularMesh, InterfaceMixins
DiffOperators,
InnerProducts,
BaseRectangularMesh,
InterfaceMixins,
):
"""Curvilinear mesh class.

Expand Down Expand Up @@ -797,10 +801,13 @@ def _get_edge_surf_int_proj_mats(self, only_boundary=False, with_area=True):
edge_dirs = self.edge_tangents[node_edges]
t_for = np.concatenate((edge_dirs, face_normals[:, None, :]), axis=1)
t_inv = np.linalg.inv(t_for)
t_inv = t_inv[:, :, :-1] / 4 # n_edges_per_thing
t_inv = t_inv[:, :, :-1]

if with_area:
t_inv *= face_areas[:, None, None]
t_inv /= 4 # n_edges_per_thing
else:
t_inv /= 2 # sqrt n_edges_per_thing

T = C2F @ sp.csr_matrix(
(t_inv.reshape(-1), T_col_inds, T_ind_ptr),
Expand All @@ -809,6 +816,132 @@ def _get_edge_surf_int_proj_mats(self, only_boundary=False, with_area=True):
Ps.append((T @ P))
return Ps

def get_edge_inner_product_surface( # NOQA D102
self, model=None, invert_model=False, invert_matrix=False
):
# Documentation inherited from discretize.base.BaseMesh
dim = self.dim
if dim == 2:
# in 2D faces_x -> edges_y and edges_x -> faces_y, so need to permute the x and y faces to x and y edges.
P_e2f = sp.diags(
[1, 1],
(-self.n_edges_y, self.n_edges_x),
shape=(self.n_edges, self.n_edges),
format="csr",
)
return (
P_e2f.T
@ super().get_face_inner_product_surface(
model=model, invert_model=invert_model, invert_matrix=invert_matrix
)
@ P_e2f
)

if invert_matrix:
raise NotImplementedError(
"The inverse of the inner product matrix with a tetrahedral mesh is not supported."
)

# Edge inner product surface projection matrices
n_faces = self.n_faces
face_areas = self.face_areas

Ps = self._get_edge_surf_int_proj_mats(with_area=False)

if model is None:
Mu = sp.diags(np.tile(face_areas, dim)) # Number of edges per face
else:
if invert_model:
model = 1.0 / model

if (model.size == 1) | (model.size == n_faces):
Mu = sp.diags(
np.tile(model * face_areas, dim)
) # Number of edges per face
else:
raise ValueError(
"Unrecognized size of model vector.",
"Must be scalar or have length equal to total number of faces.",
)

A = np.sum([P.T @ Mu @ P for P in Ps])

return A

def get_edge_inner_product_surface_deriv( # NOQA D102
self,
model,
invert_model=False,
invert_matrix=False,
):
# Documentation inherited from discretize.base.BaseMesh
dim = self.dim
if dim == 2:
# in 2D faces_x -> edges_y and edges_x -> faces_y, so need to permute the x and y faces to x and y edges.
P_e2f = sp.diags(
[1, 1],
(-self.n_edges_y, self.n_edges_x),
shape=(self.n_edges, self.n_edges),
format="csr",
)
face_func = self.get_face_inner_product_surface_deriv(
model=model, invert_model=invert_model, invert_matrix=invert_matrix
)

def func(v):
return P_e2f.T @ (face_func(P_e2f @ v))

return func

if invert_model:
raise NotImplementedError(
"Inverted model derivatives are not supported here"
)
if invert_matrix:
raise NotImplementedError(
"The inverse of the inner product matrix with a tetrahedral mesh is not supported."
)
model = np.asarray(model)
# Edge inner product surface projection matrices
n_faces = self.n_faces
n_edges = self.n_edges
face_areas = self.face_areas

Ps = self._get_edge_surf_int_proj_mats(with_area=False)
area = sp.diags(np.tile(np.sqrt(face_areas), dim))
Ps = list([area @ P for P in Ps])

if model.size == 1:

def func(v):
dMdm = spzeros(n_edges, 1)
for P in Ps:
dMdm = dMdm + sp.csr_matrix(
(P.T @ (P @ v), (range(n_edges), np.zeros(n_edges))),
shape=(n_edges, 1),
)
return dMdm

elif model.size == n_faces:
col_inds = np.tile(np.arange(n_faces), dim)
ind_ptr = np.arange(n_faces * dim + 1)

def func(v):
dMdm = spzeros(n_edges, n_faces)
for P in Ps:
ys = P @ v
dMdm = dMdm + P.T @ sp.csr_matrix(
(ys, col_inds, ind_ptr), shape=(n_faces * dim, n_faces)
)
return dMdm

else:
raise ValueError(
"Unrecognized size of model vector.",
"Must be scalar or have length equal to total number of faces.",
)
return func

@property
def boundary_edge_vector_integral(self): # NOQA D102
# Documentation inherited from discretize.base.BaseMesh
Expand Down
2 changes: 1 addition & 1 deletion discretize/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
================================================
.. currentmodule:: discretize.operators

The ``operators`` package contains the classes discretize meshes with regular structure
The ``operators`` package contains the classes discretize meshes
use to construct discrete versions of the differential operators.

Operator Classes
Expand Down
108 changes: 107 additions & 1 deletion discretize/unstructured_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,47 @@ def get_edge_inner_product( # NOQA D102
)
return self.__get_inner_product("E", model, invert_model)

def get_edge_inner_product_surface( # NOQA D102
self, model=None, invert_model=False, invert_matrix=False
):
# Documentation inherited from discretize.base.BaseMesh
dim = self.dim
if dim == 2:
return self.get_face_inner_product_surface(
model=model, invert_model=invert_model, invert_matrix=invert_matrix
)

if invert_matrix:
raise NotImplementedError(
"The inverse of the inner product matrix with a tetrahedral mesh is not supported."
)

# Edge inner product surface projection matrices
n_faces = self.n_faces
face_areas = self.face_areas

Ps = self._get_edge_surf_int_proj_mats(with_area=False)

if model is None:
Mu = sp.diags(np.tile(face_areas, dim)) # Number of edges per face
else:
if invert_model:
model = 1.0 / model

if (model.size == 1) | (model.size == n_faces):
Mu = sp.diags(
np.tile(model * face_areas, dim)
) # Number of edges per face
else:
raise ValueError(
"Unrecognized size of model vector.",
"Must be scalar or have length equal to total number of faces.",
)

A = np.sum([P.T @ Mu @ P for P in Ps])

return A

def __get_inner_product_deriv_func(self, i_type, model):
Ps, _ = self.__get_inner_product_projection_matrices(i_type)
dim = self.dim
Expand Down Expand Up @@ -611,6 +652,68 @@ def get_edge_inner_product_deriv( # NOQA D102
raise NotImplementedError("Inverted matrix derivatives are not supported")
return self.__get_inner_product_deriv_func("E", model)

def get_edge_inner_product_surface_deriv( # NOQA D102
self,
model,
invert_model=False,
invert_matrix=False,
):
# Documentation inherited from discretize.base.BaseMesh
dim = self.dim
if dim == 2:
return super().get_face_inner_product_surface_deriv(
model=model, invert_model=invert_model, invert_matrix=invert_matrix
)

if invert_model:
raise NotImplementedError(
"Inverted model derivatives are not supported here"
)
if invert_matrix:
raise NotImplementedError(
"The inverse of the inner product matrix with a tetrahedral mesh is not supported."
)
model = np.asarray(model)
# Edge inner product surface projection matrices
n_faces = self.n_faces
n_edges = self.n_edges
face_areas = self.face_areas

Ps = self._get_edge_surf_int_proj_mats(with_area=False)
area = sp.diags(np.tile(np.sqrt(face_areas), dim))
Ps = list([area @ P for P in Ps])

if model.size == 1:

def func(v):
dMdm = spzeros(n_edges, 1)
for P in Ps:
dMdm = dMdm + sp.csr_matrix(
(P.T @ (P @ v), (range(n_edges), np.zeros(n_edges))),
shape=(n_edges, 1),
)
return dMdm

elif model.size == n_faces:
col_inds = np.tile(np.arange(n_faces), dim)
ind_ptr = np.arange(n_faces * dim + 1)

def func(v):
dMdm = spzeros(n_edges, n_faces)
for P in Ps:
ys = P @ v
dMdm = dMdm + P.T @ sp.csr_matrix(
(ys, col_inds, ind_ptr), shape=(n_faces * dim, n_faces)
)
return dMdm

else:
raise ValueError(
"Unrecognized size of model vector.",
"Must be scalar or have length equal to total number of faces.",
)
return func

def _get_edge_surf_int_proj_mats(self, only_boundary=False, with_area=True):
"""Return the projection operators for integrating edges on each face.

Expand Down Expand Up @@ -668,10 +771,13 @@ def _get_edge_surf_int_proj_mats(self, only_boundary=False, with_area=True):
edge_dirs = self.edge_tangents[node_edges]
t_for = np.concatenate((edge_dirs, face_normals[:, None, :]), axis=1)
t_inv = invert_blocks(t_for)
t_inv = t_inv[:, :, :-1] / 3 # n_edges_per_thing
t_inv = t_inv[:, :, :-1]

if with_area:
t_inv *= face_areas[:, None, None]
t_inv /= 3 # n_edges_per_thing
else:
t_inv /= np.sqrt(3) # sqrt n_edges_per_thing

T = C2F @ sp.csr_matrix(
(t_inv.reshape(-1), T_col_inds, T_ind_ptr),
Expand Down
Loading
Loading