Repository: emsig/emg3d

Issue:
Bug in Dipole and Wire sources
# Horrible bug in Dipole and Wire sources

```python
import emg3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm

h = np.ones(24)*100
grid = emg3d.TensorMesh([h, h, h], 'CCC')

src_coo = np.array([[-1000, +1000, 0], [+1000, -1000, 0]])
source = emg3d.TxElectricDipole(coordinates=src_coo)
frequency = 10
sfield = emg3d.get_source_field(grid, source, frequency)

sfield.grid.plot_3d_slicer(sfield.fx.ravel('F'), view='abs', v_type='Ex', pcolor_opts={'norm': LogNorm()})
```

Output:
```bash
emg3d/fields.py:923: UserWarning: emg3d: Normalizing Source: 58.0000000000.
emg3d/fields.py:923: UserWarning: emg3d: Normalizing Source: 58.0000000000.
emg3d/fields.py:923: UserWarning: emg3d: Normalizing Source: 58.0000000000.
```

![Figure 1](https://github.com/user-attachments/assets/b1f46737-88fa-4096-b373-db653d582d2e)



Discussion:
coveralls: ## Pull Request Test Coverage Report for [Build 12585375841](https://coveralls.io/builds/71559695)


### Details

* **1** of **1**   **(100.0%)**  changed or added relevant line in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage remained the same at **99.829%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/71559695/badge)](https://coveralls.io/builds/71559695) |
| :-- | --: |
| Change from base [Build 11975277304](https://coveralls.io/builds/71010079): |  0.0% |
| Covered Lines: | 4663 |
| Relevant Lines: | 4671 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index afdab7f..b104b1a 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,12 +6,15 @@ Changelog
 """"""""""
 
 
-latest
-------
+v1.8.5 : Bugfix and pyproject
+-----------------------------
 
-- Maintenance:
+- Bugfix and maintenance:
 
-  - Changed from ``setup.py`` to ``pyproject.toml``.
+  - Fix bug in Dipole/Wire sources when coordinates are mixed increasing and
+    decreasing over several cells
+    [#348](https://github.com/emsig/emg3d/issues/348).
+  - Changed build backend from ``setup.py`` to ``pyproject.toml``.
 
 
 v1.8.4 : NumPy v2
diff --git a/emg3d/fields.py b/emg3d/fields.py
index ceb1549..3572955 100644
--- a/emg3d/fields.py
+++ b/emg3d/fields.py
@@ -898,7 +898,7 @@ def _dipole_vector(grid, points, decimals=9):
                 ez = 1 - rz
 
                 # Add to field (only if segment inside cell).
-                if min(rx, ry, rz) >= 0 and np.max(np.abs(ar-al)) > 0:
+                if min(rx, ex, ry, ey, rz, ez) >= 0 and np.max(abs(ar-al)) > 0:
 
                     vfield.fx[ix, iy, iz] += ey*ez*x_len
                     vfield.fx[ix, iy+1, iz] += ry*ez*x_len
@@ -918,7 +918,9 @@ def _dipole_vector(grid, points, decimals=9):
     # Ensure unity (should not be necessary).
     for field in [vfield.fx, vfield.fy, vfield.fz]:
         sum_s = abs(field.sum())
-        if abs(sum_s-1) > 1e-6:  # Normalize and warn.
+        # 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
             msg = f"emg3d: Normalizing Source: {sum_s:.10f}."
             warnings.warn(msg, UserWarning)
             field /= sum_s


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
index b273ec7..2b68e3d 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -652,6 +652,7 @@ class TestDipoleVector:
         grid = emg3d.TensorMesh([h, h, h], (-3, -3, -3))
 
         # Large diagonal source in the middle
+        # x, y, z increasing
         source = np.array([[-2.5, -2.5, -2.5], [2.5, 2.5, 2.5]])
         vfield = fields._dipole_vector(grid, source)
 
@@ -672,6 +673,22 @@ class TestDipoleVector:
         assert_allclose(vfield.fx[1, 1:3, 1:3].ravel(),
                         vfield.fx[2, 2:4, 2:4].ravel()[::-1])
 
+        # Large diagonal source in the middle
+        # x, z increasing, y decreasing
+        source = np.array([[-2.5, 2.5, -2.5], [2.5, -2.5, 2.5]])
+        vfield2 = fields._dipole_vector(grid, source)
+        assert_allclose(vfield.fx, np.fliplr(vfield2.fx))
+        assert_allclose(vfield.fy, -np.fliplr(vfield2.fy))
+        assert_allclose(vfield.fz, np.fliplr(vfield2.fz))
+
+        # Large diagonal source in the middle
+        # x, z decreasing, y increasing
+        source = np.array([[2.5, -2.5, 2.5], [-2.5, 2.5, -2.5]])
+        vfield3 = fields._dipole_vector(grid, source)
+        assert_allclose(vfield.fx, -np.fliplr(vfield3.fx))
+        assert_allclose(vfield.fy, np.fliplr(vfield3.fy))
+        assert_allclose(vfield.fz, -np.fliplr(vfield3.fz))
+
     def test_decimals(self):
         h1 = [2, 1, 1, 2]
         h2 = [2, 1.04, 1.04, 2]
@@ -698,13 +715,6 @@ class TestDipoleVector:
         with pytest.raises(ValueError, match='Provided finite dipole'):
             fields._dipole_vector(grid, source)
 
-        # This is a warning that should never be raised...
-        hx, x0 = np.ones(4), -2
-        grid = emg3d.TensorMesh([hx, hx, hx], (x0, x0, x0))
-        source = np.array([[-2, 2, 0], [0, -2, 0]])
-        with pytest.warns(UserWarning, match="Normalizing Source: 1.25000000"):
-            fields._dipole_vector(grid, source, 30)
-
 
 @pytest.mark.parametrize("njit", [True, False])
 def test_edge_curl_factor(njit):