|
10 | 10 | from kwave.ksensor import kSensor |
11 | 11 | from kwave.ksource import kSource |
12 | 12 | from kwave.kspaceFirstOrder import reshape_to_grid |
| 13 | +from kwave.solvers.kspace_solver import _f_to_c_source_reorder |
13 | 14 |
|
14 | 15 | # --------------------------------------------------------------------------- |
15 | 16 | # _fix_output_order (CppSimulation) |
@@ -141,6 +142,101 @@ def test_passthrough_higher_dim(self): |
141 | 142 | assert out.shape == (2, 3, 4, 5) |
142 | 143 |
|
143 | 144 |
|
| 145 | +# --------------------------------------------------------------------------- |
| 146 | +# _f_to_c_source_reorder (MATLAB interop) |
| 147 | +# --------------------------------------------------------------------------- |
| 148 | + |
| 149 | + |
| 150 | +class TestFToCSourceReorder: |
| 151 | + def test_1d_noop(self): |
| 152 | + """1D grids have no F/C ordering difference — source unchanged.""" |
| 153 | + source = {"p_mask": np.array([0, 1, 0, 1, 0, 0, 0, 0], dtype=bool), "p": np.array([[10, 11], [20, 21]])} |
| 154 | + out = _f_to_c_source_reorder(source, (8,)) |
| 155 | + np.testing.assert_array_equal(out["p"], np.array([[10, 11], [20, 21]])) |
| 156 | + |
| 157 | + def test_pressure_multi_source_2d(self): |
| 158 | + """Multi-row pressure source rows are reordered from F-flat to C-flat.""" |
| 159 | + grid_shape = (3, 4) |
| 160 | + mask = np.zeros(grid_shape, dtype=bool) |
| 161 | + mask[0, 0] = True # F-idx 0, C-idx 0 |
| 162 | + mask[1, 0] = True # F-idx 1, C-idx 4 |
| 163 | + mask[0, 1] = True # F-idx 3, C-idx 1 |
| 164 | + |
| 165 | + # Signal rows in F-flat order: row0→(0,0), row1→(1,0), row2→(0,1) |
| 166 | + p_signal = np.array([[100, 101], [200, 201], [300, 301]]) |
| 167 | + source = {"p_mask": mask, "p": p_signal.copy()} |
| 168 | + out = _f_to_c_source_reorder(source, grid_shape) |
| 169 | + |
| 170 | + # C-flat order: (0,0)=idx0, (0,1)=idx1, (1,0)=idx4 |
| 171 | + # So C-flat rows should be: (0,0), (0,1), (1,0) = F-rows 0, 2, 1 |
| 172 | + np.testing.assert_array_equal(out["p"][0], [100, 101]) # (0,0) |
| 173 | + np.testing.assert_array_equal(out["p"][1], [300, 301]) # (0,1) |
| 174 | + np.testing.assert_array_equal(out["p"][2], [200, 201]) # (1,0) |
| 175 | + |
| 176 | + def test_velocity_multi_source_2d(self): |
| 177 | + """Multi-row velocity source rows are reordered.""" |
| 178 | + grid_shape = (3, 4) |
| 179 | + mask = np.zeros(grid_shape, dtype=bool) |
| 180 | + mask[0, 0] = True |
| 181 | + mask[2, 1] = True # F-idx 5, C-idx 6 |
| 182 | + |
| 183 | + ux = np.array([[10, 11], [20, 21]]) |
| 184 | + source = {"u_mask": mask, "ux": ux.copy(), "uy": None} |
| 185 | + out = _f_to_c_source_reorder(source, grid_shape) |
| 186 | + # With only 2 points, check that reorder happened (or is identity if indices align) |
| 187 | + assert out["ux"].shape == (2, 2) |
| 188 | + |
| 189 | + def test_single_source_noop(self): |
| 190 | + """Single-source (n_src=1) is not reordered.""" |
| 191 | + grid_shape = (4, 4) |
| 192 | + mask = np.zeros(grid_shape, dtype=bool) |
| 193 | + mask[2, 3] = True |
| 194 | + p_signal = np.array([[5, 6, 7]]) |
| 195 | + source = {"p_mask": mask, "p": p_signal.copy()} |
| 196 | + out = _f_to_c_source_reorder(source, grid_shape) |
| 197 | + np.testing.assert_array_equal(out["p"], p_signal) |
| 198 | + |
| 199 | + def test_uniform_source_noop(self): |
| 200 | + """Uniform source (1 row broadcast to all) is not reordered.""" |
| 201 | + grid_shape = (4, 4) |
| 202 | + mask = np.zeros(grid_shape, dtype=bool) |
| 203 | + mask[0, 0] = True |
| 204 | + mask[1, 1] = True |
| 205 | + mask[2, 2] = True |
| 206 | + # 1 row, broadcast — n_rows != n_src |
| 207 | + p_signal = np.array([[1, 2, 3]]) |
| 208 | + source = {"p_mask": mask, "p": p_signal.copy()} |
| 209 | + out = _f_to_c_source_reorder(source, grid_shape) |
| 210 | + np.testing.assert_array_equal(out["p"], p_signal) |
| 211 | + |
| 212 | + def test_no_mask_noop(self): |
| 213 | + """Missing mask key is silently skipped.""" |
| 214 | + source = {"p": np.array([[1, 2]])} |
| 215 | + out = _f_to_c_source_reorder(source, (4, 4)) |
| 216 | + np.testing.assert_array_equal(out["p"], np.array([[1, 2]])) |
| 217 | + |
| 218 | + def test_scalar_mask_noop(self): |
| 219 | + """Scalar mask (size 1) is skipped.""" |
| 220 | + source = {"p_mask": np.array([True]), "p": np.array([[1, 2]])} |
| 221 | + out = _f_to_c_source_reorder(source, (4, 4)) |
| 222 | + np.testing.assert_array_equal(out["p"], np.array([[1, 2]])) |
| 223 | + |
| 224 | + def test_3d_grid(self): |
| 225 | + """Works with 3D grids.""" |
| 226 | + grid_shape = (2, 3, 4) |
| 227 | + mask = np.zeros(grid_shape, dtype=bool) |
| 228 | + mask[0, 0, 0] = True # F-idx 0, C-idx 0 |
| 229 | + mask[1, 0, 0] = True # F-idx 1, C-idx 12 |
| 230 | + mask[0, 1, 0] = True # F-idx 2, C-idx 4 |
| 231 | + p_signal = np.array([[10, 11], [20, 21], [30, 31]]) |
| 232 | + source = {"p_mask": mask, "p": p_signal.copy()} |
| 233 | + out = _f_to_c_source_reorder(source, grid_shape) |
| 234 | + # C-flat: (0,0,0)=0, (0,1,0)=4, (1,0,0)=12 → F-rows 0, 2, 1 |
| 235 | + np.testing.assert_array_equal(out["p"][0], [10, 11]) |
| 236 | + np.testing.assert_array_equal(out["p"][1], [30, 31]) |
| 237 | + np.testing.assert_array_equal(out["p"][2], [20, 21]) |
| 238 | + |
| 239 | + |
144 | 240 | # --------------------------------------------------------------------------- |
145 | 241 | # FutureWarning tests |
146 | 242 | # --------------------------------------------------------------------------- |
|
0 commit comments