diff --git a/emg3d/fields.py b/emg3d/fields.py index 3572955c..425352c4 100644 --- a/emg3d/fields.py +++ b/emg3d/fields.py @@ -789,7 +789,7 @@ def _point_vector_magnetic(grid, coordinates, frequency): return vfield -def _dipole_vector(grid, points, decimals=9): +def _dipole_vector(grid, points, decimals=9, nodes=None): """Get n-segment dipole source by distributing them to the relevant cells. @@ -814,6 +814,26 @@ def _dipole_vector(grid, points, decimals=9): """ + # Round nodes and source coordinates (to avoid floating point issues etc). + # Recursion: only necessary in first go. + if nodes: + nodes_x, nodes_y, nodes_z = nodes + else: + nodes_x = np.round(grid.nodes_x, decimals) + nodes_y = np.round(grid.nodes_y, decimals) + nodes_z = np.round(grid.nodes_z, decimals) + pts = np.round(np.asarray(points, dtype=float), decimals) + + # Ensure source is within nodes. + outside = ( + min(pts[:, 0]) < nodes_x[0] or max(pts[:, 0]) > nodes_x[-1] or + min(pts[:, 1]) < nodes_y[0] or max(pts[:, 1]) > nodes_y[-1] or + min(pts[:, 2]) < nodes_z[0] or max(pts[:, 2]) > nodes_z[-1] + ) + if outside: + raise ValueError(f"Provided source outside grid: {pts}.") + points = pts + vfield = Field(grid, dtype=float) # Recursively loop through segments. @@ -822,26 +842,12 @@ def _dipole_vector(grid, points, decimals=9): # Add each segments' vector field to total vector field. for p0, p1 in zip(points[:-1, :], points[1:, :]): vfield.field += _dipole_vector( - grid, points=np.r_[[p0, p1]], decimals=decimals + grid, points=np.r_[[p0, p1]], decimals=decimals, + nodes=(nodes_x, nodes_y, nodes_z) ).field return vfield - # Round nodes and source coordinates (to avoid floating point issues etc). - nodes_x = np.round(grid.nodes_x, decimals) - nodes_y = np.round(grid.nodes_y, decimals) - nodes_z = np.round(grid.nodes_z, decimals) - points = np.round(np.asarray(points, dtype=float), decimals) - - # Ensure source is within nodes. - outside = ( - min(points[:, 0]) < nodes_x[0] or max(points[:, 0]) > nodes_x[-1] or - min(points[:, 1]) < nodes_y[0] or max(points[:, 1]) > nodes_y[-1] or - min(points[:, 2]) < nodes_z[0] or max(points[:, 2]) > nodes_z[-1] - ) - if outside: - raise ValueError(f"Provided source outside grid: {points}.") - # Dipole lengths in x-, y-, and z-directions, and overall. dxdydz = points[1, :] - points[0, :] length = np.linalg.norm(dxdydz) @@ -919,8 +925,7 @@ def min_max_ind(vector, i): for field in [vfield.fx, vfield.fy, vfield.fz]: sum_s = abs(field.sum()) # Normalize and warn; SHOULD NEVER HAPPEN - # (if it happens add it to the tests and remove the pragma-flag! - if abs(sum_s-1) > 1e-6: # pragma: no cover + if abs(sum_s-1) > 1e-6: msg = f"emg3d: Normalizing Source: {sum_s:.10f}." warnings.warn(msg, UserWarning) field /= sum_s diff --git a/tests/test_fields.py b/tests/test_fields.py index 2b68e3d1..ccbe05d3 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -715,6 +715,15 @@ def test_warnings(self): with pytest.raises(ValueError, match='Provided finite dipole'): fields._dipole_vector(grid, source) + # Create a BaseMesh and modify the widths, so _dipole_vector will + # have to normalize. + h = np.ones(4) + grid = emg3d.meshes.BaseMesh([h, h, h], (0, 0, 0)) + grid.h = [grid.h[0], grid.h[1]*.3, grid.h[2]] + source = np.array([[0, 1, 2], [3, 3, 2]]) + with pytest.warns(UserWarning, match='Normalizing Source'): + fields._dipole_vector(grid, source) + @pytest.mark.parametrize("njit", [True, False]) def test_edge_curl_factor(njit):