diff --git a/simpeg_drivers/utils/synthetics/driver.py b/simpeg_drivers/utils/synthetics/driver.py index c4f6adfd..9b6c848f 100644 --- a/simpeg_drivers/utils/synthetics/driver.py +++ b/simpeg_drivers/utils/synthetics/driver.py @@ -50,7 +50,7 @@ def topography(self): assert self.options is not None entity = get_topography_surface( geoh5=self.geoh5, - options=self.options.survey, + options=self.options, ) self._topography = entity return self._topography diff --git a/simpeg_drivers/utils/synthetics/meshes/octrees.py b/simpeg_drivers/utils/synthetics/meshes/octrees.py index f6f12094..2281801e 100644 --- a/simpeg_drivers/utils/synthetics/meshes/octrees.py +++ b/simpeg_drivers/utils/synthetics/meshes/octrees.py @@ -75,7 +75,7 @@ def get_octree_mesh( :return mesh: The discretize TreeMesh object for computations. """ - mesh = get_base_octree(survey, topography, cell_size, (0, 0, 2), padding_distance) + mesh = get_base_octree(survey, topography, cell_size, (0, 1), padding_distance) mesh = OctreeDriver.refine_tree_from_points( mesh, survey.vertices, levels=refinement, finalize=False diff --git a/simpeg_drivers/utils/synthetics/surveys/factory.py b/simpeg_drivers/utils/synthetics/surveys/factory.py index 09ad24c5..300711dc 100644 --- a/simpeg_drivers/utils/synthetics/surveys/factory.py +++ b/simpeg_drivers/utils/synthetics/surveys/factory.py @@ -26,21 +26,21 @@ def grid_layout( limits: list[float], - station_spacing: int, - line_spacing: int, + n_stations: int, + n_lines: int, topography: Callable, ): """ Generates grid locations based on limits and spacing. :param limits: Tuple of (xmin, xmax, ymin, ymax). - :param station_spacing: Number of stations along each line. - :param line_spacing: Number of lines in the grid. + :param n_stations: Number of stations along each line. + :param n_lines: Number of lines in the grid. :param topography: Callable that generates the topography (z values). """ - x = np.linspace(limits[0], limits[1], station_spacing) - y = np.linspace(limits[2], limits[3], line_spacing) + x = np.linspace(limits[0], limits[1], n_stations) + y = np.linspace(limits[2], limits[3], n_lines) X, Y = np.meshgrid(x, y) Z = topography(X, Y) @@ -62,8 +62,8 @@ def get_survey( X, Y, Z = grid_layout( limits=options.limits, - station_spacing=options.n_stations, - line_spacing=options.n_lines, + n_stations=options.n_stations, + n_lines=options.n_lines, topography=options.topography, ) Z += options.drape diff --git a/simpeg_drivers/utils/synthetics/topography.py b/simpeg_drivers/utils/synthetics/topography.py index bf45415b..058e30c4 100644 --- a/simpeg_drivers/utils/synthetics/topography.py +++ b/simpeg_drivers/utils/synthetics/topography.py @@ -13,24 +13,36 @@ from geoh5py.objects import DrapeModel, Octree, Surface from scipy.spatial import Delaunay -from simpeg_drivers.utils.synthetics.options import SurveyOptions +from simpeg_drivers.utils.synthetics.options import ( + MeshOptions, + SurveyOptions, + SyntheticsComponentsOptions, +) from simpeg_drivers.utils.synthetics.surveys.factory import grid_layout from simpeg_drivers.utils.utils import active_from_xyz -def get_topography_surface(geoh5: Workspace, options: SurveyOptions) -> Surface: +def get_topography_surface( + geoh5: Workspace, options: SyntheticsComponentsOptions +) -> Surface: """ - Returns a topography surface with 2x the limits of the survey. + Returns topography with same limits as the mesh and 2x resolution of the survey. :param geoh5: Geoh5 workspace. - :param options: Survey options. Extents will be 2x the survey extents. + :param options: Options containing survey and mesh specifications. """ + width, height = compute_mesh_extents(options.survey, options.mesh) X, Y, Z = grid_layout( - limits=[4 * k for k in options.limits], # type: ignore - station_spacing=int(np.ceil((options.limits[1] - options.limits[0]) / 16)), - line_spacing=int(np.ceil((options.limits[3] - options.limits[2]) / 16)), - topography=options.topography, + limits=[ + options.survey.center[0] - width / 2, + options.survey.center[0] + width / 2, + options.survey.center[1] - height / 2, + options.survey.center[1] + height / 2, + ], + n_stations=int(2 * width / (options.survey.width / options.survey.n_stations)), + n_lines=int(2 * height / (options.survey.height / options.survey.n_lines)), + topography=options.survey.topography, ) vertices = np.column_stack( @@ -45,6 +57,32 @@ def get_topography_surface(geoh5: Workspace, options: SurveyOptions) -> Surface: ) +def compute_mesh_extents( + survey_options: SurveyOptions, mesh_options: MeshOptions +) -> tuple[float, float]: + """ + Estimates the extent of the mesh from survey and mesh options. + + :param survey_options: Survey options. + :param mesh_options: Mesh options. + + :return: mesh width including padding. + :return: mesh height including padding. + """ + width = survey_options.width + height = survey_options.height + cell_size = mesh_options.cell_size + padding = mesh_options.padding_distance + + def next_pow2_cells(span, cell_size, padding): + return 2 ** np.ceil(np.log2((span + 2 * padding) / cell_size)) + + return ( + cell_size[0] * next_pow2_cells(width, cell_size[0], padding), + cell_size[1] * next_pow2_cells(height, cell_size[1], padding), + ) + + def get_active( mesh: Octree | DrapeModel, topography: Surface, name: str = "active_cells" ): diff --git a/tests/plate_simulation/runtest/sweep_test.py b/tests/plate_simulation/runtest/sweep_test.py index c2d4b9e8..08d44003 100644 --- a/tests/plate_simulation/runtest/sweep_test.py +++ b/tests/plate_simulation/runtest/sweep_test.py @@ -16,17 +16,16 @@ from simpeg_drivers import assets_path from simpeg_drivers.plate_simulation.options import PlateSimulationOptions from simpeg_drivers.plate_simulation.sweep.driver import PlateSweepDriver -from simpeg_drivers.plate_simulation.sweep.options import SweepOptions from simpeg_drivers.potential_fields.gravity.options import GravityForwardOptions -from simpeg_drivers.utils.synthetics.options import SurveyOptions +from simpeg_drivers.utils.synthetics.options import SyntheticsComponentsOptions from simpeg_drivers.utils.synthetics.surveys.factory import get_survey from simpeg_drivers.utils.synthetics.topography import get_topography_surface def setup_plate_sweep(workspace) -> SimPEGGroup: - survey_options = SurveyOptions() - data = get_survey(workspace, method="gravity", options=survey_options) - topo = get_topography_surface(workspace, survey_options) + options = SyntheticsComponentsOptions() + data = get_survey(workspace, method="gravity", options=options.survey) + topo = get_topography_surface(workspace, options) gravity = SimPEGGroup.create(workspace, name="gravity fwd") options = GravityForwardOptions.model_construct() diff --git a/tests/run_tests/driver_2d_rotated_gradients_test.py b/tests/run_tests/driver_2d_rotated_gradients_test.py index 6252f982..7c3df653 100644 --- a/tests/run_tests/driver_2d_rotated_gradients_test.py +++ b/tests/run_tests/driver_2d_rotated_gradients_test.py @@ -42,7 +42,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.688327405060795, "phi_d": 190, "phi_m": 70.5} +target_run = {"data_norm": 0.644727102942656, "phi_d": 182, "phi_m": 66.3} def test_dc2d_rotated_grad_fwr_run( diff --git a/tests/run_tests/driver_airborne_fem_1d_test.py b/tests/run_tests/driver_airborne_fem_1d_test.py index 1d83133f..b39a4c9c 100644 --- a/tests/run_tests/driver_airborne_fem_1d_test.py +++ b/tests/run_tests/driver_airborne_fem_1d_test.py @@ -41,7 +41,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 654.2956399855235, "phi_d": 58300, "phi_m": 396} +target_run = {"data_norm": 652.4097142104288, "phi_d": 52800, "phi_m": 341} def test_fem_fwr_1d_run( diff --git a/tests/run_tests/driver_airborne_tem_1d_test.py b/tests/run_tests/driver_airborne_tem_1d_test.py index 6c13078f..c2beb731 100644 --- a/tests/run_tests/driver_airborne_tem_1d_test.py +++ b/tests/run_tests/driver_airborne_tem_1d_test.py @@ -38,7 +38,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 5.08855682e-10, "phi_d": 20.2, "phi_m": 169000} +target_run = {"data_norm": 5.043446361560613e-10, "phi_d": 23, "phi_m": 194000} def test_airborne_tem_1d_fwr_run( diff --git a/tests/run_tests/driver_airborne_tem_test.py b/tests/run_tests/driver_airborne_tem_test.py index ba7c94e5..69ac70e2 100644 --- a/tests/run_tests/driver_airborne_tem_test.py +++ b/tests/run_tests/driver_airborne_tem_test.py @@ -39,7 +39,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 4.0476883635425e-08, "phi_d": 22700000, "phi_m": 1030} +target_run = {"data_norm": 4.047707815081421e-08, "phi_d": 22100000, "phi_m": 2920} def test_bad_waveform(tmp_path: Path): diff --git a/tests/run_tests/driver_dc_2d_test.py b/tests/run_tests/driver_dc_2d_test.py index 15b40e30..4afc7b00 100644 --- a/tests/run_tests/driver_dc_2d_test.py +++ b/tests/run_tests/driver_dc_2d_test.py @@ -43,7 +43,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.6441011110410102, "phi_d": 644, "phi_m": 107} +target_run = {"data_norm": 0.606697124141973, "phi_d": 693, "phi_m": 88.5} def test_dc_2d_fwr_run( diff --git a/tests/run_tests/driver_dc_b2d_rotated_gradients_test.py b/tests/run_tests/driver_dc_b2d_rotated_gradients_test.py index 8cbd5cac..761101a8 100644 --- a/tests/run_tests/driver_dc_b2d_rotated_gradients_test.py +++ b/tests/run_tests/driver_dc_b2d_rotated_gradients_test.py @@ -49,7 +49,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 1.1462051301574965, "phi_d": 59.3, "phi_m": 7.24} +target_run = {"data_norm": 1.1067294238524659, "phi_d": 55.6, "phi_m": 7.08} def test_dc_rotated_p3d_fwr_run( diff --git a/tests/run_tests/driver_dc_b2d_test.py b/tests/run_tests/driver_dc_b2d_test.py index e9dde4ba..3d4fb365 100644 --- a/tests/run_tests/driver_dc_b2d_test.py +++ b/tests/run_tests/driver_dc_b2d_test.py @@ -47,7 +47,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 1.1290474826747758, "phi_d": 2550, "phi_m": 31.9} +target_run = {"data_norm": 1.0912968903879878, "phi_d": 2060, "phi_m": 34.3} def test_dc_p3d_fwr_run( diff --git a/tests/run_tests/driver_dc_test.py b/tests/run_tests/driver_dc_test.py index d0a4cdcd..bc8afa8f 100644 --- a/tests/run_tests/driver_dc_test.py +++ b/tests/run_tests/driver_dc_test.py @@ -38,7 +38,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.1404580196636685, "phi_d": 1.9, "phi_m": 127} +target_run = {"data_norm": 0.1434420468146182, "phi_d": 5.27, "phi_m": 231} def test_dc_3d_fwr_run( diff --git a/tests/run_tests/driver_fem_test.py b/tests/run_tests/driver_fem_test.py index 83864e1d..b9da6874 100644 --- a/tests/run_tests/driver_fem_test.py +++ b/tests/run_tests/driver_fem_test.py @@ -43,7 +43,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 90.93296256260754, "phi_d": 4270, "phi_m": 918} +target_run = {"data_norm": 90.98397054326658, "phi_d": 4290, "phi_m": 916} def test_fem_name_change(tmp_path, caplog): diff --git a/tests/run_tests/driver_grav_test.py b/tests/run_tests/driver_grav_test.py index 1aba275c..bf2666f2 100644 --- a/tests/run_tests/driver_grav_test.py +++ b/tests/run_tests/driver_grav_test.py @@ -41,7 +41,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.0008191079906769513, "phi_d": 1.62e-07, "phi_m": 0.00202} +target_run = {"data_norm": 0.002805526927601853, "phi_d": 1.77e-05, "phi_m": 0.023} def test_gravity_fwr_run( diff --git a/tests/run_tests/driver_ground_tem_test.py b/tests/run_tests/driver_ground_tem_test.py index 95922ac8..eb691f3f 100644 --- a/tests/run_tests/driver_ground_tem_test.py +++ b/tests/run_tests/driver_ground_tem_test.py @@ -44,7 +44,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 9.310904038470233e-07, "phi_d": 257, "phi_m": 67400} +target_run = {"data_norm": 9.165423633989594e-07, "phi_d": 314, "phi_m": 4270} def test_tiling_ground_tem( @@ -157,12 +157,12 @@ def test_ground_tem_fwr_run( assert fwr_driver.inversion_data.survey.source_list[0].n_segments == 16 if pytest and caplog: - assert len(caplog.records) == 3 - for record in caplog.records[1:]: + assert len(caplog.records) == 2 + for record in caplog.records: assert record.levelname == "INFO" assert "counter-clockwise" in record.message - assert "closed" in caplog.records[1].message + assert "closed" in caplog.records[0].message assert ( fwr_driver.data_misfit.objfcts[0].simulation.simulations[0].solver == Mumps diff --git a/tests/run_tests/driver_ip_2d_test.py b/tests/run_tests/driver_ip_2d_test.py index 67ea85c4..38f3ef9c 100644 --- a/tests/run_tests/driver_ip_2d_test.py +++ b/tests/run_tests/driver_ip_2d_test.py @@ -39,7 +39,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.06364250336901277, "phi_d": 101000, "phi_m": 4.34e-09} +target_run = {"data_norm": 0.09193270736303862, "phi_d": 211000, "phi_m": 3.11e-08} def test_ip_2d_fwr_run( diff --git a/tests/run_tests/driver_ip_b2d_test.py b/tests/run_tests/driver_ip_b2d_test.py index c168a5b1..612610a2 100644 --- a/tests/run_tests/driver_ip_b2d_test.py +++ b/tests/run_tests/driver_ip_b2d_test.py @@ -47,7 +47,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.09536558519786646, "phi_d": 227000, "phi_m": 4.73e-08} +target_run = {"data_norm": 0.09310413606088193, "phi_d": 217000, "phi_m": 5.09e-08} def test_ip_p3d_fwr_run( diff --git a/tests/run_tests/driver_ip_test.py b/tests/run_tests/driver_ip_test.py index 35680790..ff39214f 100644 --- a/tests/run_tests/driver_ip_test.py +++ b/tests/run_tests/driver_ip_test.py @@ -37,7 +37,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.006803670345605742, "phi_d": 1140, "phi_m": 8.28e-06} +target_run = {"data_norm": 0.007217903416312316, "phi_d": 1280, "phi_m": 2.52e-05} def test_ip_3d_fwr_run( diff --git a/tests/run_tests/driver_joint_cross_gradient_test.py b/tests/run_tests/driver_joint_cross_gradient_test.py index d6838476..7c52a187 100644 --- a/tests/run_tests/driver_joint_cross_gradient_test.py +++ b/tests/run_tests/driver_joint_cross_gradient_test.py @@ -57,7 +57,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 53.29582613045845, "phi_d": 9210, "phi_m": 0.133} +target_run = {"data_norm": 53.295837974010844, "phi_d": 9360, "phi_m": 0.126} INDUCING_FIELD = (50000.0, 90.0, 0.0) diff --git a/tests/run_tests/driver_joint_pgi_homogeneous_test.py b/tests/run_tests/driver_joint_pgi_homogeneous_test.py index f56174fa..121b1033 100644 --- a/tests/run_tests/driver_joint_pgi_homogeneous_test.py +++ b/tests/run_tests/driver_joint_pgi_homogeneous_test.py @@ -54,7 +54,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 440.08021575766554, "phi_d": 1010, "phi_m": 42500} +target_run = {"data_norm": 412.2653000131554, "phi_d": 975, "phi_m": 37200} INDUCING_FIELD = (50000.0, 90.0, 0.0) diff --git a/tests/run_tests/driver_joint_surveys_test.py b/tests/run_tests/driver_joint_surveys_test.py index 3c155d7e..50de74cb 100644 --- a/tests/run_tests/driver_joint_surveys_test.py +++ b/tests/run_tests/driver_joint_surveys_test.py @@ -51,7 +51,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.2997791602206556, "phi_d": 1510, "phi_m": 41} +target_run = {"data_norm": 0.29977916022065554, "phi_d": 1340, "phi_m": 40.5} def test_joint_surveys_fwr_run( @@ -192,7 +192,7 @@ def test_joint_surveys_inv_run( # The rescaling is done evenly on the two tiles for both surveys np.testing.assert_allclose( driver.data_misfit.multipliers, - [1.0, 1.0, 0.8341, 0.8341], + [1.0, 1.0, 0.92959756, 0.92959756], atol=1e-3, ) diff --git a/tests/run_tests/driver_mag_test.py b/tests/run_tests/driver_mag_test.py index a77ea120..85ee61ce 100644 --- a/tests/run_tests/driver_mag_test.py +++ b/tests/run_tests/driver_mag_test.py @@ -39,7 +39,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 13.010175158724156, "phi_d": 91.4, "phi_m": 1.01e-05} +target_run = {"data_norm": 8.712244667781324, "phi_d": 46.6, "phi_m": 3.23e-06} def test_susceptibility_fwr_run( diff --git a/tests/run_tests/driver_mt_test.py b/tests/run_tests/driver_mt_test.py index 83d7f756..93d3629e 100644 --- a/tests/run_tests/driver_mt_test.py +++ b/tests/run_tests/driver_mt_test.py @@ -40,7 +40,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.0197168136860386, "phi_d": 1.85, "phi_m": 22.9} +target_run = {"data_norm": 0.020014421089872222, "phi_d": 1.63, "phi_m": 27.6} def setup_data(workspace, survey): diff --git a/tests/run_tests/driver_mvi_test.py b/tests/run_tests/driver_mvi_test.py index 61db536c..131f7da6 100644 --- a/tests/run_tests/driver_mvi_test.py +++ b/tests/run_tests/driver_mvi_test.py @@ -46,7 +46,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_mvi_run = {"data_norm": 149.10117434016036, "phi_d": 5.49, "phi_m": 0.0315} +target_mvi_run = {"data_norm": 149.1011743401604, "phi_d": 188, "phi_m": 0.0303} def test_magnetic_vector_fwr_run( diff --git a/tests/run_tests/driver_tipper_test.py b/tests/run_tests/driver_tipper_test.py index 30fe77f6..b9160d55 100644 --- a/tests/run_tests/driver_tipper_test.py +++ b/tests/run_tests/driver_tipper_test.py @@ -39,7 +39,7 @@ # To test the full run and validate the inversion. # Move this file out of the test directory and run. -target_run = {"data_norm": 0.01938047698245966, "phi_d": 1.54, "phi_m": 59.3} +target_run = {"data_norm": 0.021515656856259276, "phi_d": 1.57, "phi_m": 44.8} def test_tipper_fwr_run( diff --git a/tests/run_tests/sensitivity_cutoff_test.py b/tests/run_tests/sensitivity_cutoff_test.py index ce4cc5fe..ada8e754 100644 --- a/tests/run_tests/sensitivity_cutoff_test.py +++ b/tests/run_tests/sensitivity_cutoff_test.py @@ -116,7 +116,7 @@ def test_sensitivity_percent_cutoff_run(tmp_path): SensitivityCutoffDriver.start(str(tmp_path / "sensitivity_cutoff_percent.ui.json")) with Workspace(tmp_path / "inversion_test.ui.geoh5") as geoh5: mask = geoh5.get_entity("5 percent cutoff")[0] - assert mask.values.sum() == 457 + assert mask.values.sum() == 722 def test_sensitivity_cutoff_percentile_run(tmp_path): @@ -144,7 +144,7 @@ def test_sensitivity_cutoff_percentile_run(tmp_path): ) with Workspace(tmp_path / "inversion_test.ui.geoh5") as geoh5: mask = geoh5.get_entity("5 percentile cutoff")[0] - assert mask.values.sum() == 4288 + assert mask.values.sum() == 9743 def test_sensitivity_cutoff_log_percent_run(tmp_path): @@ -172,4 +172,4 @@ def test_sensitivity_cutoff_log_percent_run(tmp_path): ) with Workspace(tmp_path / "inversion_test.ui.geoh5") as geoh5: mask = geoh5.get_entity("5 percent log cutoff")[0] - assert mask.values.sum() == 4329 + assert mask.values.sum() == 9830