From 48d95a0b3bd984b89b56ed9f2170d91c74a6656b Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 3 Feb 2023 13:24:43 -0800 Subject: [PATCH 01/41] fundamental functions for face properties and cyl mesh averaging. --- discretize/base/base_mesh.py | 1 + discretize/base/base_tensor_mesh.py | 71 +++++++++++- discretize/cylindrical_mesh.py | 148 +++++++++++++++++++++++++ discretize/operators/inner_products.py | 41 +++++++ 4 files changed, 258 insertions(+), 3 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 53c82723e..54808cbdf 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -33,6 +33,7 @@ class BaseMesh: "aveCCV2F": "average_cell_vector_to_face", "aveE2CC": "average_edge_to_cell", "aveE2CCV": "average_edge_to_cell_vector", + "aveE2FV": "average_edge_to_face_vector", "aveN2CC": "average_node_to_cell", "aveN2E": "average_node_to_edge", "aveN2F": "average_node_to_face", diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index 92dacab85..ae4dfc53a 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -809,9 +809,6 @@ def _fastInnerProduct( projection_type : str 'edges' or 'faces' - returnP : bool - returns the projection matrices - invert_model : bool inverts the material property @@ -1001,6 +998,74 @@ def innerProductDeriv(v=None): else: return None + + def _fastEdgeMassMatrixFaceProperties( + self,model, invert_model=False, invert_matrix=False + ): + """Fast version of get_edge_mass_matrix_face_properties. + + This does not handle the case of a full tensor property. + + Parameters + ---------- + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + + invert_model : bool + inverts the material property + + invert_matrix : bool + inverts the matrix + + Returns + ------- + (n_edges, n_edges) scipy.sparse.csr_matrix + M, the mass matrix + + """ + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables) + if self._meshType == "CYL": + n_elements = 1 + else: + n_elements = self.dim + + # Isotropic? or anisotropic? + if model.size == self.nF: + Av = getattr(self, "aveE2FV") + Vprop = self.face_areas * mkvc(model) + M = n_elements * sdiag(Av.T * Vprop) + + # TODO, FIX ANISOTROPIC CASE + elif model.size == self.nF * self.dim: + Av = getattr(self, "aveE2FV") + + # if cyl, then only certain components are relevant due to symmetry + # for faces, x, z matters, for edges, y (which is theta) matters + if self._meshType == "CYL": + if projection_type == "E": + model = model[:, 1] # this is the action of a projection mat + elif projection_type == "F": + model = model[:, [0, 2]] + + V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) + M = sdiag(Av.T * V * mkvc(model)) + else: + return None + + if invert_matrix: + return sdinv(M) + else: + return M + # DEPRECATED @property def hx(self): diff --git a/discretize/cylindrical_mesh.py b/discretize/cylindrical_mesh.py index b5c0ddc02..8c2699a61 100644 --- a/discretize/cylindrical_mesh.py +++ b/discretize/cylindrical_mesh.py @@ -1424,6 +1424,154 @@ def average_edge_to_cell_vector(self): # NOQA D102 ) return self._average_edge_to_cell_vector + + + + + + + + + + + @property + def average_edge_x_to_face_x(self): # NOQA D102 + if self.is_symmetric: + raise Exception("There are no x-edges on a cyl symmetric mesh") + else: + raise Exception("X-edges are not projected to x-faces") + + @property + def average_edge_x_to_face_y(self): # NOQA D102 + # Documentation inherited from discretize.operators.DiffOperators + if self.is_symmetric: + raise Exception("There are no x-edges on a cyl symmetric mesh") + # NEEDS TESTING + return ( + kron3( + av(self.shape_cells[2]), + speye(self.shape_nodes[1]), + speye(self.shape_cells[0]), + ) + * self._deflation_matrix("Ex", as_ones=True).T + ) + + @property + def average_edge_x_to_face_z(self): # NOQA D102 + # Documentation inherited from discretize.operators.DiffOperators + if self.is_symmetric: + raise Exception("There are no x-edges on a cyl symmetric mesh") + # NEEDS TESTING + return ( + kron3( + speye(self.shape_nodes[2]), + av(self.shape_cells[1]), + speye(self.shape_cells[0]), + ) + * self._deflation_matrix("Ex", as_ones=True).T + ) + + @property + def average_edge_y_to_face_x(self): # NOQA D102 + # Documentation inherited from discretize.operators.DiffOperators + if self.is_symmetric: + # No x-edges at center axis of symmetry + return sp.kron( + av(self.shape_cells[2]), + speye(self.shape_cells[0]), + format="csr" + ) + else: + # NEEDS TESTING + return ( + kron3( + av(self.shape_cells[2]), + speye(self.shape_cells[1]), + speye(self.shape_nodes[0]), + ) + * self._deflation_matrix("Ey", as_ones=True).T + ) + + @property + def average_edge_y_to_face_y(self): # NOQA D102 + raise Exception("Y-edges are not projected to y-faces") + + @property + def average_edge_y_to_face_z(self): # NOQA D102 + # Documentation inherited from discretize.operators.DiffOperators + if self.is_symmetric: + # No x-edges at center axis of symmetry + return sp.kron( + speye(self.shape_cells[2]), + av(self.shape_cells[0])[:, 1:], + format="csr" + ) + else: + # NEEDS TESTING + return ( + kron3( + speye(self.shape_nodes[2]), + speye(self.shape_cells[1]), + av(self.shape_cells[0]), + ) + * self._deflation_matrix("Ey", as_ones=True).T + ) + + @property + def average_edge_z_to_face_x(self): # NOQA D102 + # Documentation inherited from discretize.operators.DiffOperators + if self.is_symmetric: + raise Exception("There are no x-edges on a cyl symmetric mesh") + # NEEDS TESTING + return ( + kron3( + speye(self.shape_cells[2]), + av(self.shape_cells[1]), + speye(self.shape_nodes[0]), + ) + * self._deflation_matrix("Ez", as_ones=True).T + ) + + @property + def average_edge_z_to_face_y(self): # NOQA D102 + # Documentation inherited from discretize.operators.DiffOperators + if self.is_symmetric: + raise Exception("There are no x-edges on a cyl symmetric mesh") + # NEEDS TESTING + return ( + kron3( + speye(self.shape_cells[2]), + speye(self.shape_nodes[1]), + av(self.shape_cells[0]), + ) + * self._deflation_matrix("Ez", as_ones=True).T + ) + + @property + def average_edge_z_to_face_z(self): # NOQA D102 + if self.is_symmetric: + raise Exception("There are no z-edges on a cyl symmetric mesh") + else: + raise Exception("Z-edges are not projected to z-faces") + + @property + def average_edge_to_face(self): # NOQA D102 + # Documentation inherited from discretize.base.BaseMesh + if getattr(self, "_average_edge_to_face", None) is None: + + if self.is_symmetric: + self._average_edge_to_face = sp.vstack(self.aveEy2Fx, self.aveEy2Fz) + else: + + self._average_edge_to_face = (1./2.) * sp.vstack( + sp.hstack((sp.csr_matrix((self.n_faces_x, self.n_edges_x)), self.aveEy2Fx, self.aveEz2Fx), format="csr"), + sp.hstack((self.aveEx2Fy, sp.csr_matrix((self.n_faces_y, self.n_edges_y)), self.aveEz2Fy), format="csr"), + sp.hstack((self.aveEx2Fz, self.aveEy2Fz, sp.csr_matrix((self.n_faces_z, self.n_edges_z))), format="csr"), + format="csr" + ) + return self._average_edge_to_face + + @property def average_face_x_to_cell(self): # NOQA D102 # Documentation inherited from discretize.operators.DiffOperators diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 822037e84..91fbf4b7c 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -107,6 +107,47 @@ def get_edge_inner_product( # NOQA D102 do_fast=do_fast, ) + def get_edge_mass_matrix_face_properties( + self, + model, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs + ): + + """Get edge mass matrix for properties defined on cell faces. + + Parameters + ---------- + numpy.ndarray : model + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + bool : invert_model + inverts the material property + bool : invert_matrix + inverts the matrix + bool : do_fast + do a faster implementation if available. + + Returns + ------- + scipy.sparse.csr_matrix + M, the mass matrix. (nE, nE) + """ + + fast = None + if hasattr(self, "_fastEdgeMassMatrixFaceProperties") and do_fast: + fast = self._fastEdgeMassMatrixFaceProperties( + model=model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + if fast is not None: + return fast + + raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") + + def _getInnerProduct( self, projection_type, From aaa39f00b1bcfa74d0e98e8d0995ce351da19e09 Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 3 Feb 2023 14:10:24 -0800 Subject: [PATCH 02/41] Revert "fundamental functions for face properties and cyl mesh averaging." This reverts commit 48d95a0b3bd984b89b56ed9f2170d91c74a6656b. --- discretize/base/base_mesh.py | 1 - discretize/base/base_tensor_mesh.py | 71 +----------- discretize/cylindrical_mesh.py | 148 ------------------------- discretize/operators/inner_products.py | 41 ------- 4 files changed, 3 insertions(+), 258 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 54808cbdf..53c82723e 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -33,7 +33,6 @@ class BaseMesh: "aveCCV2F": "average_cell_vector_to_face", "aveE2CC": "average_edge_to_cell", "aveE2CCV": "average_edge_to_cell_vector", - "aveE2FV": "average_edge_to_face_vector", "aveN2CC": "average_node_to_cell", "aveN2E": "average_node_to_edge", "aveN2F": "average_node_to_face", diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index ae4dfc53a..92dacab85 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -809,6 +809,9 @@ def _fastInnerProduct( projection_type : str 'edges' or 'faces' + returnP : bool + returns the projection matrices + invert_model : bool inverts the material property @@ -998,74 +1001,6 @@ def innerProductDeriv(v=None): else: return None - - def _fastEdgeMassMatrixFaceProperties( - self,model, invert_model=False, invert_matrix=False - ): - """Fast version of get_edge_mass_matrix_face_properties. - - This does not handle the case of a full tensor property. - - Parameters - ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) - - invert_model : bool - inverts the material property - - invert_matrix : bool - inverts the matrix - - Returns - ------- - (n_edges, n_edges) scipy.sparse.csr_matrix - M, the mass matrix - - """ - - if invert_model: - model = 1.0 / model - - if is_scalar(model): - model = model * np.ones(self.nF) - - # number of elements we are averaging (equals dim for regular - # meshes, but for cyl, where we use symmetry, it is 1 for edge - # variables) - if self._meshType == "CYL": - n_elements = 1 - else: - n_elements = self.dim - - # Isotropic? or anisotropic? - if model.size == self.nF: - Av = getattr(self, "aveE2FV") - Vprop = self.face_areas * mkvc(model) - M = n_elements * sdiag(Av.T * Vprop) - - # TODO, FIX ANISOTROPIC CASE - elif model.size == self.nF * self.dim: - Av = getattr(self, "aveE2FV") - - # if cyl, then only certain components are relevant due to symmetry - # for faces, x, z matters, for edges, y (which is theta) matters - if self._meshType == "CYL": - if projection_type == "E": - model = model[:, 1] # this is the action of a projection mat - elif projection_type == "F": - model = model[:, [0, 2]] - - V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) - M = sdiag(Av.T * V * mkvc(model)) - else: - return None - - if invert_matrix: - return sdinv(M) - else: - return M - # DEPRECATED @property def hx(self): diff --git a/discretize/cylindrical_mesh.py b/discretize/cylindrical_mesh.py index 8c2699a61..b5c0ddc02 100644 --- a/discretize/cylindrical_mesh.py +++ b/discretize/cylindrical_mesh.py @@ -1424,154 +1424,6 @@ def average_edge_to_cell_vector(self): # NOQA D102 ) return self._average_edge_to_cell_vector - - - - - - - - - - - @property - def average_edge_x_to_face_x(self): # NOQA D102 - if self.is_symmetric: - raise Exception("There are no x-edges on a cyl symmetric mesh") - else: - raise Exception("X-edges are not projected to x-faces") - - @property - def average_edge_x_to_face_y(self): # NOQA D102 - # Documentation inherited from discretize.operators.DiffOperators - if self.is_symmetric: - raise Exception("There are no x-edges on a cyl symmetric mesh") - # NEEDS TESTING - return ( - kron3( - av(self.shape_cells[2]), - speye(self.shape_nodes[1]), - speye(self.shape_cells[0]), - ) - * self._deflation_matrix("Ex", as_ones=True).T - ) - - @property - def average_edge_x_to_face_z(self): # NOQA D102 - # Documentation inherited from discretize.operators.DiffOperators - if self.is_symmetric: - raise Exception("There are no x-edges on a cyl symmetric mesh") - # NEEDS TESTING - return ( - kron3( - speye(self.shape_nodes[2]), - av(self.shape_cells[1]), - speye(self.shape_cells[0]), - ) - * self._deflation_matrix("Ex", as_ones=True).T - ) - - @property - def average_edge_y_to_face_x(self): # NOQA D102 - # Documentation inherited from discretize.operators.DiffOperators - if self.is_symmetric: - # No x-edges at center axis of symmetry - return sp.kron( - av(self.shape_cells[2]), - speye(self.shape_cells[0]), - format="csr" - ) - else: - # NEEDS TESTING - return ( - kron3( - av(self.shape_cells[2]), - speye(self.shape_cells[1]), - speye(self.shape_nodes[0]), - ) - * self._deflation_matrix("Ey", as_ones=True).T - ) - - @property - def average_edge_y_to_face_y(self): # NOQA D102 - raise Exception("Y-edges are not projected to y-faces") - - @property - def average_edge_y_to_face_z(self): # NOQA D102 - # Documentation inherited from discretize.operators.DiffOperators - if self.is_symmetric: - # No x-edges at center axis of symmetry - return sp.kron( - speye(self.shape_cells[2]), - av(self.shape_cells[0])[:, 1:], - format="csr" - ) - else: - # NEEDS TESTING - return ( - kron3( - speye(self.shape_nodes[2]), - speye(self.shape_cells[1]), - av(self.shape_cells[0]), - ) - * self._deflation_matrix("Ey", as_ones=True).T - ) - - @property - def average_edge_z_to_face_x(self): # NOQA D102 - # Documentation inherited from discretize.operators.DiffOperators - if self.is_symmetric: - raise Exception("There are no x-edges on a cyl symmetric mesh") - # NEEDS TESTING - return ( - kron3( - speye(self.shape_cells[2]), - av(self.shape_cells[1]), - speye(self.shape_nodes[0]), - ) - * self._deflation_matrix("Ez", as_ones=True).T - ) - - @property - def average_edge_z_to_face_y(self): # NOQA D102 - # Documentation inherited from discretize.operators.DiffOperators - if self.is_symmetric: - raise Exception("There are no x-edges on a cyl symmetric mesh") - # NEEDS TESTING - return ( - kron3( - speye(self.shape_cells[2]), - speye(self.shape_nodes[1]), - av(self.shape_cells[0]), - ) - * self._deflation_matrix("Ez", as_ones=True).T - ) - - @property - def average_edge_z_to_face_z(self): # NOQA D102 - if self.is_symmetric: - raise Exception("There are no z-edges on a cyl symmetric mesh") - else: - raise Exception("Z-edges are not projected to z-faces") - - @property - def average_edge_to_face(self): # NOQA D102 - # Documentation inherited from discretize.base.BaseMesh - if getattr(self, "_average_edge_to_face", None) is None: - - if self.is_symmetric: - self._average_edge_to_face = sp.vstack(self.aveEy2Fx, self.aveEy2Fz) - else: - - self._average_edge_to_face = (1./2.) * sp.vstack( - sp.hstack((sp.csr_matrix((self.n_faces_x, self.n_edges_x)), self.aveEy2Fx, self.aveEz2Fx), format="csr"), - sp.hstack((self.aveEx2Fy, sp.csr_matrix((self.n_faces_y, self.n_edges_y)), self.aveEz2Fy), format="csr"), - sp.hstack((self.aveEx2Fz, self.aveEy2Fz, sp.csr_matrix((self.n_faces_z, self.n_edges_z))), format="csr"), - format="csr" - ) - return self._average_edge_to_face - - @property def average_face_x_to_cell(self): # NOQA D102 # Documentation inherited from discretize.operators.DiffOperators diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 91fbf4b7c..822037e84 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -107,47 +107,6 @@ def get_edge_inner_product( # NOQA D102 do_fast=do_fast, ) - def get_edge_mass_matrix_face_properties( - self, - model, - invert_model=False, - invert_matrix=False, - do_fast=True, - **kwargs - ): - - """Get edge mass matrix for properties defined on cell faces. - - Parameters - ---------- - numpy.ndarray : model - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) - bool : invert_model - inverts the material property - bool : invert_matrix - inverts the matrix - bool : do_fast - do a faster implementation if available. - - Returns - ------- - scipy.sparse.csr_matrix - M, the mass matrix. (nE, nE) - """ - - fast = None - if hasattr(self, "_fastEdgeMassMatrixFaceProperties") and do_fast: - fast = self._fastEdgeMassMatrixFaceProperties( - model=model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - if fast is not None: - return fast - - raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") - - def _getInnerProduct( self, projection_type, From 29f0b778263cefedef682aa83e16837356b6aaaf Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 3 Feb 2023 14:11:28 -0800 Subject: [PATCH 03/41] Starting Mass matrix funs --- discretize/base/base_tensor_mesh.py | 139 ++++++++++++++++++++++++- discretize/operators/inner_products.py | 41 ++++++++ 2 files changed, 177 insertions(+), 3 deletions(-) diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index 92dacab85..49db2cef0 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -809,9 +809,6 @@ def _fastInnerProduct( projection_type : str 'edges' or 'faces' - returnP : bool - returns the projection matrices - invert_model : bool inverts the material property @@ -1001,6 +998,142 @@ def innerProductDeriv(v=None): else: return None + + def _fastEdgeMassMatrixFaceProperties( + self,model, invert_model=False, invert_matrix=False + ): + """Fast version of get_edge_mass_matrix_face_properties. + + This does not handle the case of a full tensor property. + + Parameters + ---------- + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + + invert_model : bool + inverts the material property + + invert_matrix : bool + inverts the matrix + + Returns + ------- + (n_edges, n_edges) scipy.sparse.csr_matrix + M, the mass matrix + + """ + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables) + if self._meshType == "CYL": + n_elements = 1 + else: + n_elements = self.dim + + # Isotropic? or anisotropic? + if model.size == self.nF: + Av = getattr(self, "aveE2FV") + Vprop = self.face_areas * mkvc(model) + M = n_elements * sdiag(Av.T * Vprop) + + # TODO, FIX ANISOTROPIC CASE + elif model.size == self.nF * self.dim: + Av = getattr(self, "aveE2FV") + + # if cyl, then only certain components are relevant due to symmetry + # for faces, x, z matters, for edges, y (which is theta) matters + if self._meshType == "CYL": + if projection_type == "E": + model = model[:, 1] # this is the action of a projection mat + elif projection_type == "F": + model = model[:, [0, 2]] + + V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) + M = sdiag(Av.T * V * mkvc(model)) + else: + return None + + if invert_matrix: + return sdinv(M) + else: + return M + + + def _fastEdgeMassMatrixFaceProperties( + self,model, invert_model=False, invert_matrix=False + ): + """Fast version of get_edge_mass_matrix_face_properties. + + This does not handle the case of a full tensor property. + + Parameters + ---------- + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + + invert_model : bool + inverts the material property + + invert_matrix : bool + inverts the matrix + + Returns + ------- + (n_edges, n_edges) scipy.sparse.csr_matrix + M, the mass matrix + + """ + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables) + if self._meshType == "CYL": + n_elements = 1 + else: + n_elements = self.dim + + # Isotropic? or anisotropic? + if model.size == self.nF: + Av = getattr(self, "aveE2FV") + Vprop = self.face_areas * mkvc(model) + M = n_elements * sdiag(Av.T * Vprop) + + # TODO, FIX ANISOTROPIC CASE + elif model.size == self.nF * self.dim: + Av = getattr(self, "aveE2FV") + + # if cyl, then only certain components are relevant due to symmetry + # for faces, x, z matters, for edges, y (which is theta) matters + if self._meshType == "CYL": + if projection_type == "E": + model = model[:, 1] # this is the action of a projection mat + elif projection_type == "F": + model = model[:, [0, 2]] + + V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) + M = sdiag(Av.T * V * mkvc(model)) + else: + return None + + if invert_matrix: + return sdinv(M) + else: + return M + # DEPRECATED @property def hx(self): diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 822037e84..91fbf4b7c 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -107,6 +107,47 @@ def get_edge_inner_product( # NOQA D102 do_fast=do_fast, ) + def get_edge_mass_matrix_face_properties( + self, + model, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs + ): + + """Get edge mass matrix for properties defined on cell faces. + + Parameters + ---------- + numpy.ndarray : model + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + bool : invert_model + inverts the material property + bool : invert_matrix + inverts the matrix + bool : do_fast + do a faster implementation if available. + + Returns + ------- + scipy.sparse.csr_matrix + M, the mass matrix. (nE, nE) + """ + + fast = None + if hasattr(self, "_fastEdgeMassMatrixFaceProperties") and do_fast: + fast = self._fastEdgeMassMatrixFaceProperties( + model=model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + if fast is not None: + return fast + + raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") + + def _getInnerProduct( self, projection_type, From a716702fccff0888697d1fb9b15c71c9b6edaf16 Mon Sep 17 00:00:00 2001 From: dccowan Date: Thu, 16 Mar 2023 15:29:35 +1000 Subject: [PATCH 04/41] basic face properties implementation --- discretize/base/base_mesh.py | 8 + discretize/base/base_tensor_mesh.py | 212 +++++++----------- discretize/cylindrical_mesh.py | 37 +++ .../operators/differential_operators.py | 43 ++++ 4 files changed, 167 insertions(+), 133 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 53c82723e..f440662a0 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -3069,6 +3069,14 @@ def average_edge_to_face_vector(self): f"average_edge_to_face_vector not implemented for {type(self)}" ) + @property + def average_edge_to_face_by_component(self): + r"""Averaging operator from edges to faces (vector quantities).""" + + raise NotImplementedError( + f"average_edge_to_face_by_component not implemented for {type(self)}" + ) + @property def average_node_to_cell(self): r"""Averaging operator from nodes to cell centers (scalar quantities). diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index 49db2cef0..3d204edc0 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -999,140 +999,86 @@ def innerProductDeriv(v=None): return None - def _fastEdgeMassMatrixFaceProperties( - self,model, invert_model=False, invert_matrix=False - ): - """Fast version of get_edge_mass_matrix_face_properties. - - This does not handle the case of a full tensor property. - - Parameters - ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) - - invert_model : bool - inverts the material property - - invert_matrix : bool - inverts the matrix - - Returns - ------- - (n_edges, n_edges) scipy.sparse.csr_matrix - M, the mass matrix - - """ - - if invert_model: - model = 1.0 / model - - if is_scalar(model): - model = model * np.ones(self.nF) - - # number of elements we are averaging (equals dim for regular - # meshes, but for cyl, where we use symmetry, it is 1 for edge - # variables) - if self._meshType == "CYL": - n_elements = 1 - else: - n_elements = self.dim - - # Isotropic? or anisotropic? - if model.size == self.nF: - Av = getattr(self, "aveE2FV") - Vprop = self.face_areas * mkvc(model) - M = n_elements * sdiag(Av.T * Vprop) - - # TODO, FIX ANISOTROPIC CASE - elif model.size == self.nF * self.dim: - Av = getattr(self, "aveE2FV") - - # if cyl, then only certain components are relevant due to symmetry - # for faces, x, z matters, for edges, y (which is theta) matters - if self._meshType == "CYL": - if projection_type == "E": - model = model[:, 1] # this is the action of a projection mat - elif projection_type == "F": - model = model[:, [0, 2]] - - V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) - M = sdiag(Av.T * V * mkvc(model)) - else: - return None - - if invert_matrix: - return sdinv(M) - else: - return M - + # def _fastEdgeMassMatrixFaceProperties( + # self, model, invert_model=False, invert_matrix=False + # ): + # """Fast version of get_edge_mass_matrix_face_properties. + + # This does not handle the case of a full tensor property. + + # Parameters + # ---------- + # model : numpy.ndarray + # material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + + # invert_model : bool + # inverts the material property + + # invert_matrix : bool + # inverts the matrix + + # Returns + # ------- + # (n_edges, n_edges) scipy.sparse.csr_matrix + # M, the mass matrix + + # """ + + # if self.dim == 1: + # raise NotImplementedError("Mass matrix for face properties not implemented for 1D meshes.") + + # if invert_model: + # model = 1.0 / model + + # # Define the model on all faces + # if is_scalar(model): + # model = model * np.ones(self.nF) + # elif len(model) == self.dim: + # model = np.hstack([model[ii]*np.ones(self.vnF[ii]) for ii in range(0, self.dim)]) + + # # number of elements we are averaging (equals dim for regular + # # meshes, but for cyl, where we use symmetry, it is 1 for edge + # # variables) + # if self._meshType == "CYL": + # n_elements = 1 + # else: + # n_elements = self.dim + + # # Isotropic + # if model.size == self.nF: + + # # Extraction matrix + # E = + + # # Averaging matrix + # Av = getattr(self, "_average_edge_to_face_by_component") + + # Vprop = self.face_areas * mkvc(model) + + # M = n_elements * sdiag(Av.T * Vprop) + + # # TODO, FIX ANISOTROPIC CASE + # # elif model.size == self.nF * self.dim: + # # Av = getattr(self, "aveE2FV") + + # # # if cyl, then only certain components are relevant due to symmetry + # # # for faces, x, z matters, for edges, y (which is theta) matters + # # if self._meshType == "CYL": + # # if projection_type == "E": + # # model = model[:, 1] # this is the action of a projection mat + # # elif projection_type == "F": + # # model = model[:, [0, 2]] + + # # V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) + # # M = sdiag(Av.T * V * mkvc(model)) + # else: + # return None + + # if invert_matrix: + # return sdinv(M) + # else: + # return M - def _fastEdgeMassMatrixFaceProperties( - self,model, invert_model=False, invert_matrix=False - ): - """Fast version of get_edge_mass_matrix_face_properties. - - This does not handle the case of a full tensor property. - - Parameters - ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) - - invert_model : bool - inverts the material property - - invert_matrix : bool - inverts the matrix - - Returns - ------- - (n_edges, n_edges) scipy.sparse.csr_matrix - M, the mass matrix - - """ - - if invert_model: - model = 1.0 / model - - if is_scalar(model): - model = model * np.ones(self.nF) - - # number of elements we are averaging (equals dim for regular - # meshes, but for cyl, where we use symmetry, it is 1 for edge - # variables) - if self._meshType == "CYL": - n_elements = 1 - else: - n_elements = self.dim - - # Isotropic? or anisotropic? - if model.size == self.nF: - Av = getattr(self, "aveE2FV") - Vprop = self.face_areas * mkvc(model) - M = n_elements * sdiag(Av.T * Vprop) - - # TODO, FIX ANISOTROPIC CASE - elif model.size == self.nF * self.dim: - Av = getattr(self, "aveE2FV") - - # if cyl, then only certain components are relevant due to symmetry - # for faces, x, z matters, for edges, y (which is theta) matters - if self._meshType == "CYL": - if projection_type == "E": - model = model[:, 1] # this is the action of a projection mat - elif projection_type == "F": - model = model[:, [0, 2]] - - V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) - M = sdiag(Av.T * V * mkvc(model)) - else: - return None - - if invert_matrix: - return sdinv(M) - else: - return M # DEPRECATED @property diff --git a/discretize/cylindrical_mesh.py b/discretize/cylindrical_mesh.py index b5c0ddc02..80243e382 100644 --- a/discretize/cylindrical_mesh.py +++ b/discretize/cylindrical_mesh.py @@ -1424,6 +1424,43 @@ def average_edge_to_cell_vector(self): # NOQA D102 ) return self._average_edge_to_cell_vector + @property + def _average_edge_to_face_by_component(self): # NOQA D102 + # Documentation inherited from discretize.base.BaseMesh + + if self.is_symmetric: + + ey_to_fx = self.average_node_to_face_x + ey_to_fz = self.average_node_to_face_y + + return sp.vstack([ey_to_fx, ey_to_fz], format="csr") + + else: + + n1, n2, n3 = self.shape_cells + + ex_to_fy = kron3(av(n3), speye(n2 + 1), speye(n1)) * self._deflation_matrix("Ex", as_ones=True).T + ex_to_fz = kron3(speye(n3 + 1), av(n2), speye(n1)) * self._deflation_matrix("Ex", as_ones=True).T + + ey_to_fx = kron3(av(n3), speye(n2), speye(n1 + 1)) * self._deflation_matrix("Ey", as_ones=True).T + ey_to_fz = kron3(speye(n3 + 1), speye(n2), av(n1)) * self._deflation_matrix("Ey", as_ones=True).T + + ez_to_fx = kron3(speye(n3), av(n2), speye(n1 + 1)) * self._deflation_matrix("Ez", as_ones=True).T + ez_to_fy = kron3(speye(n3), speye(n2 + 1), av(n1)) * self._deflation_matrix("Ez", as_ones=True).T + + e_to_f = sp.bmat( + [ + [None, ey_to_fx, None], + [None, None, ez_to_fx], + [ex_to_fy, None, None], + [None, None, ez_to_fy], + [ex_to_fz, None, None], + [None, ey_to_fz, None], + ], + format="csr", + ) + return e_to_f + @property def average_face_x_to_cell(self): # NOQA D102 # Documentation inherited from discretize.operators.DiffOperators diff --git a/discretize/operators/differential_operators.py b/discretize/operators/differential_operators.py index e62fda0ba..88c4ca9a5 100644 --- a/discretize/operators/differential_operators.py +++ b/discretize/operators/differential_operators.py @@ -3296,6 +3296,49 @@ def average_edge_to_face_vector(self): # NOQA D102 ) return e_to_f + @property + def _average_edge_to_face_by_component(self): # NOQA D102 + # Documentation inherited from discretize.base.BaseMesh + if self.dim == 1: + return self.average_cell_to_face + + elif self.dim == 2: + n1, n2 = self.shape_cells + + e_to_f = sp.bmat( + [ + [None, sp.identity(n2, format="csr")], + [sp.identity(n1, format="csr"), None], + ], + format="csr", + ) + return e_to_f + + else: + n1, n2, n3 = self.shape_cells + + ex_to_fy = kron3(av(n3), speye(n2 + 1), speye(n1)) + ex_to_fz = kron3(speye(n3 + 1), av(n2), speye(n1)) + + ey_to_fx = kron3(av(n3), speye(n2), speye(n1 + 1)) + ey_to_fz = kron3(speye(n3 + 1), speye(n2), av(n1)) + + ez_to_fx = kron3(speye(n3), av(n2), speye(n1 + 1)) + ez_to_fy = kron3(speye(n3), speye(n2 + 1), av(n1)) + + e_to_f = sp.bmat( + [ + [None, ey_to_fx, None], + [None, None, ez_to_fx], + [ex_to_fy, None, None], + [None, None, ez_to_fy], + [ex_to_fz, None, None], + [None, ey_to_fz, None], + ], + format="csr", + ) + return e_to_f + @property def average_node_to_cell(self): # NOQA D102 # Documentation inherited from discretize.base.BaseMesh From 70648731aef12ef88fe9bb03d8a18ed538780df4 Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 12 Jun 2023 13:41:26 -0700 Subject: [PATCH 05/41] re-add property --- discretize/cylindrical_mesh.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/discretize/cylindrical_mesh.py b/discretize/cylindrical_mesh.py index 73bdf3e4f..b641f56fb 100644 --- a/discretize/cylindrical_mesh.py +++ b/discretize/cylindrical_mesh.py @@ -1520,6 +1520,42 @@ def average_edge_to_cell_vector(self): # NOQA D102 return self._average_edge_to_cell_vector @property + def _average_edge_to_face_by_component(self): # NOQA D102 + # Documentation inherited from discretize.base.BaseMesh + + if self.is_symmetric: + + ey_to_fx = self.average_node_to_face_x + ey_to_fz = self.average_node_to_face_y + + return sp.vstack([ey_to_fx, ey_to_fz], format="csr") + + else: + + n1, n2, n3 = self.shape_cells + + ex_to_fy = kron3(av(n3), speye(n2 + 1), speye(n1)) * self._deflation_matrix("Ex", as_ones=True).T + ex_to_fz = kron3(speye(n3 + 1), av(n2), speye(n1)) * self._deflation_matrix("Ex", as_ones=True).T + + ey_to_fx = kron3(av(n3), speye(n2), speye(n1 + 1)) * self._deflation_matrix("Ey", as_ones=True).T + ey_to_fz = kron3(speye(n3 + 1), speye(n2), av(n1)) * self._deflation_matrix("Ey", as_ones=True).T + + ez_to_fx = kron3(speye(n3), av(n2), speye(n1 + 1)) * self._deflation_matrix("Ez", as_ones=True).T + ez_to_fy = kron3(speye(n3), speye(n2 + 1), av(n1)) * self._deflation_matrix("Ez", as_ones=True).T + + e_to_f = sp.bmat( + [ + [None, ey_to_fx, None], + [None, None, ez_to_fx], + [ex_to_fy, None, None], + [None, None, ez_to_fy], + [ex_to_fz, None, None], + [None, ey_to_fz, None], + ], + format="csr", + ) + return e_to_f + @property def average_edge_to_face(self): # NOQA D102 # Documentation inherited from discretize.base.BaseMesh if self.is_symmetric: From 6f1d1076b35a2e95737228aa22ccf117a23c521c Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 16 Jun 2023 11:19:47 -0700 Subject: [PATCH 06/41] remove average edge to face by component --- discretize/base/base_mesh.py | 8 ---- discretize/cylindrical_mesh.py | 36 ---------------- .../operators/differential_operators.py | 43 ------------------- 3 files changed, 87 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index ce868f2f1..9d29d738b 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -3000,14 +3000,6 @@ def average_edge_to_face(self): f"average_edge_to_face not implemented for {type(self)}" ) - @property - def average_edge_to_face_by_component(self): - r"""Averaging operator from edges to faces (vector quantities).""" - - raise NotImplementedError( - f"average_edge_to_face_by_component not implemented for {type(self)}" - ) - @property def average_node_to_cell(self): r"""Averaging operator from nodes to cell centers (scalar quantities). diff --git a/discretize/cylindrical_mesh.py b/discretize/cylindrical_mesh.py index b641f56fb..73bdf3e4f 100644 --- a/discretize/cylindrical_mesh.py +++ b/discretize/cylindrical_mesh.py @@ -1520,42 +1520,6 @@ def average_edge_to_cell_vector(self): # NOQA D102 return self._average_edge_to_cell_vector @property - def _average_edge_to_face_by_component(self): # NOQA D102 - # Documentation inherited from discretize.base.BaseMesh - - if self.is_symmetric: - - ey_to_fx = self.average_node_to_face_x - ey_to_fz = self.average_node_to_face_y - - return sp.vstack([ey_to_fx, ey_to_fz], format="csr") - - else: - - n1, n2, n3 = self.shape_cells - - ex_to_fy = kron3(av(n3), speye(n2 + 1), speye(n1)) * self._deflation_matrix("Ex", as_ones=True).T - ex_to_fz = kron3(speye(n3 + 1), av(n2), speye(n1)) * self._deflation_matrix("Ex", as_ones=True).T - - ey_to_fx = kron3(av(n3), speye(n2), speye(n1 + 1)) * self._deflation_matrix("Ey", as_ones=True).T - ey_to_fz = kron3(speye(n3 + 1), speye(n2), av(n1)) * self._deflation_matrix("Ey", as_ones=True).T - - ez_to_fx = kron3(speye(n3), av(n2), speye(n1 + 1)) * self._deflation_matrix("Ez", as_ones=True).T - ez_to_fy = kron3(speye(n3), speye(n2 + 1), av(n1)) * self._deflation_matrix("Ez", as_ones=True).T - - e_to_f = sp.bmat( - [ - [None, ey_to_fx, None], - [None, None, ez_to_fx], - [ex_to_fy, None, None], - [None, None, ez_to_fy], - [ex_to_fz, None, None], - [None, ey_to_fz, None], - ], - format="csr", - ) - return e_to_f - @property def average_edge_to_face(self): # NOQA D102 # Documentation inherited from discretize.base.BaseMesh if self.is_symmetric: diff --git a/discretize/operators/differential_operators.py b/discretize/operators/differential_operators.py index 3e6264add..51f5c11dc 100644 --- a/discretize/operators/differential_operators.py +++ b/discretize/operators/differential_operators.py @@ -3226,49 +3226,6 @@ def average_edge_to_face(self): # NOQA D102 ) return e_to_f - @property - def _average_edge_to_face_by_component(self): # NOQA D102 - # Documentation inherited from discretize.base.BaseMesh - if self.dim == 1: - return self.average_cell_to_face - - elif self.dim == 2: - n1, n2 = self.shape_cells - - e_to_f = sp.bmat( - [ - [None, sp.identity(n2, format="csr")], - [sp.identity(n1, format="csr"), None], - ], - format="csr", - ) - return e_to_f - - else: - n1, n2, n3 = self.shape_cells - - ex_to_fy = kron3(av(n3), speye(n2 + 1), speye(n1)) - ex_to_fz = kron3(speye(n3 + 1), av(n2), speye(n1)) - - ey_to_fx = kron3(av(n3), speye(n2), speye(n1 + 1)) - ey_to_fz = kron3(speye(n3 + 1), speye(n2), av(n1)) - - ez_to_fx = kron3(speye(n3), av(n2), speye(n1 + 1)) - ez_to_fy = kron3(speye(n3), speye(n2 + 1), av(n1)) - - e_to_f = sp.bmat( - [ - [None, ey_to_fx, None], - [None, None, ez_to_fx], - [ex_to_fy, None, None], - [None, None, ez_to_fy], - [ex_to_fz, None, None], - [None, ey_to_fz, None], - ], - format="csr", - ) - return e_to_f - @property def average_node_to_cell(self): # NOQA D102 # Documentation inherited from discretize.base.BaseMesh From 021ba3df9328f2e882d6b021f399a158262dc2dc Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 16 Jun 2023 17:41:54 -0700 Subject: [PATCH 07/41] Test tensor for inner products with face properties --- discretize/base/base_mesh.py | 40 +++++ discretize/base/base_tensor_mesh.py | 212 ++++++++++++++++--------- discretize/operators/inner_products.py | 88 +++++++++- tests/base/test_tensor_innerproduct.py | 155 ++++++++++++++++++ 4 files changed, 413 insertions(+), 82 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 9d29d738b..60097b2e5 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1880,6 +1880,46 @@ def get_edge_inner_product( f"get_edge_inner_product not implemented for {type(self)}" ) + + def get_edge_inner_product_face_properties( + self, + model=None, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs, + ): + + raise NotImplementedError( + f"get_edge_inner_product_face_properties not implemented for {type(self)}" + ) + + def get_face_inner_product_face_properties( + self, + model=None, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs, + ): + + raise NotImplementedError( + f"get_edge_inner_product_face_properties not implemented for {type(self)}" + ) + + def get_edge_inner_product_edge_properties( + self, + model=None, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs, + ): + + raise NotImplementedError( + f"get_edge_inner_product_face_properties not implemented for {type(self)}" + ) + def get_face_inner_product_deriv( self, model, do_fast=True, invert_model=False, invert_matrix=False, **kwargs ): diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index d9fa2d8ff..e00e22401 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -994,85 +994,139 @@ def innerProductDeriv(v=None): return None - # def _fastEdgeMassMatrixFaceProperties( - # self, model, invert_model=False, invert_matrix=False - # ): - # """Fast version of get_edge_mass_matrix_face_properties. - - # This does not handle the case of a full tensor property. - - # Parameters - # ---------- - # model : numpy.ndarray - # material property (tensor properties are possible) at each cell center (nF, (1 or 2)) - - # invert_model : bool - # inverts the material property - - # invert_matrix : bool - # inverts the matrix - - # Returns - # ------- - # (n_edges, n_edges) scipy.sparse.csr_matrix - # M, the mass matrix - - # """ - - # if self.dim == 1: - # raise NotImplementedError("Mass matrix for face properties not implemented for 1D meshes.") - - # if invert_model: - # model = 1.0 / model - - # # Define the model on all faces - # if is_scalar(model): - # model = model * np.ones(self.nF) - # elif len(model) == self.dim: - # model = np.hstack([model[ii]*np.ones(self.vnF[ii]) for ii in range(0, self.dim)]) - - # # number of elements we are averaging (equals dim for regular - # # meshes, but for cyl, where we use symmetry, it is 1 for edge - # # variables) - # if self._meshType == "CYL": - # n_elements = 1 - # else: - # n_elements = self.dim - - # # Isotropic - # if model.size == self.nF: - - # # Extraction matrix - # E = - - # # Averaging matrix - # Av = getattr(self, "_average_edge_to_face_by_component") - - # Vprop = self.face_areas * mkvc(model) - - # M = n_elements * sdiag(Av.T * Vprop) - - # # TODO, FIX ANISOTROPIC CASE - # # elif model.size == self.nF * self.dim: - # # Av = getattr(self, "aveE2FV") - - # # # if cyl, then only certain components are relevant due to symmetry - # # # for faces, x, z matters, for edges, y (which is theta) matters - # # if self._meshType == "CYL": - # # if projection_type == "E": - # # model = model[:, 1] # this is the action of a projection mat - # # elif projection_type == "F": - # # model = model[:, [0, 2]] - - # # V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes)) - # # M = sdiag(Av.T * V * mkvc(model)) - # else: - # return None - - # if invert_matrix: - # return sdinv(M) - # else: - # return M + def _fastFacePropertiesInnerProduct( + self, projection_type, model=None, invert_model=False, invert_matrix=False + ): + """Fast version of get_face_inner_product_deriv. + + This does not handle the case of a full tensor property. + + Parameters + ---------- + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) + + projection_type : str + 'edges' or 'faces' + + invert_model : bool + inverts the material property + + invert_matrix : bool + inverts the matrix + + Returns + ------- + (n_faces, n_faces) or (n_edges, n_edges) scipy.sparse.csr_matrix + M, the inner product matrix + + """ + projection_type = projection_type[0].upper() + if projection_type not in ["F", "E"]: + raise ValueError("projection_type must be 'F' for faces or 'E' for edges") + + if model is None: + model = np.ones(self.nF) + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + elif len(model) == self.dim: + model = np.r_[ + [model[ii]*self.vnF[ii] for ii in range(0, self.dim)] + ] + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables and 2 for face variables) + if self._meshType == "CYL": + shape = getattr(self, "vn" + projection_type) + n_elements = sum([1 if x != 0 else 0 for x in shape]) + else: + n_elements = self.dim - 1 + + # Isotropic case only + if model.size == self.nF: + Aprop = self.face_areas * mkvc(model) + if projection_type == 'E': + Av = getattr(self, "average_edge_to_face") + M = n_elements * sdiag(Av.T * Aprop) + else: + M = sdiag(Aprop) + + else: + raise NotImplementedError( + "FacePropertiesInnerProduct not implemented for anisotropy." + ) + + if invert_matrix: + return sdinv(M) + else: + return M + + def _fastEdgePropertiesInnerProduct( + self, model=None, invert_model=False, invert_matrix=False + ): + """Fast version of get_face_inner_product_deriv. + + This does not handle the case of a full tensor property. + + Parameters + ---------- + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) + + invert_model : bool + inverts the material property + + invert_matrix : bool + inverts the matrix + + Returns + ------- + (n_edges, n_edges) scipy.sparse.csr_matrix + M, the inner product matrix + + """ + if model is None: + model = np.ones(self.nE) + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nE) + elif len(model) == self.dim: + model = np.r_[ + [model[ii]*self.vnE[ii] for ii in range(0, self.dim)] + ] + + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables and 2 for face variables) + # if self._meshType == "CYL": + # shape = getattr(self, "vn" + projection_type) + # n_elements = sum([1 if x != 0 else 0 for x in shape]) + # else: + # n_elements = self.dim - 1 + + # Isotropic case only + if model.size == self.nE: + Lprop = self.edge_lengths * mkvc(model) + M = sdiag(Lprop) + + else: + raise NotImplementedError( + "FacePropertiesInnerProduct not implemented for anisotropy." + ) + + if invert_matrix: + return sdinv(M) + else: + return M # DEPRECATED diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index bfb58a5b1..cbbac686e 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -95,7 +95,7 @@ def get_edge_inner_product( # NOQA D102 do_fast=do_fast, ) - def get_edge_mass_matrix_face_properties( + def get_edge_inner_product_face_properties( self, model, invert_model=False, @@ -124,8 +124,90 @@ def get_edge_mass_matrix_face_properties( """ fast = None - if hasattr(self, "_fastEdgeMassMatrixFaceProperties") and do_fast: - fast = self._fastEdgeMassMatrixFaceProperties( + if hasattr(self, "_fastFacePropertiesInnerProduct") and do_fast: + fast = self._fastFacePropertiesInnerProduct( + projection_type='E', + model=model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + if fast is not None: + return fast + + raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") + + def get_face_inner_product_face_properties( + self, + model, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs + ): + + """Get face mass matrix for properties defined on cell faces. + + Parameters + ---------- + numpy.ndarray : model + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + bool : invert_model + inverts the material property + bool : invert_matrix + inverts the matrix + bool : do_fast + do a faster implementation if available. + + Returns + ------- + scipy.sparse.csr_matrix + M, the mass matrix. (nE, nE) + """ + + fast = None + if hasattr(self, "_fastFacePropertiesInnerProduct") and do_fast: + fast = self._fastFacePropertiesInnerProduct( + projection_type='F', + model=model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + if fast is not None: + return fast + + raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") + + def get_edge_inner_product_edge_properties( + self, + model, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs + ): + + """Get edge mass matrix for properties defined on cell faces. + + Parameters + ---------- + numpy.ndarray : model + material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + bool : invert_model + inverts the material property + bool : invert_matrix + inverts the matrix + bool : do_fast + do a faster implementation if available. + + Returns + ------- + scipy.sparse.csr_matrix + M, the mass matrix. (nE, nE) + """ + + fast = None + if hasattr(self, "_fastEdgePropertiesInnerProduct") and do_fast: + fast = self._fastEdgePropertiesInnerProduct( model=model, invert_model=invert_model, invert_matrix=invert_matrix, diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 4865ed636..9f4cbc3af 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -161,6 +161,161 @@ def test_order6_faces_invert_model(self): self.invert_model = True self.orderTest() +class TestInnerProductsFaceProperties(discretize.tests.OrderTest): + """Integrate a function over a surface within a unit cube domain + using edgeInnerProducts and faceInnerProducts.""" + + meshTypes = ["uniformTensorMesh"] + meshDimension = 3 + meshSizes = [16, 32] + + def getError(self): + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z + + tau_x = lambda x, y, z: y * z + 1 # x-face properties + tau_y = lambda x, y, z: x * z + 2 # y-face properties + tau_z = lambda x, y, z: 3 + x * y # z-face properties + + tau = 3 * [None] + for ii, comp in enumerate(['x', 'y', 'z']): + k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, y or z location for each plane + tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + # integrate components parallel to the plane of integration + if self.location == "edges": + + analytic = 5.02760416666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_face_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + # integrate component normal to the plane of integration + elif self.location == "faces": + + analytic = 2.66979166666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + + Fc = np.vstack( + (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) + ) + F = self.M.project_face_vector(Fc) + + if self.invert_model: + A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_face_inner_product_face_properties(tau) + + numeric = F.T.dot(A.dot(F)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + def test_order1_faces(self): + self.name = "Face Inner Product - Isotropic" + self.location = "faces" + self.invert_model = False + self.orderTest() + + def test_order1_faces_invert_model(self): + self.name = "Face Inner Product - Isotropic - invert_model" + self.location = "faces" + self.invert_model = True + self.orderTest() + +class TestInnerProductsEdgeProperties(discretize.tests.OrderTest): + """Integrate a function over a line within a unit cube domain + using edgeInnerProducts.""" + + meshTypes = ["uniformTensorMesh"] + meshDimension = 3 + meshSizes = [16, 32] + + def getError(self): + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z + + tau_x = lambda x, y, z: x + 1 # x-face properties + tau_y = lambda x, y, z: y + 2 # y-face properties + tau_z = lambda x, y, z: 3*z + 1 # z-face properties + + tau = 3 * [None] + for ii, comp in enumerate(['x', 'y', 'z']): + k = ( + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + ) # x, y or z location for each line + tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + analytic = 1.98906250000000 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_edge_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + class TestInnerProducts2D(discretize.tests.OrderTest): """Integrate an function over a unit cube domain From e1158547c2e656b998a3139f452eeb6158704790 Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 19 Jun 2023 11:38:42 -0700 Subject: [PATCH 08/41] face/edge properties for tree meshes --- tests/tree/test_tree_operators.py | 156 ++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 62412372a..aa55b5c05 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -465,6 +465,162 @@ def test_order6_faces_invert_model(self): self.orderTest() +class TestInnerProductsFaceProperties(discretize.tests.OrderTest): + """Integrate a function over a surface within a unit cube domain + using edgeInnerProducts and faceInnerProducts.""" + + meshTypes = ["uniformTree", "notatreeTree"] + meshDimension = 3 + meshSizes = [8, 16] + + def getError(self): + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z + + tau_x = lambda x, y, z: y * z + 1 # x-face properties + tau_y = lambda x, y, z: x * z + 2 # y-face properties + tau_z = lambda x, y, z: 3 + x * y # z-face properties + + tau = 3 * [None] + for ii, comp in enumerate(['x', 'y', 'z']): + k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, y or z location for each plane + tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + # integrate components parallel to the plane of integration + if self.location == "edges": + + analytic = 5.02760416666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_face_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + # integrate component normal to the plane of integration + elif self.location == "faces": + + analytic = 2.66979166666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + + Fc = np.vstack( + (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) + ) + F = self.M.project_face_vector(Fc) + + if self.invert_model: + A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_face_inner_product_face_properties(tau) + + numeric = F.T.dot(A.dot(F)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + def test_order1_faces(self): + self.name = "Face Inner Product - Isotropic" + self.location = "faces" + self.invert_model = False + self.orderTest() + + def test_order1_faces_invert_model(self): + self.name = "Face Inner Product - Isotropic - invert_model" + self.location = "faces" + self.invert_model = True + self.orderTest() + +class TestInnerProductsEdgeProperties(discretize.tests.OrderTest): + """Integrate a function over a line within a unit cube domain + using edgeInnerProducts.""" + + meshTypes = ["uniformTree", "notatreeTree"] + meshDimension = 3 + meshSizes = [16, 32] + + def getError(self): + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z + + tau_x = lambda x, y, z: x + 1 # x-face properties + tau_y = lambda x, y, z: y + 2 # y-face properties + tau_z = lambda x, y, z: 3*z + 1 # z-face properties + + tau = 3 * [None] + for ii, comp in enumerate(['x', 'y', 'z']): + k = ( + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + ) # x, y or z location for each line + tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + analytic = 1.98906250000000 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_edge_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + class TestTreeInnerProducts2D(discretize.tests.OrderTest): """Integrate an function over a unit cube domain using edgeInnerProducts and faceInnerProducts.""" From 166358ef22a6701027040a8fd33eee1e2d8c959c Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 19 Jun 2023 12:09:27 -0700 Subject: [PATCH 09/41] add 2D tensor and tree tests for face and edge property inner products --- tests/base/test_tensor_innerproduct.py | 156 ++++++++++++++++++++++++- tests/tree/test_tree_operators.py | 156 ++++++++++++++++++++++++- 2 files changed, 308 insertions(+), 4 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 9f4cbc3af..79135058c 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -161,7 +161,7 @@ def test_order6_faces_invert_model(self): self.invert_model = True self.orderTest() -class TestInnerProductsFaceProperties(discretize.tests.OrderTest): +class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): """Integrate a function over a surface within a unit cube domain using edgeInnerProducts and faceInnerProducts.""" @@ -254,7 +254,7 @@ def test_order1_faces_invert_model(self): self.invert_model = True self.orderTest() -class TestInnerProductsEdgeProperties(discretize.tests.OrderTest): +class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): """Integrate a function over a line within a unit cube domain using edgeInnerProducts.""" @@ -462,6 +462,158 @@ def test_order3_faces_invert_model(self): self.orderTest() +class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): + """Integrate a function over a surface within a unit cube domain + using edgeInnerProducts and faceInnerProducts.""" + + meshTypes = ["uniformTensorMesh"] + meshDimension = 2 + meshSizes = [8, 16, 32] + + def getError(self): + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + + ex = lambda x, y: x**2 + y + ey = lambda x, y: (y**2) * x + + tau_x = lambda x, y: 2*y + 1 # x-face properties + tau_y = lambda x, y: x + 2 # y-face properties + + tau = 2 * [None] + for ii, comp in enumerate(['x', 'y']): + k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, or y location for each plane + tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + # integrate components parallel to the plane of integration + if self.location == "edges": + + analytic = 2.24166666666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_face_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + # integrate component normal to the plane of integration + elif self.location == "faces": + + analytic = 1.59895833333333 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g)] + + Fc = np.vstack( + (cart(self.M.gridFx), cart(self.M.gridFy)) + ) + F = self.M.project_face_vector(Fc) + + if self.invert_model: + A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_face_inner_product_face_properties(tau) + + numeric = F.T.dot(A.dot(F)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + def test_order1_faces(self): + self.name = "Face Inner Product - Isotropic" + self.location = "faces" + self.invert_model = False + self.orderTest() + + def test_order1_faces_invert_model(self): + self.name = "Face Inner Product - Isotropic - invert_model" + self.location = "faces" + self.invert_model = True + self.orderTest() + +class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): + """Integrate a function over a line within a unit cube domain + using edgeInnerProducts.""" + + meshTypes = ["uniformTensorMesh"] + meshDimension = 2 + meshSizes = [8, 16, 32] + + def getError(self): + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + + ex = lambda x, y: x**2 + y + ey = lambda x, y: (x**2) * y + + tau_x = lambda x, y: x + 1 # x-face properties + tau_y = lambda x, y: y + 2 # y-face properties + + tau = 2 * [None] + for ii, comp in enumerate(['x', 'y']): + k = ( + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + ) # x, y or z location for each line + tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + analytic = 1.38229166666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_edge_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + class TestInnerProducts1D(discretize.tests.OrderTest): """Integrate an function over a unit cube domain using edgeInnerProducts and faceInnerProducts.""" diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index aa55b5c05..e6e06ca65 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -465,7 +465,7 @@ def test_order6_faces_invert_model(self): self.orderTest() -class TestInnerProductsFaceProperties(discretize.tests.OrderTest): +class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): """Integrate a function over a surface within a unit cube domain using edgeInnerProducts and faceInnerProducts.""" @@ -558,7 +558,7 @@ def test_order1_faces_invert_model(self): self.invert_model = True self.orderTest() -class TestInnerProductsEdgeProperties(discretize.tests.OrderTest): +class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): """Integrate a function over a line within a unit cube domain using edgeInnerProducts.""" @@ -765,6 +765,158 @@ def test_order3_faces_invert_model(self): self.orderTest() +class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): + """Integrate a function over a surface within a unit cube domain + using edgeInnerProducts and faceInnerProducts.""" + + meshTypes = ["uniformTree", "notatreeTree"] + meshDimension = 2 + meshSizes = [16, 32] + + def getError(self): + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + + ex = lambda x, y: x**2 + y + ey = lambda x, y: (y**2) * x + + tau_x = lambda x, y: 2*y + 1 # x-face properties + tau_y = lambda x, y: x + 2 # y-face properties + + tau = 2 * [None] + for ii, comp in enumerate(['x', 'y']): + k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, or y location for each plane + tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + # integrate components parallel to the plane of integration + if self.location == "edges": + + analytic = 2.24166666666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_face_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + # integrate component normal to the plane of integration + elif self.location == "faces": + + analytic = 1.59895833333333 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g)] + + Fc = np.vstack( + (cart(self.M.gridFx), cart(self.M.gridFy)) + ) + F = self.M.project_face_vector(Fc) + + if self.invert_model: + A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + else: + A = self.M.get_face_inner_product_face_properties(tau) + + numeric = F.T.dot(A.dot(F)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + def test_order1_faces(self): + self.name = "Face Inner Product - Isotropic" + self.location = "faces" + self.invert_model = False + self.orderTest() + + def test_order1_faces_invert_model(self): + self.name = "Face Inner Product - Isotropic - invert_model" + self.location = "faces" + self.invert_model = True + self.orderTest() + +class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): + """Integrate a function over a line within a unit cube domain + using edgeInnerProducts.""" + + meshTypes = ["uniformTree", "notatreeTree"] + meshDimension = 2 + meshSizes = [16, 32] + + def getError(self): + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + + ex = lambda x, y: x**2 + y + ey = lambda x, y: (x**2) * y + + tau_x = lambda x, y: x + 1 # x-face properties + tau_y = lambda x, y: y + 2 # y-face properties + + tau = 2 * [None] + for ii, comp in enumerate(['x', 'y']): + k = ( + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & + np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + ) # x, y or z location for each line + tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable + tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau[ii] = tau_ii + tau = np.hstack(tau) + + analytic = 1.38229166666667 # Found using sympy. + + cart = lambda g: np.c_[call(ex, g), call(ey, g)] + + Ec = np.vstack( + (cart(self.M.gridEx), cart(self.M.gridEy)) + ) + E = self.M.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_edge_properties(tau) + + numeric = E.T.dot(A.dot(E)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + class TestTreeAveraging2D(discretize.tests.OrderTest): """Integrate an function over a unit cube domain using edgeInnerProducts and faceInnerProducts.""" From df9ca182ba03d169e0249456023b37e229604750 Mon Sep 17 00:00:00 2001 From: dccowan Date: Tue, 20 Jun 2023 12:56:54 -0700 Subject: [PATCH 10/41] add derivative tests for face and edge properties (TENSOR and TREE) --- discretize/base/base_mesh.py | 43 ++- discretize/base/base_tensor_mesh.py | 249 +++++++++++-- discretize/operators/inner_products.py | 62 ++++ tests/base/test_tensor_innerproduct_derivs.py | 148 ++++++++ tests/tree/test_tree_innerproduct_derivs.py | 327 +++++++++++++----- 5 files changed, 710 insertions(+), 119 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 60097b2e5..047dfd0a6 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1904,7 +1904,7 @@ def get_face_inner_product_face_properties( ): raise NotImplementedError( - f"get_edge_inner_product_face_properties not implemented for {type(self)}" + f"get_face_inner_product_face_properties not implemented for {type(self)}" ) def get_edge_inner_product_edge_properties( @@ -1917,7 +1917,7 @@ def get_edge_inner_product_edge_properties( ): raise NotImplementedError( - f"get_edge_inner_product_face_properties not implemented for {type(self)}" + f"get_edge_inner_product_edge_properties not implemented for {type(self)}" ) def get_face_inner_product_deriv( @@ -2286,6 +2286,45 @@ def get_edge_inner_product_deriv( f"get_edge_inner_product_deriv not implemented for {type(self)}" ) + def get_edge_inner_product_face_properties_deriv( + self, + model=None, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs, + ): + + raise NotImplementedError( + f"get_edge_inner_product_face_properties_deriv not implemented for {type(self)}" + ) + + def get_face_inner_product_face_properties_deriv( + self, + model=None, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs, + ): + + raise NotImplementedError( + f"get_face_inner_product_face_properties_deriv not implemented for {type(self)}" + ) + + def get_edge_inner_product_edge_properties_deriv( + self, + model=None, + invert_model=False, + invert_matrix=False, + do_fast=True, + **kwargs, + ): + + raise NotImplementedError( + f"get_edge_inner_product_edge_properties_deriv not implemented for {type(self)}" + ) + # Averaging @property def average_face_to_cell(self): diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index e00e22401..43cb0d465 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -797,15 +797,12 @@ def _fastInnerProduct( Parameters ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - projection_type : str 'edges' or 'faces' - + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) invert_model : bool inverts the material property - invert_matrix : bool inverts the matrix @@ -872,9 +869,9 @@ def _fastInnerProductDeriv( Parameters ---------- projection_type : str - 'E' or 'F' - tensorType : TensorType - type of the tensor + 'edges' or 'faces' + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) invert_model : bool inverts the material property invert_matrix : bool @@ -1003,15 +1000,12 @@ def _fastFacePropertiesInnerProduct( Parameters ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - projection_type : str 'edges' or 'faces' - + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) invert_model : bool inverts the material property - invert_matrix : bool inverts the matrix @@ -1033,10 +1027,11 @@ def _fastFacePropertiesInnerProduct( if is_scalar(model): model = model * np.ones(self.nF) - elif len(model) == self.dim: - model = np.r_[ - [model[ii]*self.vnF[ii] for ii in range(0, self.dim)] - ] + # COULD ADD THIS CASE IF DESIRED + # elif len(model) == self.dim: + # model = np.r_[ + # [model[ii]*self.vnF[ii] for ii in range(0, self.dim)] + # ] # number of elements we are averaging (equals dim for regular # meshes, but for cyl, where we use symmetry, it is 1 for edge @@ -1057,8 +1052,9 @@ def _fastFacePropertiesInnerProduct( M = sdiag(Aprop) else: - raise NotImplementedError( - "FacePropertiesInnerProduct not implemented for anisotropy." + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces." ) if invert_matrix: @@ -1066,6 +1062,116 @@ def _fastFacePropertiesInnerProduct( else: return M + def _fastFacePropertiesInnerProductDeriv( + self, projection_type, model, invert_model=False, invert_matrix=False + ): + """Faster function for inner product derivatives on tensor meshes. + + Parameters + ---------- + projection_type : str + 'edges' or 'faces' + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) + invert_model : bool + inverts the material property + invert_matrix : bool + inverts the matrix + + Returns + ------- + function + dMdmu, the derivative of the inner product matrix + """ + projection_type = projection_type[0].upper() + if projection_type not in ["F", "E"]: + raise ValueError("projection_type must be 'F' for faces or 'E' for edges") + + if is_scalar(model): + tensorType = 0 + elif model.size == self.nF: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.".format(self.nF) + ) + + dMdprop = None + + if invert_matrix or invert_model: + MI = self._fastFacePropertiesInnerProduct( + projection_type, + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables and 2 for face variables) + if self._meshType == "CYL": + shape = getattr(self, "vn" + projection_type) + n_elements = sum([1 if x != 0 else 0 for x in shape]) + else: + n_elements = self.dim - 1 + + A = sdiag(self.face_areas) + if projection_type == 'E': + Av = getattr(self, "average_edge_to_face") + else: + Av = sdiag(np.ones(self.nF) / n_elements) + + if tensorType == 0: # isotropic, constant + ones = sp.csr_matrix( + (np.ones(self.nF), (range(self.nF), np.zeros(self.nF))), + shape=(self.nF, 1), + ) + if not invert_matrix and not invert_model: + dMdprop = n_elements * Av.T * A * ones + elif invert_matrix and invert_model: + dMdprop = n_elements * ( + sdiag(MI.diagonal() ** 2) + * Av.T + * A + * ones + * sdiag(1.0 / model**2) + ) + elif invert_model: + dMdprop = n_elements * Av.T * A * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) + + else: # isotropic, variable in space + if not invert_matrix and not invert_model: + dMdprop = n_elements * Av.T * A + elif invert_matrix and invert_model: + dMdprop = n_elements * ( + sdiag(MI.diagonal() ** 2) * Av.T * A * sdiag(1.0 / model**2) + ) + elif invert_model: + dMdprop = n_elements * Av.T * A * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) + + if dMdprop is not None: + + def innerProductDeriv(v=None): + if v is None: + warnings.warn( + "Depreciation Warning: TensorMesh.innerProductDeriv." + " You should be supplying a vector. " + "Use: sdiag(u)*dMdprop", + FutureWarning, + stacklevel=2, + ) + return dMdprop + return sdiag(v) * dMdprop + + return innerProductDeriv + else: + return None + def _fastEdgePropertiesInnerProduct( self, model=None, invert_model=False, invert_matrix=False ): @@ -1077,10 +1183,8 @@ def _fastEdgePropertiesInnerProduct( ---------- model : numpy.ndarray material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - invert_model : bool inverts the material property - invert_matrix : bool inverts the matrix @@ -1098,10 +1202,6 @@ def _fastEdgePropertiesInnerProduct( if is_scalar(model): model = model * np.ones(self.nE) - elif len(model) == self.dim: - model = np.r_[ - [model[ii]*self.vnE[ii] for ii in range(0, self.dim)] - ] # number of elements we are averaging (equals dim for regular @@ -1119,8 +1219,9 @@ def _fastEdgePropertiesInnerProduct( M = sdiag(Lprop) else: - raise NotImplementedError( - "FacePropertiesInnerProduct not implemented for anisotropy." + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of edges." ) if invert_matrix: @@ -1128,6 +1229,100 @@ def _fastEdgePropertiesInnerProduct( else: return M + def _fastEdgePropertiesInnerProductDeriv( + self, model, invert_model=False, invert_matrix=False + ): + """Faster function for inner product derivatives on tensor meshes. + + Parameters + ---------- + model : numpy.ndarray + material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) + invert_model : bool + inverts the material property + invert_matrix : bool + inverts the matrix + + Returns + ------- + function + dMdmu, the derivative of the inner product matrix + """ + if is_scalar(model): + tensorType = 0 + elif model.size == self.nE: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}.".format(model.shape), + "Must be scalar or have length equal to total number of edges: {}.".format(self.nE) + ) + + dMdprop = None + + if invert_matrix or invert_model: + MI = self._fastEdgePropertiesInnerProduct( + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + + L = sdiag(self.edge_lengths) + if tensorType == 0: # isotropic, constant + ones = sp.csr_matrix( + (np.ones(self.nE), (range(self.nE), np.zeros(self.nE))), + shape=(self.nE, 1), + ) + if not invert_matrix and not invert_model: + dMdprop = L * ones + elif invert_matrix and invert_model: + dMdprop = ( + sdiag(MI.diagonal() ** 2) + * L + * ones + * sdiag(1.0 / model**2) + ) + elif invert_model: + dMdprop = L * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = (sdiag(-MI.diagonal() ** 2) * L) + + elif tensorType == 1: # isotropic, variable in space + if not invert_matrix and not invert_model: + dMdprop = L + elif invert_matrix and invert_model: + dMdprop = ( + sdiag(MI.diagonal() ** 2) * L * sdiag(1.0 / model**2) + ) + elif invert_model: + dMdprop = L * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = (sdiag(-MI.diagonal() ** 2) * L) + + elif tensorType == 2: # anisotropic + + raise NotImplementedError( + "EdgePropertiesInnerProductDeriv not implemented for anisotropy." + ) + + if dMdprop is not None: + + def innerProductDeriv(v=None): + if v is None: + warnings.warn( + "Depreciation Warning: TensorMesh.innerProductDeriv." + " You should be supplying a vector. " + "Use: sdiag(u)*dMdprop", + FutureWarning, + stacklevel=2, + ) + return dMdprop + return sdiag(v) * dMdprop + + return innerProductDeriv + else: + return None + # DEPRECATED @property diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index cbbac686e..e7b54368f 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -399,6 +399,68 @@ def get_edge_inner_product_deriv( # NOQA D102 invert_matrix=invert_matrix, ) + def get_face_inner_product_face_properties_deriv( # NOQA D102 + self, model, invert_model=False, invert_matrix=False, **kwargs + ): + # Inherited documentation from discretize.base.BaseMesh + if "invProp" in kwargs: + raise TypeError( + "The invProp keyword argument has been removed, please use invert_model. " + "This will be removed in discretize 1.0.0", + ) + if "invMat" in kwargs: + raise TypeError( + "The invMat keyword argument has been removed, please use invert_matrix. " + "This will be removed in discretize 1.0.0", + ) + return self._fastFacePropertiesInnerProductDeriv( + "F", + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + + def get_edge_inner_product_face_properties_deriv( # NOQA D102 + self, model, invert_model=False, invert_matrix=False, **kwargs + ): + # Inherited documentation from discretize.base.BaseMesh + if "invProp" in kwargs: + raise TypeError( + "The invProp keyword argument has been removed, please use invert_model. " + "This will be removed in discretize 1.0.0", + ) + if "invMat" in kwargs: + raise TypeError( + "The invMat keyword argument has been removed, please use invert_matrix. " + "This will be removed in discretize 1.0.0", + ) + return self._fastFacePropertiesInnerProductDeriv( + "E", + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + + def get_edge_inner_product_edge_properties_deriv( # NOQA D102 + self, model, invert_model=False, invert_matrix=False, **kwargs + ): + # Inherited documentation from discretize.base.BaseMesh + if "invProp" in kwargs: + raise TypeError( + "The invProp keyword argument has been removed, please use invert_model. " + "This will be removed in discretize 1.0.0", + ) + if "invMat" in kwargs: + raise TypeError( + "The invMat keyword argument has been removed, please use invert_matrix. " + "This will be removed in discretize 1.0.0", + ) + return self._fastEdgePropertiesInnerProductDeriv( + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + def _getInnerProductDeriv( self, model, diff --git a/tests/base/test_tensor_innerproduct_derivs.py b/tests/base/test_tensor_innerproduct_derivs.py index 98440de24..2de7ead5f 100644 --- a/tests/base/test_tensor_innerproduct_derivs.py +++ b/tests/base/test_tensor_innerproduct_derivs.py @@ -329,5 +329,153 @@ def test_EdgeIP_3D_anisotropic_fast_Curv(self): self.assertTrue(self.doTestEdge([10, 4, 5], 3, True, "Curv")) +class TestFacePropertiesInnerProductsDerivsTensor(unittest.TestCase): + def doTestFace(self, h, rep, meshType, invert_model=False, invert_matrix=False): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + mesh.number(balance=False) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nF) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) + + def fun(sig): + M = mesh.get_face_inner_product_face_properties( + sig, invert_model=invert_model, invert_matrix=invert_matrix + ) + Md = mesh.get_face_inner_product_face_properties_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + # do_fast=fast, + ) + return M * v, Md(v) + + print( + meshType, + "Face", + h, + rep, + ("harmonic" if invert_model and invert_matrix else "standard"), + ) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + mesh.number(balance=False) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nE) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) + + def fun(sig): + M = mesh.get_edge_inner_product_face_properties( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + do_fast=True, + ) + Md = mesh.get_edge_inner_product_face_properties_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + # do_fast=fast, + ) + return M * v, Md(v) + + print( + meshType, + "Edge", + h, + rep, + ("harmonic" if invert_model and invert_matrix else "standard"), + ) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def test_FaceIP_2D_float_fast(self): + self.assertTrue(self.doTestFace([10, 4], 0, "Tensor")) + + def test_FaceIP_3D_float_fast(self): + self.assertTrue(self.doTestFace([10, 4, 5], 0, "Tensor")) + + def test_FaceIP_2D_isotropic_fast(self): + self.assertTrue(self.doTestFace([10, 4], 1, "Tensor")) + + def test_FaceIP_3D_isotropic_fast(self): + self.assertTrue(self.doTestFace([10, 4, 5], 1, "Tensor")) + + def test_EdgeIP_2D_float_fast(self): + self.assertTrue(self.doTestEdge([10, 4], 0, "Tensor")) + + def test_EdgeIP_3D_float_fast(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 0, "Tensor")) + + def test_EdgeIP_2D_isotropic_fast(self): + self.assertTrue(self.doTestEdge([10, 4], 1, "Tensor")) + + def test_EdgeIP_3D_isotropic_fast(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 1, "Tensor")) + + +class TestEdgePropertiesInnerProductsDerivsTensor(unittest.TestCase): + def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + mesh.number(balance=False) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nE) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nE * rep) + + def fun(sig): + M = mesh.get_edge_inner_product_edge_properties( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + do_fast=True, + ) + Md = mesh.get_edge_inner_product_edge_properties_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + # do_fast=fast, + ) + return M * v, Md(v) + + print( + meshType, + "Edge", + h, + rep, + ("harmonic" if invert_model and invert_matrix else "standard"), + ) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def test_EdgeIP_2D_float_fast(self): + self.assertTrue(self.doTestEdge([10, 4], 0, "Tensor")) + + def test_EdgeIP_3D_float_fast(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 0, "Tensor")) + + def test_EdgeIP_2D_isotropic_fast(self): + self.assertTrue(self.doTestEdge([10, 4], 1, "Tensor")) + + def test_EdgeIP_3D_isotropic_fast(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 1, "Tensor")) + + if __name__ == "__main__": unittest.main() diff --git a/tests/tree/test_tree_innerproduct_derivs.py b/tests/tree/test_tree_innerproduct_derivs.py index c03dc89f9..fcb47d446 100644 --- a/tests/tree/test_tree_innerproduct_derivs.py +++ b/tests/tree/test_tree_innerproduct_derivs.py @@ -3,152 +3,299 @@ import discretize -def doTestFace(h, rep, fast, meshType, invert_model=False, invert_matrix=False): - if meshType == "Curv": - hRect = discretize.utils.example_curvilinear_grid(h, "rotate") - mesh = discretize.CurvilinearMesh(hRect) - elif meshType == "Tree": - mesh = discretize.TreeMesh(h, levels=3) - mesh.refine(lambda xc: 3) - elif meshType == "Tensor": - mesh = discretize.TensorMesh(h) - v = np.random.rand(mesh.nF) - sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nC * rep) - - def fun(sig): - M = mesh.get_face_inner_product( - sig, invert_model=invert_model, invert_matrix=invert_matrix - ) - Md = mesh.get_face_inner_product_deriv( - sig, invert_model=invert_model, invert_matrix=invert_matrix, do_fast=fast - ) - return M * v, Md(v) - - print( - meshType, - "Face", - h, - rep, - fast, - ("harmonic" if invert_model and invert_matrix else "standard"), - ) - return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) - - -def doTestEdge(h, rep, fast, meshType, invert_model=False, invert_matrix=False): - if meshType == "Curv": - hRect = discretize.utils.example_curvilinear_grid(h, "rotate") - mesh = discretize.CurvilinearMesh(hRect) - elif meshType == "Tree": - mesh = discretize.TreeMesh(h, levels=3) - mesh.refine(lambda xc: 3) - elif meshType == "Tensor": - mesh = discretize.TensorMesh(h) - v = np.random.rand(mesh.nE) - sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nC * rep) - - def fun(sig): - M = mesh.get_edge_inner_product( - sig, invert_model=invert_model, invert_matrix=invert_matrix +class TestInnerProductsDerivsTensor(unittest.TestCase): + def doTestFace( + self, h, rep, fast, meshType, invert_model=False, invert_matrix=False + ): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nF) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nC * rep) + + def fun(sig): + M = mesh.get_face_inner_product( + sig, invert_model=invert_model, invert_matrix=invert_matrix + ) + Md = mesh.get_face_inner_product_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + do_fast=fast, + ) + return M * v, Md(v) + + print( + meshType, + "Face", + h, + rep, + fast, + ("harmonic" if invert_model and invert_matrix else "standard"), ) - Md = mesh.get_edge_inner_product_deriv( - sig, invert_model=invert_model, invert_matrix=invert_matrix, do_fast=fast + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def doTestEdge( + self, h, rep, fast, meshType, invert_model=False, invert_matrix=False + ): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nE) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nC * rep) + + def fun(sig): + M = mesh.get_edge_inner_product( + sig, invert_model=invert_model, invert_matrix=invert_matrix + ) + Md = mesh.get_edge_inner_product_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, + do_fast=fast, + ) + return M * v, Md(v) + + print( + meshType, + "Edge", + h, + rep, + fast, + ("harmonic" if invert_model and invert_matrix else "standard"), ) - return M * v, Md(v) - - print( - meshType, - "Edge", - h, - rep, - fast, - ("harmonic" if invert_model and invert_matrix else "standard"), - ) - return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) - -class TestInnerProductsDerivsTensor(unittest.TestCase): def test_FaceIP_2D_float_Tree(self): - self.assertTrue(doTestFace([8, 8], 0, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 0, False, "Tree")) def test_FaceIP_3D_float_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 0, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 0, False, "Tree")) def test_FaceIP_2D_isotropic_Tree(self): - self.assertTrue(doTestFace([8, 8], 1, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 1, False, "Tree")) def test_FaceIP_3D_isotropic_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 1, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 1, False, "Tree")) def test_FaceIP_2D_anisotropic_Tree(self): - self.assertTrue(doTestFace([8, 8], 2, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 2, False, "Tree")) def test_FaceIP_3D_anisotropic_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 3, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 3, False, "Tree")) def test_FaceIP_2D_tensor_Tree(self): - self.assertTrue(doTestFace([8, 8], 3, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 3, False, "Tree")) def test_FaceIP_3D_tensor_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 6, False, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 6, False, "Tree")) def test_FaceIP_2D_float_fast_Tree(self): - self.assertTrue(doTestFace([8, 8], 0, True, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 0, True, "Tree")) def test_FaceIP_3D_float_fast_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 0, True, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 0, True, "Tree")) def test_FaceIP_2D_isotropic_fast_Tree(self): - self.assertTrue(doTestFace([8, 8], 1, True, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 1, True, "Tree")) def test_FaceIP_3D_isotropic_fast_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 1, True, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 1, True, "Tree")) def test_FaceIP_2D_anisotropic_fast_Tree(self): - self.assertTrue(doTestFace([8, 8], 2, True, "Tree")) + self.assertTrue(self.doTestFace([8, 8], 2, True, "Tree")) def test_FaceIP_3D_anisotropic_fast_Tree(self): - self.assertTrue(doTestFace([8, 8, 8], 3, True, "Tree")) + self.assertTrue(self.doTestFace([8, 8, 8], 3, True, "Tree")) # def test_EdgeIP_2D_float_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 0, False, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 0, False, 'Tree')) def test_EdgeIP_3D_float_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 0, False, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 0, False, "Tree")) # def test_EdgeIP_2D_isotropic_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 1, False, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 1, False, 'Tree')) def test_EdgeIP_3D_isotropic_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 1, False, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 1, False, "Tree")) # def test_EdgeIP_2D_anisotropic_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 2, False, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 2, False, 'Tree')) def test_EdgeIP_3D_anisotropic_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 3, False, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 3, False, "Tree")) # def test_EdgeIP_2D_tensor_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 3, False, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 3, False, 'Tree')) def test_EdgeIP_3D_tensor_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 6, False, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 6, False, "Tree")) # def test_EdgeIP_2D_float_fast_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 0, True, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 0, True, 'Tree')) def test_EdgeIP_3D_float_fast_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 0, True, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 0, True, "Tree")) # def test_EdgeIP_2D_isotropic_fast_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 1, True, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 1, True, 'Tree')) def test_EdgeIP_3D_isotropic_fast_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 1, True, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 1, True, "Tree")) # def test_EdgeIP_2D_anisotropic_fast_Tree(self): - # self.assertTrue(doTestEdge([8, 8], 2, True, 'Tree')) + # self.assertTrue(self.doTestEdge([8, 8], 2, True, 'Tree')) def test_EdgeIP_3D_anisotropic_fast_Tree(self): - self.assertTrue(doTestEdge([8, 8, 8], 3, True, "Tree")) + self.assertTrue(self.doTestEdge([8, 8, 8], 3, True, "Tree")) + + +class TestFacePropertiesInnerProductsDerivsTensor(unittest.TestCase): + def doTestFace(self, h, rep, meshType, invert_model=False, invert_matrix=False): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nF) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) + + def fun(sig): + M = mesh.get_face_inner_product_face_properties( + sig, invert_model=invert_model, invert_matrix=invert_matrix + ) + Md = mesh.get_face_inner_product_face_properties_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, # , do_fast=fast + ) + return M * v, Md(v) + + print( + meshType, + "Face", + h, + rep, + # fast, + ("harmonic" if invert_model and invert_matrix else "standard"), + ) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nE) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) + + def fun(sig): + M = mesh.get_edge_inner_product_face_properties( + sig, invert_model=invert_model, invert_matrix=invert_matrix + ) + Md = mesh.get_edge_inner_product_face_properties_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, # , do_fast=fast + ) + return M * v, Md(v) + + print( + meshType, + "Edge", + h, + rep, + # fast, + ("harmonic" if invert_model and invert_matrix else "standard"), + ) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def test_FaceIP_2D_float_fast_Tree(self): + self.assertTrue(self.doTestFace([8, 8], 0, "Tree")) + + def test_FaceIP_3D_float_fast_Tree(self): + self.assertTrue(self.doTestFace([8, 8, 8], 0, "Tree")) + + def test_FaceIP_2D_isotropic_fast_Tree(self): + self.assertTrue(self.doTestFace([8, 8], 1, "Tree")) + + def test_FaceIP_3D_isotropic_fast_Tree(self): + self.assertTrue(self.doTestFace([8, 8, 8], 1, "Tree")) + + def test_EdgeIP_2D_float_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8], 0, "Tree")) + + def test_EdgeIP_3D_float_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8, 8], 0, "Tree")) + + def test_EdgeIP_2D_isotropic_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8], 1, "Tree")) + + def test_EdgeIP_3D_isotropic_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8, 8], 1, "Tree")) + + +class TestEdgePropertiesInnerProductsDerivsTensor(unittest.TestCase): + def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): + if meshType == "Curv": + hRect = discretize.utils.example_curvilinear_grid(h, "rotate") + mesh = discretize.CurvilinearMesh(hRect) + elif meshType == "Tree": + mesh = discretize.TreeMesh(h, levels=3) + mesh.refine(lambda xc: 3) + elif meshType == "Tensor": + mesh = discretize.TensorMesh(h) + v = np.random.rand(mesh.nE) + sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nE * rep) + + def fun(sig): + M = mesh.get_edge_inner_product_edge_properties( + sig, invert_model=invert_model, invert_matrix=invert_matrix + ) + Md = mesh.get_edge_inner_product_edge_properties_deriv( + sig, + invert_model=invert_model, + invert_matrix=invert_matrix, # , do_fast=fast + ) + return M * v, Md(v) + + print( + meshType, + "Edge", + h, + rep, + # fast, + ("harmonic" if invert_model and invert_matrix else "standard"), + ) + return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) + + def test_EdgeIP_2D_float_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8], 0, "Tree")) + + def test_EdgeIP_3D_float_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8, 8], 0, "Tree")) + + def test_EdgeIP_2D_isotropic_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8], 1, "Tree")) + + def test_EdgeIP_3D_isotropic_fast_Tree(self): + self.assertTrue(self.doTestEdge([8, 8, 8], 1, "Tree")) if __name__ == "__main__": From f4fc5148aa8b5492ef1d786f7aaf5b7e8d113373 Mon Sep 17 00:00:00 2001 From: dccowan Date: Wed, 21 Jun 2023 13:09:50 -0700 Subject: [PATCH 11/41] Add analytic, order and derivative tests for cyl mesh (face properties) --- discretize/base/base_tensor_mesh.py | 10 +- discretize/operators/inner_products.py | 14 +- tests/cyl/test_cyl_innerproducts.py | 273 +++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 9 deletions(-) diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index 43cb0d465..d08bd8c1d 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -1038,7 +1038,10 @@ def _fastFacePropertiesInnerProduct( # variables and 2 for face variables) if self._meshType == "CYL": shape = getattr(self, "vn" + projection_type) - n_elements = sum([1 if x != 0 else 0 for x in shape]) + if self.is_symmetric: + n_elements = 1 + else: + n_elements = sum([1 if x != 0 else 0 for x in shape]) - 1 else: n_elements = self.dim - 1 @@ -1112,7 +1115,10 @@ def _fastFacePropertiesInnerProductDeriv( # variables and 2 for face variables) if self._meshType == "CYL": shape = getattr(self, "vn" + projection_type) - n_elements = sum([1 if x != 0 else 0 for x in shape]) + if self.is_symmetric: + n_elements = 1 + else: + n_elements = sum([1 if x != 0 else 0 for x in shape]) - 1 else: n_elements = self.dim - 1 diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index e7b54368f..23853b46c 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -104,12 +104,12 @@ def get_edge_inner_product_face_properties( **kwargs ): - """Get edge mass matrix for properties defined on cell faces. + """Get edge inner product matrix for properties defined on cell faces. Parameters ---------- numpy.ndarray : model - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + material property (isotropic only) for all mesh faces (nF, ) bool : invert_model inverts the material property bool : invert_matrix @@ -145,12 +145,12 @@ def get_face_inner_product_face_properties( **kwargs ): - """Get face mass matrix for properties defined on cell faces. + """Get face inner product matrix for properties defined on cell faces. Parameters ---------- numpy.ndarray : model - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + material property (isotropic only) for all mesh faces (nF, ) bool : invert_model inverts the material property bool : invert_matrix @@ -161,7 +161,7 @@ def get_face_inner_product_face_properties( Returns ------- scipy.sparse.csr_matrix - M, the mass matrix. (nE, nE) + M, the mass matrix. (nF, nF) """ fast = None @@ -186,12 +186,12 @@ def get_edge_inner_product_edge_properties( **kwargs ): - """Get edge mass matrix for properties defined on cell faces. + """Get edge inner product matrix for properties defined on cell edges. Parameters ---------- numpy.ndarray : model - material property (tensor properties are possible) at each cell center (nF, (1 or 2)) + material property (isotropic only) for all mesh edges (nE, ) bool : invert_model inverts the material property bool : invert_matrix diff --git a/tests/cyl/test_cyl_innerproducts.py b/tests/cyl/test_cyl_innerproducts.py index efcbe56fa..efde52ccb 100644 --- a/tests/cyl/test_cyl_innerproducts.py +++ b/tests/cyl/test_cyl_innerproducts.py @@ -148,6 +148,124 @@ def vectors(self, mesh): return np.c_[sig, sig, sig], np.r_[ht] +class FaceInnerProductFctsFacePropertiesIsotropic(object): + """Some made up face functions to test the face inner product""" + + def fcts(self): + + r_plane = 0.5 + z_plane = 0.5 + + j_r = (r_plane**2) * z # radial component + j_z = r * z_plane # vertical component + + # Create an isotropic sigma vector + tau_r = (r_plane + z) ** 2 # r-faces + tau_z = r + z_plane**2 # z_faces + + return j_r, j_z, tau_r, tau_z + + def sol(self): + + r_plane = 0.5 + z_plane = 0.5 + + # Do the inner product! - we are in cyl coordinates! + j_r, j_z, tau_r, tau_z = self.fcts() + + # we are integrating in cyl coordinates + int_r = sympy.integrate(r_plane * j_r**2 * tau_r, (z, 0, 1), (t, 0, 2 * np.pi)) + int_z = sympy.integrate(r * j_z**2 * tau_z, (r, 0, 1), (t, 0, 2 * np.pi)) + + # return int_z(z_plane) + return int_r + int_z + + def vectors(self, mesh): + + r_plane = 0.5 + z_plane = 0.5 + + """Get Vectors sig, sr. jx from sympy""" + j_r, j_z, tau_r, tau_z = self.fcts() + + fun_j_r = sympy.lambdify(z, j_r, "numpy") + fun_j_z = sympy.lambdify(r, j_z, "numpy") + fun_tau_r = sympy.lambdify(z, tau_r, "numpy") + fun_tau_z = sympy.lambdify(r, tau_z, "numpy") + + eval_j_r = fun_j_r(mesh.gridFx[:, 2]) + eval_j_z = fun_j_z(mesh.gridFz[:, 0]) + eval_tau_r = 1e-12 * np.ones(mesh.nFx) + eval_tau_z = 1e-12 * np.ones(mesh.nFz) + + k_r = np.isclose(mesh.faces_x[:, 0], r_plane) + k_z = np.isclose(mesh.faces_z[:, 2], z_plane) + + eval_tau_r[k_r] = fun_tau_r(mesh.gridFx[k_r, 2]) + eval_tau_z[k_z] = fun_tau_z(mesh.gridFz[k_z, 0]) + + return np.r_[eval_tau_r, eval_tau_z], np.r_[eval_j_r, eval_j_z] + +class EdgeInnerProductFctsFacePropertiesIsotropic(object): + """Some made up face functions to test the face inner product""" + + def fcts(self): + + r_plane = 0.5 + z_plane = 0.5 + + j_t = 0.5 * r * z # azimuthal + + # Create an isotropic sigma vector + tau_r = (r_plane + z) ** 2 # r-faces + tau_z = r + z_plane**2 # z_faces + + return j_t, tau_r, tau_z + + def sol(self): + + r_plane = 0.5 + z_plane = 0.5 + + # Do the inner product! - we are in cyl coordinates! + j_t, tau_r, tau_z = self.fcts() + + # we are integrating in cyl coordinates + int_r = sympy.lambdify( + r, sympy.integrate(r * j_t**2 * tau_r, (z, 0, 1), (t, 0, 2 * np.pi)), "numpy" + ) + + int_z = sympy.lambdify( + z, sympy.integrate(r * j_t**2 * tau_z, (r, 0, 1), (t, 0, 2 * np.pi)), "numpy" + ) + + return int_r(r_plane) + int_z(z_plane) + + def vectors(self, mesh): + + r_plane = 0.5 + z_plane = 0.5 + + """Get Vectors sig, sr. jx from sympy""" + j_t, tau_r, tau_z = self.fcts() + + fun_j_t = sympy.lambdify((r, z), j_t, "numpy") + fun_tau_r = sympy.lambdify(z, tau_r, "numpy") + fun_tau_z = sympy.lambdify(r, tau_z, "numpy") + + eval_j_t = fun_j_t(mesh.gridEy[:, 0], mesh.gridEy[:, 2]) + eval_tau_r = 1e-8 * np.ones(mesh.nFx) + eval_tau_z = 1e-8 * np.ones(mesh.nFz) + + k_r = np.isclose(mesh.faces_x[:, 0], r_plane) + k_z = np.isclose(mesh.faces_z[:, 2], z_plane) + + eval_tau_r[k_r] = fun_tau_r(mesh.faces_x[k_r, 2]) + eval_tau_z[k_z] = fun_tau_z(mesh.faces_z[k_z, 0]) + + return np.r_[eval_tau_r, eval_tau_z], eval_j_t + + class TestCylInnerProducts_simple(unittest.TestCase): def setUp(self): n = 100.0 @@ -234,6 +352,46 @@ def test_EdgeInnerProductDiagAnisotropic(self): ) assert np.abs(ans - numeric_ans) < TOL + def test_FaceInnerProductFacePropertiesIsotropic(self): + # Here we will make up some j vectors that vary in space + # j = [j_r, j_z] - to test face inner products + + fcts = FaceInnerProductFctsFacePropertiesIsotropic() + tau, jv = fcts.vectors(self.mesh) + Mftau = self.mesh.get_face_inner_product_face_properties(tau) + numeric_ans = jv.T.dot(Mftau.dot(jv)) + + ans = fcts.sol() + + print("--- Testing Face Inner Product (face properties) ---") + print( + " Analytic: {analytic}, Numeric: {numeric}, " + "ratio (num/ana): {ratio}".format( + analytic=ans, numeric=numeric_ans, ratio=float(numeric_ans) / ans + ) + ) + assert np.abs(ans - numeric_ans) < TOL + + def test_EdgeInnerProductFacePropertiesIsotropic(self): + # Here we will make up some j vectors that vary in space + # j = [j_r, j_z] - to test face inner products + + fcts = EdgeInnerProductFctsFacePropertiesIsotropic() + tau, jv = fcts.vectors(self.mesh) + Metau = self.mesh.get_edge_inner_product_face_properties(tau) + numeric_ans = jv.T.dot(Metau.dot(jv)) + + ans = fcts.sol() + + print("--- Testing Edge Inner Product (face properties) ---") + print( + " Analytic: {analytic}, Numeric: {numeric}, " + "ratio (num/ana): {ratio}".format( + analytic=ans, numeric=numeric_ans, ratio=float(numeric_ans) / ans + ) + ) + assert np.abs(ans - numeric_ans) < TOL + class TestCylFaceInnerProducts_Order(tests.OrderTest): meshTypes = ["uniform_symmetric_CylMesh"] @@ -277,6 +435,34 @@ def test_order(self): self.orderTest() +class TestCylFaceInnerProductsFaceProperties_Order(tests.OrderTest): + meshTypes = ["uniform_symmetric_CylMesh"] + meshDimension = 3 + + def getError(self): + fct = FaceInnerProductFctsFacePropertiesIsotropic() + tau, jv = fct.vectors(self.M) + Mtau = self.M.get_face_inner_product_face_properties(tau) + return float(fct.sol()) - jv.T.dot(Mtau.dot(jv)) + + def test_order(self): + self.orderTest() + + +class TestCylEdgeInnerProductsFaceProperties_Order(tests.OrderTest): + meshTypes = ["uniform_symmetric_CylMesh"] + meshDimension = 3 + + def getError(self): + fct = EdgeInnerProductFctsFacePropertiesIsotropic() + tau, jv = fct.vectors(self.M) + Mtau = self.M.get_edge_inner_product_face_properties(tau) + return float(fct.sol()) - jv.T.dot(Mtau.dot(jv)) + + def test_order(self): + self.orderTest() + + class TestCylInnerProducts_Deriv(unittest.TestCase): def setUp(self): n = 2 @@ -557,5 +743,92 @@ def fun(x): ) +class TestCylInnerProductsFaceProperties_Deriv(unittest.TestCase): + def setUp(self): + n = 2 + self.mesh = discretize.CylindricalMesh([n, 1, n]) + self.face_vec = np.random.rand(self.mesh.nF) + self.edge_vec = np.random.rand(self.mesh.nE) + # make up a smooth function + self.x0 = np.r_[ + 2 * self.mesh.gridFx[:, 0] ** 2 + self.mesh.gridFx[:, 2] ** 4, + 2 * self.mesh.gridFz[:, 0] ** 2 + self.mesh.gridFz[:, 2] ** 4 + ] + + def test_FaceInnerProductIsotropicDeriv(self): + def fun(x): + MfTau = self.mesh.get_face_inner_product_face_properties(x) + MfTauDeriv = self.mesh.get_face_inner_product_face_properties_deriv(self.x0) + return MfTau * self.face_vec, MfTauDeriv(self.face_vec) + + print("Testing FaceInnerProduct Isotropic (Face Properties)") + return self.assertTrue( + tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) + ) + + def test_FaceInnerProductIsotropicDerivInvProp(self): + def fun(x): + MfTau = self.mesh.get_face_inner_product_face_properties(x, invert_model=True) + MfTauDeriv = self.mesh.get_face_inner_product_face_properties_deriv( + self.x0, invert_model=True + ) + return MfTau * self.face_vec, MfTauDeriv(self.face_vec) + + print("Testing FaceInnerProduct Isotropic InvProp (Face Properties)") + return self.assertTrue( + tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) + ) + + def test_FaceInnerProductIsotropicDerivInvMat(self): + def fun(x): + MfTau = self.mesh.get_face_inner_product_face_properties(x, invert_matrix=True) + MfTauDeriv = self.mesh.get_face_inner_product_face_properties_deriv( + self.x0, invert_matrix=True + ) + return MfTau * self.face_vec, MfTauDeriv(self.face_vec) + + print("Testing FaceInnerProduct Isotropic InvMat (Face Properties)") + return self.assertTrue( + tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) + ) + + def test_EdgeInnerProductIsotropicDeriv(self): + def fun(x): + MeTau = self.mesh.get_edge_inner_product_face_properties(x) + MeTauDeriv = self.mesh.get_edge_inner_product_face_properties_deriv(self.x0) + return MeTau * self.edge_vec, MeTauDeriv(self.edge_vec) + + print("Testing EdgeInnerProduct Isotropic (Face Properties)") + return self.assertTrue( + tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) + ) + + def test_EdgeInnerProductIsotropicDerivInvProp(self): + def fun(x): + MeTau = self.mesh.get_edge_inner_product_face_properties(x, invert_model=True) + MeTauDeriv = self.mesh.get_edge_inner_product_face_properties_deriv( + self.x0, invert_model=True + ) + return MeTau * self.edge_vec, MeTauDeriv(self.edge_vec) + + print("Testing EdgeInnerProduct Isotropic InvProp (Face Properties)") + return self.assertTrue( + tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) + ) + + def test_EdgeInnerProductIsotropicDerivInvMat(self): + def fun(x): + MeTau = self.mesh.get_edge_inner_product_face_properties(x, invert_matrix=True) + MeTauDeriv = self.mesh.get_edge_inner_product_face_properties_deriv( + self.x0, invert_matrix=True + ) + return MeTau * self.edge_vec, MeTauDeriv(self.edge_vec) + + print("Testing EdgeInnerProduct Isotropic InvMat (Face Properties)") + return self.assertTrue( + tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) + ) + + if __name__ == "__main__": unittest.main() From 3dae379ae6d93ff45a475d32be8e92d380be2e8f Mon Sep 17 00:00:00 2001 From: dccowan Date: Thu, 22 Jun 2023 13:31:43 -0700 Subject: [PATCH 12/41] finalize new inner product names, black and flake8 --- discretize/base/base_mesh.py | 24 +-- discretize/base/base_tensor_mesh.py | 10 +- discretize/operators/inner_products.py | 12 +- tests/base/test_tensor_innerproduct.py | 179 +++++++++--------- tests/base/test_tensor_innerproduct_derivs.py | 12 +- tests/cyl/test_cyl_innerproducts.py | 88 ++++----- tests/tree/test_tree_innerproduct_derivs.py | 12 +- tests/tree/test_tree_operators.py | 178 ++++++++--------- 8 files changed, 262 insertions(+), 253 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 047dfd0a6..0f9aeab0a 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1881,7 +1881,7 @@ def get_edge_inner_product( ) - def get_edge_inner_product_face_properties( + def get_edge_inner_product_surface( self, model=None, invert_model=False, @@ -1891,10 +1891,10 @@ def get_edge_inner_product_face_properties( ): raise NotImplementedError( - f"get_edge_inner_product_face_properties not implemented for {type(self)}" + f"get_edge_inner_product_surface not implemented for {type(self)}" ) - def get_face_inner_product_face_properties( + def get_face_inner_product_surface( self, model=None, invert_model=False, @@ -1904,10 +1904,10 @@ def get_face_inner_product_face_properties( ): raise NotImplementedError( - f"get_face_inner_product_face_properties not implemented for {type(self)}" + f"get_face_inner_product_surface not implemented for {type(self)}" ) - def get_edge_inner_product_edge_properties( + def get_edge_inner_product_line( self, model=None, invert_model=False, @@ -1917,7 +1917,7 @@ def get_edge_inner_product_edge_properties( ): raise NotImplementedError( - f"get_edge_inner_product_edge_properties not implemented for {type(self)}" + f"get_edge_inner_product_line not implemented for {type(self)}" ) def get_face_inner_product_deriv( @@ -2286,7 +2286,7 @@ def get_edge_inner_product_deriv( f"get_edge_inner_product_deriv not implemented for {type(self)}" ) - def get_edge_inner_product_face_properties_deriv( + def get_edge_inner_product_surface_deriv( self, model=None, invert_model=False, @@ -2296,10 +2296,10 @@ def get_edge_inner_product_face_properties_deriv( ): raise NotImplementedError( - f"get_edge_inner_product_face_properties_deriv not implemented for {type(self)}" + f"get_edge_inner_product_surface_deriv not implemented for {type(self)}" ) - def get_face_inner_product_face_properties_deriv( + def get_face_inner_product_surface_deriv( self, model=None, invert_model=False, @@ -2309,10 +2309,10 @@ def get_face_inner_product_face_properties_deriv( ): raise NotImplementedError( - f"get_face_inner_product_face_properties_deriv not implemented for {type(self)}" + f"get_face_inner_product_surface_deriv not implemented for {type(self)}" ) - def get_edge_inner_product_edge_properties_deriv( + def get_edge_inner_product_line_deriv( self, model=None, invert_model=False, @@ -2322,7 +2322,7 @@ def get_edge_inner_product_edge_properties_deriv( ): raise NotImplementedError( - f"get_edge_inner_product_edge_properties_deriv not implemented for {type(self)}" + f"get_edge_inner_product_line_deriv not implemented for {type(self)}" ) # Averaging diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index d08bd8c1d..ca3a155e3 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -991,10 +991,10 @@ def innerProductDeriv(v=None): return None - def _fastFacePropertiesInnerProduct( + def _fastInnerProductSurface( self, projection_type, model=None, invert_model=False, invert_matrix=False ): - """Fast version of get_face_inner_product_deriv. + """Fast version of get_face_inner_product_surface. This does not handle the case of a full tensor property. @@ -1065,7 +1065,7 @@ def _fastFacePropertiesInnerProduct( else: return M - def _fastFacePropertiesInnerProductDeriv( + def _fastInnerProductSurfaceDeriv( self, projection_type, model, invert_model=False, invert_matrix=False ): """Faster function for inner product derivatives on tensor meshes. @@ -1178,7 +1178,7 @@ def innerProductDeriv(v=None): else: return None - def _fastEdgePropertiesInnerProduct( + def _fastInnerProductLine( self, model=None, invert_model=False, invert_matrix=False ): """Fast version of get_face_inner_product_deriv. @@ -1235,7 +1235,7 @@ def _fastEdgePropertiesInnerProduct( else: return M - def _fastEdgePropertiesInnerProductDeriv( + def _fastInnerProductLineDeriv( self, model, invert_model=False, invert_matrix=False ): """Faster function for inner product derivatives on tensor meshes. diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 23853b46c..7cf267562 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -95,7 +95,7 @@ def get_edge_inner_product( # NOQA D102 do_fast=do_fast, ) - def get_edge_inner_product_face_properties( + def get_edge_inner_product_surface( self, model, invert_model=False, @@ -136,7 +136,7 @@ def get_edge_inner_product_face_properties( raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") - def get_face_inner_product_face_properties( + def get_face_inner_product_surface( self, model, invert_model=False, @@ -177,7 +177,7 @@ def get_face_inner_product_face_properties( raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") - def get_edge_inner_product_edge_properties( + def get_edge_inner_product_line( self, model, invert_model=False, @@ -399,7 +399,7 @@ def get_edge_inner_product_deriv( # NOQA D102 invert_matrix=invert_matrix, ) - def get_face_inner_product_face_properties_deriv( # NOQA D102 + def get_face_inner_product_surface_deriv( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh @@ -420,7 +420,7 @@ def get_face_inner_product_face_properties_deriv( # NOQA D102 invert_matrix=invert_matrix, ) - def get_edge_inner_product_face_properties_deriv( # NOQA D102 + def get_edge_inner_product_surface_deriv( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh @@ -441,7 +441,7 @@ def get_edge_inner_product_face_properties_deriv( # NOQA D102 invert_matrix=invert_matrix, ) - def get_edge_inner_product_edge_properties_deriv( # NOQA D102 + def get_edge_inner_product_line_deriv( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 79135058c..e026b7ba9 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -161,6 +161,7 @@ def test_order6_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): """Integrate a function over a surface within a unit cube domain using edgeInnerProducts and faceInnerProducts.""" @@ -179,55 +180,57 @@ def getError(self): tau_x = lambda x, y, z: y * z + 1 # x-face properties tau_y = lambda x, y, z: x * z + 2 # y-face properties tau_z = lambda x, y, z: 3 + x * y # z-face properties - + tau = 3 * [None] - for ii, comp in enumerate(['x', 'y', 'z']): - k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, y or z location for each plane - tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + for ii, comp in enumerate(["x", "y", "z"]): + k = np.isclose( + eval("self.M.faces_{}".format(comp))[:, ii], 0.5 + ) # x, y or z location for each plane + tau_ii = 1e-8 * eval( + "np.ones(self.M.nF{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.faces_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + # integrate components parallel to the plane of integration if self.location == "edges": - analytic = 5.02760416666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] - + Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) ) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_face_properties(tau) - + A = self.M.get_edge_inner_product_surface(tau) + numeric = E.T.dot(A.dot(E)) - + # integrate component normal to the plane of integration elif self.location == "faces": - analytic = 2.66979166666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] - + Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) ) F = self.M.project_face_vector(Fc) if self.invert_model: - A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_face_inner_product_face_properties(tau) - + A = self.M.get_face_inner_product_surface(tau) + numeric = F.T.dot(A.dot(F)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): @@ -254,6 +257,7 @@ def test_order1_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): """Integrate a function over a line within a unit cube domain using edgeInnerProducts.""" @@ -271,37 +275,38 @@ def getError(self): tau_x = lambda x, y, z: x + 1 # x-face properties tau_y = lambda x, y, z: y + 2 # y-face properties - tau_z = lambda x, y, z: 3*z + 1 # z-face properties - + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties + tau = 3 * [None] - for ii, comp in enumerate(['x', 'y', 'z']): - k = ( - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + for ii, comp in enumerate(["x", "y", "z"]): + k = np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 1], 0.5 + ) & np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 2], 0.5 ) # x, y or z location for each line - tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau_ii = 1e-8 * eval( + "np.ones(self.M.nE{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.edges_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + analytic = 1.98906250000000 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] - - Ec = np.vstack( - (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) - ) + + Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_edge_properties(tau) - + A = self.M.get_edge_inner_product_line(tau) + numeric = E.T.dot(A.dot(E)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): @@ -476,57 +481,55 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2*y + 1 # x-face properties - tau_y = lambda x, y: x + 2 # y-face properties - + tau_x = lambda x, y: 2 * y + 1 # x-face properties + tau_y = lambda x, y: x + 2 # y-face properties + tau = 2 * [None] - for ii, comp in enumerate(['x', 'y']): - k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, or y location for each plane - tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + for ii, comp in enumerate(["x", "y"]): + k = np.isclose( + eval("self.M.faces_{}".format(comp))[:, ii], 0.5 + ) # x, or y location for each plane + tau_ii = 1e-8 * eval( + "np.ones(self.M.nF{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.faces_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + # integrate components parallel to the plane of integration if self.location == "edges": - analytic = 2.24166666666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g)] - - Ec = np.vstack( - (cart(self.M.gridEx), cart(self.M.gridEy)) - ) + + Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_face_properties(tau) - + A = self.M.get_edge_inner_product_surface(tau) + numeric = E.T.dot(A.dot(E)) - + # integrate component normal to the plane of integration elif self.location == "faces": - analytic = 1.59895833333333 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g)] - - Fc = np.vstack( - (cart(self.M.gridFx), cart(self.M.gridFy)) - ) + + Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) if self.invert_model: - A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_face_inner_product_face_properties(tau) - + A = self.M.get_face_inner_product_surface(tau) + numeric = F.T.dot(A.dot(F)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): @@ -553,6 +556,7 @@ def test_order1_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): """Integrate a function over a line within a unit cube domain using edgeInnerProducts.""" @@ -569,36 +573,37 @@ def getError(self): tau_x = lambda x, y: x + 1 # x-face properties tau_y = lambda x, y: y + 2 # y-face properties - + tau = 2 * [None] - for ii, comp in enumerate(['x', 'y']): - k = ( - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + for ii, comp in enumerate(["x", "y"]): + k = np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 1], 0.5 + ) & np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 2], 0.5 ) # x, y or z location for each line - tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau_ii = 1e-8 * eval( + "np.ones(self.M.nE{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.edges_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + analytic = 1.38229166666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g)] - - Ec = np.vstack( - (cart(self.M.gridEx), cart(self.M.gridEy)) - ) + + Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_edge_properties(tau) - + A = self.M.get_edge_inner_product_line(tau) + numeric = E.T.dot(A.dot(E)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): diff --git a/tests/base/test_tensor_innerproduct_derivs.py b/tests/base/test_tensor_innerproduct_derivs.py index 2de7ead5f..e15f87f14 100644 --- a/tests/base/test_tensor_innerproduct_derivs.py +++ b/tests/base/test_tensor_innerproduct_derivs.py @@ -344,10 +344,10 @@ def doTestFace(self, h, rep, meshType, invert_model=False, invert_matrix=False): sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) def fun(sig): - M = mesh.get_face_inner_product_face_properties( + M = mesh.get_face_inner_product_surface( sig, invert_model=invert_model, invert_matrix=invert_matrix ) - Md = mesh.get_face_inner_product_face_properties_deriv( + Md = mesh.get_face_inner_product_surface_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, @@ -378,13 +378,13 @@ def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) def fun(sig): - M = mesh.get_edge_inner_product_face_properties( + M = mesh.get_edge_inner_product_surface( sig, invert_model=invert_model, invert_matrix=invert_matrix, do_fast=True, ) - Md = mesh.get_edge_inner_product_face_properties_deriv( + Md = mesh.get_edge_inner_product_surface_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, @@ -441,13 +441,13 @@ def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nE * rep) def fun(sig): - M = mesh.get_edge_inner_product_edge_properties( + M = mesh.get_edge_inner_product_line( sig, invert_model=invert_model, invert_matrix=invert_matrix, do_fast=True, ) - Md = mesh.get_edge_inner_product_edge_properties_deriv( + Md = mesh.get_edge_inner_product_line_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, diff --git a/tests/cyl/test_cyl_innerproducts.py b/tests/cyl/test_cyl_innerproducts.py index efde52ccb..a591a8114 100644 --- a/tests/cyl/test_cyl_innerproducts.py +++ b/tests/cyl/test_cyl_innerproducts.py @@ -152,42 +152,40 @@ class FaceInnerProductFctsFacePropertiesIsotropic(object): """Some made up face functions to test the face inner product""" def fcts(self): - r_plane = 0.5 z_plane = 0.5 - + j_r = (r_plane**2) * z # radial component - j_z = r * z_plane # vertical component + j_z = r * z_plane # vertical component # Create an isotropic sigma vector tau_r = (r_plane + z) ** 2 # r-faces - tau_z = r + z_plane**2 # z_faces + tau_z = r + z_plane**2 # z_faces return j_r, j_z, tau_r, tau_z def sol(self): - r_plane = 0.5 - z_plane = 0.5 - + # Do the inner product! - we are in cyl coordinates! j_r, j_z, tau_r, tau_z = self.fcts() # we are integrating in cyl coordinates - int_r = sympy.integrate(r_plane * j_r**2 * tau_r, (z, 0, 1), (t, 0, 2 * np.pi)) + int_r = sympy.integrate( + r_plane * j_r**2 * tau_r, (z, 0, 1), (t, 0, 2 * np.pi) + ) int_z = sympy.integrate(r * j_z**2 * tau_z, (r, 0, 1), (t, 0, 2 * np.pi)) - + # return int_z(z_plane) return int_r + int_z def vectors(self, mesh): - r_plane = 0.5 z_plane = 0.5 - + """Get Vectors sig, sr. jx from sympy""" j_r, j_z, tau_r, tau_z = self.fcts() - + fun_j_r = sympy.lambdify(z, j_r, "numpy") fun_j_z = sympy.lambdify(r, j_z, "numpy") fun_tau_r = sympy.lambdify(z, tau_r, "numpy") @@ -197,55 +195,57 @@ def vectors(self, mesh): eval_j_z = fun_j_z(mesh.gridFz[:, 0]) eval_tau_r = 1e-12 * np.ones(mesh.nFx) eval_tau_z = 1e-12 * np.ones(mesh.nFz) - + k_r = np.isclose(mesh.faces_x[:, 0], r_plane) k_z = np.isclose(mesh.faces_z[:, 2], z_plane) - + eval_tau_r[k_r] = fun_tau_r(mesh.gridFx[k_r, 2]) eval_tau_z[k_z] = fun_tau_z(mesh.gridFz[k_z, 0]) return np.r_[eval_tau_r, eval_tau_z], np.r_[eval_j_r, eval_j_z] + class EdgeInnerProductFctsFacePropertiesIsotropic(object): """Some made up face functions to test the face inner product""" def fcts(self): - r_plane = 0.5 z_plane = 0.5 - + j_t = 0.5 * r * z # azimuthal # Create an isotropic sigma vector tau_r = (r_plane + z) ** 2 # r-faces - tau_z = r + z_plane**2 # z_faces + tau_z = r + z_plane**2 # z_faces return j_t, tau_r, tau_z def sol(self): - r_plane = 0.5 z_plane = 0.5 - + # Do the inner product! - we are in cyl coordinates! j_t, tau_r, tau_z = self.fcts() # we are integrating in cyl coordinates int_r = sympy.lambdify( - r, sympy.integrate(r * j_t**2 * tau_r, (z, 0, 1), (t, 0, 2 * np.pi)), "numpy" + r, + sympy.integrate(r * j_t**2 * tau_r, (z, 0, 1), (t, 0, 2 * np.pi)), + "numpy", ) - + int_z = sympy.lambdify( - z, sympy.integrate(r * j_t**2 * tau_z, (r, 0, 1), (t, 0, 2 * np.pi)), "numpy" + z, + sympy.integrate(r * j_t**2 * tau_z, (r, 0, 1), (t, 0, 2 * np.pi)), + "numpy", ) return int_r(r_plane) + int_z(z_plane) def vectors(self, mesh): - r_plane = 0.5 z_plane = 0.5 - + """Get Vectors sig, sr. jx from sympy""" j_t, tau_r, tau_z = self.fcts() @@ -256,10 +256,10 @@ def vectors(self, mesh): eval_j_t = fun_j_t(mesh.gridEy[:, 0], mesh.gridEy[:, 2]) eval_tau_r = 1e-8 * np.ones(mesh.nFx) eval_tau_z = 1e-8 * np.ones(mesh.nFz) - + k_r = np.isclose(mesh.faces_x[:, 0], r_plane) k_z = np.isclose(mesh.faces_z[:, 2], z_plane) - + eval_tau_r[k_r] = fun_tau_r(mesh.faces_x[k_r, 2]) eval_tau_z[k_z] = fun_tau_z(mesh.faces_z[k_z, 0]) @@ -358,7 +358,7 @@ def test_FaceInnerProductFacePropertiesIsotropic(self): fcts = FaceInnerProductFctsFacePropertiesIsotropic() tau, jv = fcts.vectors(self.mesh) - Mftau = self.mesh.get_face_inner_product_face_properties(tau) + Mftau = self.mesh.get_face_inner_product_surface(tau) numeric_ans = jv.T.dot(Mftau.dot(jv)) ans = fcts.sol() @@ -371,14 +371,14 @@ def test_FaceInnerProductFacePropertiesIsotropic(self): ) ) assert np.abs(ans - numeric_ans) < TOL - + def test_EdgeInnerProductFacePropertiesIsotropic(self): # Here we will make up some j vectors that vary in space # j = [j_r, j_z] - to test face inner products fcts = EdgeInnerProductFctsFacePropertiesIsotropic() tau, jv = fcts.vectors(self.mesh) - Metau = self.mesh.get_edge_inner_product_face_properties(tau) + Metau = self.mesh.get_edge_inner_product_surface(tau) numeric_ans = jv.T.dot(Metau.dot(jv)) ans = fcts.sol() @@ -442,7 +442,7 @@ class TestCylFaceInnerProductsFaceProperties_Order(tests.OrderTest): def getError(self): fct = FaceInnerProductFctsFacePropertiesIsotropic() tau, jv = fct.vectors(self.M) - Mtau = self.M.get_face_inner_product_face_properties(tau) + Mtau = self.M.get_face_inner_product_surface(tau) return float(fct.sol()) - jv.T.dot(Mtau.dot(jv)) def test_order(self): @@ -456,7 +456,7 @@ class TestCylEdgeInnerProductsFaceProperties_Order(tests.OrderTest): def getError(self): fct = EdgeInnerProductFctsFacePropertiesIsotropic() tau, jv = fct.vectors(self.M) - Mtau = self.M.get_edge_inner_product_face_properties(tau) + Mtau = self.M.get_edge_inner_product_surface(tau) return float(fct.sol()) - jv.T.dot(Mtau.dot(jv)) def test_order(self): @@ -752,13 +752,13 @@ def setUp(self): # make up a smooth function self.x0 = np.r_[ 2 * self.mesh.gridFx[:, 0] ** 2 + self.mesh.gridFx[:, 2] ** 4, - 2 * self.mesh.gridFz[:, 0] ** 2 + self.mesh.gridFz[:, 2] ** 4 + 2 * self.mesh.gridFz[:, 0] ** 2 + self.mesh.gridFz[:, 2] ** 4, ] def test_FaceInnerProductIsotropicDeriv(self): def fun(x): - MfTau = self.mesh.get_face_inner_product_face_properties(x) - MfTauDeriv = self.mesh.get_face_inner_product_face_properties_deriv(self.x0) + MfTau = self.mesh.get_face_inner_product_surface(x) + MfTauDeriv = self.mesh.get_face_inner_product_surface_deriv(self.x0) return MfTau * self.face_vec, MfTauDeriv(self.face_vec) print("Testing FaceInnerProduct Isotropic (Face Properties)") @@ -768,8 +768,8 @@ def fun(x): def test_FaceInnerProductIsotropicDerivInvProp(self): def fun(x): - MfTau = self.mesh.get_face_inner_product_face_properties(x, invert_model=True) - MfTauDeriv = self.mesh.get_face_inner_product_face_properties_deriv( + MfTau = self.mesh.get_face_inner_product_surface(x, invert_model=True) + MfTauDeriv = self.mesh.get_face_inner_product_surface_deriv( self.x0, invert_model=True ) return MfTau * self.face_vec, MfTauDeriv(self.face_vec) @@ -781,8 +781,8 @@ def fun(x): def test_FaceInnerProductIsotropicDerivInvMat(self): def fun(x): - MfTau = self.mesh.get_face_inner_product_face_properties(x, invert_matrix=True) - MfTauDeriv = self.mesh.get_face_inner_product_face_properties_deriv( + MfTau = self.mesh.get_face_inner_product_surface(x, invert_matrix=True) + MfTauDeriv = self.mesh.get_face_inner_product_surface_deriv( self.x0, invert_matrix=True ) return MfTau * self.face_vec, MfTauDeriv(self.face_vec) @@ -794,8 +794,8 @@ def fun(x): def test_EdgeInnerProductIsotropicDeriv(self): def fun(x): - MeTau = self.mesh.get_edge_inner_product_face_properties(x) - MeTauDeriv = self.mesh.get_edge_inner_product_face_properties_deriv(self.x0) + MeTau = self.mesh.get_edge_inner_product_surface(x) + MeTauDeriv = self.mesh.get_edge_inner_product_surface_deriv(self.x0) return MeTau * self.edge_vec, MeTauDeriv(self.edge_vec) print("Testing EdgeInnerProduct Isotropic (Face Properties)") @@ -805,8 +805,8 @@ def fun(x): def test_EdgeInnerProductIsotropicDerivInvProp(self): def fun(x): - MeTau = self.mesh.get_edge_inner_product_face_properties(x, invert_model=True) - MeTauDeriv = self.mesh.get_edge_inner_product_face_properties_deriv( + MeTau = self.mesh.get_edge_inner_product_surface(x, invert_model=True) + MeTauDeriv = self.mesh.get_edge_inner_product_surface_deriv( self.x0, invert_model=True ) return MeTau * self.edge_vec, MeTauDeriv(self.edge_vec) @@ -818,8 +818,8 @@ def fun(x): def test_EdgeInnerProductIsotropicDerivInvMat(self): def fun(x): - MeTau = self.mesh.get_edge_inner_product_face_properties(x, invert_matrix=True) - MeTauDeriv = self.mesh.get_edge_inner_product_face_properties_deriv( + MeTau = self.mesh.get_edge_inner_product_surface(x, invert_matrix=True) + MeTauDeriv = self.mesh.get_edge_inner_product_surface_deriv( self.x0, invert_matrix=True ) return MeTau * self.edge_vec, MeTauDeriv(self.edge_vec) diff --git a/tests/tree/test_tree_innerproduct_derivs.py b/tests/tree/test_tree_innerproduct_derivs.py index fcb47d446..6f6fcf9fc 100644 --- a/tests/tree/test_tree_innerproduct_derivs.py +++ b/tests/tree/test_tree_innerproduct_derivs.py @@ -173,10 +173,10 @@ def doTestFace(self, h, rep, meshType, invert_model=False, invert_matrix=False): sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) def fun(sig): - M = mesh.get_face_inner_product_face_properties( + M = mesh.get_face_inner_product_surface( sig, invert_model=invert_model, invert_matrix=invert_matrix ) - Md = mesh.get_face_inner_product_face_properties_deriv( + Md = mesh.get_face_inner_product_surface_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, # , do_fast=fast @@ -206,10 +206,10 @@ def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) def fun(sig): - M = mesh.get_edge_inner_product_face_properties( + M = mesh.get_edge_inner_product_surface( sig, invert_model=invert_model, invert_matrix=invert_matrix ) - Md = mesh.get_edge_inner_product_face_properties_deriv( + Md = mesh.get_edge_inner_product_surface_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, # , do_fast=fast @@ -265,10 +265,10 @@ def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): sig = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nE * rep) def fun(sig): - M = mesh.get_edge_inner_product_edge_properties( + M = mesh.get_edge_inner_product_line( sig, invert_model=invert_model, invert_matrix=invert_matrix ) - Md = mesh.get_edge_inner_product_edge_properties_deriv( + Md = mesh.get_edge_inner_product_line_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, # , do_fast=fast diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index e6e06ca65..754da691d 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -483,55 +483,57 @@ def getError(self): tau_x = lambda x, y, z: y * z + 1 # x-face properties tau_y = lambda x, y, z: x * z + 2 # y-face properties tau_z = lambda x, y, z: 3 + x * y # z-face properties - + tau = 3 * [None] - for ii, comp in enumerate(['x', 'y', 'z']): - k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, y or z location for each plane - tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + for ii, comp in enumerate(["x", "y", "z"]): + k = np.isclose( + eval("self.M.faces_{}".format(comp))[:, ii], 0.5 + ) # x, y or z location for each plane + tau_ii = 1e-8 * eval( + "np.ones(self.M.nF{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.faces_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + # integrate components parallel to the plane of integration if self.location == "edges": - analytic = 5.02760416666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] - + Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) ) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_face_properties(tau) - + A = self.M.get_edge_inner_product_surface(tau) + numeric = E.T.dot(A.dot(E)) - + # integrate component normal to the plane of integration elif self.location == "faces": - analytic = 2.66979166666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] - + Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) ) F = self.M.project_face_vector(Fc) if self.invert_model: - A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_face_inner_product_face_properties(tau) - + A = self.M.get_face_inner_product_surface(tau) + numeric = F.T.dot(A.dot(F)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): @@ -558,6 +560,7 @@ def test_order1_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): """Integrate a function over a line within a unit cube domain using edgeInnerProducts.""" @@ -575,37 +578,38 @@ def getError(self): tau_x = lambda x, y, z: x + 1 # x-face properties tau_y = lambda x, y, z: y + 2 # y-face properties - tau_z = lambda x, y, z: 3*z + 1 # z-face properties - + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties + tau = 3 * [None] - for ii, comp in enumerate(['x', 'y', 'z']): - k = ( - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + for ii, comp in enumerate(["x", "y", "z"]): + k = np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 1], 0.5 + ) & np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 2], 0.5 ) # x, y or z location for each line - tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau_ii = 1e-8 * eval( + "np.ones(self.M.nE{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.edges_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + analytic = 1.98906250000000 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] - - Ec = np.vstack( - (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) - ) + + Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_edge_properties(tau) - + A = self.M.get_edge_inner_product_line(tau) + numeric = E.T.dot(A.dot(E)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): @@ -779,57 +783,55 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2*y + 1 # x-face properties - tau_y = lambda x, y: x + 2 # y-face properties - + tau_x = lambda x, y: 2 * y + 1 # x-face properties + tau_y = lambda x, y: x + 2 # y-face properties + tau = 2 * [None] - for ii, comp in enumerate(['x', 'y']): - k = np.isclose(eval('self.M.faces_{}'.format(comp))[:, ii], 0.5) # x, or y location for each plane - tau_ii = 1e-8*eval('np.ones(self.M.nF{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.faces_{}[k, :])'.format(comp, comp)) + for ii, comp in enumerate(["x", "y"]): + k = np.isclose( + eval("self.M.faces_{}".format(comp))[:, ii], 0.5 + ) # x, or y location for each plane + tau_ii = 1e-8 * eval( + "np.ones(self.M.nF{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.faces_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + # integrate components parallel to the plane of integration if self.location == "edges": - analytic = 2.24166666666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g)] - - Ec = np.vstack( - (cart(self.M.gridEx), cart(self.M.gridEy)) - ) + + Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_face_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_face_properties(tau) - + A = self.M.get_edge_inner_product_surface(tau) + numeric = E.T.dot(A.dot(E)) - + # integrate component normal to the plane of integration elif self.location == "faces": - analytic = 1.59895833333333 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g)] - - Fc = np.vstack( - (cart(self.M.gridFx), cart(self.M.gridFy)) - ) + + Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) if self.invert_model: - A = self.M.get_face_inner_product_face_properties(1/tau, invert_model=True) + A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) else: - A = self.M.get_face_inner_product_face_properties(tau) - + A = self.M.get_face_inner_product_surface(tau) + numeric = F.T.dot(A.dot(F)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): @@ -856,6 +858,7 @@ def test_order1_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): """Integrate a function over a line within a unit cube domain using edgeInnerProducts.""" @@ -872,36 +875,37 @@ def getError(self): tau_x = lambda x, y: x + 1 # x-face properties tau_y = lambda x, y: y + 2 # y-face properties - + tau = 2 * [None] - for ii, comp in enumerate(['x', 'y']): - k = ( - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-1], 0.5) & - np.isclose(eval('self.M.edges_{}'.format(comp))[:, ii-2], 0.5) + for ii, comp in enumerate(["x", "y"]): + k = np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 1], 0.5 + ) & np.isclose( + eval("self.M.edges_{}".format(comp))[:, ii - 2], 0.5 ) # x, y or z location for each line - tau_ii = 1e-8*eval('np.ones(self.M.nE{})'.format(comp)) # effectively zeros but stable - tau_ii[k] = eval('call(tau_{}, self.M.edges_{}[k, :])'.format(comp, comp)) + tau_ii = 1e-8 * eval( + "np.ones(self.M.nE{})".format(comp) + ) # effectively zeros but stable + tau_ii[k] = eval("call(tau_{}, self.M.edges_{}[k, :])".format(comp, comp)) tau[ii] = tau_ii tau = np.hstack(tau) - + analytic = 1.38229166666667 # Found using sympy. - + cart = lambda g: np.c_[call(ex, g), call(ey, g)] - - Ec = np.vstack( - (cart(self.M.gridEx), cart(self.M.gridEy)) - ) + + Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) - if self.invert_model: - A = self.M.get_edge_inner_product_edge_properties(1/tau, invert_model=True) + if self.invert_model: + A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) else: - A = self.M.get_edge_inner_product_edge_properties(tau) - + A = self.M.get_edge_inner_product_line(tau) + numeric = E.T.dot(A.dot(E)) err = np.abs(numeric - analytic) - + return err def test_order1_edges(self): From 60559fb29df992a397f9399a3e94264f7578d4cb Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 23 Jun 2023 13:04:53 -0700 Subject: [PATCH 13/41] add documentation --- discretize/base/base_mesh.py | 445 ++++++++++++++++++++++++- discretize/base/base_tensor_mesh.py | 37 +- discretize/operators/inner_products.py | 98 +----- 3 files changed, 475 insertions(+), 105 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 0f9aeab0a..174ad2abf 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1741,7 +1741,7 @@ def get_edge_inner_product( invert_model : bool, optional The inverse of *model* is used as the physical property. invert_matrix : bool, optional - Teturns the inverse of the inner product matrix. + Returns the inverse of the inner product matrix. The inverse not implemented for full tensor properties. do_fast : bool, optional Do a faster implementation (if available). @@ -1880,7 +1880,6 @@ def get_edge_inner_product( f"get_edge_inner_product not implemented for {type(self)}" ) - def get_edge_inner_product_surface( self, model=None, @@ -1889,6 +1888,117 @@ def get_edge_inner_product_surface( do_fast=True, **kwargs, ): + r"""Generate the edge inner product surface matrix or its inverse. + + This method generates the inner product surface matrix (or its inverse) + when discrete variables are defined on mesh edges. It is also capable of + constructing the inner product surface matrix when diagnostic + properties (e.g. conductance) are defined on mesh faces. For a comprehensive + description of the inner product surface matrices that can be constructed + with **get_edge_inner_product_surface**, see *Notes*. + + Parameters + ---------- + model : None or numpy.ndarray + Parameters defining the diagnostic properties for every face in the mesh. + Inner product surface matrices can be constructed for the following cases: + + - *None* : returns the basic inner product surface matrix + - *(n_faces)* :class:`numpy.ndarray` : returns inner product surface matrix + for an isotropic model. The array contains a scalar diagnostic property value + for each face. + + invert_model : bool, optional + The inverse of *model* is used as the diagnostic property. + invert_matrix : bool, optional + Returns the inverse of the inner product surface matrix. + The inverse not implemented for full tensor properties. + do_fast : bool, optional + Do a faster implementation (if available). + + Returns + ------- + (n_edges, n_edges) scipy.sparse.csr_matrix + inner product surface matrix + + Notes + ----- + For continuous vector quantities :math:`\vec{u}` and :math:`\vec{w}`, and + scalar physical property distribution :math:`\sigma`, we define the following inner product: + + .. math:: + \langle \vec{u}, \sigma \vec{w} \rangle = \int_\Omega \, \vec{u} \cdot \sigma \vec{v} \, dv + + If the material property is distributed over a set of surfaces :math:`S_i` with thickness + :math:`h`, we can define a diagnostic property value :math:`\tau = \sigma h`. + And the inner-product can be approximated by a set of surface integrals as follows: + + .. math:: + \langle \vec{u}, \sigma \vec{w} \rangle = + \sum_i \int_{S_i} \, \vec{u} \cdot \tau \vec{v} \, da + + Let :math:`\vec{u}` and :math:`\vec{w}` have discrete representations :math:`\mathbf{u}` + and :math:`\mathbf{w}` that live on the edges. Assuming the contribution of vector components + normal to the surface are negligible compared to tangential components, + **get_edge_inner_product_suface** constructs the inner product matrix :math:`\mathbf{M_\tau}` + (or its inverse :math:`\mathbf{M_\tau^{-1}}`) such that: + + .. math:: + \sum_i \int_{S_i} \, \vec{u} \cdot \tau \vec{v} \, da + \approx \sum_i \int_{S_i} \, \vec{u}_\parallel \cdot \tau \vec{v}_\parallel \, da + = \mathbf{u^T \, M_\tau \, w} + + where the diagnostic properties on mesh faces (i.e. the model) are stored within an array of the form: + + .. math:: + \boldsymbol{\tau} = \begin{bmatrix} \boldsymbol{\tau_x} \\ + \boldsymbol{\tau_y} \\ \boldsymbol{\tau_z} \end{bmatrix} + + Examples + -------- + Here we provide an example of edge inner product surface matrix. + For simplicity, we will work on a 2 x 2 x 2 tensor mesh. + As seen below, we begin by constructing and imaging the basic + edge inner product surface matrix. + + >>> from discretize import TensorMesh + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> import matplotlib as mpl + + >>> h = np.ones(2) + >>> mesh = TensorMesh([h, h, h]) + >>> Me = mesh.get_edge_inner_product_surface() + + >>> fig = plt.figure(figsize=(6, 6)) + >>> ax = fig.add_subplot(111) + >>> ax.imshow(Me.todense()) + >>> ax.set_title('Basic Edge Inner Product Surface Matrix', fontsize=18) + >>> plt.show() + + Next, we consider the case where the physical properties + are defined by diagnostic properties on mesh faces. For the isotropic case, + we show the physical property tensor for a single cell. + + Define the diagnostic property values for x, y and z faces. + + >>> tau_x, tau_y, tau_z = 3, 2, 1 + + Here construct and image the edge inner product surface matrix for the isotropic case. + Spy plots are used to demonstrate the sparsity of the inner product surface matrices. + + >>> tau = np.r_[tau_x * mesh.n_faces_x, tau_y * mesh.n_faces_y, tau_z * mesh.n_faces_z] + >>> M = mesh.get_edge_inner_product_surface(tau) + + Then plot the sparse representation, + + >>> fig = plt.figure(figsize=(4, 4)) + >>> ax1 = fig.add_subplot(111) + >>> ax1.spy(M, ms=5) + >>> ax1.set_title("M (isotropic)", fontsize=16) + >>> plt.show() + + """ raise NotImplementedError( f"get_edge_inner_product_surface not implemented for {type(self)}" @@ -1902,6 +2012,116 @@ def get_face_inner_product_surface( do_fast=True, **kwargs, ): + r"""Generate the face inner product matrix or its inverse. + + This method generates the inner product surface matrix (or its inverse) + when discrete variables are defined on mesh faces. It is also capable of + constructing the inner product surface matrix when diagnostic quantitative + properties (e.g. conductance) are defined on mesh faces. For a comprehensive + description of the inner product surface matrices that can be constructed + with **get_face_inner_product_surface**, see *Notes*. + + Parameters + ---------- + model : None or numpy.ndarray + Parameters defining the diagnostic properties for every face in the mesh. + Inner product surface matrices can be constructed for the following cases: + + - *None* : returns the basic inner product surface matrix + - *(n_faces)* :class:`numpy.ndarray` : returns inner product surface matrix + for an isotropic model. The array contains a scalar diagnostic property value + for each face. + + invert_model : bool, optional + The inverse of *model* is used as the diagnostic property. + invert_matrix : bool, optional + Returns the inverse of the inner product surface matrix. + The inverse not implemented for full tensor properties. + do_fast : bool, optional + Do a faster implementation (if available). + + Returns + ------- + (n_faces, n_faces) scipy.sparse.csr_matrix + inner product matrix + + Notes + ----- + For continuous vector quantities :math:`\vec{u}` and :math:`\vec{w}`, and + scalar physical property distribution :math:`\sigma`, we define the following inner product: + + .. math:: + \langle \vec{u}, \sigma \vec{w} \rangle = \int_\Omega \, \vec{u} \cdot \sigma \vec{v} \, dv + + If the material property is distributed over a set of surfaces :math:`S_i` with thickness + :math:`h`, we can define a diagnostic property value :math:`\tau = \sigma h`. + And the inner-product can be approximated by a set of surface integrals as follows: + + .. math:: + \langle \vec{u}, \sigma \vec{w} \rangle = + \sum_i \int_{S_i} \, \vec{u} \cdot \tau \vec{v} \, da + + Let :math:`\vec{u}` and :math:`\vec{w}` have discrete representations :math:`\mathbf{u}` + and :math:`\mathbf{w}` that live on the edges. Assuming the contribution of vector components + tangential to the surface are negligible compared to normal components, + **get_face_inner_product_suface** constructs the inner product matrix :math:`\mathbf{M_\tau}` + (or its inverse :math:`\mathbf{M_\tau^{-1}}`) such that: + + .. math:: + \sum_i \int_{S_i} \, \vec{u} \cdot \tau \vec{v} \, da + \approx \sum_i \int_{S_i} \, \vec{u}_\perp \cdot \tau \vec{v}_\perp \, da + = \mathbf{u^T \, M_\tau \, w} + + where the diagnostic properties on mesh faces (i.e. the model) are stored within an array of the form: + + .. math:: + \boldsymbol{\tau} = \begin{bmatrix} \boldsymbol{\tau_x} \\ + \boldsymbol{\tau_y} \\ \boldsymbol{\tau_z} \end{bmatrix} + + Examples + -------- + Here we provide an example of face inner product surface matrix. + For simplicity, we will work on a 2 x 2 x 2 tensor mesh. + As seen below, we begin by constructing and imaging the basic + face inner product surface matrix. + + >>> from discretize import TensorMesh + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> import matplotlib as mpl + + >>> h = np.ones(2) + >>> mesh = TensorMesh([h, h, h]) + >>> Mf = mesh.get_face_inner_product_surface() + + >>> fig = plt.figure(figsize=(6, 6)) + >>> ax = fig.add_subplot(111) + >>> ax.imshow(Mf.todense()) + >>> ax.set_title('Basic Face Inner Product Surface Matrix', fontsize=18) + >>> plt.show() + + Next, we consider the case where the physical properties + are defined by diagnostic properties on mesh faces. For the isotropic case, + we show the physical property tensor for a single cell. + + Define the diagnostic property values for x, y and z faces. + + >>> tau_x, tau_y, tau_z = 3, 2, 1 + + Here construct and image the face inner product surface matrix for the isotropic case. + Spy plots are used to demonstrate the sparsity of the inner product surface matrices. + + >>> tau = np.r_[tau_x * mesh.n_faces_x, tau_y * mesh.n_faces_y, tau_z * mesh.n_faces_z] + >>> M = mesh.get_face_inner_product_surface(tau) + + Then plot the sparse representation, + + >>> fig = plt.figure(figsize=(4, 4)) + >>> ax1 = fig.add_subplot(111) + >>> ax1.spy(M, ms=5) + >>> ax1.set_title("M (isotropic)", fontsize=16) + >>> plt.show() + """ raise NotImplementedError( f"get_face_inner_product_surface not implemented for {type(self)}" @@ -1915,6 +2135,116 @@ def get_edge_inner_product_line( do_fast=True, **kwargs, ): + r"""Generate the edge inner product line matrix or its inverse. + + This method generates the inner product line matrix (or its inverse) + when discrete variables are defined on mesh edges. It is also capable of + constructing the inner product line matrix when diagnostic + properties (e.g. integrated conductance) are defined on mesh edges. + For a comprehensive description of the inner product line matrices that + can be constructed with **get_edge_inner_product_line**, see *Notes*. + + Parameters + ---------- + model : None or numpy.ndarray + Parameters defining the diagnostic property for every edge in the mesh. + Inner product line matrices can be constructed for the following cases: + + - *None* : returns the basic inner product line matrix + - *(n_edges)* :class:`numpy.ndarray` : returns inner product line matrix + for an isotropic model. The array contains a scalar diagnostic property value + for each edge. + + invert_model : bool, optional + The inverse of *model* is used as the diagnostic property. + invert_matrix : bool, optional + Returns the inverse of the inner product line matrix. + The inverse not implemented for full tensor properties. + do_fast : bool, optional + Do a faster implementation (if available). + + Returns + ------- + (n_edges, n_edges) scipy.sparse.csr_matrix + inner product line matriz + + Notes + ----- + For continuous vector quantities :math:`\vec{u}` and :math:`\vec{w}`, and + scalar physical property distribution :math:`\sigma`, we define the following inner product: + + .. math:: + \langle \vec{u}, \sigma \vec{w} \rangle = \int_\Omega \, \vec{u} \cdot \sigma \vec{v} \, dv + + If the material property is distributed over a set of lines :math:`\ell_i` with cross-sectional + area :math:`a`, we can define a diagnostic property value :math:`\lambda = \sigma a`. + And the inner-product can be approximated by a set of line integrals as follows: + + .. math:: + \langle \vec{u}, \sigma \vec{w} \rangle = + \sum_i \int_{\ell_i} \, \vec{u} \cdot \lambda \vec{v} \, ds + + Let :math:`\vec{u}` and :math:`\vec{w}` have discrete representations :math:`\mathbf{u}` + and :math:`\mathbf{w}` that live on the edges. Assuming the contribution of vector components + perpendicular to the lines are negligible compared to parallel components, + **get_edge_inner_product_line** constructs the inner product matrix :math:`\mathbf{M_\lambda}` + (or its inverse :math:`\mathbf{M_\lambda^{-1}}`) such that: + + .. math:: + \sum_i \int_{\ell_i} \, \vec{u} \cdot \lambda \vec{v} \, ds + \approx \sum_i \int_{\ell_i} \, \vec{u}_\parallel \cdot \lambda \vec{v}_\parallel \, ds + = \mathbf{u^T \, M_\lambda \, w} + + where the diagnostic properties on mesh edges (i.e. the model) are stored within an array of the form: + + .. math:: + \boldsymbol{\lambda} = \begin{bmatrix} \boldsymbol{\lambda_x} \\ + \boldsymbol{\lambda_y} \\ \boldsymbol{\lambda_z} \end{bmatrix} + + Examples + -------- + Here we provide an example of edge inner product line matrix. + For simplicity, we will work on a 2 x 2 x 2 tensor mesh. + As seen below, we begin by constructing and imaging the basic + edge inner product line matrix. + + >>> from discretize import TensorMesh + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> import matplotlib as mpl + + >>> h = np.ones(2) + >>> mesh = TensorMesh([h, h, h]) + >>> Me = mesh.get_edge_inner_product_line() + + >>> fig = plt.figure(figsize=(6, 6)) + >>> ax = fig.add_subplot(111) + >>> ax.imshow(Me.todense()) + >>> ax.set_title('Basic Edge Inner Product Line Matrix', fontsize=18) + >>> plt.show() + + Next, we consider the case where the physical properties + are defined by diagnostic properties on mesh edges. For the isotropic case, + we show the physical property tensor for a single cell. + + Define the diagnostic property values for x, y and z faces. + + >>> tau_x, tau_y, tau_z = 3, 2, 1 + + Here construct and image the edge inner product line matrix for the isotropic case. + Spy plots are used to demonstrate the sparsity of the matrix. + + >>> tau = np.r_[tau_x * mesh.n_edges_x, tau_y * mesh.n_edges_y, tau_z * mesh.n_edges_z] + >>> M = mesh.get_edge_inner_product_line(tau) + + Then plot the sparse representation, + + >>> fig = plt.figure(figsize=(4, 4)) + >>> ax1 = fig.add_subplot(111) + >>> ax1.spy(M, ms=5) + >>> ax1.set_title("M (isotropic)", fontsize=16) + >>> plt.show() + """ raise NotImplementedError( f"get_edge_inner_product_line not implemented for {type(self)}" @@ -2294,6 +2624,43 @@ def get_edge_inner_product_surface_deriv( do_fast=True, **kwargs, ): + r"""Get a function handle to multiply a vector with derivative of edge inner product surface matrix (or its inverse). + + Let :math:`\mathbf{M}(\mathbf{m})` be the edge inner product surface matrix + constructed with a set of diagnostic property parameters :math:`\mathbf{m}` + (or its inverse) defined on mesh faces. **get_edge_inner_product_surface_deriv** + constructs a function handle + + .. math:: + \mathbf{F}(\mathbf{u}) = \mathbf{u}^T \, \frac{\partial \mathbf{M}(\mathbf{m})}{\partial \mathbf{m}} + + which accepts any numpy.array :math:`\mathbf{u}` of shape (n_edges,). That is, + **get_edge_inner_product_surface_deriv** constructs a function handle for computing + the dot product between a vector :math:`\mathbf{u}` and the derivative of the + edge inner product surface matrix (or its inverse) with respect to the property parameters. + When computed, :math:`\mathbf{F}(\mathbf{u})` returns a ``scipy.sparse.csr_matrix`` + of shape (n_edges, n_param). + + The function handle can only be created for isotropic diagnostic properties. + + Parameters + ---------- + model : (n_faces, ) numpy.ndarray + Parameters defining the diagnostic property values for every face in the mesh. + invert_model : bool, optional + The inverse of *model* is used as the diagnostic property. + invert_matrix : bool, optional + Returns the inverse of the inner product surface matrix. + do_fast : bool, optional + Do a faster implementation (if available). + + Returns + ------- + function + The function handle :math:`\mathbf{F}(\mathbf{u})` which accepts a + (``n_edges``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function + returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. + """ raise NotImplementedError( f"get_edge_inner_product_surface_deriv not implemented for {type(self)}" @@ -2307,6 +2674,43 @@ def get_face_inner_product_surface_deriv( do_fast=True, **kwargs, ): + r"""Get a function handle to multiply a vector with derivative of face inner product surface matrix (or its inverse). + + Let :math:`\mathbf{M}(\mathbf{m})` be the face inner product surface matrix + constructed with a set of diagnostic property parameters :math:`\mathbf{m}` + (or its inverse) defined on mesh faces. **get_face_inner_product_surface_deriv** + constructs a function handle + + .. math:: + \mathbf{F}(\mathbf{u}) = \mathbf{u}^T \, \frac{\partial \mathbf{M}(\mathbf{m})}{\partial \mathbf{m}} + + which accepts any numpy.array :math:`\mathbf{u}` of shape (n_faces,). That is, + **get_face_inner_product_surface_deriv** constructs a function handle for computing + the dot product between a vector :math:`\mathbf{u}` and the derivative of the + face inner product surface matrix (or its inverse) with respect to the property parameters. + When computed, :math:`\mathbf{F}(\mathbf{u})` returns a ``scipy.sparse.csr_matrix`` + of shape (n_faces, n_param). + + The function handle can only be created for isotropic diagnostic properties. + + Parameters + ---------- + model : (n_faces, ) numpy.ndarray + Parameters defining the diagnostic property values for every face in the mesh. + invert_model : bool, optional + The inverse of *model* is used as the diagnostic property. + invert_matrix : bool, optional + Returns the inverse of the inner product surface matrix. + do_fast : bool, optional + Do a faster implementation (if available). + + Returns + ------- + function + The function handle :math:`\mathbf{F}(\mathbf{u})` which accepts a + (``n_faces``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function + returns a (``n_faces``, ``n_params``) :class:`scipy.sparse.csr_matrix`. + """ raise NotImplementedError( f"get_face_inner_product_surface_deriv not implemented for {type(self)}" @@ -2320,6 +2724,43 @@ def get_edge_inner_product_line_deriv( do_fast=True, **kwargs, ): + r"""Get a function handle to multiply a vector with derivative of edge inner product line matrix (or its inverse). + + Let :math:`\mathbf{M}(\mathbf{m})` be the edge inner product line matrix + constructed with a set of diagnostic property parameters :math:`\mathbf{m}` + (or its inverse) defined on mesh edges. **get_edge_inner_product_line_deriv** + constructs a function handle + + .. math:: + \mathbf{F}(\mathbf{u}) = \mathbf{u}^T \, \frac{\partial \mathbf{M}(\mathbf{m})}{\partial \mathbf{m}} + + which accepts any numpy.array :math:`\mathbf{u}` of shape (n_edges,). That is, + **get_edge_inner_product_line_deriv** constructs a function handle for computing + the dot product between a vector :math:`\mathbf{u}` and the derivative of the + edge inner product line matrix (or its inverse) with respect to the diagnostic parameters. + When computed, :math:`\mathbf{F}(\mathbf{u})` returns a ``scipy.sparse.csr_matrix`` + of shape (n_edges, n_param). + + The function handle can only be created for isotropic diagnostic properties. + + Parameters + ---------- + model : (n_edges, ) numpy.ndarray + Parameters defining the diagnostic property values for every edge in the mesh. + invert_model : bool, optional + The inverse of *model* is used as the diagnostic property. + invert_matrix : bool, optional + Returns the inverse of the inner product line matrix. + do_fast : bool, optional + Do a faster implementation (if available). + + Returns + ------- + function + The function handle :math:`\mathbf{F}(\mathbf{u})` which accepts a + (``n_edges``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function + returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. + """ raise NotImplementedError( f"get_edge_inner_product_line_deriv not implemented for {type(self)}" diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index ca3a155e3..44e3d9e16 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -990,7 +990,6 @@ def innerProductDeriv(v=None): else: return None - def _fastInnerProductSurface( self, projection_type, model=None, invert_model=False, invert_matrix=False ): @@ -1048,7 +1047,7 @@ def _fastInnerProductSurface( # Isotropic case only if model.size == self.nF: Aprop = self.face_areas * mkvc(model) - if projection_type == 'E': + if projection_type == "E": Av = getattr(self, "average_edge_to_face") M = n_elements * sdiag(Av.T * Aprop) else: @@ -1057,7 +1056,7 @@ def _fastInnerProductSurface( else: raise Exception( "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces." + "Must be scalar or have length equal to total number of faces.", ) if invert_matrix: @@ -1097,9 +1096,11 @@ def _fastInnerProductSurfaceDeriv( else: raise Exception( "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.".format(self.nF) + "Must be scalar or have length equal to total number of faces.".format( + self.nF + ), ) - + dMdprop = None if invert_matrix or invert_model: @@ -1123,7 +1124,7 @@ def _fastInnerProductSurfaceDeriv( n_elements = self.dim - 1 A = sdiag(self.face_areas) - if projection_type == 'E': + if projection_type == "E": Av = getattr(self, "average_edge_to_face") else: Av = sdiag(np.ones(self.nF) / n_elements) @@ -1209,7 +1210,6 @@ def _fastInnerProductLine( if is_scalar(model): model = model * np.ones(self.nE) - # number of elements we are averaging (equals dim for regular # meshes, but for cyl, where we use symmetry, it is 1 for edge # variables and 2 for face variables) @@ -1227,7 +1227,7 @@ def _fastInnerProductLine( else: raise Exception( "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of edges." + "Must be scalar or have length equal to total number of edges.", ) if invert_matrix: @@ -1261,7 +1261,9 @@ def _fastInnerProductLineDeriv( else: raise Exception( "Unexpected shape of tensor: {}.".format(model.shape), - "Must be scalar or have length equal to total number of edges: {}.".format(self.nE) + "Must be scalar or have length equal to total number of edges: {}.".format( + self.nE + ), ) dMdprop = None @@ -1282,31 +1284,23 @@ def _fastInnerProductLineDeriv( if not invert_matrix and not invert_model: dMdprop = L * ones elif invert_matrix and invert_model: - dMdprop = ( - sdiag(MI.diagonal() ** 2) - * L - * ones - * sdiag(1.0 / model**2) - ) + dMdprop = sdiag(MI.diagonal() ** 2) * L * ones * sdiag(1.0 / model**2) elif invert_model: dMdprop = L * sdiag(-1.0 / model**2) elif invert_matrix: - dMdprop = (sdiag(-MI.diagonal() ** 2) * L) + dMdprop = sdiag(-MI.diagonal() ** 2) * L elif tensorType == 1: # isotropic, variable in space if not invert_matrix and not invert_model: dMdprop = L elif invert_matrix and invert_model: - dMdprop = ( - sdiag(MI.diagonal() ** 2) * L * sdiag(1.0 / model**2) - ) + dMdprop = sdiag(MI.diagonal() ** 2) * L * sdiag(1.0 / model**2) elif invert_model: dMdprop = L * sdiag(-1.0 / model**2) elif invert_matrix: - dMdprop = (sdiag(-MI.diagonal() ** 2) * L) + dMdprop = sdiag(-MI.diagonal() ** 2) * L elif tensorType == 2: # anisotropic - raise NotImplementedError( "EdgePropertiesInnerProductDeriv not implemented for anisotropy." ) @@ -1329,7 +1323,6 @@ def innerProductDeriv(v=None): else: return None - # DEPRECATED @property def hx(self): diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 7cf267562..55a3bef26 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -96,37 +96,14 @@ def get_edge_inner_product( # NOQA D102 ) def get_edge_inner_product_surface( - self, - model, - invert_model=False, - invert_matrix=False, - do_fast=True, - **kwargs + self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs ): - - """Get edge inner product matrix for properties defined on cell faces. - - Parameters - ---------- - numpy.ndarray : model - material property (isotropic only) for all mesh faces (nF, ) - bool : invert_model - inverts the material property - bool : invert_matrix - inverts the matrix - bool : do_fast - do a faster implementation if available. - - Returns - ------- - scipy.sparse.csr_matrix - M, the mass matrix. (nE, nE) - """ + # Inherited documentation from discretize.base.BaseMesh fast = None if hasattr(self, "_fastFacePropertiesInnerProduct") and do_fast: fast = self._fastFacePropertiesInnerProduct( - projection_type='E', + projection_type="E", model=model, invert_model=invert_model, invert_matrix=invert_matrix, @@ -134,40 +111,19 @@ def get_edge_inner_product_surface( if fast is not None: return fast - raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") + raise NotImplementedError( + "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" + ) def get_face_inner_product_surface( - self, - model, - invert_model=False, - invert_matrix=False, - do_fast=True, - **kwargs + self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs ): - - """Get face inner product matrix for properties defined on cell faces. - - Parameters - ---------- - numpy.ndarray : model - material property (isotropic only) for all mesh faces (nF, ) - bool : invert_model - inverts the material property - bool : invert_matrix - inverts the matrix - bool : do_fast - do a faster implementation if available. - - Returns - ------- - scipy.sparse.csr_matrix - M, the mass matrix. (nF, nF) - """ + # Inherited documentation from discretize.base.BaseMesh fast = None if hasattr(self, "_fastFacePropertiesInnerProduct") and do_fast: fast = self._fastFacePropertiesInnerProduct( - projection_type='F', + projection_type="F", model=model, invert_model=invert_model, invert_matrix=invert_matrix, @@ -175,35 +131,14 @@ def get_face_inner_product_surface( if fast is not None: return fast - raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") + raise NotImplementedError( + "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" + ) def get_edge_inner_product_line( - self, - model, - invert_model=False, - invert_matrix=False, - do_fast=True, - **kwargs + self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs ): - - """Get edge inner product matrix for properties defined on cell edges. - - Parameters - ---------- - numpy.ndarray : model - material property (isotropic only) for all mesh edges (nE, ) - bool : invert_model - inverts the material property - bool : invert_matrix - inverts the matrix - bool : do_fast - do a faster implementation if available. - - Returns - ------- - scipy.sparse.csr_matrix - M, the mass matrix. (nE, nE) - """ + # Inherited documentation from discretize.base.BaseMesh fast = None if hasattr(self, "_fastEdgePropertiesInnerProduct") and do_fast: @@ -215,8 +150,9 @@ def get_edge_inner_product_line( if fast is not None: return fast - raise NotImplementedError("General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible") - + raise NotImplementedError( + "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" + ) def _getInnerProduct( self, From 1129e0bb8cda301e314d7d7cd825a4160b708937 Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 23 Jun 2023 23:14:15 -0700 Subject: [PATCH 14/41] review and bug fixes --- discretize/base/base_mesh.py | 8 -------- discretize/base/base_tensor_mesh.py | 8 +++----- discretize/operators/inner_products.py | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 174ad2abf..df905a638 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1997,9 +1997,7 @@ def get_edge_inner_product_surface( >>> ax1.spy(M, ms=5) >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() - """ - raise NotImplementedError( f"get_edge_inner_product_surface not implemented for {type(self)}" ) @@ -2122,7 +2120,6 @@ def get_face_inner_product_surface( >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ - raise NotImplementedError( f"get_face_inner_product_surface not implemented for {type(self)}" ) @@ -2245,7 +2242,6 @@ def get_edge_inner_product_line( >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ - raise NotImplementedError( f"get_edge_inner_product_line not implemented for {type(self)}" ) @@ -2428,7 +2424,6 @@ def get_face_inner_product_deriv( >>> ax2.set_xlabel("Parameter Index", fontsize=12) >>> ax2.set_ylabel("Face Index", fontsize=12) >>> plt.show() - """ raise NotImplementedError( f"get_face_inner_product_deriv not implemented for {type(self)}" @@ -2661,7 +2656,6 @@ def get_edge_inner_product_surface_deriv( (``n_edges``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - raise NotImplementedError( f"get_edge_inner_product_surface_deriv not implemented for {type(self)}" ) @@ -2711,7 +2705,6 @@ def get_face_inner_product_surface_deriv( (``n_faces``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_faces``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - raise NotImplementedError( f"get_face_inner_product_surface_deriv not implemented for {type(self)}" ) @@ -2761,7 +2754,6 @@ def get_edge_inner_product_line_deriv( (``n_edges``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - raise NotImplementedError( f"get_edge_inner_product_line_deriv not implemented for {type(self)}" ) diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index 44e3d9e16..1a2f943d7 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -1048,7 +1048,7 @@ def _fastInnerProductSurface( if model.size == self.nF: Aprop = self.face_areas * mkvc(model) if projection_type == "E": - Av = getattr(self, "average_edge_to_face") + Av = self.average_edge_to_face M = n_elements * sdiag(Av.T * Aprop) else: M = sdiag(Aprop) @@ -1096,9 +1096,7 @@ def _fastInnerProductSurfaceDeriv( else: raise Exception( "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.".format( - self.nF - ), + "Must be scalar or have length equal to total number of faces." ) dMdprop = None @@ -1125,7 +1123,7 @@ def _fastInnerProductSurfaceDeriv( A = sdiag(self.face_areas) if projection_type == "E": - Av = getattr(self, "average_edge_to_face") + Av = self.average_edge_to_face else: Av = sdiag(np.ones(self.nF) / n_elements) diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 55a3bef26..3a8958c5d 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -95,14 +95,14 @@ def get_edge_inner_product( # NOQA D102 do_fast=do_fast, ) - def get_edge_inner_product_surface( + def get_edge_inner_product_surface( # NOQA D102 self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs ): # Inherited documentation from discretize.base.BaseMesh fast = None - if hasattr(self, "_fastFacePropertiesInnerProduct") and do_fast: - fast = self._fastFacePropertiesInnerProduct( + if hasattr(self, "_fastInnerProductSurface") and do_fast: + fast = self._fastInnerProductSurface( projection_type="E", model=model, invert_model=invert_model, @@ -115,14 +115,14 @@ def get_edge_inner_product_surface( "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" ) - def get_face_inner_product_surface( + def get_face_inner_product_surface( # NOQA D102 self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs ): # Inherited documentation from discretize.base.BaseMesh fast = None - if hasattr(self, "_fastFacePropertiesInnerProduct") and do_fast: - fast = self._fastFacePropertiesInnerProduct( + if hasattr(self, "_fastInnerProductSurface") and do_fast: + fast = self._fastInnerProductSurface( projection_type="F", model=model, invert_model=invert_model, @@ -135,14 +135,14 @@ def get_face_inner_product_surface( "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" ) - def get_edge_inner_product_line( + def get_edge_inner_product_line( # NOQA D102 self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs ): # Inherited documentation from discretize.base.BaseMesh fast = None - if hasattr(self, "_fastEdgePropertiesInnerProduct") and do_fast: - fast = self._fastEdgePropertiesInnerProduct( + if hasattr(self, "_fastInnerProductLine") and do_fast: + fast = self._fastInnerProductLine( model=model, invert_model=invert_model, invert_matrix=invert_matrix, @@ -349,7 +349,7 @@ def get_face_inner_product_surface_deriv( # NOQA D102 "The invMat keyword argument has been removed, please use invert_matrix. " "This will be removed in discretize 1.0.0", ) - return self._fastFacePropertiesInnerProductDeriv( + return self._fastInnerProductSurfaceDeriv( "F", model, invert_model=invert_model, @@ -370,7 +370,7 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 "The invMat keyword argument has been removed, please use invert_matrix. " "This will be removed in discretize 1.0.0", ) - return self._fastFacePropertiesInnerProductDeriv( + return self._fastInnerProductSurfaceDeriv( "E", model, invert_model=invert_model, @@ -391,7 +391,7 @@ def get_edge_inner_product_line_deriv( # NOQA D102 "The invMat keyword argument has been removed, please use invert_matrix. " "This will be removed in discretize 1.0.0", ) - return self._fastEdgePropertiesInnerProductDeriv( + return self._fastInnerProductLineDeriv( model, invert_model=invert_model, invert_matrix=invert_matrix, From f894add963b93fc24cd5198a54fa093a00adcf31 Mon Sep 17 00:00:00 2001 From: dccowan Date: Thu, 29 Jun 2023 10:33:56 -0700 Subject: [PATCH 15/41] add ignore lambda fun for flake8 in tests --- tests/base/test_tensor_innerproduct.py | 106 ++++++------ tests/tree/test_tree_operators.py | 224 ++++++++++++------------- 2 files changed, 165 insertions(+), 165 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index e026b7ba9..9e37a52cc 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -12,18 +12,18 @@ class TestInnerProducts(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 - ex = lambda x, y, z: x**2 + y * z - ey = lambda x, y, z: (z**2) * x + y * z - ez = lambda x, y, z: y**2 + x * z + ex = lambda x, y, z: x**2 + y * z # NOQA E731 + ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 + ez = lambda x, y, z: y**2 + x * z # NOQA E731 - sigma1 = lambda x, y, z: x * y + 1 - sigma2 = lambda x, y, z: x * z + 2 - sigma3 = lambda x, y, z: 3 + z * y - sigma4 = lambda x, y, z: 0.1 * x * y * z - sigma5 = lambda x, y, z: 0.2 * x * y - sigma6 = lambda x, y, z: 0.1 * z + sigma1 = lambda x, y, z: x * y + 1 # NOQA E731 + sigma2 = lambda x, y, z: x * z + 2 # NOQA E731 + sigma3 = lambda x, y, z: 3 + z * y # NOQA E731 + sigma4 = lambda x, y, z: 0.1 * x * y * z # NOQA E731 + sigma5 = lambda x, y, z: 0.2 * x * y # NOQA E731 + sigma6 = lambda x, y, z: 0.1 * z # NOQA E731 Gc = self.M.gridCC if self.sigmaTest == 1: @@ -44,7 +44,7 @@ def getError(self): analytic = 69881.0 / 21600 # Found using sympy. if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) ) @@ -59,7 +59,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) ) @@ -171,15 +171,15 @@ class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 - ex = lambda x, y, z: x**2 + y * z - ey = lambda x, y, z: (z**2) * x + y * z - ez = lambda x, y, z: y**2 + x * z + ex = lambda x, y, z: x**2 + y * z # NOQA E731 + ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 + ez = lambda x, y, z: y**2 + x * z # NOQA E731 - tau_x = lambda x, y, z: y * z + 1 # x-face properties - tau_y = lambda x, y, z: x * z + 2 # y-face properties - tau_z = lambda x, y, z: 3 + x * y # z-face properties + tau_x = lambda x, y, z: y * z + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y, z: x * z + 2 # NOQA E731 # y-face properties + tau_z = lambda x, y, z: 3 + x * y # NOQA E731 # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -197,7 +197,7 @@ def getError(self): if self.location == "edges": analytic = 5.02760416666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) @@ -215,7 +215,7 @@ def getError(self): elif self.location == "faces": analytic = 2.66979166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) @@ -267,15 +267,15 @@ class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 - ex = lambda x, y, z: x**2 + y * z - ey = lambda x, y, z: (z**2) * x + y * z - ez = lambda x, y, z: y**2 + x * z + ex = lambda x, y, z: x**2 + y * z # NOQA E731 + ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 + ez = lambda x, y, z: y**2 + x * z # NOQA E731 - tau_x = lambda x, y, z: x + 1 # x-face properties - tau_y = lambda x, y, z: y + 2 # y-face properties - tau_z = lambda x, y, z: 3 * z + 1 # z-face properties + tau_x = lambda x, y, z: x + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y, z: y + 2 # NOQA E731 # y-face properties + tau_z = lambda x, y, z: 3 * z + 1 # NOQA E731 # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -293,7 +293,7 @@ def getError(self): analytic = 1.98906250000000 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) @@ -333,14 +333,14 @@ class TestInnerProducts2D(discretize.tests.OrderTest): def getError(self): z = 5 # Because 5 is just such a great number. - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 - ex = lambda x, y: x**2 + y * z - ey = lambda x, y: (z**2) * x + y * z + ex = lambda x, y: x**2 + y * z # NOQA E731 + ey = lambda x, y: (z**2) * x + y * z # NOQA E731 - sigma1 = lambda x, y: x * y + 1 - sigma2 = lambda x, y: x * z + 2 - sigma3 = lambda x, y: 3 + z * y + sigma1 = lambda x, y: x * y + 1 # NOQA E731 + sigma2 = lambda x, y: x * z + 2 # NOQA E731 + sigma3 = lambda x, y: 3 + z * y # NOQA E731 Gc = self.M.gridCC if self.sigmaTest == 1: @@ -354,7 +354,7 @@ def getError(self): analytic = 781427.0 / 360 # Found using sympy. z=5 if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) if self.invert_model: @@ -366,7 +366,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -476,13 +476,13 @@ class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 - ex = lambda x, y: x**2 + y - ey = lambda x, y: (y**2) * x + ex = lambda x, y: x**2 + y # NOQA E731 + ey = lambda x, y: (y**2) * x # NOQA E731 - tau_x = lambda x, y: 2 * y + 1 # x-face properties - tau_y = lambda x, y: x + 2 # y-face properties + tau_x = lambda x, y: 2 * y + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y: x + 2 # NOQA E731 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -500,7 +500,7 @@ def getError(self): if self.location == "edges": analytic = 2.24166666666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -516,7 +516,7 @@ def getError(self): elif self.location == "faces": analytic = 1.59895833333333 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -566,13 +566,13 @@ class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 - ex = lambda x, y: x**2 + y - ey = lambda x, y: (x**2) * y + ex = lambda x, y: x**2 + y # NOQA E731 + ey = lambda x, y: (x**2) * y # NOQA E731 - tau_x = lambda x, y: x + 1 # x-face properties - tau_y = lambda x, y: y + 2 # y-face properties + tau_x = lambda x, y: x + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y: y + 2 # NOQA E731 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -590,7 +590,7 @@ def getError(self): analytic = 1.38229166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -631,11 +631,11 @@ def getError(self): y = 12 # Because 12 is just such a great number. z = 5 # Because 5 is just such a great number as well! - call = lambda fun, x: fun(x) + call = lambda fun, x: fun(x) # NOQA E731 - ex = lambda x: x**2 + y * z + ex = lambda x: x**2 + y * z # NOQA E731 - sigma1 = lambda x: x * y + 1 + sigma1 = lambda x: x * y + 1 # NOQA E731 Gc = self.M.gridCC sigma = call(sigma1, Gc) diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 754da691d..72b780bfd 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -4,26 +4,26 @@ MESHTYPES = ["uniformTree", "randomTree"] # MESHTYPES = ['randomTree'] -call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) -call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) -cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] -cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ +call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) # NOQA E731 +call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 +cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] # NOQA E731 +cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ # NOQA E731 call3(xfun, g), call3(yfun, g), call3(zfun, g) ] -cartF2 = lambda M, fx, fy: np.vstack( +cartF2 = lambda M, fx, fy: np.vstack( # NOQA E731 (cart_row2(M.gridFx, fx, fy), cart_row2(M.gridFy, fx, fy)) ) -cartE2 = lambda M, ex, ey: np.vstack( +cartE2 = lambda M, ex, ey: np.vstack( # NOQA E731 (cart_row2(M.gridEx, ex, ey), cart_row2(M.gridEy, ex, ey)) ) -cartF3 = lambda M, fx, fy, fz: np.vstack( +cartF3 = lambda M, fx, fy, fz: np.vstack( # NOQA E731 ( cart_row3(M.gridFx, fx, fy, fz), cart_row3(M.gridFy, fx, fy, fz), cart_row3(M.gridFz, fx, fy, fz), ) ) -cartE3 = lambda M, ex, ey, ez: np.vstack( +cartE3 = lambda M, ex, ey, ez: np.vstack( # NOQA E731 ( cart_row3(M.gridEx, ex, ey, ez), cart_row3(M.gridEy, ex, ey, ez), @@ -45,9 +45,9 @@ class TestCellGrad2D(discretize.tests.OrderTest): def getError(self): # Test function - sol = lambda x, y: np.cos(2 * np.pi * x) * np.cos(2 * np.pi * y) - fx = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) - fy = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) + sol = lambda x, y: np.cos(2 * np.pi * x) * np.cos(2 * np.pi * y) # NOQA E731 + fx = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) # NOQA E731 + fy = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) # NOQA E731 phi = call2(sol, self.M.gridCC) gradF = self.M.cell_gradient * phi @@ -121,9 +121,9 @@ class TestFaceDivxy2D(discretize.tests.OrderTest): def getError(self): # Test function - fx = lambda x, y: np.sin(2 * np.pi * x) - fy = lambda x, y: np.sin(2 * np.pi * y) - sol = lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) + fx = lambda x, y: np.sin(2 * np.pi * x) # NOQA E731 + fy = lambda x, y: np.sin(2 * np.pi * y) # NOQA E731 + sol = lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) # NOQA E731 Fx = call2(fx, self.M.gridFx) Fy = call2(fy, self.M.gridFy) @@ -150,10 +150,10 @@ class TestFaceDiv3D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] def getError(self): - fx = lambda x, y, z: np.sin(2 * np.pi * x) - fy = lambda x, y, z: np.sin(2 * np.pi * y) - fz = lambda x, y, z: np.sin(2 * np.pi * z) - sol = lambda x, y, z: ( + fx = lambda x, y, z: np.sin(2 * np.pi * x) # NOQA E731 + fy = lambda x, y, z: np.sin(2 * np.pi * y) # NOQA E731 + fz = lambda x, y, z: np.sin(2 * np.pi * z) # NOQA E731 + sol = lambda x, y, z: ( # NOQA E731 2 * np.pi * np.cos(2 * np.pi * x) + 2 * np.pi * np.cos(2 * np.pi * y) + 2 * np.pi * np.cos(2 * np.pi * z) @@ -180,10 +180,10 @@ class TestFaceDivxyz3D(discretize.tests.OrderTest): def getError(self): # Test function - fx = lambda x, y, z: np.sin(2 * np.pi * x) - fy = lambda x, y, z: np.sin(2 * np.pi * y) - fz = lambda x, y, z: np.sin(2 * np.pi * z) - sol = lambda x, y, z: ( + fx = lambda x, y, z: np.sin(2 * np.pi * x) # NOQA E731 + fy = lambda x, y, z: np.sin(2 * np.pi * y) # NOQA E731 + fz = lambda x, y, z: np.sin(2 * np.pi * z) # NOQA E731 + sol = lambda x, y, z: ( # NOQA E731 2 * np.pi * np.cos(2 * np.pi * x) + 2 * np.pi * np.cos(2 * np.pi * y) + 2 * np.pi * np.cos(2 * np.pi * z) @@ -220,13 +220,13 @@ def getError(self): # fun: i (cos(y)) + j (cos(z)) + k (cos(x)) # sol: i (sin(z)) + j (sin(x)) + k (sin(y)) - funX = lambda x, y, z: np.cos(2 * np.pi * y) - funY = lambda x, y, z: np.cos(2 * np.pi * z) - funZ = lambda x, y, z: np.cos(2 * np.pi * x) + funX = lambda x, y, z: np.cos(2 * np.pi * y) # NOQA E731 + funY = lambda x, y, z: np.cos(2 * np.pi * z) # NOQA E731 + funZ = lambda x, y, z: np.cos(2 * np.pi * x) # NOQA E731 - solX = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * z) - solY = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * x) - solZ = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * y) + solX = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * z) # NOQA E731 + solY = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * x) # NOQA E731 + solZ = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * y) # NOQA E731 Ec = cartE3(self.M, funX, funY, funZ) E = self.M.project_edge_vector(Ec) @@ -254,11 +254,11 @@ class TestNodalGrad(discretize.tests.OrderTest): def getError(self): # Test function - fun = lambda x, y, z: (np.cos(x) + np.cos(y) + np.cos(z)) + fun = lambda x, y, z: (np.cos(x) + np.cos(y) + np.cos(z)) # NOQA E731 # i (sin(x)) + j (sin(y)) + k (sin(z)) - solX = lambda x, y, z: -np.sin(x) - solY = lambda x, y, z: -np.sin(y) - solZ = lambda x, y, z: -np.sin(z) + solX = lambda x, y, z: -np.sin(x) # NOQA E731 + solY = lambda x, y, z: -np.sin(y) # NOQA E731 + solZ = lambda x, y, z: -np.sin(z) # NOQA E731 phi = call3(fun, self.M.gridN) gradE = self.M.nodal_gradient.dot(phi) @@ -284,10 +284,10 @@ class TestNodalGrad2D(discretize.tests.OrderTest): def getError(self): # Test function - fun = lambda x, y: (np.cos(x) + np.cos(y)) + fun = lambda x, y: (np.cos(x) + np.cos(y)) # NOQA E731 # i (sin(x)) + j (sin(y)) + k (sin(z)) - solX = lambda x, y: -np.sin(x) - solY = lambda x, y: -np.sin(y) + solX = lambda x, y: -np.sin(x) # NOQA E731 + solY = lambda x, y: -np.sin(y) # NOQA E731 phi = call2(fun, self.M.gridN) gradE = self.M.nodal_gradient.dot(phi) @@ -315,18 +315,18 @@ class TestTreeInnerProducts(discretize.tests.OrderTest): meshSizes = [4, 8] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 - ex = lambda x, y, z: x**2 + y * z - ey = lambda x, y, z: (z**2) * x + y * z - ez = lambda x, y, z: y**2 + x * z + ex = lambda x, y, z: x**2 + y * z # NOQA E731 + ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 + ez = lambda x, y, z: y**2 + x * z # NOQA E731 - sigma1 = lambda x, y, z: x * y + 1 - sigma2 = lambda x, y, z: x * z + 2 - sigma3 = lambda x, y, z: 3 + z * y - sigma4 = lambda x, y, z: 0.1 * x * y * z - sigma5 = lambda x, y, z: 0.2 * x * y - sigma6 = lambda x, y, z: 0.1 * z + sigma1 = lambda x, y, z: x * y + 1 # NOQA E731 + sigma2 = lambda x, y, z: x * z + 2 # NOQA E731 + sigma3 = lambda x, y, z: 3 + z * y # NOQA E731 + sigma4 = lambda x, y, z: 0.1 * x * y * z # NOQA E731 + sigma5 = lambda x, y, z: 0.2 * x * y # NOQA E731 + sigma6 = lambda x, y, z: 0.1 * z # NOQA E731 Gc = self.M.gridCC if self.sigmaTest == 1: @@ -347,7 +347,7 @@ def getError(self): analytic = 69881.0 / 21600 # Found using sympy. if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) ) @@ -362,7 +362,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) ) @@ -474,15 +474,15 @@ class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): meshSizes = [8, 16] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 - ex = lambda x, y, z: x**2 + y * z - ey = lambda x, y, z: (z**2) * x + y * z - ez = lambda x, y, z: y**2 + x * z + ex = lambda x, y, z: x**2 + y * z # NOQA E731 + ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 + ez = lambda x, y, z: y**2 + x * z # NOQA E731 - tau_x = lambda x, y, z: y * z + 1 # x-face properties - tau_y = lambda x, y, z: x * z + 2 # y-face properties - tau_z = lambda x, y, z: 3 + x * y # z-face properties + tau_x = lambda x, y, z: y * z + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y, z: x * z + 2 # NOQA E731 # y-face properties + tau_z = lambda x, y, z: 3 + x * y # NOQA E731 # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -500,7 +500,7 @@ def getError(self): if self.location == "edges": analytic = 5.02760416666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) @@ -518,7 +518,7 @@ def getError(self): elif self.location == "faces": analytic = 2.66979166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) @@ -570,15 +570,15 @@ class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 - ex = lambda x, y, z: x**2 + y * z - ey = lambda x, y, z: (z**2) * x + y * z - ez = lambda x, y, z: y**2 + x * z + ex = lambda x, y, z: x**2 + y * z # NOQA E731 + ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 + ez = lambda x, y, z: y**2 + x * z # NOQA E731 - tau_x = lambda x, y, z: x + 1 # x-face properties - tau_y = lambda x, y, z: y + 2 # y-face properties - tau_z = lambda x, y, z: 3 * z + 1 # z-face properties + tau_x = lambda x, y, z: x + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y, z: y + 2 # NOQA E731 # y-face properties + tau_z = lambda x, y, z: 3 * z + 1 # NOQA E731 # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -596,7 +596,7 @@ def getError(self): analytic = 1.98906250000000 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) @@ -635,14 +635,14 @@ class TestTreeInnerProducts2D(discretize.tests.OrderTest): def getError(self): z = 5 # Because 5 is just such a great number. - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 - ex = lambda x, y: x**2 + y * z - ey = lambda x, y: (z**2) * x + y * z + ex = lambda x, y: x**2 + y * z # NOQA E731 + ey = lambda x, y: (z**2) * x + y * z # NOQA E731 - sigma1 = lambda x, y: x * y + 1 - sigma2 = lambda x, y: x * z + 2 - sigma3 = lambda x, y: 3 + z * y + sigma1 = lambda x, y: x * y + 1 # NOQA E731 + sigma2 = lambda x, y: x * z + 2 # NOQA E731 + sigma3 = lambda x, y: 3 + z * y # NOQA E731 Gc = self.M.gridCC if self.sigmaTest == 1: @@ -656,7 +656,7 @@ def getError(self): analytic = 781427.0 / 360 # Found using sympy. z=5 if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) if self.invert_model: @@ -668,7 +668,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -778,13 +778,13 @@ class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 # NOQA E731 - ex = lambda x, y: x**2 + y - ey = lambda x, y: (y**2) * x + ex = lambda x, y: x**2 + y # NOQA E731 + ey = lambda x, y: (y**2) * x # NOQA E731 - tau_x = lambda x, y: 2 * y + 1 # x-face properties - tau_y = lambda x, y: x + 2 # y-face properties + tau_x = lambda x, y: 2 * y + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y: x + 2 # NOQA E731 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -802,7 +802,7 @@ def getError(self): if self.location == "edges": analytic = 2.24166666666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -818,7 +818,7 @@ def getError(self): elif self.location == "faces": analytic = 1.59895833333333 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -868,13 +868,13 @@ class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 - ex = lambda x, y: x**2 + y - ey = lambda x, y: (x**2) * y + ex = lambda x, y: x**2 + y # NOQA E731 + ey = lambda x, y: (x**2) * y # NOQA E731 - tau_x = lambda x, y: x + 1 # x-face properties - tau_y = lambda x, y: y + 2 # y-face properties + tau_x = lambda x, y: x + 1 # NOQA E731 # x-face properties + tau_y = lambda x, y: y + 2 # NOQA E731 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -892,7 +892,7 @@ def getError(self): analytic = 1.38229166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] + cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -936,7 +936,7 @@ def getError(self): def test_orderN2CC(self): self.name = "Averaging 2D: N2CC" - fun = lambda x, y: (np.cos(x) + np.sin(y)) + fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 self.getHere = lambda M: call2(fun, M.gridN) self.getThere = lambda M: call2(fun, M.gridCC) self.getAve = lambda M: M.aveN2CC @@ -944,7 +944,7 @@ def test_orderN2CC(self): def test_orderN2Fx(self): self.name = "Averaging 2D: N2Fx" - fun = lambda x, y: (np.cos(x) + np.sin(y)) + fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 self.getHere = lambda M: call2(fun, M.gridN) self.getThere = lambda M: np.r_[call2(fun, M.gridFx), call2(fun, M.gridFy)] self.getAve = lambda M: M.aveN2F @@ -952,7 +952,7 @@ def test_orderN2Fx(self): def test_orderN2E(self): self.name = "Averaging 2D: N2E" - fun = lambda x, y: (np.cos(x) + np.sin(y)) + fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 self.getHere = lambda M: call2(fun, M.gridN) self.getThere = lambda M: np.r_[call2(fun, M.gridEx), call2(fun, M.gridEy)] self.getAve = lambda M: M.aveN2E @@ -960,7 +960,7 @@ def test_orderN2E(self): def test_orderF2CC(self): self.name = "Averaging 2D: F2CC" - fun = lambda x, y: (np.cos(x) + np.sin(y)) + fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 self.getHere = lambda M: np.r_[call2(fun, np.r_[M.gridFx, M.gridFy])] self.getThere = lambda M: call2(fun, M.gridCC) self.getAve = lambda M: M.aveF2CC @@ -968,7 +968,7 @@ def test_orderF2CC(self): def test_orderFx2CC(self): self.name = "Averaging 2D: Fx2CC" - funX = lambda x, y: (np.cos(x) + np.sin(y)) + funX = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 self.getHere = lambda M: np.r_[call2(funX, M.gridFx)] self.getThere = lambda M: np.r_[call2(funX, M.gridCC)] self.getAve = lambda M: M.aveFx2CC @@ -976,7 +976,7 @@ def test_orderFx2CC(self): def test_orderFy2CC(self): self.name = "Averaging 2D: Fy2CC" - funY = lambda x, y: (np.cos(y) * np.sin(x)) + funY = lambda x, y: (np.cos(y) * np.sin(x)) # NOQA E731 self.getHere = lambda M: np.r_[call2(funY, M.gridFy)] self.getThere = lambda M: np.r_[call2(funY, M.gridCC)] self.getAve = lambda M: M.aveFy2CC @@ -984,8 +984,8 @@ def test_orderFy2CC(self): def test_orderF2CCV(self): self.name = "Averaging 2D: F2CCV" - funX = lambda x, y: (np.cos(x) + np.sin(y)) - funY = lambda x, y: (np.cos(y) * np.sin(x)) + funX = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + funY = lambda x, y: (np.cos(y) * np.sin(x)) # NOQA E731 self.getHere = lambda M: np.r_[call2(funX, M.gridFx), call2(funY, M.gridFy)] self.getThere = lambda M: np.r_[call2(funX, M.gridCC), call2(funY, M.gridCC)] self.getAve = lambda M: M.aveF2CCV @@ -993,7 +993,7 @@ def test_orderF2CCV(self): def test_orderCC2F(self): self.name = "Averaging 2D: CC2F" - fun = lambda x, y: (np.cos(x) + np.sin(y)) + fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 self.getHere = lambda M: call2(fun, M.gridCC) self.getThere = lambda M: np.r_[call2(fun, M.gridFx), call2(fun, M.gridFy)] self.getAve = lambda M: M.aveCC2F @@ -1016,7 +1016,7 @@ def getError(self): def test_orderN2CC(self): self.name = "Averaging 3D: N2CC" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: call3(fun, M.gridN) self.getThere = lambda M: call3(fun, M.gridCC) self.getAve = lambda M: M.aveN2CC @@ -1024,7 +1024,7 @@ def test_orderN2CC(self): def test_orderN2F(self): self.name = "Averaging 3D: N2F" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: call3(fun, M.gridN) self.getThere = lambda M: np.r_[ call3(fun, M.gridFx), call3(fun, M.gridFy), call3(fun, M.gridFz) @@ -1034,7 +1034,7 @@ def test_orderN2F(self): def test_orderN2E(self): self.name = "Averaging 3D: N2E" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: call3(fun, M.gridN) self.getThere = lambda M: np.r_[ call3(fun, M.gridEx), call3(fun, M.gridEy), call3(fun, M.gridEz) @@ -1044,7 +1044,7 @@ def test_orderN2E(self): def test_orderF2CC(self): self.name = "Averaging 3D: F2CC" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[ call3(fun, M.gridFx), call3(fun, M.gridFy), call3(fun, M.gridFz) ] @@ -1054,7 +1054,7 @@ def test_orderF2CC(self): def test_orderFx2CC(self): self.name = "Averaging 3D: Fx2CC" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[call3(funX, M.gridFx)] self.getThere = lambda M: np.r_[call3(funX, M.gridCC)] self.getAve = lambda M: M.aveFx2CC @@ -1062,7 +1062,7 @@ def test_orderFx2CC(self): def test_orderFy2CC(self): self.name = "Averaging 3D: Fy2CC" - funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) + funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[call3(funY, M.gridFy)] self.getThere = lambda M: np.r_[call3(funY, M.gridCC)] self.getAve = lambda M: M.aveFy2CC @@ -1070,7 +1070,7 @@ def test_orderFy2CC(self): def test_orderFz2CC(self): self.name = "Averaging 3D: Fz2CC" - funZ = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) + funZ = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[call3(funZ, M.gridFz)] self.getThere = lambda M: np.r_[call3(funZ, M.gridCC)] self.getAve = lambda M: M.aveFz2CC @@ -1078,9 +1078,9 @@ def test_orderFz2CC(self): def test_orderF2CCV(self): self.name = "Averaging 3D: F2CCV" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) - funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) - funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 + funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[ call3(funX, M.gridFx), call3(funY, M.gridFy), call3(funZ, M.gridFz) ] @@ -1092,7 +1092,7 @@ def test_orderF2CCV(self): def test_orderEx2CC(self): self.name = "Averaging 3D: Ex2CC" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[call3(funX, M.gridEx)] self.getThere = lambda M: np.r_[call3(funX, M.gridCC)] self.getAve = lambda M: M.aveEx2CC @@ -1100,7 +1100,7 @@ def test_orderEx2CC(self): def test_orderEy2CC(self): self.name = "Averaging 3D: Ey2CC" - funY = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + funY = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[call3(funY, M.gridEy)] self.getThere = lambda M: np.r_[call3(funY, M.gridCC)] self.getAve = lambda M: M.aveEy2CC @@ -1108,7 +1108,7 @@ def test_orderEy2CC(self): def test_orderEz2CC(self): self.name = "Averaging 3D: Ez2CC" - funZ = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + funZ = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[call3(funZ, M.gridEz)] self.getThere = lambda M: np.r_[call3(funZ, M.gridCC)] self.getAve = lambda M: M.aveEz2CC @@ -1116,7 +1116,7 @@ def test_orderEz2CC(self): def test_orderE2CC(self): self.name = "Averaging 3D: E2CC" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[ call3(fun, M.gridEx), call3(fun, M.gridEy), call3(fun, M.gridEz) ] @@ -1126,9 +1126,9 @@ def test_orderE2CC(self): def test_orderE2CCV(self): self.name = "Averaging 3D: E2CCV" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) - funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) - funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 + funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: np.r_[ call3(funX, M.gridEx), call3(funY, M.gridEy), call3(funZ, M.gridEz) ] @@ -1140,7 +1140,7 @@ def test_orderE2CCV(self): def test_orderCC2F(self): self.name = "Averaging 3D: CC2F" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 self.getHere = lambda M: call3(fun, M.gridCC) self.getThere = lambda M: np.r_[ call3(fun, M.gridFx), call3(fun, M.gridFy), call3(fun, M.gridFz) From 6ba84771f235d8ca84161decaf7bd44b5c53c0e0 Mon Sep 17 00:00:00 2001 From: dccowan Date: Tue, 4 Jul 2023 12:16:53 -0700 Subject: [PATCH 16/41] Eliminate 'fast inner products' for face and edge properties. Done naturally. --- discretize/base/base_mesh.py | 214 +++++++++++++--- discretize/base/base_tensor_mesh.py | 331 ------------------------- discretize/operators/inner_products.py | 300 +++++++++++++++------- 3 files changed, 386 insertions(+), 459 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index df905a638..d8442708d 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1,8 +1,11 @@ """Module for the base ``discretize`` mesh.""" import numpy as np +import scipy.sparse as sp import os import json +import warnings from scipy.spatial import KDTree +from discretize.utils import is_scalar, mkvc, sdiag, sdinv from discretize.utils.code_utils import ( deprecate_property, deprecate_method, @@ -1885,7 +1888,6 @@ def get_edge_inner_product_surface( model=None, invert_model=False, invert_matrix=False, - do_fast=True, **kwargs, ): r"""Generate the edge inner product surface matrix or its inverse. @@ -1913,8 +1915,6 @@ def get_edge_inner_product_surface( invert_matrix : bool, optional Returns the inverse of the inner product surface matrix. The inverse not implemented for full tensor properties. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2007,7 +2007,6 @@ def get_face_inner_product_surface( model=None, invert_model=False, invert_matrix=False, - do_fast=True, **kwargs, ): r"""Generate the face inner product matrix or its inverse. @@ -2035,8 +2034,6 @@ def get_face_inner_product_surface( invert_matrix : bool, optional Returns the inverse of the inner product surface matrix. The inverse not implemented for full tensor properties. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2120,16 +2117,35 @@ def get_face_inner_product_surface( >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ - raise NotImplementedError( - f"get_face_inner_product_surface not implemented for {type(self)}" - ) + if model is None: + model = np.ones(self.nF) + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + + # Isotropic case only + if model.size == self.nF: + Aprop = self.face_areas * mkvc(model) + M = sdiag(Aprop) + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + + if invert_matrix: + return sdinv(M) + else: + return M def get_edge_inner_product_line( self, model=None, invert_model=False, invert_matrix=False, - do_fast=True, **kwargs, ): r"""Generate the edge inner product line matrix or its inverse. @@ -2157,8 +2173,6 @@ def get_edge_inner_product_line( invert_matrix : bool, optional Returns the inverse of the inner product line matrix. The inverse not implemented for full tensor properties. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2242,9 +2256,30 @@ def get_edge_inner_product_line( >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ - raise NotImplementedError( - f"get_edge_inner_product_line not implemented for {type(self)}" - ) + if model is None: + model = np.ones(self.nE) + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nE) + + # Isotropic case only + if model.size == self.nE: + Lprop = self.edge_lengths * mkvc(model) + M = sdiag(Lprop) + + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of edges.", + ) + + if invert_matrix: + return sdinv(M) + else: + return M def get_face_inner_product_deriv( self, model, do_fast=True, invert_model=False, invert_matrix=False, **kwargs @@ -2284,13 +2319,13 @@ def get_face_inner_product_deriv( are ordered ``np.c_[σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz]`` This can also be a 1D array with the same number of total elements in column major order. + do_fast : bool, optional + Do a faster implementation (if available). invert_model : bool, optional The inverse of *model* is used as the physical property. invert_matrix : bool, optional Returns the inverse of the inner product matrix. The inverse not implemented for full tensor properties. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2616,7 +2651,6 @@ def get_edge_inner_product_surface_deriv( model=None, invert_model=False, invert_matrix=False, - do_fast=True, **kwargs, ): r"""Get a function handle to multiply a vector with derivative of edge inner product surface matrix (or its inverse). @@ -2646,8 +2680,6 @@ def get_edge_inner_product_surface_deriv( The inverse of *model* is used as the diagnostic property. invert_matrix : bool, optional Returns the inverse of the inner product surface matrix. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2665,7 +2697,6 @@ def get_face_inner_product_surface_deriv( model=None, invert_model=False, invert_matrix=False, - do_fast=True, **kwargs, ): r"""Get a function handle to multiply a vector with derivative of face inner product surface matrix (or its inverse). @@ -2695,8 +2726,6 @@ def get_face_inner_product_surface_deriv( The inverse of *model* is used as the diagnostic property. invert_matrix : bool, optional Returns the inverse of the inner product surface matrix. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2705,16 +2734,74 @@ def get_face_inner_product_surface_deriv( (``n_faces``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_faces``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - raise NotImplementedError( - f"get_face_inner_product_surface_deriv not implemented for {type(self)}" - ) + if is_scalar(model): + tensorType = 0 + elif model.size == self.nF: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + + dMdprop = None + + if invert_matrix or invert_model: + MI = self.get_face_inner_product_surface( + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + + A = sdiag(self.face_areas) + + if tensorType == 0: # isotropic, constant + ones = sp.csr_matrix( + (np.ones(self.nF), (range(self.nF), np.zeros(self.nF))), + shape=(self.nF, 1), + ) + if not invert_matrix and not invert_model: + dMdprop = A * ones + elif invert_matrix and invert_model: + dMdprop = sdiag(MI.diagonal() ** 2) * A * ones * sdiag(1.0 / model**2) + elif invert_model: + dMdprop = A * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = sdiag(-MI.diagonal() ** 2) * A + + else: # isotropic, variable in space + if not invert_matrix and not invert_model: + dMdprop = A + elif invert_matrix and invert_model: + dMdprop = sdiag(MI.diagonal() ** 2) * A * sdiag(1.0 / model**2) + elif invert_model: + dMdprop = A * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = sdiag(-MI.diagonal() ** 2) * A + + if dMdprop is not None: + + def innerProductDeriv(v=None): + if v is None: + warnings.warn( + "Depreciation Warning: TensorMesh.innerProductDeriv." + " You should be supplying a vector. " + "Use: sdiag(u)*dMdprop", + FutureWarning, + stacklevel=2, + ) + return dMdprop + return sdiag(v) * dMdprop + + return innerProductDeriv + else: + return None def get_edge_inner_product_line_deriv( self, model=None, invert_model=False, invert_matrix=False, - do_fast=True, **kwargs, ): r"""Get a function handle to multiply a vector with derivative of edge inner product line matrix (or its inverse). @@ -2744,8 +2831,6 @@ def get_edge_inner_product_line_deriv( The inverse of *model* is used as the diagnostic property. invert_matrix : bool, optional Returns the inverse of the inner product line matrix. - do_fast : bool, optional - Do a faster implementation (if available). Returns ------- @@ -2754,9 +2839,74 @@ def get_edge_inner_product_line_deriv( (``n_edges``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - raise NotImplementedError( - f"get_edge_inner_product_line_deriv not implemented for {type(self)}" - ) + if is_scalar(model): + tensorType = 0 + elif model.size == self.nE: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}.".format(model.shape), + "Must be scalar or have length equal to total number of edges: {}.".format( + self.nE + ), + ) + + dMdprop = None + + if invert_matrix or invert_model: + MI = self.get_edge_inner_product_line( + model, + invert_model=invert_model, + invert_matrix=invert_matrix, + ) + + L = sdiag(self.edge_lengths) + if tensorType == 0: # isotropic, constant + ones = sp.csr_matrix( + (np.ones(self.nE), (range(self.nE), np.zeros(self.nE))), + shape=(self.nE, 1), + ) + if not invert_matrix and not invert_model: + dMdprop = L * ones + elif invert_matrix and invert_model: + dMdprop = sdiag(MI.diagonal() ** 2) * L * ones * sdiag(1.0 / model**2) + elif invert_model: + dMdprop = L * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = sdiag(-MI.diagonal() ** 2) * L + + elif tensorType == 1: # isotropic, variable in space + if not invert_matrix and not invert_model: + dMdprop = L + elif invert_matrix and invert_model: + dMdprop = sdiag(MI.diagonal() ** 2) * L * sdiag(1.0 / model**2) + elif invert_model: + dMdprop = L * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = sdiag(-MI.diagonal() ** 2) * L + + elif tensorType == 2: # anisotropic + raise NotImplementedError( + "EdgePropertiesInnerProductDeriv not implemented for anisotropy." + ) + + if dMdprop is not None: + + def innerProductDeriv(v=None): + if v is None: + warnings.warn( + "Depreciation Warning: TensorMesh.innerProductDeriv." + " You should be supplying a vector. " + "Use: sdiag(u)*dMdprop", + FutureWarning, + stacklevel=2, + ) + return dMdprop + return sdiag(v) * dMdprop + + return innerProductDeriv + else: + return None # Averaging @property diff --git a/discretize/base/base_tensor_mesh.py b/discretize/base/base_tensor_mesh.py index 1a2f943d7..436744316 100644 --- a/discretize/base/base_tensor_mesh.py +++ b/discretize/base/base_tensor_mesh.py @@ -990,337 +990,6 @@ def innerProductDeriv(v=None): else: return None - def _fastInnerProductSurface( - self, projection_type, model=None, invert_model=False, invert_matrix=False - ): - """Fast version of get_face_inner_product_surface. - - This does not handle the case of a full tensor property. - - Parameters - ---------- - projection_type : str - 'edges' or 'faces' - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - invert_model : bool - inverts the material property - invert_matrix : bool - inverts the matrix - - Returns - ------- - (n_faces, n_faces) or (n_edges, n_edges) scipy.sparse.csr_matrix - M, the inner product matrix - - """ - projection_type = projection_type[0].upper() - if projection_type not in ["F", "E"]: - raise ValueError("projection_type must be 'F' for faces or 'E' for edges") - - if model is None: - model = np.ones(self.nF) - - if invert_model: - model = 1.0 / model - - if is_scalar(model): - model = model * np.ones(self.nF) - # COULD ADD THIS CASE IF DESIRED - # elif len(model) == self.dim: - # model = np.r_[ - # [model[ii]*self.vnF[ii] for ii in range(0, self.dim)] - # ] - - # number of elements we are averaging (equals dim for regular - # meshes, but for cyl, where we use symmetry, it is 1 for edge - # variables and 2 for face variables) - if self._meshType == "CYL": - shape = getattr(self, "vn" + projection_type) - if self.is_symmetric: - n_elements = 1 - else: - n_elements = sum([1 if x != 0 else 0 for x in shape]) - 1 - else: - n_elements = self.dim - 1 - - # Isotropic case only - if model.size == self.nF: - Aprop = self.face_areas * mkvc(model) - if projection_type == "E": - Av = self.average_edge_to_face - M = n_elements * sdiag(Av.T * Aprop) - else: - M = sdiag(Aprop) - - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.", - ) - - if invert_matrix: - return sdinv(M) - else: - return M - - def _fastInnerProductSurfaceDeriv( - self, projection_type, model, invert_model=False, invert_matrix=False - ): - """Faster function for inner product derivatives on tensor meshes. - - Parameters - ---------- - projection_type : str - 'edges' or 'faces' - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - invert_model : bool - inverts the material property - invert_matrix : bool - inverts the matrix - - Returns - ------- - function - dMdmu, the derivative of the inner product matrix - """ - projection_type = projection_type[0].upper() - if projection_type not in ["F", "E"]: - raise ValueError("projection_type must be 'F' for faces or 'E' for edges") - - if is_scalar(model): - tensorType = 0 - elif model.size == self.nF: - tensorType = 1 - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces." - ) - - dMdprop = None - - if invert_matrix or invert_model: - MI = self._fastFacePropertiesInnerProduct( - projection_type, - model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - - # number of elements we are averaging (equals dim for regular - # meshes, but for cyl, where we use symmetry, it is 1 for edge - # variables and 2 for face variables) - if self._meshType == "CYL": - shape = getattr(self, "vn" + projection_type) - if self.is_symmetric: - n_elements = 1 - else: - n_elements = sum([1 if x != 0 else 0 for x in shape]) - 1 - else: - n_elements = self.dim - 1 - - A = sdiag(self.face_areas) - if projection_type == "E": - Av = self.average_edge_to_face - else: - Av = sdiag(np.ones(self.nF) / n_elements) - - if tensorType == 0: # isotropic, constant - ones = sp.csr_matrix( - (np.ones(self.nF), (range(self.nF), np.zeros(self.nF))), - shape=(self.nF, 1), - ) - if not invert_matrix and not invert_model: - dMdprop = n_elements * Av.T * A * ones - elif invert_matrix and invert_model: - dMdprop = n_elements * ( - sdiag(MI.diagonal() ** 2) - * Av.T - * A - * ones - * sdiag(1.0 / model**2) - ) - elif invert_model: - dMdprop = n_elements * Av.T * A * sdiag(-1.0 / model**2) - elif invert_matrix: - dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) - - else: # isotropic, variable in space - if not invert_matrix and not invert_model: - dMdprop = n_elements * Av.T * A - elif invert_matrix and invert_model: - dMdprop = n_elements * ( - sdiag(MI.diagonal() ** 2) * Av.T * A * sdiag(1.0 / model**2) - ) - elif invert_model: - dMdprop = n_elements * Av.T * A * sdiag(-1.0 / model**2) - elif invert_matrix: - dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) - - if dMdprop is not None: - - def innerProductDeriv(v=None): - if v is None: - warnings.warn( - "Depreciation Warning: TensorMesh.innerProductDeriv." - " You should be supplying a vector. " - "Use: sdiag(u)*dMdprop", - FutureWarning, - stacklevel=2, - ) - return dMdprop - return sdiag(v) * dMdprop - - return innerProductDeriv - else: - return None - - def _fastInnerProductLine( - self, model=None, invert_model=False, invert_matrix=False - ): - """Fast version of get_face_inner_product_deriv. - - This does not handle the case of a full tensor property. - - Parameters - ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - invert_model : bool - inverts the material property - invert_matrix : bool - inverts the matrix - - Returns - ------- - (n_edges, n_edges) scipy.sparse.csr_matrix - M, the inner product matrix - - """ - if model is None: - model = np.ones(self.nE) - - if invert_model: - model = 1.0 / model - - if is_scalar(model): - model = model * np.ones(self.nE) - - # number of elements we are averaging (equals dim for regular - # meshes, but for cyl, where we use symmetry, it is 1 for edge - # variables and 2 for face variables) - # if self._meshType == "CYL": - # shape = getattr(self, "vn" + projection_type) - # n_elements = sum([1 if x != 0 else 0 for x in shape]) - # else: - # n_elements = self.dim - 1 - - # Isotropic case only - if model.size == self.nE: - Lprop = self.edge_lengths * mkvc(model) - M = sdiag(Lprop) - - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of edges.", - ) - - if invert_matrix: - return sdinv(M) - else: - return M - - def _fastInnerProductLineDeriv( - self, model, invert_model=False, invert_matrix=False - ): - """Faster function for inner product derivatives on tensor meshes. - - Parameters - ---------- - model : numpy.ndarray - material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) - invert_model : bool - inverts the material property - invert_matrix : bool - inverts the matrix - - Returns - ------- - function - dMdmu, the derivative of the inner product matrix - """ - if is_scalar(model): - tensorType = 0 - elif model.size == self.nE: - tensorType = 1 - else: - raise Exception( - "Unexpected shape of tensor: {}.".format(model.shape), - "Must be scalar or have length equal to total number of edges: {}.".format( - self.nE - ), - ) - - dMdprop = None - - if invert_matrix or invert_model: - MI = self._fastEdgePropertiesInnerProduct( - model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - - L = sdiag(self.edge_lengths) - if tensorType == 0: # isotropic, constant - ones = sp.csr_matrix( - (np.ones(self.nE), (range(self.nE), np.zeros(self.nE))), - shape=(self.nE, 1), - ) - if not invert_matrix and not invert_model: - dMdprop = L * ones - elif invert_matrix and invert_model: - dMdprop = sdiag(MI.diagonal() ** 2) * L * ones * sdiag(1.0 / model**2) - elif invert_model: - dMdprop = L * sdiag(-1.0 / model**2) - elif invert_matrix: - dMdprop = sdiag(-MI.diagonal() ** 2) * L - - elif tensorType == 1: # isotropic, variable in space - if not invert_matrix and not invert_model: - dMdprop = L - elif invert_matrix and invert_model: - dMdprop = sdiag(MI.diagonal() ** 2) * L * sdiag(1.0 / model**2) - elif invert_model: - dMdprop = L * sdiag(-1.0 / model**2) - elif invert_matrix: - dMdprop = sdiag(-MI.diagonal() ** 2) * L - - elif tensorType == 2: # anisotropic - raise NotImplementedError( - "EdgePropertiesInnerProductDeriv not implemented for anisotropy." - ) - - if dMdprop is not None: - - def innerProductDeriv(v=None): - if v is None: - warnings.warn( - "Depreciation Warning: TensorMesh.innerProductDeriv." - " You should be supplying a vector. " - "Use: sdiag(u)*dMdprop", - FutureWarning, - stacklevel=2, - ) - return dMdprop - return sdiag(v) * dMdprop - - return innerProductDeriv - else: - return None - # DEPRECATED @property def hx(self): diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 3a8958c5d..23f4d9bb6 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -1,5 +1,6 @@ """Construct inner product operators for tensor like meshes.""" from scipy import sparse as sp +import warnings from discretize.base import BaseMesh from discretize.utils import ( sub2ind, @@ -13,6 +14,8 @@ inverse_3x3_block_diagonal, spzeros, sdinv, + mkvc, + is_scalar, ) import numpy as np @@ -96,63 +99,90 @@ def get_edge_inner_product( # NOQA D102 ) def get_edge_inner_product_surface( # NOQA D102 - self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs - ): - # Inherited documentation from discretize.base.BaseMesh - - fast = None - if hasattr(self, "_fastInnerProductSurface") and do_fast: - fast = self._fastInnerProductSurface( - projection_type="E", - model=model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - if fast is not None: - return fast - - raise NotImplementedError( - "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" - ) - - def get_face_inner_product_surface( # NOQA D102 - self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs - ): - # Inherited documentation from discretize.base.BaseMesh - - fast = None - if hasattr(self, "_fastInnerProductSurface") and do_fast: - fast = self._fastInnerProductSurface( - projection_type="F", - model=model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - if fast is not None: - return fast - - raise NotImplementedError( - "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" - ) - - def get_edge_inner_product_line( # NOQA D102 - self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs + self, model, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh - fast = None - if hasattr(self, "_fastInnerProductLine") and do_fast: - fast = self._fastInnerProductLine( - model=model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - if fast is not None: - return fast + if model is None: + model = np.ones(self.nF) - raise NotImplementedError( - "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" - ) + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + # COULD ADD THIS CASE IF DESIRED + # elif len(model) == self.dim: + # model = np.r_[ + # [model[ii]*self.vnF[ii] for ii in range(0, self.dim)] + # ] + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables and 2 for face variables) + if self._meshType == "CYL": + shape = getattr(self, "vnE") + if self.is_symmetric: + n_elements = 1 + else: + n_elements = sum([1 if x != 0 else 0 for x in shape]) - 1 + else: + n_elements = self.dim - 1 + + # Isotropic case only + if model.size == self.nF: + Aprop = self.face_areas * mkvc(model) + Av = self.average_edge_to_face + M = n_elements * sdiag(Av.T * Aprop) + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + + if invert_matrix: + return sdinv(M) + else: + return M + + # def get_face_inner_product_surface( # NOQA D102 + # self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs + # ): + # # Inherited documentation from discretize.base.BaseMesh + + # fast = None + # if hasattr(self, "_fastInnerProductSurface") and do_fast: + # fast = self._fastInnerProductSurface( + # projection_type="F", + # model=model, + # invert_model=invert_model, + # invert_matrix=invert_matrix, + # ) + # if fast is not None: + # return fast + + # raise NotImplementedError( + # "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" + # ) + + # def get_edge_inner_product_line( # NOQA D102 + # self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs + # ): + # # Inherited documentation from discretize.base.BaseMesh + + # fast = None + # if hasattr(self, "_fastInnerProductLine") and do_fast: + # fast = self._fastInnerProductLine( + # model=model, + # invert_model=invert_model, + # invert_matrix=invert_matrix, + # ) + # if fast is not None: + # return fast + + # raise NotImplementedError( + # "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" + # ) def _getInnerProduct( self, @@ -335,26 +365,26 @@ def get_edge_inner_product_deriv( # NOQA D102 invert_matrix=invert_matrix, ) - def get_face_inner_product_surface_deriv( # NOQA D102 - self, model, invert_model=False, invert_matrix=False, **kwargs - ): - # Inherited documentation from discretize.base.BaseMesh - if "invProp" in kwargs: - raise TypeError( - "The invProp keyword argument has been removed, please use invert_model. " - "This will be removed in discretize 1.0.0", - ) - if "invMat" in kwargs: - raise TypeError( - "The invMat keyword argument has been removed, please use invert_matrix. " - "This will be removed in discretize 1.0.0", - ) - return self._fastInnerProductSurfaceDeriv( - "F", - model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) + # def get_face_inner_product_surface_deriv( # NOQA D102 + # self, model, invert_model=False, invert_matrix=False, **kwargs + # ): + # # Inherited documentation from discretize.base.BaseMesh + # if "invProp" in kwargs: + # raise TypeError( + # "The invProp keyword argument has been removed, please use invert_model. " + # "This will be removed in discretize 1.0.0", + # ) + # if "invMat" in kwargs: + # raise TypeError( + # "The invMat keyword argument has been removed, please use invert_matrix. " + # "This will be removed in discretize 1.0.0", + # ) + # return self._fastInnerProductSurfaceDeriv( + # "F", + # model, + # invert_model=invert_model, + # invert_matrix=invert_matrix, + # ) def get_edge_inner_product_surface_deriv( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs @@ -370,32 +400,110 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 "The invMat keyword argument has been removed, please use invert_matrix. " "This will be removed in discretize 1.0.0", ) - return self._fastInnerProductSurfaceDeriv( - "E", - model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) - def get_edge_inner_product_line_deriv( # NOQA D102 - self, model, invert_model=False, invert_matrix=False, **kwargs - ): - # Inherited documentation from discretize.base.BaseMesh - if "invProp" in kwargs: - raise TypeError( - "The invProp keyword argument has been removed, please use invert_model. " - "This will be removed in discretize 1.0.0", + if is_scalar(model): + tensorType = 0 + elif model.size == self.nF: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", ) - if "invMat" in kwargs: - raise TypeError( - "The invMat keyword argument has been removed, please use invert_matrix. " - "This will be removed in discretize 1.0.0", + + dMdprop = None + + if invert_matrix or invert_model: + MI = self.get_edge_inner_product_surface( + model, + invert_model=invert_model, + invert_matrix=invert_matrix, ) - return self._fastInnerProductLineDeriv( - model, - invert_model=invert_model, - invert_matrix=invert_matrix, - ) + + # number of elements we are averaging (equals dim for regular + # meshes, but for cyl, where we use symmetry, it is 1 for edge + # variables and 2 for face variables) + if self._meshType == "CYL": + shape = getattr(self, "vnE") + if self.is_symmetric: + n_elements = 1 + else: + n_elements = sum([1 if x != 0 else 0 for x in shape]) - 1 + else: + n_elements = self.dim - 1 + + A = sdiag(self.face_areas) + Av = self.average_edge_to_face + + if tensorType == 0: # isotropic, constant + ones = sp.csr_matrix( + (np.ones(self.nF), (range(self.nF), np.zeros(self.nF))), + shape=(self.nF, 1), + ) + if not invert_matrix and not invert_model: + dMdprop = n_elements * Av.T * A * ones + elif invert_matrix and invert_model: + dMdprop = n_elements * ( + sdiag(MI.diagonal() ** 2) + * Av.T + * A + * ones + * sdiag(1.0 / model**2) + ) + elif invert_model: + dMdprop = n_elements * Av.T * A * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) + + else: # isotropic, variable in space + if not invert_matrix and not invert_model: + dMdprop = n_elements * Av.T * A + elif invert_matrix and invert_model: + dMdprop = n_elements * ( + sdiag(MI.diagonal() ** 2) * Av.T * A * sdiag(1.0 / model**2) + ) + elif invert_model: + dMdprop = n_elements * Av.T * A * sdiag(-1.0 / model**2) + elif invert_matrix: + dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) + + if dMdprop is not None: + + def innerProductDeriv(v=None): + if v is None: + warnings.warn( + "Depreciation Warning: TensorMesh.innerProductDeriv." + " You should be supplying a vector. " + "Use: sdiag(u)*dMdprop", + FutureWarning, + stacklevel=2, + ) + return dMdprop + return sdiag(v) * dMdprop + + return innerProductDeriv + else: + return None + + # def get_edge_inner_product_line_deriv( # NOQA D102 + # self, model, invert_model=False, invert_matrix=False, **kwargs + # ): + # # Inherited documentation from discretize.base.BaseMesh + # if "invProp" in kwargs: + # raise TypeError( + # "The invProp keyword argument has been removed, please use invert_model. " + # "This will be removed in discretize 1.0.0", + # ) + # if "invMat" in kwargs: + # raise TypeError( + # "The invMat keyword argument has been removed, please use invert_matrix. " + # "This will be removed in discretize 1.0.0", + # ) + # return self._fastInnerProductLineDeriv( + # model, + # invert_model=invert_model, + # invert_matrix=invert_matrix, + # ) def _getInnerProductDeriv( self, From f8862fb9fc0b1a774889e6767d1a2974e36d80fe Mon Sep 17 00:00:00 2001 From: dccowan Date: Tue, 4 Jul 2023 12:30:28 -0700 Subject: [PATCH 17/41] final style check --- discretize/operators/inner_products.py | 4 ++-- tests/tree/test_tree_operators.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 23f4d9bb6..c12a2a03e 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -121,7 +121,7 @@ def get_edge_inner_product_surface( # NOQA D102 # meshes, but for cyl, where we use symmetry, it is 1 for edge # variables and 2 for face variables) if self._meshType == "CYL": - shape = getattr(self, "vnE") + shape = self.vnE if self.is_symmetric: n_elements = 1 else: @@ -424,7 +424,7 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 # meshes, but for cyl, where we use symmetry, it is 1 for edge # variables and 2 for face variables) if self._meshType == "CYL": - shape = getattr(self, "vnE") + shape = self.vnE if self.is_symmetric: n_elements = 1 else: diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 72b780bfd..0cd7268a2 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -46,8 +46,12 @@ class TestCellGrad2D(discretize.tests.OrderTest): def getError(self): # Test function sol = lambda x, y: np.cos(2 * np.pi * x) * np.cos(2 * np.pi * y) # NOQA E731 - fx = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) # NOQA E731 - fy = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) # NOQA E731 + fx = ( + lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) + ) # NOQA E731 + fy = ( + lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) + ) # NOQA E731 phi = call2(sol, self.M.gridCC) gradF = self.M.cell_gradient * phi @@ -123,7 +127,9 @@ def getError(self): # Test function fx = lambda x, y: np.sin(2 * np.pi * x) # NOQA E731 fy = lambda x, y: np.sin(2 * np.pi * y) # NOQA E731 - sol = lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) # NOQA E731 + sol = ( + lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) + ) # NOQA E731 Fx = call2(fx, self.M.gridFx) Fy = call2(fy, self.M.gridFy) From 1e42b16b47079494724d54d60aa61f88dc01c5de Mon Sep 17 00:00:00 2001 From: dccowan Date: Wed, 5 Jul 2023 11:31:41 -0700 Subject: [PATCH 18/41] base mesh tests --- discretize/base/base_mesh.py | 132 +++++++++++++++---------- discretize/operators/inner_products.py | 84 +--------------- tests/base/test_basemesh.py | 6 ++ 3 files changed, 89 insertions(+), 133 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index d8442708d..cd5eb3638 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -2117,23 +2117,28 @@ def get_face_inner_product_surface( >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ - if model is None: - model = np.ones(self.nF) + try: + if model is None: + model = np.ones(self.nF) - if invert_model: - model = 1.0 / model + if invert_model: + model = 1.0 / model - if is_scalar(model): - model = model * np.ones(self.nF) + if is_scalar(model): + model = model * np.ones(self.nF) - # Isotropic case only - if model.size == self.nF: - Aprop = self.face_areas * mkvc(model) - M = sdiag(Aprop) - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.", + # Isotropic case only + if model.size == self.nF: + Aprop = self.face_areas * mkvc(model) + M = sdiag(Aprop) + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + except NotImplementedError: + raise NotImplementedError( + f"get_face_inner_product_surface not implemented for {type(self)}" ) if invert_matrix: @@ -2256,24 +2261,30 @@ def get_edge_inner_product_line( >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ - if model is None: - model = np.ones(self.nE) + try: + if model is None: + model = np.ones(self.nE) - if invert_model: - model = 1.0 / model + if invert_model: + model = 1.0 / model - if is_scalar(model): - model = model * np.ones(self.nE) + if is_scalar(model): + model = model * np.ones(self.nE) - # Isotropic case only - if model.size == self.nE: - Lprop = self.edge_lengths * mkvc(model) - M = sdiag(Lprop) + # Isotropic case only + if model.size == self.nE: + Lprop = self.edge_lengths * mkvc(model) + M = sdiag(Lprop) - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of edges.", + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of edges.", + ) + + except NotImplementedError: + raise NotImplementedError( + f"get_edge_inner_product_line not implemented for {type(self)}" ) if invert_matrix: @@ -2648,7 +2659,7 @@ def get_edge_inner_product_deriv( def get_edge_inner_product_surface_deriv( self, - model=None, + model, invert_model=False, invert_matrix=False, **kwargs, @@ -2694,7 +2705,7 @@ def get_edge_inner_product_surface_deriv( def get_face_inner_product_surface_deriv( self, - model=None, + model, invert_model=False, invert_matrix=False, **kwargs, @@ -2734,14 +2745,24 @@ def get_face_inner_product_surface_deriv( (``n_faces``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_faces``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - if is_scalar(model): - tensorType = 0 - elif model.size == self.nF: - tensorType = 1 - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.", + try: + if model is None: + tensorType = -1 + elif is_scalar(model): + tensorType = 0 + elif model.size == self.nF: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + + A = sdiag(self.face_areas) + + except NotImplementedError: + raise NotImplementedError( + f"get_face_inner_product_surface_deriv not implemented for {type(self)}" ) dMdprop = None @@ -2753,8 +2774,6 @@ def get_face_inner_product_surface_deriv( invert_matrix=invert_matrix, ) - A = sdiag(self.face_areas) - if tensorType == 0: # isotropic, constant ones = sp.csr_matrix( (np.ones(self.nF), (range(self.nF), np.zeros(self.nF))), @@ -2799,7 +2818,7 @@ def innerProductDeriv(v=None): def get_edge_inner_product_line_deriv( self, - model=None, + model, invert_model=False, invert_matrix=False, **kwargs, @@ -2839,16 +2858,26 @@ def get_edge_inner_product_line_deriv( (``n_edges``) :class:`numpy.ndarray` :math:`\mathbf{u}`. The function returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ - if is_scalar(model): - tensorType = 0 - elif model.size == self.nE: - tensorType = 1 - else: - raise Exception( - "Unexpected shape of tensor: {}.".format(model.shape), - "Must be scalar or have length equal to total number of edges: {}.".format( - self.nE - ), + try: + if model is None: + tensorType = -1 + elif is_scalar(model): + tensorType = 0 + elif model.size == self.nE: + tensorType = 1 + else: + raise Exception( + "Unexpected shape of tensor: {}.".format(model.shape), + "Must be scalar or have length equal to total number of edges: {}.".format( + self.nE + ), + ) + + L = sdiag(self.edge_lengths) + + except NotImplementedError: + raise NotImplementedError( + f"get_edge_inner_product_line_deriv not implemented for {type(self)}" ) dMdprop = None @@ -2860,7 +2889,6 @@ def get_edge_inner_product_line_deriv( invert_matrix=invert_matrix, ) - L = sdiag(self.edge_lengths) if tensorType == 0: # isotropic, constant ones = sp.csr_matrix( (np.ones(self.nE), (range(self.nE), np.zeros(self.nE))), diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index c12a2a03e..ba25527bc 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -145,45 +145,6 @@ def get_edge_inner_product_surface( # NOQA D102 else: return M - # def get_face_inner_product_surface( # NOQA D102 - # self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs - # ): - # # Inherited documentation from discretize.base.BaseMesh - - # fast = None - # if hasattr(self, "_fastInnerProductSurface") and do_fast: - # fast = self._fastInnerProductSurface( - # projection_type="F", - # model=model, - # invert_model=invert_model, - # invert_matrix=invert_matrix, - # ) - # if fast is not None: - # return fast - - # raise NotImplementedError( - # "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" - # ) - - # def get_edge_inner_product_line( # NOQA D102 - # self, model, invert_model=False, invert_matrix=False, do_fast=True, **kwargs - # ): - # # Inherited documentation from discretize.base.BaseMesh - - # fast = None - # if hasattr(self, "_fastInnerProductLine") and do_fast: - # fast = self._fastInnerProductLine( - # model=model, - # invert_model=invert_model, - # invert_matrix=invert_matrix, - # ) - # if fast is not None: - # return fast - - # raise NotImplementedError( - # "General edge mass matrix for face properties is not implemented. Only meshes with fast implementation possible" - # ) - def _getInnerProduct( self, projection_type, @@ -365,27 +326,6 @@ def get_edge_inner_product_deriv( # NOQA D102 invert_matrix=invert_matrix, ) - # def get_face_inner_product_surface_deriv( # NOQA D102 - # self, model, invert_model=False, invert_matrix=False, **kwargs - # ): - # # Inherited documentation from discretize.base.BaseMesh - # if "invProp" in kwargs: - # raise TypeError( - # "The invProp keyword argument has been removed, please use invert_model. " - # "This will be removed in discretize 1.0.0", - # ) - # if "invMat" in kwargs: - # raise TypeError( - # "The invMat keyword argument has been removed, please use invert_matrix. " - # "This will be removed in discretize 1.0.0", - # ) - # return self._fastInnerProductSurfaceDeriv( - # "F", - # model, - # invert_model=invert_model, - # invert_matrix=invert_matrix, - # ) - def get_edge_inner_product_surface_deriv( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs ): @@ -401,7 +341,9 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 "This will be removed in discretize 1.0.0", ) - if is_scalar(model): + if model is None: + tensorType = -1 + elif is_scalar(model): tensorType = 0 elif model.size == self.nF: tensorType = 1 @@ -485,26 +427,6 @@ def innerProductDeriv(v=None): else: return None - # def get_edge_inner_product_line_deriv( # NOQA D102 - # self, model, invert_model=False, invert_matrix=False, **kwargs - # ): - # # Inherited documentation from discretize.base.BaseMesh - # if "invProp" in kwargs: - # raise TypeError( - # "The invProp keyword argument has been removed, please use invert_model. " - # "This will be removed in discretize 1.0.0", - # ) - # if "invMat" in kwargs: - # raise TypeError( - # "The invMat keyword argument has been removed, please use invert_matrix. " - # "This will be removed in discretize 1.0.0", - # ) - # return self._fastInnerProductLineDeriv( - # model, - # invert_model=invert_model, - # invert_matrix=invert_matrix, - # ) - def _getInnerProductDeriv( self, model, diff --git a/tests/base/test_basemesh.py b/tests/base/test_basemesh.py index b4a77e6a6..76f645e84 100644 --- a/tests/base/test_basemesh.py +++ b/tests/base/test_basemesh.py @@ -50,8 +50,14 @@ class TestBaseMesh(unittest.TestCase): not_implemented_functions = [ "get_face_inner_product", "get_edge_inner_product", + "get_face_inner_product_surface", + "get_edge_inner_product_surface", + "get_edge_inner_product_line", "get_face_inner_product_deriv", "get_edge_inner_product_deriv", + "get_face_inner_product_surface_deriv", + "get_edge_inner_product_surface_deriv", + "get_edge_inner_product_line_deriv", "point2index", "get_interpolation_matrix", ] From 93802061a28a957682add6e88510a49b92013f04 Mon Sep 17 00:00:00 2001 From: dccowan Date: Wed, 5 Jul 2023 13:37:04 -0700 Subject: [PATCH 19/41] code coverage and error raise tests --- discretize/base/base_mesh.py | 7 +- discretize/operators/inner_products.py | 14 +-- tests/base/test_tensor_innerproduct.py | 26 ++++ tests/base/test_tensor_innerproduct_derivs.py | 111 ++++++++++++++++-- 4 files changed, 127 insertions(+), 31 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index cd5eb3638..17cf92bbb 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -2788,7 +2788,7 @@ def get_face_inner_product_surface_deriv( elif invert_matrix: dMdprop = sdiag(-MI.diagonal() ** 2) * A - else: # isotropic, variable in space + elif tensorType == 1: # isotropic, variable in space if not invert_matrix and not invert_model: dMdprop = A elif invert_matrix and invert_model: @@ -2913,11 +2913,6 @@ def get_edge_inner_product_line_deriv( elif invert_matrix: dMdprop = sdiag(-MI.diagonal() ** 2) * L - elif tensorType == 2: # anisotropic - raise NotImplementedError( - "EdgePropertiesInnerProductDeriv not implemented for anisotropy." - ) - if dMdprop is not None: def innerProductDeriv(v=None): diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index ba25527bc..ceff1907e 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -102,7 +102,6 @@ def get_edge_inner_product_surface( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh - if model is None: model = np.ones(self.nF) @@ -330,17 +329,6 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 self, model, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh - if "invProp" in kwargs: - raise TypeError( - "The invProp keyword argument has been removed, please use invert_model. " - "This will be removed in discretize 1.0.0", - ) - if "invMat" in kwargs: - raise TypeError( - "The invMat keyword argument has been removed, please use invert_matrix. " - "This will be removed in discretize 1.0.0", - ) - if model is None: tensorType = -1 elif is_scalar(model): @@ -397,7 +385,7 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 elif invert_matrix: dMdprop = n_elements * (sdiag(-MI.diagonal() ** 2) * Av.T * A) - else: # isotropic, variable in space + elif tensorType == 1: # isotropic, variable in space if not invert_matrix and not invert_model: dMdprop = n_elements * Av.T * A elif invert_matrix and invert_model: diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 9e37a52cc..9d21b17f1 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -1,6 +1,9 @@ import numpy as np import unittest import discretize +from discretize import TensorMesh + +np.random.seed(50) class TestInnerProducts(discretize.tests.OrderTest): @@ -667,6 +670,29 @@ def test_order1_faces_invert_model(self): self.orderTest() +class TestTensorSizeErrorRaises(unittest.TestCase): + """Ensure exception error when model is incorrect size""" + + def setUp(self): + self.mesh3D = TensorMesh([4, 4, 4]) + self.model = np.random.rand(self.mesh3D.nC) + + def test_edge_inner_product_surface(self): + self.assertRaises( + Exception, self.mesh3D.get_edge_inner_product_surface, self.model + ) + + def test_face_inner_product_surface(self): + self.assertRaises( + Exception, self.mesh3D.get_face_inner_product_surface, self.model + ) + + def test_edge_inner_product_line(self): + self.assertRaises( + Exception, self.mesh3D.get_edge_inner_product_line, self.model + ) + + if __name__ == "__main__": unittest.main() diff --git a/tests/base/test_tensor_innerproduct_derivs.py b/tests/base/test_tensor_innerproduct_derivs.py index e15f87f14..a61e184be 100644 --- a/tests/base/test_tensor_innerproduct_derivs.py +++ b/tests/base/test_tensor_innerproduct_derivs.py @@ -1,6 +1,7 @@ import numpy as np import unittest import discretize +from discretize import TensorMesh np.random.seed(50) @@ -348,10 +349,7 @@ def fun(sig): sig, invert_model=invert_model, invert_matrix=invert_matrix ) Md = mesh.get_face_inner_product_surface_deriv( - sig, - invert_model=invert_model, - invert_matrix=invert_matrix, - # do_fast=fast, + sig, invert_model=invert_model, invert_matrix=invert_matrix ) return M * v, Md(v) @@ -379,16 +377,10 @@ def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): def fun(sig): M = mesh.get_edge_inner_product_surface( - sig, - invert_model=invert_model, - invert_matrix=invert_matrix, - do_fast=True, + sig, invert_model=invert_model, invert_matrix=invert_matrix ) Md = mesh.get_edge_inner_product_surface_deriv( - sig, - invert_model=invert_model, - invert_matrix=invert_matrix, - # do_fast=fast, + sig, invert_model=invert_model, invert_matrix=invert_matrix ) return M * v, Md(v) @@ -425,6 +417,54 @@ def test_EdgeIP_2D_isotropic_fast(self): def test_EdgeIP_3D_isotropic_fast(self): self.assertTrue(self.doTestEdge([10, 4, 5], 1, "Tensor")) + def test_FaceIP_2D_float_invert(self): + self.assertTrue( + self.doTestFace([10, 4], 0, "Tensor", invert_model=True, invert_matrix=True) + ) + + def test_FaceIP_3D_float_invert(self): + self.assertTrue( + self.doTestFace( + [10, 4, 5], 0, "Tensor", invert_model=True, invert_matrix=True + ) + ) + + def test_FaceIP_2D_isotropic_invert(self): + self.assertTrue( + self.doTestFace([10, 4], 1, "Tensor", invert_model=True, invert_matrix=True) + ) + + def test_FaceIP_3D_isotropic_invert(self): + self.assertTrue( + self.doTestFace( + [10, 4, 5], 1, "Tensor", invert_model=True, invert_matrix=True + ) + ) + + def test_EdgeIP_2D_float_invert(self): + self.assertTrue( + self.doTestEdge([10, 4], 0, "Tensor", invert_model=True, invert_matrix=True) + ) + + def test_EdgeIP_3D_float_invert(self): + self.assertTrue( + self.doTestEdge( + [10, 4, 5], 0, "Tensor", invert_model=True, invert_matrix=True + ) + ) + + def test_EdgeIP_2D_isotropic_invert(self): + self.assertTrue( + self.doTestEdge([10, 4], 1, "Tensor", invert_model=True, invert_matrix=True) + ) + + def test_EdgeIP_3D_isotropic_invert(self): + self.assertTrue( + self.doTestEdge( + [10, 4, 5], 1, "Tensor", invert_model=True, invert_matrix=True + ) + ) + class TestEdgePropertiesInnerProductsDerivsTensor(unittest.TestCase): def doTestEdge(self, h, rep, meshType, invert_model=False, invert_matrix=False): @@ -476,6 +516,53 @@ def test_EdgeIP_2D_isotropic_fast(self): def test_EdgeIP_3D_isotropic_fast(self): self.assertTrue(self.doTestEdge([10, 4, 5], 1, "Tensor")) + def test_EdgeIP_2D_float_invert(self): + self.assertTrue( + self.doTestEdge([10, 4], 0, "Tensor", invert_model=True, invert_matrix=True) + ) + + def test_EdgeIP_3D_float_invert(self): + self.assertTrue( + self.doTestEdge( + [10, 4, 5], 0, "Tensor", invert_model=True, invert_matrix=True + ) + ) + + def test_EdgeIP_2D_isotropic_invert(self): + self.assertTrue( + self.doTestEdge([10, 4], 1, "Tensor", invert_model=True, invert_matrix=True) + ) + + def test_EdgeIP_3D_isotropic_invert(self): + self.assertTrue( + self.doTestEdge( + [10, 4, 5], 1, "Tensor", invert_model=True, invert_matrix=True + ) + ) + + +class TestTensorSizeErrorRaises(unittest.TestCase): + """Ensure exception error when model is incorrect size""" + + def setUp(self): + self.mesh3D = TensorMesh([4, 4, 4]) + self.model = np.random.rand(self.mesh3D.nC) + + def test_edge_inner_product_surface_deriv(self): + self.assertRaises( + Exception, self.mesh3D.get_edge_inner_product_surface_deriv, self.model + ) + + def test_face_inner_product_surface_deriv(self): + self.assertRaises( + Exception, self.mesh3D.get_face_inner_product_surface_deriv, self.model + ) + + def test_edge_inner_product_line_deriv(self): + self.assertRaises( + Exception, self.mesh3D.get_edge_inner_product_line_deriv, self.model + ) + if __name__ == "__main__": unittest.main() From 8cac8f775ec633b817f8ca4913a6664fe718e97f Mon Sep 17 00:00:00 2001 From: dccowan Date: Wed, 5 Jul 2023 14:31:53 -0700 Subject: [PATCH 20/41] post Joe comments --- discretize/base/base_mesh.py | 128 +++++++++--------- discretize/operators/inner_products.py | 22 +-- tests/base/test_tensor_innerproduct.py | 6 +- tests/base/test_tensor_innerproduct_derivs.py | 6 +- 4 files changed, 80 insertions(+), 82 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 17cf92bbb..2b9cc68b4 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -2118,29 +2118,30 @@ def get_face_inner_product_surface( >>> plt.show() """ try: - if model is None: - model = np.ones(self.nF) - - if invert_model: - model = 1.0 / model - - if is_scalar(model): - model = model * np.ones(self.nF) - - # Isotropic case only - if model.size == self.nF: - Aprop = self.face_areas * mkvc(model) - M = sdiag(Aprop) - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.", - ) + face_areas = self.face_areas except NotImplementedError: raise NotImplementedError( f"get_face_inner_product_surface not implemented for {type(self)}" ) + if model is None: + model = np.ones(self.nF) + + if invert_model: + model = 1.0 / model + + if is_scalar(model): + model = model * np.ones(self.nF) + + # Isotropic case only + if model.size != self.nF: + raise ValueError( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + + M = sdiag(face_areas * mkvc(model)) + if invert_matrix: return sdinv(M) else: @@ -2262,31 +2263,30 @@ def get_edge_inner_product_line( >>> plt.show() """ try: - if model is None: - model = np.ones(self.nE) - - if invert_model: - model = 1.0 / model + edge_lengths = self.edge_lengths + except NotImplementedError: + raise NotImplementedError( + f"get_edge_inner_product_line not implemented for {type(self)}" + ) - if is_scalar(model): - model = model * np.ones(self.nE) + if model is None: + model = np.ones(self.nE) - # Isotropic case only - if model.size == self.nE: - Lprop = self.edge_lengths * mkvc(model) - M = sdiag(Lprop) + if invert_model: + model = 1.0 / model - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of edges.", - ) + if is_scalar(model): + model = model * np.ones(self.nE) - except NotImplementedError: - raise NotImplementedError( - f"get_edge_inner_product_line not implemented for {type(self)}" + # Isotropic case only + if model.size != self.nE: + raise ValueError( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of edges.", ) + M = sdiag(edge_lengths * mkvc(model)) + if invert_matrix: return sdinv(M) else: @@ -2746,25 +2746,24 @@ def get_face_inner_product_surface_deriv( returns a (``n_faces``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ try: - if model is None: - tensorType = -1 - elif is_scalar(model): - tensorType = 0 - elif model.size == self.nF: - tensorType = 1 - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.", - ) - A = sdiag(self.face_areas) - except NotImplementedError: raise NotImplementedError( f"get_face_inner_product_surface_deriv not implemented for {type(self)}" ) + if model is None: + tensorType = -1 + elif is_scalar(model): + tensorType = 0 + elif model.size == self.nF: + tensorType = 1 + else: + raise ValueError( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + dMdprop = None if invert_matrix or invert_model: @@ -2859,27 +2858,26 @@ def get_edge_inner_product_line_deriv( returns a (``n_edges``, ``n_params``) :class:`scipy.sparse.csr_matrix`. """ try: - if model is None: - tensorType = -1 - elif is_scalar(model): - tensorType = 0 - elif model.size == self.nE: - tensorType = 1 - else: - raise Exception( - "Unexpected shape of tensor: {}.".format(model.shape), - "Must be scalar or have length equal to total number of edges: {}.".format( - self.nE - ), - ) - L = sdiag(self.edge_lengths) - except NotImplementedError: raise NotImplementedError( f"get_edge_inner_product_line_deriv not implemented for {type(self)}" ) + if model is None: + tensorType = -1 + elif is_scalar(model): + tensorType = 0 + elif model.size == self.nE: + tensorType = 1 + else: + raise ValueError( + "Unexpected shape of tensor: {}.".format(model.shape), + "Must be scalar or have length equal to total number of edges: {}.".format( + self.nE + ), + ) + dMdprop = None if invert_matrix or invert_model: diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index ceff1907e..ac5f66e02 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -116,6 +116,13 @@ def get_edge_inner_product_surface( # NOQA D102 # [model[ii]*self.vnF[ii] for ii in range(0, self.dim)] # ] + # Isotropic case only + if model.size != self.nF: + raise ValueError( + "Unexpected shape of tensor: {}".format(model.shape), + "Must be scalar or have length equal to total number of faces.", + ) + # number of elements we are averaging (equals dim for regular # meshes, but for cyl, where we use symmetry, it is 1 for edge # variables and 2 for face variables) @@ -128,16 +135,9 @@ def get_edge_inner_product_surface( # NOQA D102 else: n_elements = self.dim - 1 - # Isotropic case only - if model.size == self.nF: - Aprop = self.face_areas * mkvc(model) - Av = self.average_edge_to_face - M = n_elements * sdiag(Av.T * Aprop) - else: - raise Exception( - "Unexpected shape of tensor: {}".format(model.shape), - "Must be scalar or have length equal to total number of faces.", - ) + Aprop = self.face_areas * mkvc(model) + Av = self.average_edge_to_face + M = n_elements * sdiag(Av.T * Aprop) if invert_matrix: return sdinv(M) @@ -336,7 +336,7 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 elif model.size == self.nF: tensorType = 1 else: - raise Exception( + raise ValueError( "Unexpected shape of tensor: {}".format(model.shape), "Must be scalar or have length equal to total number of faces.", ) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 9d21b17f1..18e3f15b1 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -679,17 +679,17 @@ def setUp(self): def test_edge_inner_product_surface(self): self.assertRaises( - Exception, self.mesh3D.get_edge_inner_product_surface, self.model + ValueError, self.mesh3D.get_edge_inner_product_surface, self.model ) def test_face_inner_product_surface(self): self.assertRaises( - Exception, self.mesh3D.get_face_inner_product_surface, self.model + ValueError, self.mesh3D.get_face_inner_product_surface, self.model ) def test_edge_inner_product_line(self): self.assertRaises( - Exception, self.mesh3D.get_edge_inner_product_line, self.model + ValueError, self.mesh3D.get_edge_inner_product_line, self.model ) diff --git a/tests/base/test_tensor_innerproduct_derivs.py b/tests/base/test_tensor_innerproduct_derivs.py index a61e184be..be8e362fe 100644 --- a/tests/base/test_tensor_innerproduct_derivs.py +++ b/tests/base/test_tensor_innerproduct_derivs.py @@ -550,17 +550,17 @@ def setUp(self): def test_edge_inner_product_surface_deriv(self): self.assertRaises( - Exception, self.mesh3D.get_edge_inner_product_surface_deriv, self.model + ValueError, self.mesh3D.get_edge_inner_product_surface_deriv, self.model ) def test_face_inner_product_surface_deriv(self): self.assertRaises( - Exception, self.mesh3D.get_face_inner_product_surface_deriv, self.model + ValueError, self.mesh3D.get_face_inner_product_surface_deriv, self.model ) def test_edge_inner_product_line_deriv(self): self.assertRaises( - Exception, self.mesh3D.get_edge_inner_product_line_deriv, self.model + ValueError, self.mesh3D.get_edge_inner_product_line_deriv, self.model ) From 6cad7230b6739183604448e2f1e5bcfa918754d2 Mon Sep 17 00:00:00 2001 From: dccowan Date: Wed, 5 Jul 2023 20:10:40 -0700 Subject: [PATCH 21/41] More coverage tests --- tests/base/test_tensor_innerproduct.py | 89 +++++++++++++++++-- tests/base/test_tensor_innerproduct_derivs.py | 66 ++++++++------ 2 files changed, 123 insertions(+), 32 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 18e3f15b1..b36ee7a18 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -2,6 +2,7 @@ import unittest import discretize from discretize import TensorMesh +from discretize.utils import sdinv np.random.seed(50) @@ -207,10 +208,20 @@ def getError(self): ) E = self.M.project_edge_vector(Ec) - if self.invert_model: + if not self.invert_model and not self.invert_matrix: + A = self.M.get_edge_inner_product_surface(tau) + elif self.invert_model: A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) + elif self.invert_matrix: + A = sdinv( + self.M.get_edge_inner_product_surface(tau, invert_matrix=True) + ) else: - A = self.M.get_edge_inner_product_surface(tau) + A = sdinv( + self.M.get_edge_inner_product_surface( + 1 / tau, invert_model=True, invert_matrix=True + ) + ) numeric = E.T.dot(A.dot(E)) @@ -225,10 +236,20 @@ def getError(self): ) F = self.M.project_face_vector(Fc) - if self.invert_model: + if not self.invert_model and not self.invert_matrix: + A = self.M.get_face_inner_product_surface(tau) + elif self.invert_model: A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) + elif self.invert_matrix: + A = sdinv( + self.M.get_face_inner_product_surface(tau, invert_matrix=True) + ) else: - A = self.M.get_face_inner_product_surface(tau) + A = sdinv( + self.M.get_face_inner_product_surface( + 1 / tau, invert_model=True, invert_matrix=True + ) + ) numeric = F.T.dot(A.dot(F)) @@ -240,24 +261,56 @@ def test_order1_edges(self): self.name = "Edge Inner Product - Isotropic" self.location = "edges" self.invert_model = False + self.invert_matrix = False self.orderTest() def test_order1_edges_invert_model(self): self.name = "Edge Inner Product - Isotropic - invert_model" self.location = "edges" self.invert_model = True + self.invert_matrix = False + self.orderTest() + + def test_order1_edges_invert_matrix(self): + self.name = "Edge Inner Product - Isotropic - invert_matrix" + self.location = "edges" + self.invert_model = False + self.invert_matrix = True + self.orderTest() + + def test_order1_edges_invert_matrix_and_model(self): + self.name = "Edge Inner Product - Isotropic - invert_matrix and invert_model" + self.location = "edges" + self.invert_model = True + self.invert_matrix = True self.orderTest() def test_order1_faces(self): self.name = "Face Inner Product - Isotropic" self.location = "faces" self.invert_model = False + self.invert_matrix = False self.orderTest() def test_order1_faces_invert_model(self): self.name = "Face Inner Product - Isotropic - invert_model" self.location = "faces" self.invert_model = True + self.invert_matrix = False + self.orderTest() + + def test_order1_faces_invert_matrix(self): + self.name = "Face Inner Product - Isotropic - invert_matrix" + self.location = "faces" + self.invert_model = False + self.invert_matrix = True + self.orderTest() + + def test_order1_faces_invert_matrix_and_model(self): + self.name = "Face Inner Product - Isotropic - invert_matrix and invert_model" + self.location = "faces" + self.invert_model = True + self.invert_matrix = True self.orderTest() @@ -301,10 +354,18 @@ def getError(self): Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) - if self.invert_model: + if not self.invert_model and not self.invert_matrix: + A = self.M.get_edge_inner_product_line(tau) + elif self.invert_model: A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) + elif self.invert_matrix: + A = sdinv(self.M.get_edge_inner_product_line(tau, invert_matrix=True)) else: - A = self.M.get_edge_inner_product_line(tau) + A = sdinv( + self.M.get_edge_inner_product_line( + 1 / tau, invert_model=True, invert_matrix=True + ) + ) numeric = E.T.dot(A.dot(E)) @@ -316,12 +377,28 @@ def test_order1_edges(self): self.name = "Edge Inner Product - Isotropic" self.location = "edges" self.invert_model = False + self.invert_matrix = False self.orderTest() def test_order1_edges_invert_model(self): self.name = "Edge Inner Product - Isotropic - invert_model" self.location = "edges" self.invert_model = True + self.invert_matrix = False + self.orderTest() + + def test_order1_edges_invert_matrix(self): + self.name = "Edge Inner Product - Isotropic - invert_matrix" + self.location = "edges" + self.invert_model = False + self.invert_matrix = True + self.orderTest() + + def test_order1_edges_invert_matrix_and_model(self): + self.name = "Edge Inner Product - Isotropic - invert_matrix and invert_model" + self.location = "edges" + self.invert_model = True + self.invert_matrix = True self.orderTest() diff --git a/tests/base/test_tensor_innerproduct_derivs.py b/tests/base/test_tensor_innerproduct_derivs.py index be8e362fe..e49fc8d12 100644 --- a/tests/base/test_tensor_innerproduct_derivs.py +++ b/tests/base/test_tensor_innerproduct_derivs.py @@ -393,72 +393,72 @@ def fun(sig): ) return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) - def test_FaceIP_2D_float_fast(self): + def test_FaceIP_2D_float(self): self.assertTrue(self.doTestFace([10, 4], 0, "Tensor")) - def test_FaceIP_3D_float_fast(self): + def test_FaceIP_3D_float(self): self.assertTrue(self.doTestFace([10, 4, 5], 0, "Tensor")) - def test_FaceIP_2D_isotropic_fast(self): + def test_FaceIP_2D_isotropic(self): self.assertTrue(self.doTestFace([10, 4], 1, "Tensor")) - def test_FaceIP_3D_isotropic_fast(self): + def test_FaceIP_3D_isotropic(self): self.assertTrue(self.doTestFace([10, 4, 5], 1, "Tensor")) - def test_EdgeIP_2D_float_fast(self): + def test_EdgeIP_2D_float(self): self.assertTrue(self.doTestEdge([10, 4], 0, "Tensor")) - def test_EdgeIP_3D_float_fast(self): + def test_EdgeIP_3D_float(self): self.assertTrue(self.doTestEdge([10, 4, 5], 0, "Tensor")) - def test_EdgeIP_2D_isotropic_fast(self): + def test_EdgeIP_2D_isotropic(self): self.assertTrue(self.doTestEdge([10, 4], 1, "Tensor")) - def test_EdgeIP_3D_isotropic_fast(self): + def test_EdgeIP_3D_isotropic(self): self.assertTrue(self.doTestEdge([10, 4, 5], 1, "Tensor")) - def test_FaceIP_2D_float_invert(self): + def test_FaceIP_2D_float_invert_all(self): self.assertTrue( self.doTestFace([10, 4], 0, "Tensor", invert_model=True, invert_matrix=True) ) - def test_FaceIP_3D_float_invert(self): + def test_FaceIP_3D_float_invert_all(self): self.assertTrue( self.doTestFace( [10, 4, 5], 0, "Tensor", invert_model=True, invert_matrix=True ) ) - def test_FaceIP_2D_isotropic_invert(self): + def test_FaceIP_2D_isotropic_invert_all(self): self.assertTrue( self.doTestFace([10, 4], 1, "Tensor", invert_model=True, invert_matrix=True) ) - def test_FaceIP_3D_isotropic_invert(self): + def test_FaceIP_3D_isotropic_invert_all(self): self.assertTrue( self.doTestFace( [10, 4, 5], 1, "Tensor", invert_model=True, invert_matrix=True ) ) - def test_EdgeIP_2D_float_invert(self): + def test_EdgeIP_2D_float_invert_all(self): self.assertTrue( self.doTestEdge([10, 4], 0, "Tensor", invert_model=True, invert_matrix=True) ) - def test_EdgeIP_3D_float_invert(self): + def test_EdgeIP_3D_float_invert_all(self): self.assertTrue( self.doTestEdge( [10, 4, 5], 0, "Tensor", invert_model=True, invert_matrix=True ) ) - def test_EdgeIP_2D_isotropic_invert(self): + def test_EdgeIP_2D_isotropic_invert_all(self): self.assertTrue( self.doTestEdge([10, 4], 1, "Tensor", invert_model=True, invert_matrix=True) ) - def test_EdgeIP_3D_isotropic_invert(self): + def test_EdgeIP_3D_isotropic_invert_all(self): self.assertTrue( self.doTestEdge( [10, 4, 5], 1, "Tensor", invert_model=True, invert_matrix=True @@ -485,13 +485,11 @@ def fun(sig): sig, invert_model=invert_model, invert_matrix=invert_matrix, - do_fast=True, ) Md = mesh.get_edge_inner_product_line_deriv( sig, invert_model=invert_model, invert_matrix=invert_matrix, - # do_fast=fast, ) return M * v, Md(v) @@ -504,36 +502,36 @@ def fun(sig): ) return discretize.tests.check_derivative(fun, sig, num=5, plotIt=False) - def test_EdgeIP_2D_float_fast(self): + def test_EdgeIP_2D_float(self): self.assertTrue(self.doTestEdge([10, 4], 0, "Tensor")) - def test_EdgeIP_3D_float_fast(self): + def test_EdgeIP_3D_float(self): self.assertTrue(self.doTestEdge([10, 4, 5], 0, "Tensor")) - def test_EdgeIP_2D_isotropic_fast(self): + def test_EdgeIP_2D_isotropic(self): self.assertTrue(self.doTestEdge([10, 4], 1, "Tensor")) - def test_EdgeIP_3D_isotropic_fast(self): + def test_EdgeIP_3D_isotropic(self): self.assertTrue(self.doTestEdge([10, 4, 5], 1, "Tensor")) - def test_EdgeIP_2D_float_invert(self): + def test_EdgeIP_2D_float_invert_all(self): self.assertTrue( self.doTestEdge([10, 4], 0, "Tensor", invert_model=True, invert_matrix=True) ) - def test_EdgeIP_3D_float_invert(self): + def test_EdgeIP_3D_float_invert_all(self): self.assertTrue( self.doTestEdge( [10, 4, 5], 0, "Tensor", invert_model=True, invert_matrix=True ) ) - def test_EdgeIP_2D_isotropic_invert(self): + def test_EdgeIP_2D_isotropic_invert_all(self): self.assertTrue( self.doTestEdge([10, 4], 1, "Tensor", invert_model=True, invert_matrix=True) ) - def test_EdgeIP_3D_isotropic_invert(self): + def test_EdgeIP_3D_isotropic_invert_all(self): self.assertTrue( self.doTestEdge( [10, 4, 5], 1, "Tensor", invert_model=True, invert_matrix=True @@ -564,5 +562,21 @@ def test_edge_inner_product_line_deriv(self): ) +class TestNone(unittest.TestCase): + """Test None outputs""" + + def setUp(self): + self.mesh3D = TensorMesh([4, 4, 4]) + + def test_edge_inner_product_surface_deriv(self): + self.assertIsNone(self.mesh3D.get_edge_inner_product_surface_deriv(None)) + + def test_face_inner_product_surface_deriv(self): + self.assertIsNone(self.mesh3D.get_face_inner_product_surface_deriv(None)) + + def test_edge_inner_product_line_deriv(self): + self.assertIsNone(self.mesh3D.get_edge_inner_product_line_deriv(None)) + + if __name__ == "__main__": unittest.main() From 73d6b6448fc99302deb983f86f845bfecddbfdad Mon Sep 17 00:00:00 2001 From: dccowan Date: Thu, 6 Jul 2023 09:58:30 -0700 Subject: [PATCH 22/41] fix broken examples --- discretize/base/base_mesh.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index 2b9cc68b4..d23830e9d 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -1987,14 +1987,18 @@ def get_edge_inner_product_surface( Here construct and image the edge inner product surface matrix for the isotropic case. Spy plots are used to demonstrate the sparsity of the inner product surface matrices. - >>> tau = np.r_[tau_x * mesh.n_faces_x, tau_y * mesh.n_faces_y, tau_z * mesh.n_faces_z] + >>> tau = np.r_[ + >>> tau_x * np.ones(mesh.n_faces_x), + >>> tau_y * np.ones(mesh.n_faces_y), + >>> tau_z * np.ones(mesh.n_faces_z) + >>> ] >>> M = mesh.get_edge_inner_product_surface(tau) Then plot the sparse representation, >>> fig = plt.figure(figsize=(4, 4)) >>> ax1 = fig.add_subplot(111) - >>> ax1.spy(M, ms=5) + >>> ax1.imshow(M.todense()) >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ @@ -2106,14 +2110,18 @@ def get_face_inner_product_surface( Here construct and image the face inner product surface matrix for the isotropic case. Spy plots are used to demonstrate the sparsity of the inner product surface matrices. - >>> tau = np.r_[tau_x * mesh.n_faces_x, tau_y * mesh.n_faces_y, tau_z * mesh.n_faces_z] + >>> tau = np.r_[ + >>> tau_x * np.ones(mesh.n_faces_x), + >>> tau_y * np.ones(mesh.n_faces_y), + >>> tau_z * np.ones(mesh.n_faces_z) + >>> ] >>> M = mesh.get_face_inner_product_surface(tau) Then plot the sparse representation, >>> fig = plt.figure(figsize=(4, 4)) >>> ax1 = fig.add_subplot(111) - >>> ax1.spy(M, ms=5) + >>> ax1.imshow(M.todense()) >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ @@ -2228,7 +2236,6 @@ def get_edge_inner_product_line( >>> from discretize import TensorMesh >>> import matplotlib.pyplot as plt >>> import numpy as np - >>> import matplotlib as mpl >>> h = np.ones(2) >>> mesh = TensorMesh([h, h, h]) @@ -2251,14 +2258,18 @@ def get_edge_inner_product_line( Here construct and image the edge inner product line matrix for the isotropic case. Spy plots are used to demonstrate the sparsity of the matrix. - >>> tau = np.r_[tau_x * mesh.n_edges_x, tau_y * mesh.n_edges_y, tau_z * mesh.n_edges_z] + >>> tau = np.r_[ + >>> tau_x * np.ones(mesh.n_edges_x), + >>> tau_y * np.ones(mesh.n_edges_y), + >>> tau_z * np.ones(mesh.n_edges_z) + >>> ] >>> M = mesh.get_edge_inner_product_line(tau) Then plot the sparse representation, >>> fig = plt.figure(figsize=(4, 4)) >>> ax1 = fig.add_subplot(111) - >>> ax1.spy(M, ms=5) + >>> ax1.imshow(M.todense()) >>> ax1.set_title("M (isotropic)", fontsize=16) >>> plt.show() """ From a8dbe89058c8e662dbe8f989a26f86782fc23021 Mon Sep 17 00:00:00 2001 From: dccowan Date: Thu, 6 Jul 2023 15:44:40 -0700 Subject: [PATCH 23/41] fix broken example --- discretize/operators/inner_products.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index ac5f66e02..b811d7b39 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -99,7 +99,7 @@ def get_edge_inner_product( # NOQA D102 ) def get_edge_inner_product_surface( # NOQA D102 - self, model, invert_model=False, invert_matrix=False, **kwargs + self, model=None, invert_model=False, invert_matrix=False, **kwargs ): # Inherited documentation from discretize.base.BaseMesh if model is None: From 51c16c06d20f4e1536dc02c8f8f8b64db7adf1b3 Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 17 Jul 2023 11:35:22 -0700 Subject: [PATCH 24/41] remove main from tests --- tests/base/test_tensor_innerproduct.py | 4 ---- tests/base/test_tensor_innerproduct_derivs.py | 4 ---- tests/cyl/test_cyl_innerproducts.py | 4 ---- tests/tree/test_tree_innerproduct_derivs.py | 4 ---- tests/tree/test_tree_interpolation.py | 4 ---- 5 files changed, 20 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index b36ee7a18..7a3afe1db 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -769,10 +769,6 @@ def test_edge_inner_product_line(self): ValueError, self.mesh3D.get_edge_inner_product_line, self.model ) - -if __name__ == "__main__": - unittest.main() - ################################################### #### Uncomment to Reevaluate the InnerProducts #### ################################################### diff --git a/tests/base/test_tensor_innerproduct_derivs.py b/tests/base/test_tensor_innerproduct_derivs.py index e49fc8d12..0273192c6 100644 --- a/tests/base/test_tensor_innerproduct_derivs.py +++ b/tests/base/test_tensor_innerproduct_derivs.py @@ -576,7 +576,3 @@ def test_face_inner_product_surface_deriv(self): def test_edge_inner_product_line_deriv(self): self.assertIsNone(self.mesh3D.get_edge_inner_product_line_deriv(None)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/cyl/test_cyl_innerproducts.py b/tests/cyl/test_cyl_innerproducts.py index a591a8114..5a9424799 100644 --- a/tests/cyl/test_cyl_innerproducts.py +++ b/tests/cyl/test_cyl_innerproducts.py @@ -828,7 +828,3 @@ def fun(x): return self.assertTrue( tests.check_derivative(fun, self.x0, num=7, tolerance=TOLD, plotIt=False) ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/tree/test_tree_innerproduct_derivs.py b/tests/tree/test_tree_innerproduct_derivs.py index 6f6fcf9fc..96885a749 100644 --- a/tests/tree/test_tree_innerproduct_derivs.py +++ b/tests/tree/test_tree_innerproduct_derivs.py @@ -296,7 +296,3 @@ def test_EdgeIP_2D_isotropic_fast_Tree(self): def test_EdgeIP_3D_isotropic_fast_Tree(self): self.assertTrue(self.doTestEdge([8, 8, 8], 1, "Tree")) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/tree/test_tree_interpolation.py b/tests/tree/test_tree_interpolation.py index ec506f2c2..2886a2970 100644 --- a/tests/tree/test_tree_interpolation.py +++ b/tests/tree/test_tree_interpolation.py @@ -226,7 +226,3 @@ def testCaching(self): A1 = mesh.average_edge_to_face A2 = mesh.average_edge_to_face self.assertIs(A1, A2) - - -if __name__ == "__main__": - unittest.main() From 0246f5ebff957b890e5290fe681297581f58056c Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 17 Jul 2023 11:42:53 -0700 Subject: [PATCH 25/41] style check --- tests/base/test_tensor_innerproduct.py | 1 + tests/tree/test_tree_interpolation.py | 26 +++++++++++++------------- tests/tree/test_tree_operators.py | 4 ---- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 7a3afe1db..9b04b3cf8 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -769,6 +769,7 @@ def test_edge_inner_product_line(self): ValueError, self.mesh3D.get_edge_inner_product_line, self.model ) + ################################################### #### Uncomment to Reevaluate the InnerProducts #### ################################################### diff --git a/tests/tree/test_tree_interpolation.py b/tests/tree/test_tree_interpolation.py index 2886a2970..51bd2903a 100644 --- a/tests/tree/test_tree_interpolation.py +++ b/tests/tree/test_tree_interpolation.py @@ -3,26 +3,26 @@ import discretize MESHTYPES = ["uniformTree"] # ['randomTree', 'uniformTree'] -call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) -call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) -cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] -cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ +call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) # NOQA E731 +call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 +cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] # NOQA E731 +cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ # NOQA E731 call3(xfun, g), call3(yfun, g), call3(zfun, g) ] -cartF2 = lambda M, fx, fy: np.vstack( +cartF2 = lambda M, fx, fy: np.vstack( # NOQA E731 (cart_row2(M.gridFx, fx, fy), cart_row2(M.gridFy, fx, fy)) ) -cartE2 = lambda M, ex, ey: np.vstack( +cartE2 = lambda M, ex, ey: np.vstack( # NOQA E731 (cart_row2(M.gridEx, ex, ey), cart_row2(M.gridEy, ex, ey)) ) -cartF3 = lambda M, fx, fy, fz: np.vstack( +cartF3 = lambda M, fx, fy, fz: np.vstack( # NOQA E731 ( cart_row3(M.gridFx, fx, fy, fz), cart_row3(M.gridFy, fx, fy, fz), cart_row3(M.gridFz, fx, fy, fz), ) ) -cartE3 = lambda M, ex, ey, ez: np.vstack( +cartE3 = lambda M, ex, ey, ez: np.vstack( # NOQA E731 ( cart_row3(M.gridEx, ex, ey, ez), cart_row3(M.gridEy, ex, ey, ez), @@ -54,8 +54,8 @@ class TestInterpolation2d(discretize.tests.OrderTest): expectedOrders = 1 def getError(self): - funX = lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x - funY = lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y + funX = lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x # NOQA E731 + funY = lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y # NOQA E731 # self.LOCS = self.M.gridCC @@ -138,9 +138,9 @@ class TestInterpolation3D(discretize.tests.OrderTest): meshSizes = [8, 16] def getError(self): - funX = lambda x, y, z: np.cos(2 * np.pi * y) - funY = lambda x, y, z: np.cos(2 * np.pi * z) - funZ = lambda x, y, z: np.cos(2 * np.pi * x) + funX = lambda x, y, z: np.cos(2 * np.pi * y) # NOQA E731 + funY = lambda x, y, z: np.cos(2 * np.pi * z) # NOQA E731 + funZ = lambda x, y, z: np.cos(2 * np.pi * x) # NOQA E731 if "x" in self.type: ana = call3(funX, self.LOCS) diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 0cd7268a2..8369dac45 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -1155,7 +1155,3 @@ def test_orderCC2F(self): self.expectedOrders = 1 self.orderTest() self.expectedOrders = 2 - - -if __name__ == "__main__": - unittest.main() From e729631d54410b2747062089f61ee0efa05662b5 Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 17 Jul 2023 11:51:31 -0700 Subject: [PATCH 26/41] style checks --- tests/cyl/test_cyl_operators.py | 6 +++--- tests/tree/test_tree_interpolation.py | 8 ++++++-- tests/tree/test_tree_operators.py | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/cyl/test_cyl_operators.py b/tests/cyl/test_cyl_operators.py index e7e78c807..b41010b9d 100644 --- a/tests/cyl/test_cyl_operators.py +++ b/tests/cyl/test_cyl_operators.py @@ -412,7 +412,7 @@ def test_simple_face_inner_product(mesh_type): @pytest.mark.parametrize("mesh_type", NONSYMMETRIC) def test_simple_edge_ave(mesh_type): - func = lambda r, t, z: np.c_[r, r, z] + func = lambda r, t, z: np.c_[r, r, z] # NOQA E731 mesh, _ = setup_mesh(mesh_type, 10) e_ana = mesh.project_edge_vector(func(*mesh.edges.T)) ave_e = mesh.aveE2CCV @ e_ana @@ -424,7 +424,7 @@ def test_simple_edge_ave(mesh_type): @pytest.mark.parametrize("mesh_type", NONSYMMETRIC) def test_simple_face_ave(mesh_type): - func = lambda r, t, z: np.c_[r, r, z] + func = lambda r, t, z: np.c_[r, r, z] # NOQA E731 mesh, _ = setup_mesh(mesh_type, 10) f_ana = mesh.project_face_vector(func(*mesh.faces.T)) ave_f = mesh.aveF2CCV @ f_ana @@ -564,7 +564,7 @@ def get_error(n_cells): ) @pytest.mark.parametrize("mesh_type", NONSYMMETRIC) def test_interpolation(mesh_type, location_type): - u_func = lambda x, y, z: x**2 + y**2 + z**2 + u_func = lambda x, y, z: x**2 + y**2 + z**2 # NOQA E731 interp_points = ( np.mgrid[0.3:0.8:5j, np.pi / 10 : np.pi / 5 : 5j, 0.3:0.8:5j].reshape(3, -1).T diff --git a/tests/tree/test_tree_interpolation.py b/tests/tree/test_tree_interpolation.py index 51bd2903a..9faca3d5d 100644 --- a/tests/tree/test_tree_interpolation.py +++ b/tests/tree/test_tree_interpolation.py @@ -54,8 +54,12 @@ class TestInterpolation2d(discretize.tests.OrderTest): expectedOrders = 1 def getError(self): - funX = lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x # NOQA E731 - funY = lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y # NOQA E731 + funX = ( + lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x + ) # NOQA E731 + funY = ( + lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y + ) # NOQA E731 # self.LOCS = self.M.gridCC diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 8369dac45..818c51a27 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -1,5 +1,5 @@ import numpy as np -import unittest +# import unittest import discretize MESHTYPES = ["uniformTree", "randomTree"] From df6fce68befd2953c9ba3038ae61554350f6d8dc Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 17 Jul 2023 12:00:51 -0700 Subject: [PATCH 27/41] one more format...sigh --- tests/tree/test_tree_operators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 818c51a27..f285486ae 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -1,4 +1,5 @@ import numpy as np + # import unittest import discretize From 990cca14f6cc7e8d67ff8cacb52f67cea1fe8f1a Mon Sep 17 00:00:00 2001 From: dccowan Date: Tue, 18 Jul 2023 09:22:20 -0700 Subject: [PATCH 28/41] remove NOQA E731 --- tests/base/test_tensor_innerproduct.py | 106 ++++++------ tests/cyl/test_cyl_operators.py | 6 +- tests/tree/test_tree_interpolation.py | 26 +-- tests/tree/test_tree_operators.py | 224 ++++++++++++------------- 4 files changed, 181 insertions(+), 181 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 9b04b3cf8..20a0aaf11 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -16,18 +16,18 @@ class TestInnerProducts(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) - ex = lambda x, y, z: x**2 + y * z # NOQA E731 - ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 - ez = lambda x, y, z: y**2 + x * z # NOQA E731 + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z - sigma1 = lambda x, y, z: x * y + 1 # NOQA E731 - sigma2 = lambda x, y, z: x * z + 2 # NOQA E731 - sigma3 = lambda x, y, z: 3 + z * y # NOQA E731 - sigma4 = lambda x, y, z: 0.1 * x * y * z # NOQA E731 - sigma5 = lambda x, y, z: 0.2 * x * y # NOQA E731 - sigma6 = lambda x, y, z: 0.1 * z # NOQA E731 + sigma1 = lambda x, y, z: x * y + 1 + sigma2 = lambda x, y, z: x * z + 2 + sigma3 = lambda x, y, z: 3 + z * y + sigma4 = lambda x, y, z: 0.1 * x * y * z + sigma5 = lambda x, y, z: 0.2 * x * y + sigma6 = lambda x, y, z: 0.1 * z Gc = self.M.gridCC if self.sigmaTest == 1: @@ -48,7 +48,7 @@ def getError(self): analytic = 69881.0 / 21600 # Found using sympy. if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) ) @@ -63,7 +63,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) ) @@ -175,15 +175,15 @@ class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) - ex = lambda x, y, z: x**2 + y * z # NOQA E731 - ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 - ez = lambda x, y, z: y**2 + x * z # NOQA E731 + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: y * z + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y, z: x * z + 2 # NOQA E731 # y-face properties - tau_z = lambda x, y, z: 3 + x * y # NOQA E731 # z-face properties + tau_x = lambda x, y, z: y * z + 1 # x-face properties + tau_y = lambda x, y, z: x * z + 2 # y-face properties + tau_z = lambda x, y, z: 3 + x * y # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -201,7 +201,7 @@ def getError(self): if self.location == "edges": analytic = 5.02760416666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) @@ -229,7 +229,7 @@ def getError(self): elif self.location == "faces": analytic = 2.66979166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) @@ -323,15 +323,15 @@ class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) - ex = lambda x, y, z: x**2 + y * z # NOQA E731 - ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 - ez = lambda x, y, z: y**2 + x * z # NOQA E731 + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: x + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y, z: y + 2 # NOQA E731 # y-face properties - tau_z = lambda x, y, z: 3 * z + 1 # NOQA E731 # z-face properties + tau_x = lambda x, y, z: x + 1 # x-face properties + tau_y = lambda x, y, z: y + 2 # y-face properties + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -349,7 +349,7 @@ def getError(self): analytic = 1.98906250000000 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) @@ -413,14 +413,14 @@ class TestInnerProducts2D(discretize.tests.OrderTest): def getError(self): z = 5 # Because 5 is just such a great number. - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) - ex = lambda x, y: x**2 + y * z # NOQA E731 - ey = lambda x, y: (z**2) * x + y * z # NOQA E731 + ex = lambda x, y: x**2 + y * z + ey = lambda x, y: (z**2) * x + y * z - sigma1 = lambda x, y: x * y + 1 # NOQA E731 - sigma2 = lambda x, y: x * z + 2 # NOQA E731 - sigma3 = lambda x, y: 3 + z * y # NOQA E731 + sigma1 = lambda x, y: x * y + 1 + sigma2 = lambda x, y: x * z + 2 + sigma3 = lambda x, y: 3 + z * y Gc = self.M.gridCC if self.sigmaTest == 1: @@ -434,7 +434,7 @@ def getError(self): analytic = 781427.0 / 360 # Found using sympy. z=5 if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) if self.invert_model: @@ -446,7 +446,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -556,13 +556,13 @@ class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) - ex = lambda x, y: x**2 + y # NOQA E731 - ey = lambda x, y: (y**2) * x # NOQA E731 + ex = lambda x, y: x**2 + y + ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2 * y + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y: x + 2 # NOQA E731 # y-face properties + tau_x = lambda x, y: 2 * y + 1 # x-face properties + tau_y = lambda x, y: x + 2 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -580,7 +580,7 @@ def getError(self): if self.location == "edges": analytic = 2.24166666666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -596,7 +596,7 @@ def getError(self): elif self.location == "faces": analytic = 1.59895833333333 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -646,13 +646,13 @@ class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) - ex = lambda x, y: x**2 + y # NOQA E731 - ey = lambda x, y: (x**2) * y # NOQA E731 + ex = lambda x, y: x**2 + y + ey = lambda x, y: (x**2) * y - tau_x = lambda x, y: x + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y: y + 2 # NOQA E731 # y-face properties + tau_x = lambda x, y: x + 1 # x-face properties + tau_y = lambda x, y: y + 2 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -670,7 +670,7 @@ def getError(self): analytic = 1.38229166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -711,11 +711,11 @@ def getError(self): y = 12 # Because 12 is just such a great number. z = 5 # Because 5 is just such a great number as well! - call = lambda fun, x: fun(x) # NOQA E731 + call = lambda fun, x: fun(x) - ex = lambda x: x**2 + y * z # NOQA E731 + ex = lambda x: x**2 + y * z - sigma1 = lambda x: x * y + 1 # NOQA E731 + sigma1 = lambda x: x * y + 1 Gc = self.M.gridCC sigma = call(sigma1, Gc) diff --git a/tests/cyl/test_cyl_operators.py b/tests/cyl/test_cyl_operators.py index b41010b9d..e7e78c807 100644 --- a/tests/cyl/test_cyl_operators.py +++ b/tests/cyl/test_cyl_operators.py @@ -412,7 +412,7 @@ def test_simple_face_inner_product(mesh_type): @pytest.mark.parametrize("mesh_type", NONSYMMETRIC) def test_simple_edge_ave(mesh_type): - func = lambda r, t, z: np.c_[r, r, z] # NOQA E731 + func = lambda r, t, z: np.c_[r, r, z] mesh, _ = setup_mesh(mesh_type, 10) e_ana = mesh.project_edge_vector(func(*mesh.edges.T)) ave_e = mesh.aveE2CCV @ e_ana @@ -424,7 +424,7 @@ def test_simple_edge_ave(mesh_type): @pytest.mark.parametrize("mesh_type", NONSYMMETRIC) def test_simple_face_ave(mesh_type): - func = lambda r, t, z: np.c_[r, r, z] # NOQA E731 + func = lambda r, t, z: np.c_[r, r, z] mesh, _ = setup_mesh(mesh_type, 10) f_ana = mesh.project_face_vector(func(*mesh.faces.T)) ave_f = mesh.aveF2CCV @ f_ana @@ -564,7 +564,7 @@ def get_error(n_cells): ) @pytest.mark.parametrize("mesh_type", NONSYMMETRIC) def test_interpolation(mesh_type, location_type): - u_func = lambda x, y, z: x**2 + y**2 + z**2 # NOQA E731 + u_func = lambda x, y, z: x**2 + y**2 + z**2 interp_points = ( np.mgrid[0.3:0.8:5j, np.pi / 10 : np.pi / 5 : 5j, 0.3:0.8:5j].reshape(3, -1).T diff --git a/tests/tree/test_tree_interpolation.py b/tests/tree/test_tree_interpolation.py index 9faca3d5d..b80a20cab 100644 --- a/tests/tree/test_tree_interpolation.py +++ b/tests/tree/test_tree_interpolation.py @@ -3,26 +3,26 @@ import discretize MESHTYPES = ["uniformTree"] # ['randomTree', 'uniformTree'] -call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) # NOQA E731 -call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 -cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] # NOQA E731 -cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ # NOQA E731 +call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) +call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) +cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] +cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ call3(xfun, g), call3(yfun, g), call3(zfun, g) ] -cartF2 = lambda M, fx, fy: np.vstack( # NOQA E731 +cartF2 = lambda M, fx, fy: np.vstack( (cart_row2(M.gridFx, fx, fy), cart_row2(M.gridFy, fx, fy)) ) -cartE2 = lambda M, ex, ey: np.vstack( # NOQA E731 +cartE2 = lambda M, ex, ey: np.vstack( (cart_row2(M.gridEx, ex, ey), cart_row2(M.gridEy, ex, ey)) ) -cartF3 = lambda M, fx, fy, fz: np.vstack( # NOQA E731 +cartF3 = lambda M, fx, fy, fz: np.vstack( ( cart_row3(M.gridFx, fx, fy, fz), cart_row3(M.gridFy, fx, fy, fz), cart_row3(M.gridFz, fx, fy, fz), ) ) -cartE3 = lambda M, ex, ey, ez: np.vstack( # NOQA E731 +cartE3 = lambda M, ex, ey, ez: np.vstack( ( cart_row3(M.gridEx, ex, ey, ez), cart_row3(M.gridEy, ex, ey, ez), @@ -56,10 +56,10 @@ class TestInterpolation2d(discretize.tests.OrderTest): def getError(self): funX = ( lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x - ) # NOQA E731 + ) funY = ( lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y - ) # NOQA E731 + ) # self.LOCS = self.M.gridCC @@ -142,9 +142,9 @@ class TestInterpolation3D(discretize.tests.OrderTest): meshSizes = [8, 16] def getError(self): - funX = lambda x, y, z: np.cos(2 * np.pi * y) # NOQA E731 - funY = lambda x, y, z: np.cos(2 * np.pi * z) # NOQA E731 - funZ = lambda x, y, z: np.cos(2 * np.pi * x) # NOQA E731 + funX = lambda x, y, z: np.cos(2 * np.pi * y) + funY = lambda x, y, z: np.cos(2 * np.pi * z) + funZ = lambda x, y, z: np.cos(2 * np.pi * x) if "x" in self.type: ana = call3(funX, self.LOCS) diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index f285486ae..5433a9da5 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -5,26 +5,26 @@ MESHTYPES = ["uniformTree", "randomTree"] # MESHTYPES = ['randomTree'] -call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) # NOQA E731 -call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 -cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] # NOQA E731 -cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ # NOQA E731 +call2 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1]) +call3 = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) +cart_row2 = lambda g, xfun, yfun: np.c_[call2(xfun, g), call2(yfun, g)] +cart_row3 = lambda g, xfun, yfun, zfun: np.c_[ call3(xfun, g), call3(yfun, g), call3(zfun, g) ] -cartF2 = lambda M, fx, fy: np.vstack( # NOQA E731 +cartF2 = lambda M, fx, fy: np.vstack( (cart_row2(M.gridFx, fx, fy), cart_row2(M.gridFy, fx, fy)) ) -cartE2 = lambda M, ex, ey: np.vstack( # NOQA E731 +cartE2 = lambda M, ex, ey: np.vstack( (cart_row2(M.gridEx, ex, ey), cart_row2(M.gridEy, ex, ey)) ) -cartF3 = lambda M, fx, fy, fz: np.vstack( # NOQA E731 +cartF3 = lambda M, fx, fy, fz: np.vstack( ( cart_row3(M.gridFx, fx, fy, fz), cart_row3(M.gridFy, fx, fy, fz), cart_row3(M.gridFz, fx, fy, fz), ) ) -cartE3 = lambda M, ex, ey, ez: np.vstack( # NOQA E731 +cartE3 = lambda M, ex, ey, ez: np.vstack( ( cart_row3(M.gridEx, ex, ey, ez), cart_row3(M.gridEy, ex, ey, ez), @@ -46,13 +46,13 @@ class TestCellGrad2D(discretize.tests.OrderTest): def getError(self): # Test function - sol = lambda x, y: np.cos(2 * np.pi * x) * np.cos(2 * np.pi * y) # NOQA E731 + sol = lambda x, y: np.cos(2 * np.pi * x) * np.cos(2 * np.pi * y) fx = ( lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) - ) # NOQA E731 + ) fy = ( lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) - ) # NOQA E731 + ) phi = call2(sol, self.M.gridCC) gradF = self.M.cell_gradient * phi @@ -126,11 +126,11 @@ class TestFaceDivxy2D(discretize.tests.OrderTest): def getError(self): # Test function - fx = lambda x, y: np.sin(2 * np.pi * x) # NOQA E731 - fy = lambda x, y: np.sin(2 * np.pi * y) # NOQA E731 + fx = lambda x, y: np.sin(2 * np.pi * x) + fy = lambda x, y: np.sin(2 * np.pi * y) sol = ( lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) - ) # NOQA E731 + ) Fx = call2(fx, self.M.gridFx) Fy = call2(fy, self.M.gridFy) @@ -157,10 +157,10 @@ class TestFaceDiv3D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] def getError(self): - fx = lambda x, y, z: np.sin(2 * np.pi * x) # NOQA E731 - fy = lambda x, y, z: np.sin(2 * np.pi * y) # NOQA E731 - fz = lambda x, y, z: np.sin(2 * np.pi * z) # NOQA E731 - sol = lambda x, y, z: ( # NOQA E731 + fx = lambda x, y, z: np.sin(2 * np.pi * x) + fy = lambda x, y, z: np.sin(2 * np.pi * y) + fz = lambda x, y, z: np.sin(2 * np.pi * z) + sol = lambda x, y, z: ( 2 * np.pi * np.cos(2 * np.pi * x) + 2 * np.pi * np.cos(2 * np.pi * y) + 2 * np.pi * np.cos(2 * np.pi * z) @@ -187,10 +187,10 @@ class TestFaceDivxyz3D(discretize.tests.OrderTest): def getError(self): # Test function - fx = lambda x, y, z: np.sin(2 * np.pi * x) # NOQA E731 - fy = lambda x, y, z: np.sin(2 * np.pi * y) # NOQA E731 - fz = lambda x, y, z: np.sin(2 * np.pi * z) # NOQA E731 - sol = lambda x, y, z: ( # NOQA E731 + fx = lambda x, y, z: np.sin(2 * np.pi * x) + fy = lambda x, y, z: np.sin(2 * np.pi * y) + fz = lambda x, y, z: np.sin(2 * np.pi * z) + sol = lambda x, y, z: ( 2 * np.pi * np.cos(2 * np.pi * x) + 2 * np.pi * np.cos(2 * np.pi * y) + 2 * np.pi * np.cos(2 * np.pi * z) @@ -227,13 +227,13 @@ def getError(self): # fun: i (cos(y)) + j (cos(z)) + k (cos(x)) # sol: i (sin(z)) + j (sin(x)) + k (sin(y)) - funX = lambda x, y, z: np.cos(2 * np.pi * y) # NOQA E731 - funY = lambda x, y, z: np.cos(2 * np.pi * z) # NOQA E731 - funZ = lambda x, y, z: np.cos(2 * np.pi * x) # NOQA E731 + funX = lambda x, y, z: np.cos(2 * np.pi * y) + funY = lambda x, y, z: np.cos(2 * np.pi * z) + funZ = lambda x, y, z: np.cos(2 * np.pi * x) - solX = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * z) # NOQA E731 - solY = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * x) # NOQA E731 - solZ = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * y) # NOQA E731 + solX = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * z) + solY = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * x) + solZ = lambda x, y, z: 2 * np.pi * np.sin(2 * np.pi * y) Ec = cartE3(self.M, funX, funY, funZ) E = self.M.project_edge_vector(Ec) @@ -261,11 +261,11 @@ class TestNodalGrad(discretize.tests.OrderTest): def getError(self): # Test function - fun = lambda x, y, z: (np.cos(x) + np.cos(y) + np.cos(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.cos(y) + np.cos(z)) # i (sin(x)) + j (sin(y)) + k (sin(z)) - solX = lambda x, y, z: -np.sin(x) # NOQA E731 - solY = lambda x, y, z: -np.sin(y) # NOQA E731 - solZ = lambda x, y, z: -np.sin(z) # NOQA E731 + solX = lambda x, y, z: -np.sin(x) + solY = lambda x, y, z: -np.sin(y) + solZ = lambda x, y, z: -np.sin(z) phi = call3(fun, self.M.gridN) gradE = self.M.nodal_gradient.dot(phi) @@ -291,10 +291,10 @@ class TestNodalGrad2D(discretize.tests.OrderTest): def getError(self): # Test function - fun = lambda x, y: (np.cos(x) + np.cos(y)) # NOQA E731 + fun = lambda x, y: (np.cos(x) + np.cos(y)) # i (sin(x)) + j (sin(y)) + k (sin(z)) - solX = lambda x, y: -np.sin(x) # NOQA E731 - solY = lambda x, y: -np.sin(y) # NOQA E731 + solX = lambda x, y: -np.sin(x) + solY = lambda x, y: -np.sin(y) phi = call2(fun, self.M.gridN) gradE = self.M.nodal_gradient.dot(phi) @@ -322,18 +322,18 @@ class TestTreeInnerProducts(discretize.tests.OrderTest): meshSizes = [4, 8] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) - ex = lambda x, y, z: x**2 + y * z # NOQA E731 - ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 - ez = lambda x, y, z: y**2 + x * z # NOQA E731 + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z - sigma1 = lambda x, y, z: x * y + 1 # NOQA E731 - sigma2 = lambda x, y, z: x * z + 2 # NOQA E731 - sigma3 = lambda x, y, z: 3 + z * y # NOQA E731 - sigma4 = lambda x, y, z: 0.1 * x * y * z # NOQA E731 - sigma5 = lambda x, y, z: 0.2 * x * y # NOQA E731 - sigma6 = lambda x, y, z: 0.1 * z # NOQA E731 + sigma1 = lambda x, y, z: x * y + 1 + sigma2 = lambda x, y, z: x * z + 2 + sigma3 = lambda x, y, z: 3 + z * y + sigma4 = lambda x, y, z: 0.1 * x * y * z + sigma5 = lambda x, y, z: 0.2 * x * y + sigma6 = lambda x, y, z: 0.1 * z Gc = self.M.gridCC if self.sigmaTest == 1: @@ -354,7 +354,7 @@ def getError(self): analytic = 69881.0 / 21600 # Found using sympy. if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) ) @@ -369,7 +369,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) ) @@ -481,15 +481,15 @@ class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): meshSizes = [8, 16] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) - ex = lambda x, y, z: x**2 + y * z # NOQA E731 - ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 - ez = lambda x, y, z: y**2 + x * z # NOQA E731 + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: y * z + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y, z: x * z + 2 # NOQA E731 # y-face properties - tau_z = lambda x, y, z: 3 + x * y # NOQA E731 # z-face properties + tau_x = lambda x, y, z: y * z + 1 # x-face properties + tau_y = lambda x, y, z: x * z + 2 # y-face properties + tau_z = lambda x, y, z: 3 + x * y # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -507,7 +507,7 @@ def getError(self): if self.location == "edges": analytic = 5.02760416666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Ec = np.vstack( (cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz)) @@ -525,7 +525,7 @@ def getError(self): elif self.location == "faces": analytic = 2.66979166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Fc = np.vstack( (cart(self.M.gridFx), cart(self.M.gridFy), cart(self.M.gridFz)) @@ -577,15 +577,15 @@ class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) # NOQA E731 + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) - ex = lambda x, y, z: x**2 + y * z # NOQA E731 - ey = lambda x, y, z: (z**2) * x + y * z # NOQA E731 - ez = lambda x, y, z: y**2 + x * z # NOQA E731 + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: x + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y, z: y + 2 # NOQA E731 # y-face properties - tau_z = lambda x, y, z: 3 * z + 1 # NOQA E731 # z-face properties + tau_x = lambda x, y, z: x + 1 # x-face properties + tau_y = lambda x, y, z: y + 2 # y-face properties + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -603,7 +603,7 @@ def getError(self): analytic = 1.98906250000000 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g), call(ez, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy), cart(self.M.gridEz))) E = self.M.project_edge_vector(Ec) @@ -642,14 +642,14 @@ class TestTreeInnerProducts2D(discretize.tests.OrderTest): def getError(self): z = 5 # Because 5 is just such a great number. - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) - ex = lambda x, y: x**2 + y * z # NOQA E731 - ey = lambda x, y: (z**2) * x + y * z # NOQA E731 + ex = lambda x, y: x**2 + y * z + ey = lambda x, y: (z**2) * x + y * z - sigma1 = lambda x, y: x * y + 1 # NOQA E731 - sigma2 = lambda x, y: x * z + 2 # NOQA E731 - sigma3 = lambda x, y: 3 + z * y # NOQA E731 + sigma1 = lambda x, y: x * y + 1 + sigma2 = lambda x, y: x * z + 2 + sigma3 = lambda x, y: 3 + z * y Gc = self.M.gridCC if self.sigmaTest == 1: @@ -663,7 +663,7 @@ def getError(self): analytic = 781427.0 / 360 # Found using sympy. z=5 if self.location == "edges": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) if self.invert_model: @@ -675,7 +675,7 @@ def getError(self): A = self.M.get_edge_inner_product(sigma) numeric = E.T.dot(A.dot(E)) elif self.location == "faces": - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -785,13 +785,13 @@ class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 # NOQA E731 + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) - ex = lambda x, y: x**2 + y # NOQA E731 - ey = lambda x, y: (y**2) * x # NOQA E731 + ex = lambda x, y: x**2 + y + ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2 * y + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y: x + 2 # NOQA E731 # y-face properties + tau_x = lambda x, y: 2 * y + 1 # x-face properties + tau_y = lambda x, y: x + 2 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -809,7 +809,7 @@ def getError(self): if self.location == "edges": analytic = 2.24166666666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -825,7 +825,7 @@ def getError(self): elif self.location == "faces": analytic = 1.59895833333333 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Fc = np.vstack((cart(self.M.gridFx), cart(self.M.gridFy))) F = self.M.project_face_vector(Fc) @@ -875,13 +875,13 @@ class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): meshSizes = [16, 32] def getError(self): - call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA E731 + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) - ex = lambda x, y: x**2 + y # NOQA E731 - ey = lambda x, y: (x**2) * y # NOQA E731 + ex = lambda x, y: x**2 + y + ey = lambda x, y: (x**2) * y - tau_x = lambda x, y: x + 1 # NOQA E731 # x-face properties - tau_y = lambda x, y: y + 2 # NOQA E731 # y-face properties + tau_x = lambda x, y: x + 1 # x-face properties + tau_y = lambda x, y: y + 2 # y-face properties tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -899,7 +899,7 @@ def getError(self): analytic = 1.38229166666667 # Found using sympy. - cart = lambda g: np.c_[call(ex, g), call(ey, g)] # NOQA E731 + cart = lambda g: np.c_[call(ex, g), call(ey, g)] Ec = np.vstack((cart(self.M.gridEx), cart(self.M.gridEy))) E = self.M.project_edge_vector(Ec) @@ -943,7 +943,7 @@ def getError(self): def test_orderN2CC(self): self.name = "Averaging 2D: N2CC" - fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + fun = lambda x, y: (np.cos(x) + np.sin(y)) self.getHere = lambda M: call2(fun, M.gridN) self.getThere = lambda M: call2(fun, M.gridCC) self.getAve = lambda M: M.aveN2CC @@ -951,7 +951,7 @@ def test_orderN2CC(self): def test_orderN2Fx(self): self.name = "Averaging 2D: N2Fx" - fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + fun = lambda x, y: (np.cos(x) + np.sin(y)) self.getHere = lambda M: call2(fun, M.gridN) self.getThere = lambda M: np.r_[call2(fun, M.gridFx), call2(fun, M.gridFy)] self.getAve = lambda M: M.aveN2F @@ -959,7 +959,7 @@ def test_orderN2Fx(self): def test_orderN2E(self): self.name = "Averaging 2D: N2E" - fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + fun = lambda x, y: (np.cos(x) + np.sin(y)) self.getHere = lambda M: call2(fun, M.gridN) self.getThere = lambda M: np.r_[call2(fun, M.gridEx), call2(fun, M.gridEy)] self.getAve = lambda M: M.aveN2E @@ -967,7 +967,7 @@ def test_orderN2E(self): def test_orderF2CC(self): self.name = "Averaging 2D: F2CC" - fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + fun = lambda x, y: (np.cos(x) + np.sin(y)) self.getHere = lambda M: np.r_[call2(fun, np.r_[M.gridFx, M.gridFy])] self.getThere = lambda M: call2(fun, M.gridCC) self.getAve = lambda M: M.aveF2CC @@ -975,7 +975,7 @@ def test_orderF2CC(self): def test_orderFx2CC(self): self.name = "Averaging 2D: Fx2CC" - funX = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + funX = lambda x, y: (np.cos(x) + np.sin(y)) self.getHere = lambda M: np.r_[call2(funX, M.gridFx)] self.getThere = lambda M: np.r_[call2(funX, M.gridCC)] self.getAve = lambda M: M.aveFx2CC @@ -983,7 +983,7 @@ def test_orderFx2CC(self): def test_orderFy2CC(self): self.name = "Averaging 2D: Fy2CC" - funY = lambda x, y: (np.cos(y) * np.sin(x)) # NOQA E731 + funY = lambda x, y: (np.cos(y) * np.sin(x)) self.getHere = lambda M: np.r_[call2(funY, M.gridFy)] self.getThere = lambda M: np.r_[call2(funY, M.gridCC)] self.getAve = lambda M: M.aveFy2CC @@ -991,8 +991,8 @@ def test_orderFy2CC(self): def test_orderF2CCV(self): self.name = "Averaging 2D: F2CCV" - funX = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 - funY = lambda x, y: (np.cos(y) * np.sin(x)) # NOQA E731 + funX = lambda x, y: (np.cos(x) + np.sin(y)) + funY = lambda x, y: (np.cos(y) * np.sin(x)) self.getHere = lambda M: np.r_[call2(funX, M.gridFx), call2(funY, M.gridFy)] self.getThere = lambda M: np.r_[call2(funX, M.gridCC), call2(funY, M.gridCC)] self.getAve = lambda M: M.aveF2CCV @@ -1000,7 +1000,7 @@ def test_orderF2CCV(self): def test_orderCC2F(self): self.name = "Averaging 2D: CC2F" - fun = lambda x, y: (np.cos(x) + np.sin(y)) # NOQA E731 + fun = lambda x, y: (np.cos(x) + np.sin(y)) self.getHere = lambda M: call2(fun, M.gridCC) self.getThere = lambda M: np.r_[call2(fun, M.gridFx), call2(fun, M.gridFy)] self.getAve = lambda M: M.aveCC2F @@ -1023,7 +1023,7 @@ def getError(self): def test_orderN2CC(self): self.name = "Averaging 3D: N2CC" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: call3(fun, M.gridN) self.getThere = lambda M: call3(fun, M.gridCC) self.getAve = lambda M: M.aveN2CC @@ -1031,7 +1031,7 @@ def test_orderN2CC(self): def test_orderN2F(self): self.name = "Averaging 3D: N2F" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: call3(fun, M.gridN) self.getThere = lambda M: np.r_[ call3(fun, M.gridFx), call3(fun, M.gridFy), call3(fun, M.gridFz) @@ -1041,7 +1041,7 @@ def test_orderN2F(self): def test_orderN2E(self): self.name = "Averaging 3D: N2E" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: call3(fun, M.gridN) self.getThere = lambda M: np.r_[ call3(fun, M.gridEx), call3(fun, M.gridEy), call3(fun, M.gridEz) @@ -1051,7 +1051,7 @@ def test_orderN2E(self): def test_orderF2CC(self): self.name = "Averaging 3D: F2CC" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[ call3(fun, M.gridFx), call3(fun, M.gridFy), call3(fun, M.gridFz) ] @@ -1061,7 +1061,7 @@ def test_orderF2CC(self): def test_orderFx2CC(self): self.name = "Averaging 3D: Fx2CC" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[call3(funX, M.gridFx)] self.getThere = lambda M: np.r_[call3(funX, M.gridCC)] self.getAve = lambda M: M.aveFx2CC @@ -1069,7 +1069,7 @@ def test_orderFx2CC(self): def test_orderFy2CC(self): self.name = "Averaging 3D: Fy2CC" - funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 + funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) self.getHere = lambda M: np.r_[call3(funY, M.gridFy)] self.getThere = lambda M: np.r_[call3(funY, M.gridCC)] self.getAve = lambda M: M.aveFy2CC @@ -1077,7 +1077,7 @@ def test_orderFy2CC(self): def test_orderFz2CC(self): self.name = "Averaging 3D: Fz2CC" - funZ = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 + funZ = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) self.getHere = lambda M: np.r_[call3(funZ, M.gridFz)] self.getThere = lambda M: np.r_[call3(funZ, M.gridCC)] self.getAve = lambda M: M.aveFz2CC @@ -1085,9 +1085,9 @@ def test_orderFz2CC(self): def test_orderF2CCV(self): self.name = "Averaging 3D: F2CCV" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 - funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 - funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) # NOQA E731 + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) + funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[ call3(funX, M.gridFx), call3(funY, M.gridFy), call3(funZ, M.gridFz) ] @@ -1099,7 +1099,7 @@ def test_orderF2CCV(self): def test_orderEx2CC(self): self.name = "Averaging 3D: Ex2CC" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[call3(funX, M.gridEx)] self.getThere = lambda M: np.r_[call3(funX, M.gridCC)] self.getAve = lambda M: M.aveEx2CC @@ -1107,7 +1107,7 @@ def test_orderEx2CC(self): def test_orderEy2CC(self): self.name = "Averaging 3D: Ey2CC" - funY = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + funY = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[call3(funY, M.gridEy)] self.getThere = lambda M: np.r_[call3(funY, M.gridCC)] self.getAve = lambda M: M.aveEy2CC @@ -1115,7 +1115,7 @@ def test_orderEy2CC(self): def test_orderEz2CC(self): self.name = "Averaging 3D: Ez2CC" - funZ = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + funZ = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[call3(funZ, M.gridEz)] self.getThere = lambda M: np.r_[call3(funZ, M.gridCC)] self.getAve = lambda M: M.aveEz2CC @@ -1123,7 +1123,7 @@ def test_orderEz2CC(self): def test_orderE2CC(self): self.name = "Averaging 3D: E2CC" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[ call3(fun, M.gridEx), call3(fun, M.gridEy), call3(fun, M.gridEz) ] @@ -1133,9 +1133,9 @@ def test_orderE2CC(self): def test_orderE2CCV(self): self.name = "Averaging 3D: E2CCV" - funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 - funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) # NOQA E731 - funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) # NOQA E731 + funX = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) + funY = lambda x, y, z: (np.cos(x) + np.sin(y) * np.exp(z)) + funZ = lambda x, y, z: (np.cos(x) * np.sin(y) + np.exp(z)) self.getHere = lambda M: np.r_[ call3(funX, M.gridEx), call3(funY, M.gridEy), call3(funZ, M.gridEz) ] @@ -1147,7 +1147,7 @@ def test_orderE2CCV(self): def test_orderCC2F(self): self.name = "Averaging 3D: CC2F" - fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) # NOQA E731 + fun = lambda x, y, z: (np.cos(x) + np.sin(y) + np.exp(z)) self.getHere = lambda M: call3(fun, M.gridCC) self.getThere = lambda M: np.r_[ call3(fun, M.gridFx), call3(fun, M.gridFy), call3(fun, M.gridFz) From 861080bf2cc849349efff72a0752ad684031eac1 Mon Sep 17 00:00:00 2001 From: dccowan Date: Tue, 18 Jul 2023 09:33:05 -0700 Subject: [PATCH 29/41] Address latest round of review --- discretize/base/base_mesh.py | 23 ++--------------------- discretize/operators/inner_products.py | 12 +----------- tests/tree/test_tree_interpolation.py | 8 ++------ tests/tree/test_tree_operators.py | 12 +++--------- 4 files changed, 8 insertions(+), 47 deletions(-) diff --git a/discretize/base/base_mesh.py b/discretize/base/base_mesh.py index d23830e9d..d87f468b9 100644 --- a/discretize/base/base_mesh.py +++ b/discretize/base/base_mesh.py @@ -3,7 +3,6 @@ import scipy.sparse as sp import os import json -import warnings from scipy.spatial import KDTree from discretize.utils import is_scalar, mkvc, sdiag, sdinv from discretize.utils.code_utils import ( @@ -2810,16 +2809,7 @@ def get_face_inner_product_surface_deriv( if dMdprop is not None: - def innerProductDeriv(v=None): - if v is None: - warnings.warn( - "Depreciation Warning: TensorMesh.innerProductDeriv." - " You should be supplying a vector. " - "Use: sdiag(u)*dMdprop", - FutureWarning, - stacklevel=2, - ) - return dMdprop + def innerProductDeriv(v): return sdiag(v) * dMdprop return innerProductDeriv @@ -2924,16 +2914,7 @@ def get_edge_inner_product_line_deriv( if dMdprop is not None: - def innerProductDeriv(v=None): - if v is None: - warnings.warn( - "Depreciation Warning: TensorMesh.innerProductDeriv." - " You should be supplying a vector. " - "Use: sdiag(u)*dMdprop", - FutureWarning, - stacklevel=2, - ) - return dMdprop + def innerProductDeriv(v): return sdiag(v) * dMdprop return innerProductDeriv diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index b811d7b39..799ae4979 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -1,6 +1,5 @@ """Construct inner product operators for tensor like meshes.""" from scipy import sparse as sp -import warnings from discretize.base import BaseMesh from discretize.utils import ( sub2ind, @@ -399,16 +398,7 @@ def get_edge_inner_product_surface_deriv( # NOQA D102 if dMdprop is not None: - def innerProductDeriv(v=None): - if v is None: - warnings.warn( - "Depreciation Warning: TensorMesh.innerProductDeriv." - " You should be supplying a vector. " - "Use: sdiag(u)*dMdprop", - FutureWarning, - stacklevel=2, - ) - return dMdprop + def innerProductDeriv(v): return sdiag(v) * dMdprop return innerProductDeriv diff --git a/tests/tree/test_tree_interpolation.py b/tests/tree/test_tree_interpolation.py index b80a20cab..2886a2970 100644 --- a/tests/tree/test_tree_interpolation.py +++ b/tests/tree/test_tree_interpolation.py @@ -54,12 +54,8 @@ class TestInterpolation2d(discretize.tests.OrderTest): expectedOrders = 1 def getError(self): - funX = ( - lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x - ) - funY = ( - lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y - ) + funX = lambda x, y: np.cos(2.0 * np.pi * y) * np.cos(2.0 * np.pi * x) + x + funY = lambda x, y: np.cos(2.0 * np.pi * x) * np.cos(2.0 * np.pi * y) + y # self.LOCS = self.M.gridCC diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index 5433a9da5..bec90f862 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -47,12 +47,8 @@ class TestCellGrad2D(discretize.tests.OrderTest): def getError(self): # Test function sol = lambda x, y: np.cos(2 * np.pi * x) * np.cos(2 * np.pi * y) - fx = ( - lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) - ) - fy = ( - lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) - ) + fx = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * x) * np.cos(2 * np.pi * y) + fy = lambda x, y: -2 * np.pi * np.sin(2 * np.pi * y) * np.cos(2 * np.pi * x) phi = call2(sol, self.M.gridCC) gradF = self.M.cell_gradient * phi @@ -128,9 +124,7 @@ def getError(self): # Test function fx = lambda x, y: np.sin(2 * np.pi * x) fy = lambda x, y: np.sin(2 * np.pi * y) - sol = ( - lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) - ) + sol = lambda x, y: 2 * np.pi * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)) Fx = call2(fx, self.M.gridFx) Fy = call2(fy, self.M.gridFy) From 97bc7cb2dfb104ea82cee8586995b8ff4ddc2f32 Mon Sep 17 00:00:00 2001 From: dccowan Date: Tue, 18 Jul 2023 09:48:19 -0700 Subject: [PATCH 30/41] add correct exception --- tests/base/test_tensor_innerproduct.py | 20 ++++++++++---------- tests/tree/test_tree_operators.py | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/base/test_tensor_innerproduct.py b/tests/base/test_tensor_innerproduct.py index 20a0aaf11..e1e69e4bc 100644 --- a/tests/base/test_tensor_innerproduct.py +++ b/tests/base/test_tensor_innerproduct.py @@ -181,9 +181,9 @@ def getError(self): ey = lambda x, y, z: (z**2) * x + y * z ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: y * z + 1 # x-face properties - tau_y = lambda x, y, z: x * z + 2 # y-face properties - tau_z = lambda x, y, z: 3 + x * y # z-face properties + tau_x = lambda x, y, z: y * z + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y, z: x * z + 2 # y-face properties # NOQA F841 + tau_z = lambda x, y, z: 3 + x * y # z-face properties # NOQA F841 tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -329,9 +329,9 @@ def getError(self): ey = lambda x, y, z: (z**2) * x + y * z ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: x + 1 # x-face properties - tau_y = lambda x, y, z: y + 2 # y-face properties - tau_z = lambda x, y, z: 3 * z + 1 # z-face properties + tau_x = lambda x, y, z: x + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y, z: y + 2 # y-face properties # NOQA F841 + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties # NOQA F841 tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -561,8 +561,8 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2 * y + 1 # x-face properties - tau_y = lambda x, y: x + 2 # y-face properties + tau_x = lambda x, y: 2 * y + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y: x + 2 # y-face properties # NOQA F841 tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -651,8 +651,8 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (x**2) * y - tau_x = lambda x, y: x + 1 # x-face properties - tau_y = lambda x, y: y + 2 # y-face properties + tau_x = lambda x, y: x + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y: y + 2 # y-face properties # NOQA F841 tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): diff --git a/tests/tree/test_tree_operators.py b/tests/tree/test_tree_operators.py index bec90f862..5e92215a9 100644 --- a/tests/tree/test_tree_operators.py +++ b/tests/tree/test_tree_operators.py @@ -481,9 +481,9 @@ def getError(self): ey = lambda x, y, z: (z**2) * x + y * z ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: y * z + 1 # x-face properties - tau_y = lambda x, y, z: x * z + 2 # y-face properties - tau_z = lambda x, y, z: 3 + x * y # z-face properties + tau_x = lambda x, y, z: y * z + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y, z: x * z + 2 # y-face properties # NOQA F841 + tau_z = lambda x, y, z: 3 + x * y # z-face properties # NOQA F841 tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -577,9 +577,9 @@ def getError(self): ey = lambda x, y, z: (z**2) * x + y * z ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: x + 1 # x-face properties - tau_y = lambda x, y, z: y + 2 # y-face properties - tau_z = lambda x, y, z: 3 * z + 1 # z-face properties + tau_x = lambda x, y, z: x + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y, z: y + 2 # y-face properties # NOQA F841 + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties # NOQA F841 tau = 3 * [None] for ii, comp in enumerate(["x", "y", "z"]): @@ -784,8 +784,8 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2 * y + 1 # x-face properties - tau_y = lambda x, y: x + 2 # y-face properties + tau_x = lambda x, y: 2 * y + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y: x + 2 # y-face properties # NOQA F841 tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): @@ -874,8 +874,8 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (x**2) * y - tau_x = lambda x, y: x + 1 # x-face properties - tau_y = lambda x, y: y + 2 # y-face properties + tau_x = lambda x, y: x + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y: y + 2 # y-face properties # NOQA F841 tau = 2 * [None] for ii, comp in enumerate(["x", "y"]): From ac968f517462402ae6e2f827a24cf1794ae28faf Mon Sep 17 00:00:00 2001 From: dccowan Date: Thu, 20 Jul 2023 17:01:23 -0700 Subject: [PATCH 31/41] initial tests --- discretize/unstructured_mesh.py | 60 +++++++++++ tests/simplex/test_inner_products.py | 142 +++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/discretize/unstructured_mesh.py b/discretize/unstructured_mesh.py index fd24c2a2e..7726683d8 100644 --- a/discretize/unstructured_mesh.py +++ b/discretize/unstructured_mesh.py @@ -517,7 +517,67 @@ def get_edge_inner_product( # NOQA D102 "The inverse of the inner product matrix with a tetrahedral mesh is not supported." ) 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 + 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 + dim = self.dim + + if dim == 2: + Ps = sp.diags(np.ones(n_faces)) + + if model is None: + Mu = sp.diags(face_areas) + else: + if invert_model: + model = 1.0 / model + + if (model.size == 1) | (model.size == n_faces): + model = model * face_areas + Mu = sp.diags(model) + else: + raise ValueError( + "Unrecognized size of model vector.", + "Must be scalar or have length equal to total number of faces." + ) + + A = Ps.T @ Mu @ Ps + + else: + Ps = self._get_edge_surf_int_proj_mats(with_area=False) + + if model is None: + Mu = sp.diags(np.repeat(face_areas, dim)) + else: + if invert_model: + model = 1.0 / model + + if (model.size == 1) | (model.size == n_faces): + model = model * face_areas + Mu = sp.diags(np.repeat(model, dim)) + 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 diff --git a/tests/simplex/test_inner_products.py b/tests/simplex/test_inner_products.py index 88d454f8a..a9de60b1e 100644 --- a/tests/simplex/test_inner_products.py +++ b/tests/simplex/test_inner_products.py @@ -178,6 +178,148 @@ def test_order3_faces_invert_model(self): self.invert_model = True self.orderTest() +class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): + meshSizes = [8, 16, 32] + meshTypes = ["uniform simplex mesh"] + + def setupMesh(self, n): + points, simplices = example_simplex_mesh((n, n)) + self.M = discretize.SimplexMesh(points, simplices) + return 1.0 / n + + def getError(self): + + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA F841 + + ex = lambda x, y: x**2 + y + ey = lambda x, y: (y**2) * x + + tau_x = lambda x, y: 2 * y + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y: x + 2 # y-face properties # NOQA F841 + + mesh = self.M + + tau = 1e-8 * np.ones(mesh.n_faces) + for ii, comp in enumerate(["x", "y"]): + k = np.isclose(self.M.faces[:, ii], 0.5) # x, or y location for each plane + tau[k] = eval("call(tau_{}, self.M.faces[k, :])".format(comp)) + + # integrate components parallel to the plane of integration + if self.location == "edges": + analytic = 2.24166666666667 # Found using sympy. + + p = mesh.edges + Ec = np.c_[ex(*p.T), ey(*p.T)] + E = mesh.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_surface(tau) + + numeric = E.T.dot(A.dot(E)) + + # integrate component normal to the plane of integration + elif self.location == "faces": + analytic = 1.59895833333333 # Found using sympy. + + p = mesh.faces + Fc = np.c_[ex(*p.T), ey(*p.T)] + F = mesh.project_face_vector(Fc) + + if self.invert_model: + A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) + else: + A = self.M.get_face_inner_product_surface(tau) + + numeric = F.T.dot(A.dot(F)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + def test_order1_faces(self): + self.name = "Face Inner Product - Isotropic" + self.location = "faces" + self.invert_model = False + self.orderTest() + + def test_order1_faces_invert_model(self): + self.name = "Face Inner Product - Isotropic - invert_model" + self.location = "faces" + self.invert_model = True + self.orderTest() + + +class TestInnerProductsEdgeProperties2D(discretize.tests.OrderTest): + """Integrate a function over a line within a unit cube domain + using edgeInnerProducts.""" + + meshTypes = ["uniformTree", "notatreeTree"] + meshDimension = 2 + meshSizes = [16, 32] + + def getError(self): + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA F841 + + ex = lambda x, y: x**2 + y + ey = lambda x, y: (x**2) * y + + tau_x = lambda x, y: x + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y: y + 2 # y-face properties # NOQA F841 + + mesh = self.M + + tau = 1e-8 * np.ones(mesh.n_edges) + for ii, comp in enumerate(["x", "y"]): + k = ( + np.isclose(self.M.edges[:, ii - 1], 0.5) & + np.isclose(self.M.edges[:, ii - 2], 0.5) + ) # x, y or z location for each line + tau[k] = eval("call(tau_{}, self.M.edges[k, :])".format(comp)) + + + analytic = 1.38229166666667 # Found using sympy. + + p = mesh.edges + Ec = np.c_[ex(*p.T), ey(*p.T)] + E = mesh.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_line(tau) + + numeric = E.T.dot(A.dot(E)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + class TestInnerProducts3D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] From 098cbc19d5fae2df1ff611baeece2fc20aae4740 Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 21 Jul 2023 11:41:04 -0700 Subject: [PATCH 32/41] fix edge inner product surface bugs --- discretize/curvilinear_mesh.py | 5 ++++- discretize/unstructured_mesh.py | 18 +++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/discretize/curvilinear_mesh.py b/discretize/curvilinear_mesh.py index 4dbdd4b1c..301493c31 100644 --- a/discretize/curvilinear_mesh.py +++ b/discretize/curvilinear_mesh.py @@ -796,10 +796,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), diff --git a/discretize/unstructured_mesh.py b/discretize/unstructured_mesh.py index 7726683d8..a58128b77 100644 --- a/discretize/unstructured_mesh.py +++ b/discretize/unstructured_mesh.py @@ -536,7 +536,6 @@ def get_edge_inner_product_surface( # NOQA D102 dim = self.dim if dim == 2: - Ps = sp.diags(np.ones(n_faces)) if model is None: Mu = sp.diags(face_areas) @@ -545,28 +544,26 @@ def get_edge_inner_product_surface( # NOQA D102 model = 1.0 / model if (model.size == 1) | (model.size == n_faces): - model = model * face_areas - Mu = sp.diags(model) + Mu = sp.diags(model * face_areas) else: raise ValueError( "Unrecognized size of model vector.", "Must be scalar or have length equal to total number of faces." ) - A = Ps.T @ Mu @ Ps + return Mu else: Ps = self._get_edge_surf_int_proj_mats(with_area=False) if model is None: - Mu = sp.diags(np.repeat(face_areas, dim)) + Mu = sp.diags(np.tile(face_areas, dim)) else: if invert_model: model = 1.0 / model if (model.size == 1) | (model.size == n_faces): - model = model * face_areas - Mu = sp.diags(np.repeat(model, dim)) + Mu = sp.diags(np.tile(model * face_areas, dim)) else: raise ValueError( "Unrecognized size of model vector.", @@ -575,7 +572,7 @@ def get_edge_inner_product_surface( # NOQA D102 A = np.sum([P.T @ Mu @ P for P in Ps]) - return A + return A def __get_inner_product_deriv_func(self, i_type, model): @@ -726,10 +723,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), From 96d5507dbd997624aac16f8ac3e452aedbe88c64 Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 21 Jul 2023 11:44:21 -0700 Subject: [PATCH 33/41] add 3d tests --- tests/simplex/test_inner_products.py | 154 +++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/tests/simplex/test_inner_products.py b/tests/simplex/test_inner_products.py index a9de60b1e..d47ddbf39 100644 --- a/tests/simplex/test_inner_products.py +++ b/tests/simplex/test_inner_products.py @@ -469,6 +469,160 @@ def test_order6_faces_invert_model(self): self.invert_model = True self.orderTest() +class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): + meshSizes = [8, 16, 32] + meshTypes = ["uniform simplex mesh"] + + def setupMesh(self, n): + points, simplices = example_simplex_mesh((n, n, n)) + self.M = discretize.SimplexMesh(points, simplices) + return 1.0 / n + + def getError(self): + + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z + + tau_x = lambda x, y, z: y * z + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y, z: x * z + 2 # y-face properties # NOQA F841 + tau_z = lambda x, y, z: 3 + x * y # z-face properties # NOQA F841 + + mesh = self.M + + tau = 1e-8 * np.ones(mesh.n_faces) + for ii, comp in enumerate(["x", "y", "z"]): + k = np.isclose(self.M.faces[:, ii], 0.5) # x, or y location for each plane + tau[k] = eval("call(tau_{}, self.M.faces[k, :])".format(comp)) + + # integrate components parallel to the plane of integration + if self.location == "edges": + analytic = 5.02760416666667 # Found using sympy. + + p = mesh.edges + Ec = np.c_[ex(*p.T), ey(*p.T), ez(*p.T)] + E = mesh.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_surface(1 / tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_surface(tau) + + numeric = E.T.dot(A.dot(E)) + + # integrate component normal to the plane of integration + elif self.location == "faces": + analytic = 2.66979166666667 # Found using sympy. + + p = mesh.faces + Fc = np.c_[ex(*p.T), ey(*p.T), ez(*p.T)] + F = mesh.project_face_vector(Fc) + + if self.invert_model: + A = self.M.get_face_inner_product_surface(1 / tau, invert_model=True) + else: + A = self.M.get_face_inner_product_surface(tau) + + numeric = F.T.dot(A.dot(F)) + + print(analytic) + print(numeric) + print(analytic/numeric) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + + def test_order1_faces(self): + self.name = "Face Inner Product - Isotropic" + self.location = "faces" + self.invert_model = False + self.orderTest() + + def test_order1_faces_invert_model(self): + self.name = "Face Inner Product - Isotropic - invert_model" + self.location = "faces" + self.invert_model = True + self.orderTest() + + +class TestInnerProductsEdgeProperties3D(discretize.tests.OrderTest): + """Integrate a function over a line within a unit cube domain + using edgeInnerProducts.""" + + meshSizes = [8, 16, 32] + meshTypes = ["uniform simplex mesh"] + + def setupMesh(self, n): + points, simplices = example_simplex_mesh((n, n, n)) + self.M = discretize.SimplexMesh(points, simplices) + return 1.0 / n + + def getError(self): + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) + + ex = lambda x, y, z: x**2 + y * z + ey = lambda x, y, z: (z**2) * x + y * z + ez = lambda x, y, z: y**2 + x * z + + tau_x = lambda x, y, z: x + 1 # x-face properties # NOQA F841 + tau_y = lambda x, y, z: y + 2 # y-face properties # NOQA F841 + tau_z = lambda x, y, z: 3 * z + 1 # z-face properties # NOQA F841 + + mesh = self.M + + tau = 1e-8 * np.ones(mesh.n_edges) + for ii, comp in enumerate(["x", "y", "z"]): + k = ( + np.isclose(self.M.edges[:, ii - 1], 0.5) & + np.isclose(self.M.edges[:, ii - 2], 0.5) + ) # x, y or z location for each line + tau[k] = eval("call(tau_{}, self.M.edges[k, :])".format(comp)) + + + analytic = 1.98906250000000 # Found using sympy. + + p = mesh.edges + Ec = np.c_[ex(*p.T), ey(*p.T), ez(*p.T)] + E = mesh.project_edge_vector(Ec) + + if self.invert_model: + A = self.M.get_edge_inner_product_line(1 / tau, invert_model=True) + else: + A = self.M.get_edge_inner_product_line(tau) + + numeric = E.T.dot(A.dot(E)) + + err = np.abs(numeric - analytic) + + return err + + def test_order1_edges(self): + self.name = "Edge Inner Product - Isotropic" + self.location = "edges" + self.invert_model = False + self.orderTest() + + def test_order1_edges_invert_model(self): + self.name = "Edge Inner Product - Isotropic - invert_model" + self.location = "edges" + self.invert_model = True + self.orderTest() + class TestInnerProductsDerivs(unittest.TestCase): def doTestFace(self, h, rep): From 6174069573898afc407cbf96835647adbeb1659a Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 21 Jul 2023 14:39:32 -0700 Subject: [PATCH 34/41] Create and comment out simplex deriv tests for face and edge properties --- tests/simplex/test_inner_products.py | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/simplex/test_inner_products.py b/tests/simplex/test_inner_products.py index d47ddbf39..be7a318f8 100644 --- a/tests/simplex/test_inner_products.py +++ b/tests/simplex/test_inner_products.py @@ -702,6 +702,87 @@ def test_EdgeIP_3D_tensor(self): self.assertTrue(self.doTestEdge([10, 4, 5], 6)) +class TestFacePropertiesInnerProductsDerivs(unittest.TestCase): + def doTestFace(self, h, rep): + nodes, simplices = example_simplex_mesh(h) + mesh = discretize.SimplexMesh(nodes, simplices) + v = np.random.rand(mesh.n_faces) + tau = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) + + def fun(tau): + M = mesh.get_face_inner_product_surface(tau) + Md = mesh.get_face_inner_product_surface_deriv(tau) + return M * v, Md(v) + + print("Face", rep) + return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) + + def doTestEdge(self, h, rep): + nodes, simplices = example_simplex_mesh(h) + mesh = discretize.SimplexMesh(nodes, simplices) + v = np.random.rand(mesh.n_edges) + tau = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) + + def fun(tau): + M = mesh.get_edge_inner_product_surface(tau) + Md = mesh.get_edge_inner_product_surface_deriv(tau) + return M * v, Md(v) + + print("Edge", rep) + return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) + + # def test_FaceIP_2D_float(self): + # self.assertTrue(self.doTestFace([10, 4], 0)) + + # def test_FaceIP_3D_float(self): + # self.assertTrue(self.doTestFace([10, 4, 5], 0)) + + # def test_FaceIP_2D_isotropic(self): + # self.assertTrue(self.doTestFace([10, 4], 1)) + + # def test_FaceIP_3D_isotropic(self): + # self.assertTrue(self.doTestFace([10, 4, 5], 1)) + + # def test_EdgeIP_2D_float(self): + # self.assertTrue(self.doTestEdge([10, 4], 0)) + + # def test_EdgeIP_3D_float(self): + # self.assertTrue(self.doTestEdge([10, 4, 5], 0)) + + # def test_EdgeIP_2D_isotropic(self): + # self.assertTrue(self.doTestEdge([10, 4], 1)) + + # def test_EdgeIP_3D_isotropic(self): + # self.assertTrue(self.doTestEdge([10, 4, 5], 1)) + +class TestFacePropertiesInnerProductsDerivs(unittest.TestCase): + def doTestEdge(self, h, rep): + nodes, simplices = example_simplex_mesh(h) + mesh = discretize.SimplexMesh(nodes, simplices) + v = np.random.rand(mesh.n_edges) + tau = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nE * rep) + + def fun(tau): + M = mesh.get_edge_inner_product_line(tau) + Md = mesh.get_edge_inner_product_line_deriv(tau) + return M * v, Md(v) + + print("Edge", rep) + return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) + + # def test_EdgeIP_2D_float(self): + # self.assertTrue(self.doTestEdge([10, 4], 0)) + + # def test_EdgeIP_3D_float(self): + # self.assertTrue(self.doTestEdge([10, 4, 5], 0)) + + # def test_EdgeIP_2D_isotropic(self): + # self.assertTrue(self.doTestEdge([10, 4], 1)) + + # def test_EdgeIP_3D_isotropic(self): + # self.assertTrue(self.doTestEdge([10, 4, 5], 1)) + + class Test2DBoundaryIntegral(discretize.tests.OrderTest): meshSizes = [8, 16, 32] meshTypes = ["uniform simplex mesh"] From fefa235e4f121758721683fcecba46e170975d81 Mon Sep 17 00:00:00 2001 From: dccowan Date: Mon, 7 Jul 2025 15:25:02 -0700 Subject: [PATCH 35/41] tree ext --- discretize/_extensions/tree_ext.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discretize/_extensions/tree_ext.pyx b/discretize/_extensions/tree_ext.pyx index 07de49ebd..392db934f 100644 --- a/discretize/_extensions/tree_ext.pyx +++ b/discretize/_extensions/tree_ext.pyx @@ -6,7 +6,7 @@ cimport numpy as np from libc.stdlib cimport malloc, free from libcpp.vector cimport vector from libcpp cimport bool -from numpy.math cimport INFINITY +from libc.math cimport INFINITY from .tree cimport int_t, Tree as c_Tree, PyWrapper, Node, Edge, Face, Cell as c_Cell from . cimport geom From 42fa3d6a3aa44062d82e601c3844edf8532121b4 Mon Sep 17 00:00:00 2001 From: dccowan Date: Fri, 20 Feb 2026 13:11:20 -0800 Subject: [PATCH 36/41] uncomment inner product tests --- tests/simplex/test_inner_products.py | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/simplex/test_inner_products.py b/tests/simplex/test_inner_products.py index fd71e56ae..4293b8efb 100644 --- a/tests/simplex/test_inner_products.py +++ b/tests/simplex/test_inner_products.py @@ -737,29 +737,29 @@ def fun(tau): print("Edge", rep) return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) - # def test_FaceIP_2D_float(self): - # self.assertTrue(self.doTestFace([10, 4], 0)) + def test_FaceIP_2D_float(self): + self.assertTrue(self.doTestFace([10, 4], 0)) - # def test_FaceIP_3D_float(self): - # self.assertTrue(self.doTestFace([10, 4, 5], 0)) + def test_FaceIP_3D_float(self): + self.assertTrue(self.doTestFace([10, 4, 5], 0)) - # def test_FaceIP_2D_isotropic(self): - # self.assertTrue(self.doTestFace([10, 4], 1)) + def test_FaceIP_2D_isotropic(self): + self.assertTrue(self.doTestFace([10, 4], 1)) - # def test_FaceIP_3D_isotropic(self): - # self.assertTrue(self.doTestFace([10, 4, 5], 1)) + def test_FaceIP_3D_isotropic(self): + self.assertTrue(self.doTestFace([10, 4, 5], 1)) - # def test_EdgeIP_2D_float(self): - # self.assertTrue(self.doTestEdge([10, 4], 0)) + def test_EdgeIP_2D_float(self): + self.assertTrue(self.doTestEdge([10, 4], 0)) - # def test_EdgeIP_3D_float(self): - # self.assertTrue(self.doTestEdge([10, 4, 5], 0)) + def test_EdgeIP_3D_float(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 0)) - # def test_EdgeIP_2D_isotropic(self): - # self.assertTrue(self.doTestEdge([10, 4], 1)) + def test_EdgeIP_2D_isotropic(self): + self.assertTrue(self.doTestEdge([10, 4], 1)) - # def test_EdgeIP_3D_isotropic(self): - # self.assertTrue(self.doTestEdge([10, 4, 5], 1)) + def test_EdgeIP_3D_isotropic(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 1)) class TestFacePropertiesInnerProductsDerivs(unittest.TestCase): def doTestEdge(self, h, rep): @@ -776,17 +776,17 @@ def fun(tau): print("Edge", rep) return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) - # def test_EdgeIP_2D_float(self): - # self.assertTrue(self.doTestEdge([10, 4], 0)) + def test_EdgeIP_2D_float(self): + self.assertTrue(self.doTestEdge([10, 4], 0)) - # def test_EdgeIP_3D_float(self): - # self.assertTrue(self.doTestEdge([10, 4, 5], 0)) + def test_EdgeIP_3D_float(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 0)) - # def test_EdgeIP_2D_isotropic(self): - # self.assertTrue(self.doTestEdge([10, 4], 1)) + def test_EdgeIP_2D_isotropic(self): + self.assertTrue(self.doTestEdge([10, 4], 1)) - # def test_EdgeIP_3D_isotropic(self): - # self.assertTrue(self.doTestEdge([10, 4, 5], 1)) + def test_EdgeIP_3D_isotropic(self): + self.assertTrue(self.doTestEdge([10, 4, 5], 1)) class Test2DBoundaryIntegral(discretize.tests.OrderTest): From 785202435c1dbb426faa2f4580ad03d8530254b0 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 11 Jun 2026 13:24:19 -0600 Subject: [PATCH 37/41] simplify a bit, and start working on more tests --- discretize/unstructured_mesh.py | 132 ++++++++++++++-------- tests/simplex/test_inner_products.py | 157 +++++++++++---------------- 2 files changed, 152 insertions(+), 137 deletions(-) diff --git a/discretize/unstructured_mesh.py b/discretize/unstructured_mesh.py index 61f707e47..bf5d250a2 100644 --- a/discretize/unstructured_mesh.py +++ b/discretize/unstructured_mesh.py @@ -519,64 +519,47 @@ def get_edge_inner_product( # NOQA D102 "The inverse of the inner product matrix with a tetrahedral mesh is not supported." ) 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 + self, model=None, invert_model=False, invert_matrix=False ): # Documentation inherited from discretize.base.BaseMesh + if self.dim == 2: + return super().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 - dim = self.dim - - if dim == 2: - - if model is None: - Mu = sp.diags(face_areas) - else: - if invert_model: - model = 1.0 / model - - if (model.size == 1) | (model.size == n_faces): - Mu = sp.diags(model * face_areas) - else: - raise ValueError( - "Unrecognized size of model vector.", - "Must be scalar or have length equal to total number of faces." - ) - - return Mu - + + Ps = self._get_edge_surf_int_proj_mats(with_area=False) + + if model is None: + Mu = sp.diags(np.tile(face_areas, 3)) # Number of edges per face else: - Ps = self._get_edge_surf_int_proj_mats(with_area=False) - - if model is None: - Mu = sp.diags(np.tile(face_areas, dim)) + if invert_model: + model = 1.0 / model + + if (model.size == 1) | (model.size == n_faces): + Mu = sp.diags( + np.tile(model * face_areas, 3) + ) # 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)) - else: - raise ValueError( - "Unrecognized size of model vector.", - "Must be scalar or have length equal to total number of faces." + 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 - - + 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 @@ -668,6 +651,67 @@ 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 + if self.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), 3)) # Number of edges per face + 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.repeat(np.arange(n_faces), 3) + ind_ptr = np.arange(n_faces * 3 + 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 * 3, 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. diff --git a/tests/simplex/test_inner_products.py b/tests/simplex/test_inner_products.py index 4293b8efb..d1c3682a3 100644 --- a/tests/simplex/test_inner_products.py +++ b/tests/simplex/test_inner_products.py @@ -1,5 +1,6 @@ import numpy as np import unittest +import pytest import discretize import scipy.sparse as sp from discretize.utils import example_simplex_mesh @@ -180,6 +181,7 @@ def test_order3_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsFaceProperties2D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] meshTypes = ["uniform simplex mesh"] @@ -190,7 +192,7 @@ def setupMesh(self, n): return 1.0 / n def getError(self): - + call = lambda fun, xy: fun(xy[:, 0], xy[:, 1]) # NOQA F841 ex = lambda x, y: x**2 + y @@ -209,7 +211,7 @@ def getError(self): # integrate components parallel to the plane of integration if self.location == "edges": analytic = 2.24166666666667 # Found using sympy. - + p = mesh.edges Ec = np.c_[ex(*p.T), ey(*p.T)] E = mesh.project_edge_vector(Ec) @@ -281,17 +283,15 @@ def getError(self): tau_x = lambda x, y: x + 1 # x-face properties # NOQA F841 tau_y = lambda x, y: y + 2 # y-face properties # NOQA F841 - + mesh = self.M tau = 1e-8 * np.ones(mesh.n_edges) for ii, comp in enumerate(["x", "y"]): - k = ( - np.isclose(self.M.edges[:, ii - 1], 0.5) & - np.isclose(self.M.edges[:, ii - 2], 0.5) + k = np.isclose(self.M.edges[:, ii - 1], 0.5) & np.isclose( + self.M.edges[:, ii - 2], 0.5 ) # x, y or z location for each line tau[k] = eval("call(tau_{}, self.M.edges[k, :])".format(comp)) - analytic = 1.38229166666667 # Found using sympy. @@ -471,6 +471,7 @@ def test_order6_faces_invert_model(self): self.invert_model = True self.orderTest() + class TestInnerProductsFaceProperties3D(discretize.tests.OrderTest): meshSizes = [8, 16, 32] meshTypes = ["uniform simplex mesh"] @@ -481,28 +482,30 @@ def setupMesh(self, n): return 1.0 / n def getError(self): - + call = lambda fun, xyz: fun(xyz[:, 0], xyz[:, 1], xyz[:, 2]) ex = lambda x, y, z: x**2 + y * z ey = lambda x, y, z: (z**2) * x + y * z ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: y * z + 1 # x-face properties # NOQA F841 - tau_y = lambda x, y, z: x * z + 2 # y-face properties # NOQA F841 - tau_z = lambda x, y, z: 3 + x * y # z-face properties # NOQA F841 + tau_funcs = { + "x": lambda x, y, z: y * z + 1, # x-face properties + "y": lambda x, y, z: x * z + 2, # y-face properties + "z": lambda x, y, z: 3 + x * y, # z-face properties + } mesh = self.M tau = 1e-8 * np.ones(mesh.n_faces) for ii, comp in enumerate(["x", "y", "z"]): k = np.isclose(self.M.faces[:, ii], 0.5) # x, or y location for each plane - tau[k] = eval("call(tau_{}, self.M.faces[k, :])".format(comp)) + tau[k] = call(tau_funcs[comp], self.M.faces[k, :]) # integrate components parallel to the plane of integration if self.location == "edges": analytic = 5.02760416666667 # Found using sympy. - + p = mesh.edges Ec = np.c_[ex(*p.T), ey(*p.T), ez(*p.T)] E = mesh.project_edge_vector(Ec) @@ -528,10 +531,10 @@ def getError(self): A = self.M.get_face_inner_product_surface(tau) numeric = F.T.dot(A.dot(F)) - + print(analytic) print(numeric) - print(analytic/numeric) + print(analytic / numeric) err = np.abs(numeric - analytic) @@ -581,20 +584,20 @@ def getError(self): ey = lambda x, y, z: (z**2) * x + y * z ez = lambda x, y, z: y**2 + x * z - tau_x = lambda x, y, z: x + 1 # x-face properties # NOQA F841 - tau_y = lambda x, y, z: y + 2 # y-face properties # NOQA F841 - tau_z = lambda x, y, z: 3 * z + 1 # z-face properties # NOQA F841 - + tau_funcs = { + "x": lambda x, y, z: x + 1, # x-face properties + "y": lambda x, y, z: y + 2, # y-face properties + "z": lambda x, y, z: 3 * z + 1, # z-face properties + } + mesh = self.M tau = 1e-8 * np.ones(mesh.n_edges) for ii, comp in enumerate(["x", "y", "z"]): - k = ( - np.isclose(self.M.edges[:, ii - 1], 0.5) & - np.isclose(self.M.edges[:, ii - 2], 0.5) + k = np.isclose(self.M.edges[:, ii - 1], 0.5) & np.isclose( + self.M.edges[:, ii - 2], 0.5 ) # x, y or z location for each line - tau[k] = eval("call(tau_{}, self.M.edges[k, :])".format(comp)) - + tau[k] = call(tau_funcs[comp], self.M.edges[k, :]) analytic = 1.98906250000000 # Found using sympy. @@ -708,85 +711,53 @@ def test_EdgeIP_3D_tensor(self): self.assertTrue(self.doTestEdge([10, 4, 5], 6)) -class TestFacePropertiesInnerProductsDerivs(unittest.TestCase): - def doTestFace(self, h, rep): - nodes, simplices = example_simplex_mesh(h) - mesh = discretize.SimplexMesh(nodes, simplices) - v = np.random.rand(mesh.n_faces) - tau = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) - - def fun(tau): - M = mesh.get_face_inner_product_surface(tau) - Md = mesh.get_face_inner_product_surface_deriv(tau) - return M * v, Md(v) - - print("Face", rep) - return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) - - def doTestEdge(self, h, rep): - nodes, simplices = example_simplex_mesh(h) - mesh = discretize.SimplexMesh(nodes, simplices) - v = np.random.rand(mesh.n_edges) - tau = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nF * rep) - - def fun(tau): - M = mesh.get_edge_inner_product_surface(tau) - Md = mesh.get_edge_inner_product_surface_deriv(tau) - return M * v, Md(v) - - print("Edge", rep) - return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) - - def test_FaceIP_2D_float(self): - self.assertTrue(self.doTestFace([10, 4], 0)) - - def test_FaceIP_3D_float(self): - self.assertTrue(self.doTestFace([10, 4, 5], 0)) - - def test_FaceIP_2D_isotropic(self): - self.assertTrue(self.doTestFace([10, 4], 1)) +@pytest.mark.parametrize("u_type", ["edge", "face"]) +@pytest.mark.parametrize("h", [(10, 4), (10, 4, 5)], ids=["2D", "3D"]) +@pytest.mark.parametrize("rep", [0, 1], ids=["uniform", "isotropic"]) +def test_surface_inner_product_prop_deriv(u_type, h, rep): + rng = np.random.default_rng(6732) + nodes, simplices = example_simplex_mesh(h) + mesh = discretize.SimplexMesh(nodes, simplices) + tau = rng.uniform(1, 2, 1) if rep == 0 else rng.uniform(1, 2, mesh.n_faces * rep) - def test_FaceIP_3D_isotropic(self): - self.assertTrue(self.doTestFace([10, 4, 5], 1)) + match u_type: + case "edge": + v = rng.uniform(1, 2, mesh.n_edges) - def test_EdgeIP_2D_float(self): - self.assertTrue(self.doTestEdge([10, 4], 0)) - - def test_EdgeIP_3D_float(self): - self.assertTrue(self.doTestEdge([10, 4, 5], 0)) - - def test_EdgeIP_2D_isotropic(self): - self.assertTrue(self.doTestEdge([10, 4], 1)) + def fun(tau): + M = mesh.get_edge_inner_product_surface(tau) + Md = mesh.get_edge_inner_product_surface_deriv(tau) + return M * v, Md(v) - def test_EdgeIP_3D_isotropic(self): - self.assertTrue(self.doTestEdge([10, 4, 5], 1)) + case "face": + v = rng.uniform(1, 2, mesh.n_faces) -class TestFacePropertiesInnerProductsDerivs(unittest.TestCase): - def doTestEdge(self, h, rep): - nodes, simplices = example_simplex_mesh(h) - mesh = discretize.SimplexMesh(nodes, simplices) - v = np.random.rand(mesh.n_edges) - tau = np.random.rand(1) if rep == 0 else np.random.rand(mesh.nE * rep) + def fun(tau): + M = mesh.get_face_inner_product_surface(tau) + Md = mesh.get_face_inner_product_surface_deriv(tau) + return M * v, Md(v) - def fun(tau): - M = mesh.get_edge_inner_product_line(tau) - Md = mesh.get_edge_inner_product_line_deriv(tau) - return M * v, Md(v) + case _: + raise Exception("Invalid test parameter.") - print("Edge", rep) - return discretize.tests.check_derivative(fun, tau, num=5, plotIt=False) + discretize.tests.check_derivative(fun, tau, num=5, random_seed=rng) - def test_EdgeIP_2D_float(self): - self.assertTrue(self.doTestEdge([10, 4], 0)) - def test_EdgeIP_3D_float(self): - self.assertTrue(self.doTestEdge([10, 4, 5], 0)) +@pytest.mark.parametrize("h", [(10, 4), (10, 4, 5)], ids=["2D", "3D"]) +@pytest.mark.parametrize("rep", [0, 1], ids=["uniform", "isotropic"]) +def test_line_inner_product_prop_deriv(h, rep): + rng = np.random.default_rng(6732) + nodes, simplices = example_simplex_mesh(h) + mesh = discretize.SimplexMesh(nodes, simplices) + v = rng.uniform(1, 2, mesh.n_edges) + tau = rng.uniform(1, 2, 1) if rep == 0 else rng.uniform(1, 2, mesh.n_edges * rep) - def test_EdgeIP_2D_isotropic(self): - self.assertTrue(self.doTestEdge([10, 4], 1)) + def fun(tau): + M = mesh.get_edge_inner_product_line(tau) + Md = mesh.get_edge_inner_product_line_deriv(tau) + return M * v, Md(v) - def test_EdgeIP_3D_isotropic(self): - self.assertTrue(self.doTestEdge([10, 4, 5], 1)) + discretize.tests.check_derivative(fun, tau, num=5, random_seed=rng) class Test2DBoundaryIntegral(discretize.tests.OrderTest): From 526c3baaf1e2855c0761abf3d48b93c17fe47fa4 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Thu, 11 Jun 2026 21:18:50 -0600 Subject: [PATCH 38/41] tile instead of repeat --- discretize/unstructured_mesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discretize/unstructured_mesh.py b/discretize/unstructured_mesh.py index bf5d250a2..5611c7f16 100644 --- a/discretize/unstructured_mesh.py +++ b/discretize/unstructured_mesh.py @@ -693,7 +693,7 @@ def func(v): return dMdm elif model.size == n_faces: - col_inds = np.repeat(np.arange(n_faces), 3) + col_inds = np.tile(np.arange(n_faces), 3) ind_ptr = np.arange(n_faces * 3 + 1) def func(v): From 6756233ddde6f0e5a8609270d4cda524b5f28d7e Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Fri, 12 Jun 2026 11:03:43 -0600 Subject: [PATCH 39/41] move duplicate code to a new class --- discretize/curvilinear_mesh.py | 8 +- discretize/operators/__init__.py | 5 +- discretize/operators/inner_products.py | 129 +++++++++++++++++++++++++ discretize/unstructured_mesh.py | 105 +------------------- tests/base/test_curvilinear.py | 51 ++++++++++ 5 files changed, 191 insertions(+), 107 deletions(-) diff --git a/discretize/curvilinear_mesh.py b/discretize/curvilinear_mesh.py index d06edb572..5a8e27121 100644 --- a/discretize/curvilinear_mesh.py +++ b/discretize/curvilinear_mesh.py @@ -11,7 +11,7 @@ make_boundary_bool, ) from discretize.base import BaseRectangularMesh -from discretize.operators import DiffOperators, InnerProducts +from discretize.operators import DiffOperators, InnerProducts, UnstructuredInnerProducts from discretize.mixins import InterfaceMixins @@ -33,7 +33,11 @@ def _normalize3D(x): class CurvilinearMesh( - DiffOperators, InnerProducts, BaseRectangularMesh, InterfaceMixins + DiffOperators, + UnstructuredInnerProducts, + InnerProducts, + BaseRectangularMesh, + InterfaceMixins, ): """Curvilinear mesh class. diff --git a/discretize/operators/__init__.py b/discretize/operators/__init__.py index 70c324760..cd2a6491e 100644 --- a/discretize/operators/__init__.py +++ b/discretize/operators/__init__.py @@ -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 @@ -14,7 +14,8 @@ DiffOperators InnerProducts + UnstructuredInnerProducts """ from discretize.operators.differential_operators import DiffOperators -from discretize.operators.inner_products import InnerProducts +from discretize.operators.inner_products import InnerProducts, UnstructuredInnerProducts diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index eb5976391..5122a0918 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -18,6 +18,7 @@ is_scalar, ) import numpy as np +from abc import ABC, abstractmethod class InnerProducts(BaseMesh): @@ -861,3 +862,131 @@ def Pxxx(xEdge, yEdge, zEdge): return PXXX return Pxxx + + +class UnstructuredInnerProducts(BaseMesh, ABC): + """Abstract class for constructing inner product matrices on generalized meshes. + + ``UnstructuredInnerProducts`` is a mixin class that does not assume axis-alignment + for the edges and faces of a mesh, suitable for the `SimplexMesh` and `CurvilinearMesh`. + """ + + @abstractmethod + def _get_edge_surf_int_proj_mats(self, only_boundary=False, with_area=True): + """Return the projection operators for integrating edges on each face. + + Parameters + ---------- + only_boundary : bool, optional + Whether to only operate on the boundary faces or not. + with_area : bool, optional + Whether to include the face area. + + Returns + ------- + list of (3 * n_faces, n_edges) scipy.sparse.csr_matrix + """ + + 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 edges are faces. + return super().get_face_inner_product_surface( + model=model, invert_model=invert_model, invert_matrix=invert_matrix + ) + + if invert_matrix: + raise NotImplementedError( + f"The inverse of the inner product matrix with a '{type(self).__name__}' 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: + 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 diff --git a/discretize/unstructured_mesh.py b/discretize/unstructured_mesh.py index 5611c7f16..2df24e2dd 100644 --- a/discretize/unstructured_mesh.py +++ b/discretize/unstructured_mesh.py @@ -4,7 +4,6 @@ import scipy.sparse as sp from scipy.spatial import KDTree from discretize.utils import Identity, invert_blocks, spzeros, cross2d -from discretize.base import BaseMesh from discretize._extensions.simplex_helpers import ( _build_faces_edges, _build_adjacency, @@ -12,9 +11,10 @@ _interp_cc, ) from discretize.mixins import InterfaceMixins, SimplexMeshIO +from discretize.operators import UnstructuredInnerProducts -class SimplexMesh(BaseMesh, SimplexMeshIO, InterfaceMixins): +class SimplexMesh(UnstructuredInnerProducts, SimplexMeshIO, InterfaceMixins): """Class for traingular (2D) and tetrahedral (3D) meshes. Simplex is the abstract term for triangular like elements in an arbitrary dimension. @@ -520,46 +520,6 @@ 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 - if self.dim == 2: - return super().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, 3)) # 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, 3) - ) # 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 @@ -651,67 +611,6 @@ 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 - if self.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), 3)) # Number of edges per face - 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), 3) - ind_ptr = np.arange(n_faces * 3 + 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 * 3, 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. diff --git a/tests/base/test_curvilinear.py b/tests/base/test_curvilinear.py index 65cf5c9d2..78b11cb6e 100644 --- a/tests/base/test_curvilinear.py +++ b/tests/base/test_curvilinear.py @@ -1,7 +1,11 @@ import numpy as np import unittest +import pytest + from discretize import TensorMesh, CurvilinearMesh from discretize.utils import ndgrid +from discretize.tests import setup_mesh +from discretize.tests import check_derivative class BasicCurvTests(unittest.TestCase): @@ -287,5 +291,52 @@ def test_grid(self): self.assertTrue(np.all(self.Curv3.gridEz == self.TM3.gridEz)) +@pytest.mark.parametrize("u_type", ["edge", "face"]) +@pytest.mark.parametrize("dim", [2, 3], ids=["2D", "3D"]) +@pytest.mark.parametrize("rep", [0, 1], ids=["uniform", "isotropic"]) +def test_surface_inner_product_prop_deriv(u_type, dim, rep): + rng = np.random.default_rng(6732) + mesh, _ = setup_mesh("rotateCurv", 20, dim) + tau = rng.uniform(1, 2, 1) if rep == 0 else rng.uniform(1, 2, mesh.n_faces * rep) + + match u_type: + case "edge": + v = rng.uniform(1, 2, mesh.n_edges) + + def fun(tau): + M = mesh.get_edge_inner_product_surface(tau) + Md = mesh.get_edge_inner_product_surface_deriv(tau) + return M * v, Md(v) + + case "face": + v = rng.uniform(1, 2, mesh.n_faces) + + def fun(tau): + M = mesh.get_face_inner_product_surface(tau) + Md = mesh.get_face_inner_product_surface_deriv(tau) + return M * v, Md(v) + + case _: + raise Exception("Invalid test parameter.") + + check_derivative(fun, tau, num=5, random_seed=rng) + + +@pytest.mark.parametrize("dim", [2, 3], ids=["2D", "3D"]) +@pytest.mark.parametrize("rep", [0, 1], ids=["uniform", "isotropic"]) +def test_line_inner_product_prop_deriv(dim, rep): + rng = np.random.default_rng(6732) + mesh, _ = setup_mesh("rotateCurv", 20, dim) + v = rng.uniform(1, 2, mesh.n_edges) + tau = rng.uniform(1, 2, 1) if rep == 0 else rng.uniform(1, 2, mesh.n_edges * rep) + + def fun(tau): + M = mesh.get_edge_inner_product_line(tau) + Md = mesh.get_edge_inner_product_line_deriv(tau) + return M * v, Md(v) + + check_derivative(fun, tau, num=5, random_seed=rng) + + if __name__ == "__main__": unittest.main() From c7bad4aa19529dafb7e6075024eae00619f56ea5 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Mon, 15 Jun 2026 09:56:21 -0600 Subject: [PATCH 40/41] add tests for curvilinear edge surface --- discretize/curvilinear_mesh.py | 130 ++++++++++++++++++++++++- discretize/operators/__init__.py | 3 +- discretize/operators/inner_products.py | 129 ------------------------ discretize/unstructured_mesh.py | 107 +++++++++++++++++++- tests/base/test_curvilinear.py | 116 +++++++++++++++++++++- tests/simplex/test_inner_products.py | 8 +- 6 files changed, 353 insertions(+), 140 deletions(-) diff --git a/discretize/curvilinear_mesh.py b/discretize/curvilinear_mesh.py index 5a8e27121..d2336fc5b 100644 --- a/discretize/curvilinear_mesh.py +++ b/discretize/curvilinear_mesh.py @@ -11,8 +11,9 @@ make_boundary_bool, ) from discretize.base import BaseRectangularMesh -from discretize.operators import DiffOperators, InnerProducts, UnstructuredInnerProducts +from discretize.operators import DiffOperators, InnerProducts from discretize.mixins import InterfaceMixins +from discretize.utils import spzeros # Some helper functions. @@ -34,7 +35,6 @@ def _normalize3D(x): class CurvilinearMesh( DiffOperators, - UnstructuredInnerProducts, InnerProducts, BaseRectangularMesh, InterfaceMixins, @@ -816,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 diff --git a/discretize/operators/__init__.py b/discretize/operators/__init__.py index cd2a6491e..b371f10c1 100644 --- a/discretize/operators/__init__.py +++ b/discretize/operators/__init__.py @@ -14,8 +14,7 @@ DiffOperators InnerProducts - UnstructuredInnerProducts """ from discretize.operators.differential_operators import DiffOperators -from discretize.operators.inner_products import InnerProducts, UnstructuredInnerProducts +from discretize.operators.inner_products import InnerProducts diff --git a/discretize/operators/inner_products.py b/discretize/operators/inner_products.py index 5122a0918..eb5976391 100644 --- a/discretize/operators/inner_products.py +++ b/discretize/operators/inner_products.py @@ -18,7 +18,6 @@ is_scalar, ) import numpy as np -from abc import ABC, abstractmethod class InnerProducts(BaseMesh): @@ -862,131 +861,3 @@ def Pxxx(xEdge, yEdge, zEdge): return PXXX return Pxxx - - -class UnstructuredInnerProducts(BaseMesh, ABC): - """Abstract class for constructing inner product matrices on generalized meshes. - - ``UnstructuredInnerProducts`` is a mixin class that does not assume axis-alignment - for the edges and faces of a mesh, suitable for the `SimplexMesh` and `CurvilinearMesh`. - """ - - @abstractmethod - def _get_edge_surf_int_proj_mats(self, only_boundary=False, with_area=True): - """Return the projection operators for integrating edges on each face. - - Parameters - ---------- - only_boundary : bool, optional - Whether to only operate on the boundary faces or not. - with_area : bool, optional - Whether to include the face area. - - Returns - ------- - list of (3 * n_faces, n_edges) scipy.sparse.csr_matrix - """ - - 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 edges are faces. - return super().get_face_inner_product_surface( - model=model, invert_model=invert_model, invert_matrix=invert_matrix - ) - - if invert_matrix: - raise NotImplementedError( - f"The inverse of the inner product matrix with a '{type(self).__name__}' 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: - 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 diff --git a/discretize/unstructured_mesh.py b/discretize/unstructured_mesh.py index 2df24e2dd..faec51c10 100644 --- a/discretize/unstructured_mesh.py +++ b/discretize/unstructured_mesh.py @@ -4,6 +4,7 @@ import scipy.sparse as sp from scipy.spatial import KDTree from discretize.utils import Identity, invert_blocks, spzeros, cross2d +from discretize.base import BaseMesh from discretize._extensions.simplex_helpers import ( _build_faces_edges, _build_adjacency, @@ -11,10 +12,9 @@ _interp_cc, ) from discretize.mixins import InterfaceMixins, SimplexMeshIO -from discretize.operators import UnstructuredInnerProducts -class SimplexMesh(UnstructuredInnerProducts, SimplexMeshIO, InterfaceMixins): +class SimplexMesh(BaseMesh, SimplexMeshIO, InterfaceMixins): """Class for traingular (2D) and tetrahedral (3D) meshes. Simplex is the abstract term for triangular like elements in an arbitrary dimension. @@ -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 @@ -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. diff --git a/tests/base/test_curvilinear.py b/tests/base/test_curvilinear.py index 78b11cb6e..7c0e754da 100644 --- a/tests/base/test_curvilinear.py +++ b/tests/base/test_curvilinear.py @@ -4,8 +4,7 @@ from discretize import TensorMesh, CurvilinearMesh from discretize.utils import ndgrid -from discretize.tests import setup_mesh -from discretize.tests import check_derivative +from discretize.tests import setup_mesh, check_derivative, assert_expected_order class BasicCurvTests(unittest.TestCase): @@ -338,5 +337,118 @@ def fun(tau): check_derivative(fun, tau, num=5, random_seed=rng) +def test_edge_surface_integral_2d(): + # testing the line integral here of: + # vector field w: [y**2, x**2] + # physical property u: (1 - x) * (1 + x) + # over the path x=t, y=(t - 1) * (t + 1) + # from t=-1 to 1 + def error_eval(nx): + ny = 4 # not really important to this test, as only one "surface" is non-zeros + xlocs = np.linspace(-1, 1, nx + 1) + nodes_x = xlocs[:, None] * np.ones(ny + 1)[None, :] + + ylocs = (xlocs - 1) * (xlocs + 1) + nodes_y = ylocs[:, None] + np.linspace(-1, 1, ny + 1) + + mesh = CurvilinearMesh((nodes_x, nodes_y)) + + faces = (np.arange(mesh.n_faces_y) + mesh.n_faces_x).reshape(ny + 1, nx) + face_inds = faces[2, :] + + xcs = (xlocs[1:] + xlocs[:-1]) * 0.5 + prop = (1 - xcs) * (xcs + 1) + 1 + face_props = np.zeros(mesh.n_faces) + face_props[face_inds] = prop + + edge_vectors = np.c_[mesh.edges[:, 1] ** 2, mesh.edges[:, 0] ** 2] + edge_vals = mesh.project_edge_vector(edge_vectors) + + M = mesh.get_edge_inner_product_surface(model=face_props) + + discrete_val = np.sum(M @ edge_vals) + reference_value = 208 / 105 + print(discrete_val, reference_value) + return np.abs(discrete_val - reference_value), xlocs[1] - xlocs[0] + + assert_expected_order(error_eval, [20.0, 30.0, 40.0, 50.0]) + + +def test_edge_surface_integral_3d(): + """ + For this test, we will use a single surface within the curvilinear mesh + parameterized as: + x = u + y = v + z = u**2 + v**2 + (a simple parabola) + + we want to test the integral of: + mu * w.dot(w) ds + over this surface, for some a vector field that is always tangent to the surface + the two vectors that are perpendicular and tangent to this surface are: + r_u = [1, 0, 2*u] + r_v = [0, 1, 2*v] + + so any linear combination of these vectors will be tangent to the surface, let's use: + w = x * y * z * (r_u + r_v) + + the ds integrand is sqrt(|| r_u.cross(r_v) ||) or: + sqrt(4 * x**2 + 4 * y**2 + 1) + + and a property field: mu = x**2 + y**2 + z**2 + + the analytic integral over the range u=[-1, 1], v=[-1,1] found by numeric integration: + + >>> def int_f(x, y): + ... xsq = x**2 + ... ysq = y**2 + ... z = x**2 + y**2 + ... zsq = z**2 + ... v1 = 2 * xsq * ysq * zsq + (2 * xsq * y * z + 2 * x * ysq * z)**2 + ... v2 = (xsq + ysq + zsq) * np.sqrt(4 * xsq + 4 * ysq + 1) + ... return v1 * v2 + >>> scint.dblquad(int_f, -1, 1, -1, 1, epsrel=1e-20, epsabs=1E-20) + (51.8667132595898, 2.241102757162104e-12) + """ + + def error_eval(nx): + ny = nx + nz = 4 # not really important to this test, as only one "surface" is non-zero + xlocs = np.linspace(-1, 1, nx + 1) + ylocs = np.linspace(-1, 1, ny + 1) + nodes_x = xlocs[:, None, None] * np.ones((1, ny + 1, nz + 1)) + nodes_y = ylocs[None, :, None] * np.ones((nx + 1, 1, nz + 1)) + + nodes_z = nodes_x**2 + nodes_y**2 + np.linspace(-1, 1, nz + 1)[None, None, :] + mesh = CurvilinearMesh((nodes_x, nodes_y, nodes_z)) + + faces = (np.arange(mesh.n_faces_z) + mesh.n_faces_x + mesh.n_faces_y).reshape( + nz + 1, nx * ny + ) + face_inds = faces[2, :] + + prop = mesh.faces[:, 0] ** 2 + mesh.faces[:, 1] ** 2 + mesh.faces[:, 2] ** 2 + face_props = np.zeros(mesh.n_faces) + face_props[face_inds] = prop[face_inds] + + ex = mesh.edges[:, 0] + ey = mesh.edges[:, 1] + w_vec = (ex * ey * (ex**2 + ey**2))[:, None] * np.c_[ + np.ones_like(ex), + np.ones_like(ex), + 2 * ex + 2 * ey, + ] + w = mesh.project_edge_vector(w_vec) + + M = mesh.get_edge_inner_product_surface(model=face_props) + + discrete_val = w @ M @ w + reference_value = 51.8667132595898 + return np.abs(discrete_val - reference_value), xlocs[1] - xlocs[0] + + assert_expected_order(error_eval, [20.0, 30.0, 40.0, 50.0]) + + if __name__ == "__main__": unittest.main() diff --git a/tests/simplex/test_inner_products.py b/tests/simplex/test_inner_products.py index d1c3682a3..80550afbf 100644 --- a/tests/simplex/test_inner_products.py +++ b/tests/simplex/test_inner_products.py @@ -198,15 +198,17 @@ def getError(self): ex = lambda x, y: x**2 + y ey = lambda x, y: (y**2) * x - tau_x = lambda x, y: 2 * y + 1 # x-face properties # NOQA F841 - tau_y = lambda x, y: x + 2 # y-face properties # NOQA F841 + tau_funcs = { + "x": lambda x, y: 2 * y + 1, # x-face properties + "y": lambda x, y: x + 2, # y-face properties + } mesh = self.M tau = 1e-8 * np.ones(mesh.n_faces) for ii, comp in enumerate(["x", "y"]): k = np.isclose(self.M.faces[:, ii], 0.5) # x, or y location for each plane - tau[k] = eval("call(tau_{}, self.M.faces[k, :])".format(comp)) + tau[k] = call(tau_funcs[comp], self.M.faces[k, :]) # integrate components parallel to the plane of integration if self.location == "edges": From 61cb5402c2432d7be0b97ac201b8095742291090 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Mon, 15 Jun 2026 10:37:43 -0600 Subject: [PATCH 41/41] add another test --- tests/base/test_curvilinear.py | 69 +++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/tests/base/test_curvilinear.py b/tests/base/test_curvilinear.py index 7c0e754da..c71c401cb 100644 --- a/tests/base/test_curvilinear.py +++ b/tests/base/test_curvilinear.py @@ -338,11 +338,14 @@ def fun(tau): def test_edge_surface_integral_2d(): - # testing the line integral here of: - # vector field w: [y**2, x**2] - # physical property u: (1 - x) * (1 + x) - # over the path x=t, y=(t - 1) * (t + 1) - # from t=-1 to 1 + """ + testing the line integral here of: + vector field w: [y**2, x**2] + physical property u: (1 - x) * (1 + x) + over the path x=t, y=(t - 1) * (t + 1) + from t=-1 to 1 + """ + def error_eval(nx): ny = 4 # not really important to this test, as only one "surface" is non-zeros xlocs = np.linspace(-1, 1, nx + 1) @@ -368,7 +371,6 @@ def error_eval(nx): discrete_val = np.sum(M @ edge_vals) reference_value = 208 / 105 - print(discrete_val, reference_value) return np.abs(discrete_val - reference_value), xlocs[1] - xlocs[0] assert_expected_order(error_eval, [20.0, 30.0, 40.0, 50.0]) @@ -450,5 +452,60 @@ def error_eval(nx): assert_expected_order(error_eval, [20.0, 30.0, 40.0, 50.0]) +def test_edge_line_integral_3d(): + """ + testing the line integral here of: + scalar field: v = x**2 + y**2 + times the property: mu = (1 - x) * (x + 1) + 1 + over the path x=t, y = 2 * t, z=(t - 1) * (t + 1) + from t=-1 to 1 + + >>> t = sy.Symbol('t') + >>> x, y, z = t, 2 * t, (t-1)*(t+1) + >>> mu = (1 - t) * (t+1) + 1 + >>> v = x**2 + y**2 + >>> dx = sy.diff(x, t) + >>> dy = sy.diff(y, t) + >>> dz = sy.diff(z, t) + >>> dl = sy.sqrt(dx **2 + dy**2 + dz**2) + >>> integrand = mu * v * dl + >>> val = sy.integrate(integrand, (t, -1, 1)) + >>> float(val) + 12.490674765352512 + + """ + + def error_eval(nx): + nz = ny = 4 # not really important to this test, as only one "line" is non-zero + xlocs = np.linspace(-1, 1, nx + 1) + nodes_x = xlocs[:, None, None] * np.ones((1, ny + 1, nz + 1)) + nodes_y = 2 * nodes_x + np.linspace(-1, 1, ny + 1)[None, :, None] + nodes_z = (nodes_x - 1) * (nodes_x + 1) + np.linspace(-1, 1, nz + 1)[ + None, None, : + ] + + mesh = CurvilinearMesh((nodes_x, nodes_y, nodes_z)) + + # edge indices along the path... are x-edges: + edge_inds = np.arange(mesh.n_edges_x).reshape((nz + 1, ny + 1, nx))[2, 2, :] + + prop = (1 - mesh.edges[:, 0]) * (mesh.edges[:, 0] + 1) + 1 + edge_props = np.zeros(mesh.n_edges) + edge_props[edge_inds] = prop[edge_inds] + + ex = mesh.edges[:, 0] + ey = mesh.edges[:, 1] + w = ex**2 + ey**2 + + M = mesh.get_edge_inner_product_line(model=edge_props) + + discrete_val = np.sum(M @ w) + reference_value = 12.490674765352512 + print(discrete_val) + return np.abs(discrete_val - reference_value), xlocs[1] - xlocs[0] + + assert_expected_order(error_eval, [20.0, 40.0, 80.0]) + + if __name__ == "__main__": unittest.main()