From 4033144224e41a7d55909ec30a839b091a04822b Mon Sep 17 00:00:00 2001 From: benjamink Date: Fri, 31 Jan 2025 13:58:39 -0800 Subject: [PATCH 01/13] dc3d params to basedata and runtest passing --- ...json => direct_current_2d_forward.ui.json} | 0 ...on => direct_current_2d_inversion.ui.json} | 0 ...json => direct_current_3d_forward.ui.json} | 0 ...on => direct_current_3d_inversion.ui.json} | 0 ...> direct_current_pseudo3d_forward.ui.json} | 0 ...direct_current_pseudo3d_inversion.ui.json} | 0 ...> induced_polarization_2d_forward.ui.json} | 0 ...induced_polarization_2d_inversion.ui.json} | 0 ...> induced_polarization_3d_forward.ui.json} | 0 ...induced_polarization_3d_inversion.ui.json} | 0 ...ced_polarization_pseudo3d_forward.ui.json} | 0 ...d_polarization_pseudo3d_inversion.ui.json} | 0 simpeg_drivers/__init__.py | 5 +- simpeg_drivers/electricals/__init__.py | 5 +- .../three_dimensions/__init__.py | 2 +- .../direct_current/three_dimensions/driver.py | 14 ++- .../direct_current/three_dimensions/params.py | 111 ++++++++---------- simpeg_drivers/params.py | 2 +- .../potential_fields/gravity/driver.py | 4 - .../magnetic_scalar/params.py | 4 - tests/run_tests/driver_dc_test.py | 57 ++++----- 21 files changed, 94 insertions(+), 110 deletions(-) rename simpeg_drivers-assets/uijson/{direct_current_forward_2d.ui.json => direct_current_2d_forward.ui.json} (100%) rename simpeg_drivers-assets/uijson/{direct_current_inversion_2d.ui.json => direct_current_2d_inversion.ui.json} (100%) rename simpeg_drivers-assets/uijson/{direct_current_forward_3d.ui.json => direct_current_3d_forward.ui.json} (100%) rename simpeg_drivers-assets/uijson/{direct_current_inversion_3d.ui.json => direct_current_3d_inversion.ui.json} (100%) rename simpeg_drivers-assets/uijson/{direct_current_forward_pseudo3d.ui.json => direct_current_pseudo3d_forward.ui.json} (100%) rename simpeg_drivers-assets/uijson/{direct_current_inversion_pseudo3d.ui.json => direct_current_pseudo3d_inversion.ui.json} (100%) rename simpeg_drivers-assets/uijson/{induced_polarization_forward_2d.ui.json => induced_polarization_2d_forward.ui.json} (100%) rename simpeg_drivers-assets/uijson/{induced_polarization_inversion_2d.ui.json => induced_polarization_2d_inversion.ui.json} (100%) rename simpeg_drivers-assets/uijson/{induced_polarization_forward_3d.ui.json => induced_polarization_3d_forward.ui.json} (100%) rename simpeg_drivers-assets/uijson/{induced_polarization_inversion_3d.ui.json => induced_polarization_3d_inversion.ui.json} (100%) rename simpeg_drivers-assets/uijson/{induced_polarization_forward_pseudo3d.ui.json => induced_polarization_pseudo3d_forward.ui.json} (100%) rename simpeg_drivers-assets/uijson/{induced_polarization_inversion_pseudo3d.ui.json => induced_polarization_pseudo3d_inversion.ui.json} (100%) diff --git a/simpeg_drivers-assets/uijson/direct_current_forward_2d.ui.json b/simpeg_drivers-assets/uijson/direct_current_2d_forward.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/direct_current_forward_2d.ui.json rename to simpeg_drivers-assets/uijson/direct_current_2d_forward.ui.json diff --git a/simpeg_drivers-assets/uijson/direct_current_inversion_2d.ui.json b/simpeg_drivers-assets/uijson/direct_current_2d_inversion.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/direct_current_inversion_2d.ui.json rename to simpeg_drivers-assets/uijson/direct_current_2d_inversion.ui.json diff --git a/simpeg_drivers-assets/uijson/direct_current_forward_3d.ui.json b/simpeg_drivers-assets/uijson/direct_current_3d_forward.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/direct_current_forward_3d.ui.json rename to simpeg_drivers-assets/uijson/direct_current_3d_forward.ui.json diff --git a/simpeg_drivers-assets/uijson/direct_current_inversion_3d.ui.json b/simpeg_drivers-assets/uijson/direct_current_3d_inversion.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/direct_current_inversion_3d.ui.json rename to simpeg_drivers-assets/uijson/direct_current_3d_inversion.ui.json diff --git a/simpeg_drivers-assets/uijson/direct_current_forward_pseudo3d.ui.json b/simpeg_drivers-assets/uijson/direct_current_pseudo3d_forward.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/direct_current_forward_pseudo3d.ui.json rename to simpeg_drivers-assets/uijson/direct_current_pseudo3d_forward.ui.json diff --git a/simpeg_drivers-assets/uijson/direct_current_inversion_pseudo3d.ui.json b/simpeg_drivers-assets/uijson/direct_current_pseudo3d_inversion.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/direct_current_inversion_pseudo3d.ui.json rename to simpeg_drivers-assets/uijson/direct_current_pseudo3d_inversion.ui.json diff --git a/simpeg_drivers-assets/uijson/induced_polarization_forward_2d.ui.json b/simpeg_drivers-assets/uijson/induced_polarization_2d_forward.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/induced_polarization_forward_2d.ui.json rename to simpeg_drivers-assets/uijson/induced_polarization_2d_forward.ui.json diff --git a/simpeg_drivers-assets/uijson/induced_polarization_inversion_2d.ui.json b/simpeg_drivers-assets/uijson/induced_polarization_2d_inversion.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/induced_polarization_inversion_2d.ui.json rename to simpeg_drivers-assets/uijson/induced_polarization_2d_inversion.ui.json diff --git a/simpeg_drivers-assets/uijson/induced_polarization_forward_3d.ui.json b/simpeg_drivers-assets/uijson/induced_polarization_3d_forward.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/induced_polarization_forward_3d.ui.json rename to simpeg_drivers-assets/uijson/induced_polarization_3d_forward.ui.json diff --git a/simpeg_drivers-assets/uijson/induced_polarization_inversion_3d.ui.json b/simpeg_drivers-assets/uijson/induced_polarization_3d_inversion.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/induced_polarization_inversion_3d.ui.json rename to simpeg_drivers-assets/uijson/induced_polarization_3d_inversion.ui.json diff --git a/simpeg_drivers-assets/uijson/induced_polarization_forward_pseudo3d.ui.json b/simpeg_drivers-assets/uijson/induced_polarization_pseudo3d_forward.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/induced_polarization_forward_pseudo3d.ui.json rename to simpeg_drivers-assets/uijson/induced_polarization_pseudo3d_forward.ui.json diff --git a/simpeg_drivers-assets/uijson/induced_polarization_inversion_pseudo3d.ui.json b/simpeg_drivers-assets/uijson/induced_polarization_pseudo3d_inversion.ui.json similarity index 100% rename from simpeg_drivers-assets/uijson/induced_polarization_inversion_pseudo3d.ui.json rename to simpeg_drivers-assets/uijson/induced_polarization_pseudo3d_inversion.ui.json diff --git a/simpeg_drivers/__init__.py b/simpeg_drivers/__init__.py index b81e2259..a9ddd52f 100644 --- a/simpeg_drivers/__init__.py +++ b/simpeg_drivers/__init__.py @@ -45,7 +45,10 @@ def assets_path() -> Path: DRIVER_MAP = { "direct current 3d": ( "simpeg_drivers.electricals.direct_current.three_dimensions.driver", - {"inversion": "DirectCurrent3DDriver"}, + { + "forward": "DirectCurrent3DForwardDriver", + "inversion": "DirectCurrent3DInversionDriver", + }, ), "direct current 2d": ( "simpeg_drivers.electricals.direct_current.two_dimensions.driver", diff --git a/simpeg_drivers/electricals/__init__.py b/simpeg_drivers/electricals/__init__.py index 2d06129e..f03039c4 100644 --- a/simpeg_drivers/electricals/__init__.py +++ b/simpeg_drivers/electricals/__init__.py @@ -11,7 +11,10 @@ from __future__ import annotations -from .direct_current.three_dimensions import DirectCurrent3DParams +from .direct_current.three_dimensions import ( + DirectCurrent3DForwardParams, + DirectCurrent3DInversionParams, +) from .induced_polarization.three_dimensions.params import InducedPolarization3DParams # pylint: disable=unused-import diff --git a/simpeg_drivers/electricals/direct_current/three_dimensions/__init__.py b/simpeg_drivers/electricals/direct_current/three_dimensions/__init__.py index 5a77fe95..88282d2a 100644 --- a/simpeg_drivers/electricals/direct_current/three_dimensions/__init__.py +++ b/simpeg_drivers/electricals/direct_current/three_dimensions/__init__.py @@ -9,7 +9,7 @@ # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -from .params import DirectCurrent3DParams +from .params import DirectCurrent3DForwardParams, DirectCurrent3DInversionParams # pylint: disable=unused-import # flake8: noqa diff --git a/simpeg_drivers/electricals/direct_current/three_dimensions/driver.py b/simpeg_drivers/electricals/direct_current/three_dimensions/driver.py index 80566c0a..7e1014c4 100644 --- a/simpeg_drivers/electricals/direct_current/three_dimensions/driver.py +++ b/simpeg_drivers/electricals/direct_current/three_dimensions/driver.py @@ -14,12 +14,14 @@ from simpeg_drivers.driver import InversionDriver from .constants import validations -from .params import DirectCurrent3DParams +from .params import DirectCurrent3DForwardParams, DirectCurrent3DInversionParams -class DirectCurrent3DDriver(InversionDriver): - _params_class = DirectCurrent3DParams - _validations = validations +class DirectCurrent3DForwardDriver(InversionDriver): + _params_class = DirectCurrent3DForwardParams + _validation = validations - def __init__(self, params: DirectCurrent3DParams): - super().__init__(params) + +class DirectCurrent3DInversionDriver(InversionDriver): + _params_class = DirectCurrent3DInversionParams + _validation = validations diff --git a/simpeg_drivers/electricals/direct_current/three_dimensions/params.py b/simpeg_drivers/electricals/direct_current/three_dimensions/params.py index 81dd87da..ba318393 100644 --- a/simpeg_drivers/electricals/direct_current/three_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/three_dimensions/params.py @@ -11,75 +11,56 @@ from __future__ import annotations -from copy import deepcopy +from pathlib import Path +from typing import ClassVar -from simpeg_drivers.params import InversionBaseParams +from geoh5py.data import FloatData -from .constants import ( - default_ui_json, - forward_defaults, - inversion_defaults, - validations, -) +from simpeg_drivers import assets_path +from simpeg_drivers.params import BaseForwardData, BaseInversionData -class DirectCurrent3DParams(InversionBaseParams): +class DirectCurrent3DForwardParams(BaseForwardData): """ - Parameter class for electrical->conductivity inversion. + Direct current 3D forward parameters. + + :param potential_channel_bool: Potential channel boolean. + :param model_type: Specify whether the models are provided in + resistivity or conductivity. """ - _physical_property = "conductivity" - - def __init__(self, input_file=None, forward_only=False, **kwargs): - self._default_ui_json = deepcopy(default_ui_json) - self._forward_defaults = deepcopy(forward_defaults) - self._inversion_defaults = deepcopy(inversion_defaults) - self._inversion_type = "direct current 3d" - self._validations = validations - self._potential_channel_bool = None - self._potential_channel = None - self._potential_uncertainty = None - self._model_type = "Conductivity (S/m)" - - super().__init__(input_file=input_file, forward_only=forward_only, **kwargs) - - @property - def inversion_type(self): - return self._inversion_type - - @inversion_type.setter - def inversion_type(self, val): - self.setter_validator("inversion_type", val) - - @property - def model_type(self): - """Model units.""" - return self._model_type - - @model_type.setter - def model_type(self, val): - self.setter_validator("model_type", val) - - @property - def potential_channel_bool(self): - return self._potential_channel_bool - - @potential_channel_bool.setter - def potential_channel_bool(self, val): - self.setter_validator("potential_channel_bool", val) - - @property - def potential_channel(self): - return self._potential_channel - - @potential_channel.setter - def potential_channel(self, val): - self.setter_validator("potential_channel", val, fun=self._uuid_promoter) - - @property - def potential_uncertainty(self): - return self._potential_uncertainty - - @potential_uncertainty.setter - def potential_uncertainty(self, val): - self.setter_validator("potential_uncertainty", val, fun=self._uuid_promoter) + name: ClassVar[str] = "Direct Current 3D Forward" + title: ClassVar[str] = "Direct Current 3D Forward" + default_ui_json: ClassVar[Path] = ( + assets_path() / "uijson/direct_current_3d_forward.ui.json" + ) + + inversion_type: str = "direct current 3d" + physical_property: str = "conductivity" + + potential_channel_bool: bool = True + model_type: str = "Conductivity (S/m)" + + +class DirectCurrent3DInversionParams(BaseInversionData): + """ + Direct current 3D inversion parameters. + + :param potential_channel: Potential data channel. + :param potential_uncertainty: Potential data uncertainty channel. + :param model_type: Specify whether the models are provided in + resistivity or conductivity. + """ + + name: ClassVar[str] = "Direct Current 3D Inversion" + title: ClassVar[str] = "Direct Current 3D Inversion" + default_ui_json: ClassVar[Path] = ( + assets_path() / "uijson/direct_current_3d_inversion.ui.json" + ) + + inversion_type: str = "direct current 3d" + physical_property: str = "conductivity" + + potential_channel: FloatData + potential_uncertainty: float | FloatData | None = None + model_type: str = "Conductivity (S/m)" diff --git a/simpeg_drivers/params.py b/simpeg_drivers/params.py index b412d381..f692b627 100644 --- a/simpeg_drivers/params.py +++ b/simpeg_drivers/params.py @@ -143,7 +143,7 @@ def out_group_if_none(cls, data) -> SimPEGGroup: @model_validator(mode="after") def update_out_group_options(self): assert self.out_group is not None - with fetch_active_workspace(self.geoh5): + with fetch_active_workspace(self.geoh5, mode="r+"): self.out_group.options = self.serialize() self.out_group.metadata = None return self diff --git a/simpeg_drivers/potential_fields/gravity/driver.py b/simpeg_drivers/potential_fields/gravity/driver.py index 5a4b5c40..3362d4d5 100644 --- a/simpeg_drivers/potential_fields/gravity/driver.py +++ b/simpeg_drivers/potential_fields/gravity/driver.py @@ -11,10 +11,6 @@ from __future__ import annotations -from geoapps_utils.driver.data import BaseData -from geoh5py.ui_json import InputFile - -from simpeg_drivers import DRIVER_MAP from simpeg_drivers.driver import InversionDriver from simpeg_drivers.potential_fields.gravity.constants import validations from simpeg_drivers.potential_fields.gravity.params import ( diff --git a/simpeg_drivers/potential_fields/magnetic_scalar/params.py b/simpeg_drivers/potential_fields/magnetic_scalar/params.py index 07591459..db5d8d45 100644 --- a/simpeg_drivers/potential_fields/magnetic_scalar/params.py +++ b/simpeg_drivers/potential_fields/magnetic_scalar/params.py @@ -9,10 +9,6 @@ # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -# pylint: disable=too-many-public-methods -# pylint: disable=too-many-instance-attributes - - from __future__ import annotations from pathlib import Path diff --git a/tests/run_tests/driver_dc_test.py b/tests/run_tests/driver_dc_test.py index 77d7d792..835a1e49 100644 --- a/tests/run_tests/driver_dc_test.py +++ b/tests/run_tests/driver_dc_test.py @@ -15,12 +15,15 @@ import numpy as np from geoh5py.workspace import Workspace -from simpeg_drivers.electricals.direct_current.three_dimensions import ( - DirectCurrent3DParams, -) from simpeg_drivers.electricals.direct_current.three_dimensions.driver import ( - DirectCurrent3DDriver, + DirectCurrent3DForwardDriver, + DirectCurrent3DInversionDriver, +) +from simpeg_drivers.electricals.direct_current.three_dimensions.params import ( + DirectCurrent3DForwardParams, + DirectCurrent3DInversionParams, ) +from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output @@ -63,18 +66,17 @@ def test_dc_3d_fwr_run( geoh5.close() - params = DirectCurrent3DParams( - forward_only=True, + active_cells = ActiveCellsData(topography_object=topography) + params = DirectCurrent3DForwardParams( geoh5=geoh5, - mesh=model.parent.uid, - topography_object=topography.uid, + mesh=model.parent, + active_cells=active_cells, z_from_topo=False, - data_object=survey.uid, - starting_model=model.uid, + data_object=survey, + starting_model=model, resolution=None, ) - params.workpath = tmp_path - fwr_driver = DirectCurrent3DDriver(params) + fwr_driver = DirectCurrent3DForwardDriver(params) fwr_driver.run() @@ -94,11 +96,12 @@ def test_dc_3d_run( topography = geoh5.get_entity("topography")[0] # Run the inverse - params = DirectCurrent3DParams( + active_cells = ActiveCellsData(topography_object=topography) + params = DirectCurrent3DInversionParams( geoh5=geoh5, - mesh=mesh.uid, - topography_object=topography.uid, - data_object=potential.parent.uid, + mesh=mesh, + active_cells=active_cells, + data_object=potential.parent, starting_model=1e-2, reference_model=1e-2, s_norm=0.0, @@ -108,7 +111,7 @@ def test_dc_3d_run( gradient_type="components", potential_channel_bool=True, z_from_topo=False, - potential_channel=potential.uid, + potential_channel=potential, potential_uncertainty=1e-3, max_global_iterations=max_iterations, initial_beta=None, @@ -122,9 +125,9 @@ def test_dc_3d_run( coolingRate=1, chi_factor=0.5, ) - params.write_input_file(path=tmp_path, name="Inv_run") + params.write_ui_json(path=tmp_path / "Inv_run.ui.json") - driver = DirectCurrent3DDriver.start(str(tmp_path / "Inv_run.ui.json")) + driver = DirectCurrent3DInversionDriver.start(str(tmp_path / "Inv_run.ui.json")) # Should not be auto-scaling np.testing.assert_allclose(driver.data_misfit.multipliers, [1, 1, 1]) output = get_inversion_output( @@ -157,18 +160,18 @@ def test_dc_single_line_fwr_run( inversion_type="dcip", flatten=False, ) - params = DirectCurrent3DParams( - forward_only=True, + active_cells = ActiveCellsData(topography_object=topography) + params = DirectCurrent3DForwardParams( geoh5=geoh5, - mesh=model.parent.uid, - topography_object=topography.uid, + mesh=model.parent, + active_cells=active_cells, z_from_topo=False, - data_object=survey.uid, - starting_model=model.uid, + data_object=survey, + starting_model=model, resolution=None, ) - params.workpath = tmp_path - fwr_driver = DirectCurrent3DDriver(params) + + fwr_driver = DirectCurrent3DForwardDriver(params) assert np.all(fwr_driver.window.window["size"] > 0) From 502e7d3de0c6c32adb2a8bcb5b817b6543016b8f Mon Sep 17 00:00:00 2001 From: benjamink Date: Mon, 3 Feb 2025 11:42:18 -0800 Subject: [PATCH 02/13] 2d runtest working with 2d params as basedata --- simpeg_drivers/__init__.py | 5 +- simpeg_drivers/components/data.py | 19 +-- .../direct_current/two_dimensions/__init__.py | 4 +- .../direct_current/two_dimensions/driver.py | 12 +- .../direct_current/two_dimensions/params.py | 153 ++++++++++++------ simpeg_drivers/electricals/driver.py | 12 +- simpeg_drivers/electricals/params.py | 2 +- simpeg_drivers/utils/write_default_uijson.py | 32 ---- tests/run_tests/driver_dc_2d_test.py | 76 +++++---- 9 files changed, 183 insertions(+), 132 deletions(-) diff --git a/simpeg_drivers/__init__.py b/simpeg_drivers/__init__.py index a9ddd52f..c066765b 100644 --- a/simpeg_drivers/__init__.py +++ b/simpeg_drivers/__init__.py @@ -52,7 +52,10 @@ def assets_path() -> Path: ), "direct current 2d": ( "simpeg_drivers.electricals.direct_current.two_dimensions.driver", - {"inversion": "DirectCurrent2DDriver"}, + { + "forward": "DirectCurrent2DForwardDriver", + "inversion": "DirectCurrent2DInversionDriver", + }, ), "direct current pseudo 3d": ( "simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.driver", diff --git a/simpeg_drivers/components/data.py b/simpeg_drivers/components/data.py index 91f95064..3facdaa0 100644 --- a/simpeg_drivers/components/data.py +++ b/simpeg_drivers/components/data.py @@ -110,11 +110,11 @@ def _initialize(self) -> None: self.has_tensor = InversionData.check_tensor(self.components) self.locations = super().get_locations(self.params.data_object) - if ( - getattr(self.params, "line_id", None) is not None - and getattr(self.params, "line_object", None) is not None - ): - self.mask = self.params.line_object.values == self.params.line_id + if getattr(self.params, "line_selection", None) is not None: + self.mask = ( + self.params.line_selection.line_object.values + == self.params.line_selection.line_id + ) else: self.mask = np.ones(len(self.locations), dtype=bool) @@ -469,11 +469,12 @@ def update_params(self, data_dict, uncert_dict): setattr(self.params, f"{comp}_channel", data_dict[comp]) setattr(self.params, f"{comp}_uncertainty", uncert_dict[comp]) - if getattr(self.params, "line_object", None) is not None: - new_line = self.params.line_object.copy( - parent=self.entity, values=self.params.line_object.values[self.mask] + if getattr(self.params, "line_selection", None) is not None: + new_line = self.params.line_selection.line_object.copy( + parent=self.entity, + values=self.params.line_selection.line_object.values[self.mask], ) - self.params.line_object = new_line + self.params.line_selection.line_object = new_line @property def survey(self): diff --git a/simpeg_drivers/electricals/direct_current/two_dimensions/__init__.py b/simpeg_drivers/electricals/direct_current/two_dimensions/__init__.py index 970f5f01..d9121bdc 100644 --- a/simpeg_drivers/electricals/direct_current/two_dimensions/__init__.py +++ b/simpeg_drivers/electricals/direct_current/two_dimensions/__init__.py @@ -9,7 +9,7 @@ # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -from .params import DirectCurrent2DParams +from .params import DirectCurrent2DForwardParams, DirectCurrent2DInversionParams -__all__ = ["DirectCurrent2DParams"] +__all__ = ["DirectCurrent2DForwardParams", "DirectCurrent2DInversionParams"] diff --git a/simpeg_drivers/electricals/direct_current/two_dimensions/driver.py b/simpeg_drivers/electricals/direct_current/two_dimensions/driver.py index 95dbbbbb..ab04f90b 100644 --- a/simpeg_drivers/electricals/direct_current/two_dimensions/driver.py +++ b/simpeg_drivers/electricals/direct_current/two_dimensions/driver.py @@ -14,12 +14,14 @@ from simpeg_drivers.electricals.driver import Base2DDriver from .constants import validations -from .params import DirectCurrent2DParams +from .params import DirectCurrent2DForwardParams, DirectCurrent2DInversionParams -class DirectCurrent2DDriver(Base2DDriver): - _params_class = DirectCurrent2DParams +class DirectCurrent2DForwardDriver(Base2DDriver): + _params_class = DirectCurrent2DForwardParams _validations = validations - def __init__(self, params: DirectCurrent2DParams): - super().__init__(params) + +class DirectCurrent2DInversionDriver(Base2DDriver): + _params_class = DirectCurrent2DInversionParams + _validations = validations diff --git a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py index a9deb13d..fefe1e02 100644 --- a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py @@ -11,59 +11,122 @@ from __future__ import annotations -from copy import deepcopy +from typing import ClassVar +from geoh5py.data import DataAssociationEnum, FloatData, ReferencedData +from geoh5py.objects import DrapeModel +from pydantic import BaseModel, ConfigDict, field_validator, model_validator + +from simpeg_drivers import assets_path from simpeg_drivers.electricals.params import Base2DParams +from simpeg_drivers.params import BaseForwardData, BaseInversionData from .constants import ( - default_ui_json, - forward_defaults, - inversion_defaults, validations, ) -class DirectCurrent2DParams(Base2DParams): +class LineSelectionData(BaseModel): + """ + Line selection parameters for 2D inversions. + + :param line_object: Reference data categorizing survey by line ids. + :param line_id: Line identifier for simulation/inversion. + """ + + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) + + line_object: ReferencedData + line_id: int + + @field_validator("line_object", mode="before") + @classmethod + def validate_cell_association(cls, value): + if value.association is not DataAssociationEnum.CELL: + raise ValueError("Line identifier must be associated with cells.") + return value + + @model_validator(mode="before") + @classmethod + def line_id_referenced(cls, model): + if model["line_id"] not in model["line_object"](): + raise ValueError("Line id isn't referenced in the line object.") + return model + + +class DrapeModelData(BaseModel): + """ + Drape model parameters for 2D simulation/inversion]. + + :param u_cell_size: Horizontal cell size for the drape model. + :param v_cell_size: Vertical cell size for the drape model. + :param depth_core: Depth of the core region. + :param horizontal_padding: Horizontal padding. + :param vertical_padding: Vertical padding. + :param expansion_factor: Expansion factor for the drape model. + """ + + u_cell_size: float = 25.0 + v_cell_size: float = 25.0 + depth_core: float = 100.0 + horizontal_padding: float = 100.0 + vertical_padding: float = 100.0 + expansion_factor: float = 100.0 + + +class DirectCurrent2DForwardParams(BaseForwardData): + """ + Parameter class for two dimensional electrical->conductivity forward simulation. + + :param potential_channel_bool: Potential channel boolean. + :param line_selection: Line selection parameters. + :param drape_model: Drape model parameters. + :param model_type: Specify whether the models are provided in + resistivity or conductivity. + """ + + name: ClassVar[str] = "Direct Current 2D Forward" + title: ClassVar[str] = "Direct Current 2D Forward" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/direct_current_2d_forward.ui.json" + ) + + inversion_type: str = "direct current 2d" + physical_property: str = "conductivity" + + potential_channel_bool: bool = True + line_selection: LineSelectionData + mesh: DrapeModel | None = None + drape_model: DrapeModelData + model_type: str = "Conductivity (S/m)" + + +class DirectCurrent2DInversionParams(BaseInversionData): """ - Parameter class for electrical->conductivity inversion. + Parameter class for two dimensional electrical->conductivity forward simulation. + + :param potential_channel: Potential data channel. + :param potential_uncertainty: Potential data uncertainty channel. + :param line_selection: Line selection parameters. + :param drape_model: Drape model parameters. + :param model_type: Specify whether the models are provided in + resistivity or conductivity. """ - _physical_property = "conductivity" - - def __init__(self, input_file=None, forward_only=False, **kwargs): - self._default_ui_json = deepcopy(default_ui_json) - self._forward_defaults = deepcopy(forward_defaults) - self._inversion_defaults = deepcopy(inversion_defaults) - self._inversion_type = "direct current 2d" - self._validations = validations - self._potential_channel_bool = None - self._potential_channel = None - self._potential_uncertainty = None - self._line_object = None - self._line_id = None - - super().__init__(input_file=input_file, forward_only=forward_only, **kwargs) - - @property - def potential_channel_bool(self): - return self._potential_channel_bool - - @potential_channel_bool.setter - def potential_channel_bool(self, val): - self.setter_validator("potential_channel_bool", val) - - @property - def potential_channel(self): - return self._potential_channel - - @potential_channel.setter - def potential_channel(self, val): - self.setter_validator("potential_channel", val, fun=self._uuid_promoter) - - @property - def potential_uncertainty(self): - return self._potential_uncertainty - - @potential_uncertainty.setter - def potential_uncertainty(self, val): - self.setter_validator("potential_uncertainty", val, fun=self._uuid_promoter) + name: ClassVar[str] = "Direct Current 2D Inversion" + title: ClassVar[str] = "Direct Current 2D Inversion" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/direct_current_2d_inversion.ui.json" + ) + + inversion_type: str = "direct current 2d" + physical_property: str = "conductivity" + + potential_channel: FloatData + potential_uncertainty: float | FloatData | None = None + line_selection: LineSelectionData + mesh: DrapeModel | None = None + drape_model: DrapeModelData + model_type: str = "Conductivity (S/m)" diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index f0a87d78..fd05ac10 100644 --- a/simpeg_drivers/electricals/driver.py +++ b/simpeg_drivers/electricals/driver.py @@ -59,13 +59,13 @@ def create_drape_mesh(self) -> DrapeModel: "Models", receiver_locs, [ - self.params.u_cell_size, - self.params.v_cell_size, + self.params.drape_model.u_cell_size, + self.params.drape_model.v_cell_size, ], - self.params.depth_core, - [self.params.horizontal_padding] * 2 - + [self.params.vertical_padding, 1], - self.params.expansion_factor, + self.params.drape_model.depth_core, + [self.params.drape_model.horizontal_padding] * 2 + + [self.params.drape_model.vertical_padding, 1], + self.params.drape_model.expansion_factor, )[0] return mesh diff --git a/simpeg_drivers/electricals/params.py b/simpeg_drivers/electricals/params.py index e2b55a95..8649b481 100644 --- a/simpeg_drivers/electricals/params.py +++ b/simpeg_drivers/electricals/params.py @@ -12,7 +12,7 @@ from __future__ import annotations import numpy as np -from geoh5py.data import Data, DataAssociationEnum +from geoh5py.data import Data, DataAssociationEnum, ReferencedData from simpeg_drivers.params import InversionBaseParams from simpeg_drivers.utils.utils import get_drape_model diff --git a/simpeg_drivers/utils/write_default_uijson.py b/simpeg_drivers/utils/write_default_uijson.py index c878c417..c97e2aa1 100644 --- a/simpeg_drivers/utils/write_default_uijson.py +++ b/simpeg_drivers/utils/write_default_uijson.py @@ -17,21 +17,9 @@ from simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.params import ( DirectCurrentPseudo3DParams, ) -from simpeg_drivers.electricals.direct_current.three_dimensions import ( - DirectCurrent3DParams, -) -from simpeg_drivers.electricals.direct_current.two_dimensions import ( - DirectCurrent2DParams, -) from simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.params import ( InducedPolarizationPseudo3DParams, ) -from simpeg_drivers.electricals.induced_polarization.three_dimensions import ( - InducedPolarization3DParams, -) -from simpeg_drivers.electricals.induced_polarization.two_dimensions import ( - InducedPolarization2DParams, -) from simpeg_drivers.electromagnetics.frequency_domain import ( FrequencyDomainElectromagneticsParams, ) @@ -64,32 +52,12 @@ def write_default_uijson(path: str | Path): filedict = { - "direct_current_inversion_2d.ui.json": DirectCurrent2DParams(validate=False), - "direct_current_forward_2d.ui.json": DirectCurrent2DParams( - forward_only=True, validate=False - ), - "direct_current_inversion_3d.ui.json": DirectCurrent3DParams(validate=False), - "direct_current_forward_3d.ui.json": DirectCurrent3DParams( - forward_only=True, validate=False - ), "direct_current_inversion_pseudo3d.ui.json": DirectCurrentPseudo3DParams( validate=False ), "direct_current_forward_pseudo3d.ui.json": DirectCurrentPseudo3DParams( forward_only=True, validate=False ), - "induced_polarization_inversion_2d.ui.json": InducedPolarization2DParams( - validate=False - ), - "induced_polarization_forward_2d.ui.json": InducedPolarization2DParams( - forward_only=True, validate=False - ), - "induced_polarization_inversion_3d.ui.json": InducedPolarization3DParams( - validate=False - ), - "induced_polarization_forward_3d.ui.json": InducedPolarization3DParams( - forward_only=True, validate=False - ), "induced_polarization_inversion_pseudo3d.ui.json": InducedPolarizationPseudo3DParams( validate=False ), diff --git a/tests/run_tests/driver_dc_2d_test.py b/tests/run_tests/driver_dc_2d_test.py index 26ff19a0..696e5bf0 100644 --- a/tests/run_tests/driver_dc_2d_test.py +++ b/tests/run_tests/driver_dc_2d_test.py @@ -14,14 +14,20 @@ from pathlib import Path import numpy as np +from geoh5py.data import ReferencedData from geoh5py.workspace import Workspace from simpeg_drivers.electricals.direct_current.two_dimensions.driver import ( - DirectCurrent2DDriver, + DirectCurrent2DForwardDriver, + DirectCurrent2DInversionDriver, ) from simpeg_drivers.electricals.direct_current.two_dimensions.params import ( - DirectCurrent2DParams, + DirectCurrent2DForwardParams, + DirectCurrent2DInversionParams, + DrapeModelData, + LineSelectionData, ) +from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output @@ -54,24 +60,28 @@ def test_dc_2d_fwr_run( drape_height=0.0, flatten=False, ) - params = DirectCurrent2DParams( - forward_only=True, + line_selection = LineSelectionData( + line_object=geoh5.get_entity("line_ids")[0], + line_id=101, + ) + params = DirectCurrent2DForwardParams( geoh5=geoh5, - u_cell_size=5.0, - v_cell_size=5.0, - depth_core=100.0, - horizontal_padding=100.0, - vertical_padding=100.0, - expansion_factor=1.1, - topography_object=topography.uid, + data_object=survey, + line_selection=line_selection, + drape_model=DrapeModelData( + u_cell_size=5.0, + v_cell_size=5.0, + depth_core=100.0, + horizontal_padding=100.0, + vertical_padding=100.0, + expansion_factor=1.1, + ), + starting_model=model, + active_cells=ActiveCellsData(topography_object=topography), z_from_topo=False, - data_object=survey.uid, - starting_model=model.uid, - line_object=geoh5.get_entity("line_ids")[0].uid, - line_id=101, ) - params.workpath = tmp_path - fwr_driver = DirectCurrent2DDriver(params) + + fwr_driver = DirectCurrent2DForwardDriver(params) fwr_driver.run() @@ -85,21 +95,25 @@ def test_dc_2d_run(tmp_path: Path, max_iterations=1, pytest=True): topography = geoh5.get_entity("topography")[0] # Run the inverse - params = DirectCurrent2DParams( + params = DirectCurrent2DInversionParams( geoh5=geoh5, - u_cell_size=5.0, - v_cell_size=5.0, - depth_core=100.0, - horizontal_padding=100.0, - vertical_padding=100.0, - expansion_factor=1.1, - topography_object=topography.uid, - data_object=potential.parent.uid, - potential_channel=potential.uid, + drape_model=DrapeModelData( + u_cell_size=5.0, + v_cell_size=5.0, + depth_core=100.0, + horizontal_padding=100.0, + vertical_padding=100.0, + expansion_factor=1.1, + ), + active_cells=ActiveCellsData(topography_object=topography), + line_selection=LineSelectionData( + line_object=geoh5.get_entity("line_ids")[0], + line_id=101, + ), + data_object=potential.parent, + potential_channel=potential, potential_uncertainty=1e-3, model_type="Resistivity (Ohm-m)", - line_object=geoh5.get_entity("line_ids")[0].uid, - line_id=101, starting_model=100.0, reference_model=100.0, s_norm=0.0, @@ -116,9 +130,9 @@ def test_dc_2d_run(tmp_path: Path, max_iterations=1, pytest=True): lower_bound=0.1, coolingRate=1, ) - params.write_input_file(path=tmp_path, name="Inv_run") + params.write_ui_json(path=tmp_path / "Inv_run.ui.json") - driver = DirectCurrent2DDriver.start(str(tmp_path / "Inv_run.ui.json")) + driver = DirectCurrent2DInversionDriver.start(str(tmp_path / "Inv_run.ui.json")) output = get_inversion_output( driver.params.geoh5.h5file, driver.params.out_group.uid From 72e3e9630886e9ddfae7959d9e5c3752dc44645e Mon Sep 17 00:00:00 2001 From: benjamink Date: Mon, 3 Feb 2025 13:40:11 -0800 Subject: [PATCH 03/13] Fix cross-gradient runtest --- .../pseudo_three_dimensions/driver.py | 11 +- tests/params_test.py | 150 ------------------ .../driver_joint_cross_gradient_test.py | 36 ++--- 3 files changed, 27 insertions(+), 170 deletions(-) delete mode 100644 tests/params_test.py diff --git a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py index 931f5c0f..8f30cf13 100644 --- a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py +++ b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py @@ -18,12 +18,19 @@ DirectCurrentPseudo3DParams, ) from simpeg_drivers.electricals.direct_current.two_dimensions.params import ( - DirectCurrent2DParams, + DirectCurrent2DForwardParams, + DirectCurrent2DInversionParams, ) from simpeg_drivers.electricals.driver import BasePseudo3DDriver +class DirectCurrentPseudo3DForwardDriver(BasePseudo3DDriver): + _params_class = DirectCurrentPseudo3DParams + _params_2d_class = DirectCurrent2DForwardParams + _validations = validations + + class DirectCurrentPseudo3DDriver(BasePseudo3DDriver): _params_class = DirectCurrentPseudo3DParams - _params_2d_class = DirectCurrent2DParams + _params_2d_class = DirectCurrent2DInversionParams _validations = validations diff --git a/tests/params_test.py b/tests/params_test.py deleted file mode 100644 index a7a58bbc..00000000 --- a/tests/params_test.py +++ /dev/null @@ -1,150 +0,0 @@ -# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -# Copyright (c) 2024-2025 Mira Geoscience Ltd. ' -# ' -# This file is part of simpeg-drivers package. ' -# ' -# simpeg-drivers is distributed under the terms and conditions of the MIT License ' -# (see LICENSE file at the root of this source code package). ' -# ' -# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -# pylint: disable=redefined-outer-name - -from __future__ import annotations - -import pytest -from geoh5py.objects import DrapeModel -from geoh5py.shared.exceptions import ( - AssociationValidationError, - OptionalValidationError, - RequiredValidationError, - ShapeValidationError, - TypeValidationError, - UUIDValidationError, - ValueValidationError, -) -from geoh5py.ui_json import InputFile -from geoh5py.ui_json.utils import requires_value - -from simpeg_drivers.electricals.direct_current.three_dimensions import ( - DirectCurrent3DParams, -) -from simpeg_drivers.electricals.induced_polarization.three_dimensions import ( - InducedPolarization3DParams, -) -from simpeg_drivers.params import ActiveCellsData -from simpeg_drivers.potential_fields import ( - GravityInversionParams, - MagneticScalarInversionParams, - MagneticVectorInversionParams, -) -from simpeg_drivers.utils.testing import setup_inversion_workspace - - -@pytest.fixture(scope="session") -def dc_params(tmp_path_factory): - geoh5, mesh, model, survey, topography = setup_inversion_workspace( - tmp_path_factory.mktemp("dcr"), - background=0.01, - anomaly=10, - n_electrodes=2, - n_lines=2, - inversion_type="dcip", - flatten=False, - ) - - geoh5.close() - - params = DirectCurrent3DParams( - **{ - "geoh5": geoh5, - "data_object": survey, - "topography_object": topography, - "mesh": mesh, - } - ) - params.input_file.geoh5.open() - - return params - - -@pytest.fixture(scope="session") -def ip_params(tmp_path_factory): - geoh5, mesh, model, survey, topography = setup_inversion_workspace( - tmp_path_factory.mktemp("dcr"), - background=0.01, - anomaly=10, - n_electrodes=2, - n_lines=2, - inversion_type="dcip", - flatten=False, - ) - geoh5.close() - ip_params = InducedPolarization3DParams( - **{ - "geoh5": geoh5, - "data_object": survey, - "topography": topography, - "mesh": mesh, - } - ) - ip_params.input_file.geoh5.open() - - return ip_params - - -def test_direct_current_inversion_type(dc_params): - with pytest.raises(ValueValidationError, match="Must be: 'direct current 3d'"): - dc_params.inversion_type = "alskdj" - - -def test_direct_current_data_object(dc_params): - with pytest.raises( - TypeValidationError, match="Must be one of: 'UUID', 'PotentialElectrode'." - ): - dc_params.data_object = 4 - - -def test_potential_channel(dc_params): - with pytest.raises( - TypeValidationError, match="Must be one of: 'str', 'UUID', 'Entity'." - ): - dc_params.potential_channel = 4 - - -def test_potential_uncertainty(dc_params): - with pytest.raises( - TypeValidationError, - match="Must be one of: 'str', 'UUID', 'int', 'float', 'Entity'.", - ): - dc_params.potential_uncertainty = dc_params.geoh5 - - -def test_induced_polarization_inversion_type(ip_params): - with pytest.raises( - ValueValidationError, match="Must be: 'induced polarization 3d'" - ): - ip_params.inversion_type = "alskdj" - - -def test_chargeability_channel(ip_params): - with pytest.raises( - TypeValidationError, match="Must be one of: 'str', 'UUID', 'Entity'." - ): - ip_params.chargeability_channel = 4 - - -def test_chargeability_uncertainty(ip_params): - with pytest.raises( - TypeValidationError, - match="Must be one of: 'str', 'UUID', 'int', 'float', 'Entity'.", - ): - ip_params.chargeability_uncertainty = ip_params.geoh5 - - -def test_conductivity_model(ip_params): - with pytest.raises( - TypeValidationError, - match="Must be one of: 'str', 'UUID', 'int', 'float', 'Entity'.", - ): - ip_params.conductivity_model = ip_params.geoh5 diff --git a/tests/run_tests/driver_joint_cross_gradient_test.py b/tests/run_tests/driver_joint_cross_gradient_test.py index 9697638f..fd6b1ae0 100644 --- a/tests/run_tests/driver_joint_cross_gradient_test.py +++ b/tests/run_tests/driver_joint_cross_gradient_test.py @@ -16,10 +16,12 @@ from geoh5py.workspace import Workspace from simpeg_drivers.electricals.direct_current.three_dimensions import ( - DirectCurrent3DParams, + DirectCurrent3DForwardParams, + DirectCurrent3DInversionParams, ) from simpeg_drivers.electricals.direct_current.three_dimensions.driver import ( - DirectCurrent3DDriver, + DirectCurrent3DForwardDriver, + DirectCurrent3DInversionDriver, ) from simpeg_drivers.joint.joint_cross_gradient import JointCrossGradientParams from simpeg_drivers.joint.joint_cross_gradient.driver import JointCrossGradientDriver @@ -90,11 +92,9 @@ def test_joint_cross_gradient_fwr_run( ) inducing_field = (50000.0, 90.0, 0.0) params = MagneticVectorForwardParams( - forward_only=True, geoh5=geoh5, mesh=model.parent, active_cells=ActiveCellsData(topography_object=topography), - topography_object=topography, inducing_field_strength=inducing_field[0], inducing_field_inclination=inducing_field[1], inducing_field_declination=inducing_field[2], @@ -118,15 +118,14 @@ def test_joint_cross_gradient_fwr_run( inversion_type="dcip", flatten=False, ) - params = DirectCurrent3DParams( - forward_only=True, + params = DirectCurrent3DForwardParams( geoh5=geoh5, - mesh=model.parent.uid, - topography_object=topography.uid, - data_object=survey.uid, - starting_model=model.uid, + mesh=model.parent, + active_cells=ActiveCellsData(topography_object=topography), + data_object=survey, + starting_model=model, ) - fwr_driver_c = DirectCurrent3DDriver(params) + fwr_driver_c = DirectCurrent3DForwardDriver(params) fwr_driver_c.inversion_data.entity.name = "survey" # Force co-location of meshes @@ -161,7 +160,7 @@ def test_joint_cross_gradient_inv_run( for group_name in [ "Gravity Forward", "Magnetic Vector Forward", - "Direct current 3d Forward", + "Direct Current 3D Forward", ]: group = geoh5.get_entity(group_name)[0] @@ -198,13 +197,14 @@ def test_joint_cross_gradient_inv_run( drivers.append(GravityInversionDriver(params)) elif group.options["inversion_type"] == "direct current 3d": data.values = data.values + np.random.randn(data.values.size) * 5e-4 - params = DirectCurrent3DParams( + active_cells = ActiveCellsData(topography_object=topography) + params = DirectCurrent3DInversionParams( geoh5=geoh5, - mesh=mesh.uid, + mesh=mesh, alpha_s=1.0, - topography_object=topography.uid, - data_object=survey.uid, - potential_channel=data.uid, + active_cells=active_cells, + data_object=survey, + potential_channel=data, model_type="Resistivity (Ohm-m)", potential_uncertainty=5e-4, tile_spatial=1, @@ -212,7 +212,7 @@ def test_joint_cross_gradient_inv_run( reference_model=100.0, save_sensitivities=True, ) - drivers.append(DirectCurrent3DDriver(params)) + drivers.append(DirectCurrent3DInversionDriver(params)) else: data.values = data.values + np.random.randn(data.values.size) * 10.0 params = MagneticVectorInversionParams( From 874885485ca91882b1547e90242cdeb9e806afdc Mon Sep 17 00:00:00 2001 From: benjamink Date: Mon, 3 Feb 2025 15:39:39 -0800 Subject: [PATCH 04/13] ip3d tests passing --- simpeg_drivers/__init__.py | 5 +- simpeg_drivers/electricals/__init__.py | 5 +- .../three_dimensions/__init__.py | 5 +- .../three_dimensions/driver.py | 15 ++- .../three_dimensions/params.py | 119 +++++++----------- tests/run_tests/driver_dc_test.py | 1 - tests/run_tests/driver_ip_test.py | 40 +++--- 7 files changed, 89 insertions(+), 101 deletions(-) diff --git a/simpeg_drivers/__init__.py b/simpeg_drivers/__init__.py index c066765b..a87f5b09 100644 --- a/simpeg_drivers/__init__.py +++ b/simpeg_drivers/__init__.py @@ -63,7 +63,10 @@ def assets_path() -> Path: ), "induced polarization 3d": ( "simpeg_drivers.electricals.induced_polarization.three_dimensions.driver", - {"inversion": "InducedPolarization3DDriver"}, + { + "forward": "InducedPolarization3DForwardDriver", + "inversion": "InducedPolarization3DInversionDriver", + }, ), "induced polarization 2d": ( "simpeg_drivers.electricals.induced_polarization.two_dimensions.driver", diff --git a/simpeg_drivers/electricals/__init__.py b/simpeg_drivers/electricals/__init__.py index f03039c4..a5a4b0b4 100644 --- a/simpeg_drivers/electricals/__init__.py +++ b/simpeg_drivers/electricals/__init__.py @@ -15,7 +15,10 @@ DirectCurrent3DForwardParams, DirectCurrent3DInversionParams, ) -from .induced_polarization.three_dimensions.params import InducedPolarization3DParams +from .induced_polarization.three_dimensions.params import ( + InducedPolarization3DForwardParams, + InducedPolarization3DInversionParams, +) # pylint: disable=unused-import # flake8: noqa diff --git a/simpeg_drivers/electricals/induced_polarization/three_dimensions/__init__.py b/simpeg_drivers/electricals/induced_polarization/three_dimensions/__init__.py index 4bf5a597..c641f59d 100644 --- a/simpeg_drivers/electricals/induced_polarization/three_dimensions/__init__.py +++ b/simpeg_drivers/electricals/induced_polarization/three_dimensions/__init__.py @@ -9,7 +9,10 @@ # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -from .params import InducedPolarization3DParams +from .params import ( + InducedPolarization3DForwardParams, + InducedPolarization3DInversionParams, +) # pylint: disable=unused-import # flake8: noqa diff --git a/simpeg_drivers/electricals/induced_polarization/three_dimensions/driver.py b/simpeg_drivers/electricals/induced_polarization/three_dimensions/driver.py index 7e9a33f6..90943911 100644 --- a/simpeg_drivers/electricals/induced_polarization/three_dimensions/driver.py +++ b/simpeg_drivers/electricals/induced_polarization/three_dimensions/driver.py @@ -14,12 +14,17 @@ from simpeg_drivers.driver import InversionDriver from .constants import validations -from .params import InducedPolarization3DParams +from .params import ( + InducedPolarization3DForwardParams, + InducedPolarization3DInversionParams, +) -class InducedPolarization3DDriver(InversionDriver): - _params_class = InducedPolarization3DParams +class InducedPolarization3DForwardDriver(InversionDriver): + _params_class = InducedPolarization3DForwardParams _validations = validations - def __init__(self, params: InducedPolarization3DParams): - super().__init__(params) + +class InducedPolarization3DInversionDriver(InversionDriver): + _params_class = InducedPolarization3DInversionParams + _validations = validations diff --git a/simpeg_drivers/electricals/induced_polarization/three_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/three_dimensions/params.py index e85c9af0..aae7763a 100644 --- a/simpeg_drivers/electricals/induced_polarization/three_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/three_dimensions/params.py @@ -11,84 +11,55 @@ from __future__ import annotations -from copy import deepcopy +from pathlib import Path +from typing import ClassVar -from simpeg_drivers.params import InversionBaseParams +from geoh5py.data import FloatData -from .constants import ( - default_ui_json, - forward_defaults, - inversion_defaults, - validations, -) +from simpeg_drivers import assets_path +from simpeg_drivers.params import BaseForwardData, BaseInversionData -class InducedPolarization3DParams(InversionBaseParams): +class InducedPolarization3DForwardParams(BaseForwardData): """ - Parameter class for electrical-induced polarization (IP) inversion. + Induced polarization 3D forward parameters. + + :param chargeability_channel_bool: Chargeability channel boolean. + :param conductivity_model: Conductivity model. """ - _physical_property = "chargeability" - - def __init__(self, input_file=None, forward_only=False, **kwargs): - self._default_ui_json = deepcopy(default_ui_json) - self._forward_defaults = deepcopy(forward_defaults) - self._inversion_defaults = deepcopy(inversion_defaults) - self._inversion_type = "induced polarization 3d" - self._validations = validations - self._chargeability_channel_bool = None - self._chargeability_channel = None - self._chargeability_uncertainty = None - self._conductivity_model = None - self._model_type = "Conductivity (S/m)" - - super().__init__(input_file=input_file, forward_only=forward_only, **kwargs) - - @property - def inversion_type(self): - return self._inversion_type - - @inversion_type.setter - def inversion_type(self, val): - self.setter_validator("inversion_type", val) - - @property - def chargeability_channel_bool(self): - return self._chargeability_channel_bool - - @chargeability_channel_bool.setter - def chargeability_channel_bool(self, val): - self.setter_validator("chargeability_channel_bool", val) - - @property - def chargeability_channel(self): - return self._chargeability_channel - - @chargeability_channel.setter - def chargeability_channel(self, val): - self.setter_validator("chargeability_channel", val, fun=self._uuid_promoter) - - @property - def chargeability_uncertainty(self): - return self._chargeability_uncertainty - - @chargeability_uncertainty.setter - def chargeability_uncertainty(self, val): - self.setter_validator("chargeability_uncertainty", val, fun=self._uuid_promoter) - - @property - def conductivity_model(self): - return self._conductivity_model - - @conductivity_model.setter - def conductivity_model(self, val): - self.setter_validator("conductivity_model", val, fun=self._uuid_promoter) - - @property - def model_type(self): - """Model units.""" - return self._model_type - - @model_type.setter - def model_type(self, val): - self.setter_validator("model_type", val) + name: ClassVar[str] = "Induced Polarization 3D Forward" + title: ClassVar[str] = "Induced Polarization 3D Forward" + default_ui_json: ClassVar[Path] = ( + assets_path() / "uijson/induced_polarization_3d_forward.ui.json" + ) + + inversion_type: str = "induced polarization 3d" + physical_property: str = "chargeability" + + chargeability_channel_bool: bool = True + conductivity_model: float | FloatData + + +class InducedPolarization3DInversionParams(BaseInversionData): + """ + Induced polarization 3D inversion parameters. + + :param chargeability_channel: Chargeability data channel. + :param chargeability_uncertainty: Chargeability data uncertainty channel. + :param conductivity_model: Conductivity model. + """ + + name: ClassVar[str] = "Induced Polarization 3D Inversion" + title: ClassVar[str] = "Induced Polarization 3D Inversion" + default_ui_json: ClassVar[Path] = ( + assets_path() / "uijson/induced_polarization_3d_inversion.ui.json" + ) + + inversion_type: str = "induced polarization 3d" + physical_property: str = "chargeability" + + chargeability_channel: FloatData + chargeability_uncertainty: float | FloatData | None = None + conductivity_model: float | FloatData + lower_bound: float | FloatData | None = 0.0 diff --git a/tests/run_tests/driver_dc_test.py b/tests/run_tests/driver_dc_test.py index 835a1e49..c59c1896 100644 --- a/tests/run_tests/driver_dc_test.py +++ b/tests/run_tests/driver_dc_test.py @@ -109,7 +109,6 @@ def test_dc_3d_run( y_norm=1.0, z_norm=1.0, gradient_type="components", - potential_channel_bool=True, z_from_topo=False, potential_channel=potential, potential_uncertainty=1e-3, diff --git a/tests/run_tests/driver_ip_test.py b/tests/run_tests/driver_ip_test.py index 083c2411..649f6bb7 100644 --- a/tests/run_tests/driver_ip_test.py +++ b/tests/run_tests/driver_ip_test.py @@ -15,11 +15,14 @@ from geoh5py.workspace import Workspace from simpeg_drivers.electricals.induced_polarization.three_dimensions import ( - InducedPolarization3DParams, + InducedPolarization3DForwardParams, + InducedPolarization3DInversionParams, ) from simpeg_drivers.electricals.induced_polarization.three_dimensions.driver import ( - InducedPolarization3DDriver, + InducedPolarization3DForwardDriver, + InducedPolarization3DInversionDriver, ) +from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output @@ -48,18 +51,17 @@ def test_ip_3d_fwr_run( inversion_type="dcip", flatten=False, ) - params = InducedPolarization3DParams( - forward_only=True, + params = InducedPolarization3DForwardParams( geoh5=geoh5, - mesh=model.parent.uid, - topography_object=topography.uid, + mesh=model.parent, + active_cells=ActiveCellsData(topography_object=topography), z_from_topo=True, - data_object=survey.uid, - starting_model=model.uid, + data_object=survey, + starting_model=model, conductivity_model=1e-2, ) - params.workpath = tmp_path - fwr_driver = InducedPolarization3DDriver(params) + + fwr_driver = InducedPolarization3DForwardDriver(params) fwr_driver.run() @@ -79,11 +81,11 @@ def test_ip_3d_run( topography = geoh5.get_entity("topography")[0] # Run the inverse - params = InducedPolarization3DParams( + params = InducedPolarization3DInversionParams( geoh5=geoh5, - mesh=mesh.uid, - topography_object=topography.uid, - data_object=potential.parent.uid, + mesh=mesh, + active_cells=ActiveCellsData(topography_object=topography), + data_object=potential.parent, conductivity_model=1e-2, reference_model=1e-6, starting_model=1e-6, @@ -92,9 +94,8 @@ def test_ip_3d_run( y_norm=0.0, z_norm=0.0, gradient_type="components", - chargeability_channel_bool=True, z_from_topo=False, - chargeability_channel=potential.uid, + chargeability_channel=potential, chargeability_uncertainty=2e-4, max_global_iterations=max_iterations, initial_beta=None, @@ -105,8 +106,11 @@ def test_ip_3d_run( store_sensitivities="ram", coolingRate=1, ) - params.write_input_file(path=tmp_path, name="Inv_run") - driver = InducedPolarization3DDriver.start(str(tmp_path / "Inv_run.ui.json")) + params.write_ui_json(path=tmp_path / "Inv_run.ui.json") + + driver = InducedPolarization3DInversionDriver.start( + str(tmp_path / "Inv_run.ui.json") + ) output = get_inversion_output( driver.params.geoh5.h5file, driver.params.out_group.uid From d3405551f69bd5d99c0344071f6a337c6b846d28 Mon Sep 17 00:00:00 2001 From: benjamink Date: Tue, 4 Feb 2025 09:09:25 -0800 Subject: [PATCH 05/13] almost there.. switching branches --- simpeg_drivers/__init__.py | 10 +- .../direct_current/two_dimensions/params.py | 56 +------- .../two_dimensions/__init__.py | 5 +- .../two_dimensions/driver.py | 15 ++- .../two_dimensions/params.py | 122 ++++++++---------- simpeg_drivers/electricals/params.py | 53 +++++++- tests/run_tests/driver_dc_2d_test.py | 3 +- tests/run_tests/driver_ip_2d_test.py | 52 ++++---- 8 files changed, 165 insertions(+), 151 deletions(-) diff --git a/simpeg_drivers/__init__.py b/simpeg_drivers/__init__.py index a87f5b09..782c4f62 100644 --- a/simpeg_drivers/__init__.py +++ b/simpeg_drivers/__init__.py @@ -59,7 +59,10 @@ def assets_path() -> Path: ), "direct current pseudo 3d": ( "simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.driver", - {"inversion": "DirectCurrentPseudo3DDriver"}, + { + "forward": "DirectCurrentPseudo3DForwardDriver", + "inversion": "DirectCurrentPseudo3DInversionDriver", + }, ), "induced polarization 3d": ( "simpeg_drivers.electricals.induced_polarization.three_dimensions.driver", @@ -70,7 +73,10 @@ def assets_path() -> Path: ), "induced polarization 2d": ( "simpeg_drivers.electricals.induced_polarization.two_dimensions.driver", - {"inversion": "InducedPolarization2DDriver"}, + { + "forward": "InducedPolarization2DForwardDriver", + "inversion": "InducedPolarization2DInversionDriver", + }, ), "induced polarization pseudo 3d": ( "simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.driver", diff --git a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py index fefe1e02..63c3681b 100644 --- a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py @@ -18,7 +18,11 @@ from pydantic import BaseModel, ConfigDict, field_validator, model_validator from simpeg_drivers import assets_path -from simpeg_drivers.electricals.params import Base2DParams +from simpeg_drivers.electricals.params import ( + Base2DParams, + DrapeModelData, + LineSelectionData, +) from simpeg_drivers.params import BaseForwardData, BaseInversionData from .constants import ( @@ -26,56 +30,6 @@ ) -class LineSelectionData(BaseModel): - """ - Line selection parameters for 2D inversions. - - :param line_object: Reference data categorizing survey by line ids. - :param line_id: Line identifier for simulation/inversion. - """ - - model_config = ConfigDict( - arbitrary_types_allowed=True, - ) - - line_object: ReferencedData - line_id: int - - @field_validator("line_object", mode="before") - @classmethod - def validate_cell_association(cls, value): - if value.association is not DataAssociationEnum.CELL: - raise ValueError("Line identifier must be associated with cells.") - return value - - @model_validator(mode="before") - @classmethod - def line_id_referenced(cls, model): - if model["line_id"] not in model["line_object"](): - raise ValueError("Line id isn't referenced in the line object.") - return model - - -class DrapeModelData(BaseModel): - """ - Drape model parameters for 2D simulation/inversion]. - - :param u_cell_size: Horizontal cell size for the drape model. - :param v_cell_size: Vertical cell size for the drape model. - :param depth_core: Depth of the core region. - :param horizontal_padding: Horizontal padding. - :param vertical_padding: Vertical padding. - :param expansion_factor: Expansion factor for the drape model. - """ - - u_cell_size: float = 25.0 - v_cell_size: float = 25.0 - depth_core: float = 100.0 - horizontal_padding: float = 100.0 - vertical_padding: float = 100.0 - expansion_factor: float = 100.0 - - class DirectCurrent2DForwardParams(BaseForwardData): """ Parameter class for two dimensional electrical->conductivity forward simulation. diff --git a/simpeg_drivers/electricals/induced_polarization/two_dimensions/__init__.py b/simpeg_drivers/electricals/induced_polarization/two_dimensions/__init__.py index 4a6b1cb1..dbaf3358 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/__init__.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/__init__.py @@ -9,7 +9,10 @@ # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -from .params import InducedPolarization2DParams +from .params import ( + InducedPolarization2DForwardParams, + InducedPolarization2DInversionParams, +) # pylint: disable=unused-import # flake8: noqa diff --git a/simpeg_drivers/electricals/induced_polarization/two_dimensions/driver.py b/simpeg_drivers/electricals/induced_polarization/two_dimensions/driver.py index 31450649..11f8680d 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/driver.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/driver.py @@ -14,12 +14,17 @@ from simpeg_drivers.electricals.driver import Base2DDriver from .constants import validations -from .params import InducedPolarization2DParams +from .params import ( + InducedPolarization2DForwardParams, + InducedPolarization2DInversionParams, +) -class InducedPolarization2DDriver(Base2DDriver): - _params_class = InducedPolarization2DParams +class InducedPolarization2DForwardDriver(Base2DDriver): + _params_class = InducedPolarization2DForwardParams _validations = validations - def __init__(self, params: InducedPolarization2DParams): - super().__init__(params) + +class InducedPolarization2DInversionDriver(Base2DDriver): + _params_class = InducedPolarization2DInversionParams + _validations = validations diff --git a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py index 1c7a98e3..6d347e8b 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py @@ -11,77 +11,67 @@ from __future__ import annotations -from copy import deepcopy +from typing import ClassVar -from simpeg_drivers.electricals.params import Base2DParams +from geoh5py.data import FloatData +from geoh5py.objects import DrapeModel -from .constants import ( - default_ui_json, - forward_defaults, - inversion_defaults, - validations, +from simpeg_drivers import assets_path +from simpeg_drivers.electricals.params import ( + DrapeModelData, + LineSelectionData, ) +from simpeg_drivers.params import BaseForwardData, BaseInversionData -class InducedPolarization2DParams(Base2DParams): +class InducedPolarization2DForwardParams(BaseForwardData): """ - Parameter class for electrical->induced polarization (IP) inversion. + Parameter class for two dimensional induced polarization forward simulation. + + :param chargeability_channel_bool: Chargeability channel boolean. + :param line_selection: Line selection parameters. + :param drape_model: Drape model parameters. + """ + + name: ClassVar[str] = "Induced Polarization 2D Forward" + title: ClassVar[str] = "Induced Polarization 2D Forward" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/induced_polarization_2d_forward.ui.json" + ) + + inversion_type: str = "induced polarization 2d" + physical_property: str = "chargeability" + + chargeability_channel_bool: bool = True + line_selection: LineSelectionData + mesh: DrapeModel | None = None + drape_model: DrapeModelData = DrapeModelData() + conductivity_model: float | FloatData + + +class InducedPolarization2DInversionParams(BaseInversionData): + """ + Parameter class for two dimensional induced polarization forward simulation. + + :param chargeability_channel: Chargeability data channel. + :param chargeability_uncertainty: Chargeability data uncertainty channel. + :param line_selection: Line selection parameters. + :param drape_model: Drape model parameters. """ - _physical_property = "chargeability" - - def __init__(self, input_file=None, forward_only=False, **kwargs): - self._default_ui_json = deepcopy(default_ui_json) - self._forward_defaults = deepcopy(forward_defaults) - self._inversion_defaults = deepcopy(inversion_defaults) - self._inversion_type = "induced polarization 2d" - self._validations = validations - self._chargeability_channel_bool = None - self._chargeability_channel = None - self._chargeability_uncertainty = None - self._line_object = None - self._line_id = None - self._conductivity_model_object = None - self._conductivity_model = None - - super().__init__(input_file=input_file, forward_only=forward_only, **kwargs) - - @property - def chargeability_channel_bool(self): - return self._chargeability_channel_bool - - @chargeability_channel_bool.setter - def chargeability_channel_bool(self, val): - self.setter_validator("chargeability_channel_bool", val) - - @property - def chargeability_channel(self): - return self._chargeability_channel - - @chargeability_channel.setter - def chargeability_channel(self, val): - self.setter_validator("chargeability_channel", val, fun=self._uuid_promoter) - - @property - def chargeability_uncertainty(self): - return self._chargeability_uncertainty - - @chargeability_uncertainty.setter - def chargeability_uncertainty(self, val): - self.setter_validator("chargeability_uncertainty", val, fun=self._uuid_promoter) - - @property - def conductivity_model_object(self): - return self._conductivity_model_object - - @conductivity_model_object.setter - def conductivity_model_object(self, val): - self.setter_validator("conductivity_model_object", val, fun=self._uuid_promoter) - - @property - def conductivity_model(self): - return self._conductivity_model - - @conductivity_model.setter - def conductivity_model(self, val): - self.setter_validator("conductivity_model", val, fun=self._uuid_promoter) + name: ClassVar[str] = "Induced Polarization 2D Inversion" + title: ClassVar[str] = "Induced Polarization 2D Inversion" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/induced_polarization_2d_inversion.ui.json" + ) + + inversion_type: str = "induced polarization 2d" + physical_property: str = "chargeability" + + chargeability_channel: FloatData + potential_uncertainty: float | FloatData | None = None + line_selection: LineSelectionData + mesh: DrapeModel | None = None + drape_model: DrapeModelData = DrapeModelData() + conductivity_model: float | FloatData + lower_bound: float | FloatData | None = 0.0 diff --git a/simpeg_drivers/electricals/params.py b/simpeg_drivers/electricals/params.py index 8649b481..994c0de4 100644 --- a/simpeg_drivers/electricals/params.py +++ b/simpeg_drivers/electricals/params.py @@ -11,11 +11,60 @@ from __future__ import annotations -import numpy as np from geoh5py.data import Data, DataAssociationEnum, ReferencedData +from pydantic import BaseModel, ConfigDict, field_validator, model_validator from simpeg_drivers.params import InversionBaseParams -from simpeg_drivers.utils.utils import get_drape_model + + +class LineSelectionData(BaseModel): + """ + Line selection parameters for 2D inversions. + + :param line_object: Reference data categorizing survey by line ids. + :param line_id: Line identifier for simulation/inversion. + """ + + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) + + line_object: ReferencedData + line_id: int + + @field_validator("line_object", mode="before") + @classmethod + def validate_cell_association(cls, value): + if value.association is not DataAssociationEnum.CELL: + raise ValueError("Line identifier must be associated with cells.") + return value + + @model_validator(mode="before") + @classmethod + def line_id_referenced(cls, model): + if model["line_id"] not in model["line_object"](): + raise ValueError("Line id isn't referenced in the line object.") + return model + + +class DrapeModelData(BaseModel): + """ + Drape model parameters for 2D simulation/inversion]. + + :param u_cell_size: Horizontal cell size for the drape model. + :param v_cell_size: Vertical cell size for the drape model. + :param depth_core: Depth of the core region. + :param horizontal_padding: Horizontal padding. + :param vertical_padding: Vertical padding. + :param expansion_factor: Expansion factor for the drape model. + """ + + u_cell_size: float = 25.0 + v_cell_size: float = 25.0 + depth_core: float = 100.0 + horizontal_padding: float = 100.0 + vertical_padding: float = 100.0 + expansion_factor: float = 100.0 class Core2DParams(InversionBaseParams): diff --git a/tests/run_tests/driver_dc_2d_test.py b/tests/run_tests/driver_dc_2d_test.py index 696e5bf0..0869f39a 100644 --- a/tests/run_tests/driver_dc_2d_test.py +++ b/tests/run_tests/driver_dc_2d_test.py @@ -24,9 +24,8 @@ from simpeg_drivers.electricals.direct_current.two_dimensions.params import ( DirectCurrent2DForwardParams, DirectCurrent2DInversionParams, - DrapeModelData, - LineSelectionData, ) +from simpeg_drivers.electricals.params import DrapeModelData, LineSelectionData from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output diff --git a/tests/run_tests/driver_ip_2d_test.py b/tests/run_tests/driver_ip_2d_test.py index 7cddae02..b08865cb 100644 --- a/tests/run_tests/driver_ip_2d_test.py +++ b/tests/run_tests/driver_ip_2d_test.py @@ -16,11 +16,15 @@ from geoh5py.workspace import Workspace from simpeg_drivers.electricals.induced_polarization.two_dimensions import ( - InducedPolarization2DParams, + InducedPolarization2DForwardParams, + InducedPolarization2DInversionParams, ) from simpeg_drivers.electricals.induced_polarization.two_dimensions.driver import ( - InducedPolarization2DDriver, + InducedPolarization2DForwardDriver, + InducedPolarization2DInversionDriver, ) +from simpeg_drivers.electricals.params import DrapeModelData, LineSelectionData +from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output @@ -49,20 +53,21 @@ def test_ip_2d_fwr_run( flatten=False, drape_height=0.0, ) - params = InducedPolarization2DParams( - forward_only=True, + params = InducedPolarization2DForwardParams( geoh5=geoh5, - data_object=survey.uid, - mesh=model.parent.uid, - topography_object=topography.uid, + data_object=survey, + mesh=model.parent, + active_cells=ActiveCellsData(topography_object=topography), z_from_topo=True, - starting_model=model.uid, + starting_model=model, conductivity_model=1e-2, - line_object=geoh5.get_entity("line_ids")[0].uid, - line_id=101, + line_selection=LineSelectionData( + line_object=geoh5.get_entity("line_ids")[0], + line_id=101, + ), ) - params.workpath = tmp_path - fwr_driver = InducedPolarization2DDriver(params) + + fwr_driver = InducedPolarization2DForwardDriver(params) fwr_driver.run() @@ -81,15 +86,17 @@ def test_ip_2d_run( topography = geoh5.get_entity("topography")[0] # Run the inverse - params = InducedPolarization2DParams( + params = InducedPolarization2DInversionParams( geoh5=geoh5, - mesh=mesh.uid, - topography_object=topography.uid, - data_object=chargeability.parent.uid, - chargeability_channel=chargeability.uid, + mesh=mesh, + active_cells=ActiveCellsData(topography_object=topography), + data_object=chargeability.parent, + chargeability_channel=chargeability, chargeability_uncertainty=2e-4, - line_object=geoh5.get_entity("line_ids")[0].uid, - line_id=101, + line_selection=LineSelectionData( + line_object=geoh5.get_entity("line_ids")[0], + line_id=101, + ), starting_model=1e-6, reference_model=1e-6, conductivity_model=1e-2, @@ -97,7 +104,6 @@ def test_ip_2d_run( x_norm=0.0, z_norm=0.0, gradient_type="components", - chargeability_channel_bool=True, z_from_topo=True, max_global_iterations=max_iterations, initial_beta=None, @@ -107,9 +113,11 @@ def test_ip_2d_run( store_sensitivities="ram", coolingRate=1, ) - params.write_input_file(path=tmp_path, name="Inv_run") + params.write_ui_json(path=tmp_path / "Inv_run.ui.json") - driver = InducedPolarization2DDriver.start(str(tmp_path / "Inv_run.ui.json")) + driver = InducedPolarization2DInversionDriver.start( + str(tmp_path / "Inv_run.ui.json") + ) output = get_inversion_output( driver.params.geoh5.h5file, driver.params.out_group.uid From 9f72251afd6735d2a9338980351200dd23a0fea3 Mon Sep 17 00:00:00 2001 From: benjamink Date: Tue, 4 Feb 2025 11:24:16 -0800 Subject: [PATCH 06/13] switching branches --- simpeg_drivers/__init__.py | 5 ++++- .../pseudo_three_dimensions/driver.py | 2 +- simpeg_drivers/electricals/driver.py | 4 +--- .../pseudo_three_dimensions/driver.py | 14 +++++++++++--- .../induced_polarization/two_dimensions/params.py | 2 +- tests/run_tests/driver_dc_p3d_test.py | 9 ++++++--- tests/run_tests/driver_ip_p3d_test.py | 9 ++++++--- 7 files changed, 30 insertions(+), 15 deletions(-) diff --git a/simpeg_drivers/__init__.py b/simpeg_drivers/__init__.py index 782c4f62..dfaeddd5 100644 --- a/simpeg_drivers/__init__.py +++ b/simpeg_drivers/__init__.py @@ -80,7 +80,10 @@ def assets_path() -> Path: ), "induced polarization pseudo 3d": ( "simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.driver", - {"inversion": "InducedPolarizationPseudo3DDriver"}, + { + "forward": "InducedPolarizationPseudo3DForwardDriver", + "inversion": "InducedPolarizationPseudo3DInversionDriver", + }, ), "joint surveys": ( "simpeg_drivers.joint.joint_surveys.driver", diff --git a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py index 8f30cf13..ef9db5d6 100644 --- a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py +++ b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py @@ -30,7 +30,7 @@ class DirectCurrentPseudo3DForwardDriver(BasePseudo3DDriver): _validations = validations -class DirectCurrentPseudo3DDriver(BasePseudo3DDriver): +class DirectCurrentPseudo3DInversionDriver(BasePseudo3DDriver): _params_class = DirectCurrentPseudo3DParams _params_2d_class = DirectCurrent2DInversionParams _validations = validations diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index fd05ac10..b0f83ad6 100644 --- a/simpeg_drivers/electricals/driver.py +++ b/simpeg_drivers/electricals/driver.py @@ -119,9 +119,7 @@ def transfer_models(self, mesh: DrapeModel) -> dict[str, uuid.UUID | float]: def write_files(self, lookup): """Write ui.geoh5 and ui.json files for sweep trials.""" - forward_only = self.pseudo3d_params.forward_only - ifile = self._params_2d_class(forward_only=forward_only).input_file - + ifile = self._params_2d_class().input_file with self.workspace.open(mode="r+"): self._window = InversionWindow(self.workspace, self.pseudo3d_params) self._inversion_data = InversionData(self.workspace, self.pseudo3d_params) diff --git a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py index f41ab6a7..ae017124 100644 --- a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py +++ b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py @@ -19,12 +19,20 @@ InducedPolarizationPseudo3DParams, ) from simpeg_drivers.electricals.induced_polarization.two_dimensions.params import ( - InducedPolarization2DParams, + InducedPolarization2DForwardParams, + InducedPolarization2DInversionParams, ) -class InducedPolarizationPseudo3DDriver(BasePseudo3DDriver): +class InducedPolarizationPseudo3DForwardDriver(BasePseudo3DDriver): _params_class = InducedPolarizationPseudo3DParams - _params_2d_class = InducedPolarization2DParams + _params_2d_class = InducedPolarization2DForwardParams + _validations = validations + _model_list = ["conductivity_model"] + + +class InducedPolarizationPseudo3DInversionDriver(BasePseudo3DDriver): + _params_class = InducedPolarizationPseudo3DParams + _params_2d_class = InducedPolarization2DInversionParams _validations = validations _model_list = ["conductivity_model"] diff --git a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py index 6d347e8b..3cff1327 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py @@ -69,7 +69,7 @@ class InducedPolarization2DInversionParams(BaseInversionData): physical_property: str = "chargeability" chargeability_channel: FloatData - potential_uncertainty: float | FloatData | None = None + chargeability_uncertainty: float | FloatData | None = None line_selection: LineSelectionData mesh: DrapeModel | None = None drape_model: DrapeModelData = DrapeModelData() diff --git a/tests/run_tests/driver_dc_p3d_test.py b/tests/run_tests/driver_dc_p3d_test.py index f5cf8015..f86584e9 100644 --- a/tests/run_tests/driver_dc_p3d_test.py +++ b/tests/run_tests/driver_dc_p3d_test.py @@ -18,7 +18,8 @@ from geoh5py.workspace import Workspace from simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.driver import ( - DirectCurrentPseudo3DDriver, + DirectCurrentPseudo3DForwardDriver, + DirectCurrentPseudo3DInversionDriver, ) from simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.params import ( DirectCurrentPseudo3DParams, @@ -68,7 +69,7 @@ def test_dc_p3d_fwr_run( cleanup=True, ) params.workpath = tmp_path - fwr_driver = DirectCurrentPseudo3DDriver(params) + fwr_driver = DirectCurrentPseudo3DForwardDriver(params) fwr_driver.run() @@ -120,7 +121,9 @@ def test_dc_p3d_run( ) params.write_input_file(path=tmp_path, name="Inv_run") - driver = DirectCurrentPseudo3DDriver.start(str(tmp_path / "Inv_run.ui.json")) + driver = DirectCurrentPseudo3DInversionDriver.start( + str(tmp_path / "Inv_run.ui.json") + ) basepath = workpath.parent with open(basepath / "lookup.json", encoding="utf8") as f: diff --git a/tests/run_tests/driver_ip_p3d_test.py b/tests/run_tests/driver_ip_p3d_test.py index 53f2a7bf..3687c32d 100644 --- a/tests/run_tests/driver_ip_p3d_test.py +++ b/tests/run_tests/driver_ip_p3d_test.py @@ -17,7 +17,8 @@ from geoh5py.workspace import Workspace from simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.driver import ( - InducedPolarizationPseudo3DDriver, + InducedPolarizationPseudo3DForwardDriver, + InducedPolarizationPseudo3DInversionDriver, ) from simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.params import ( InducedPolarizationPseudo3DParams, @@ -69,7 +70,7 @@ def test_ip_p3d_fwr_run( cleanup=True, ) params.workpath = tmp_path - fwr_driver = InducedPolarizationPseudo3DDriver(params) + fwr_driver = InducedPolarizationPseudo3DForwardDriver(params) fwr_driver.run() @@ -122,7 +123,9 @@ def test_ip_p3d_run( ) params.write_input_file(path=tmp_path, name="Inv_run") - driver = InducedPolarizationPseudo3DDriver.start(str(tmp_path / "Inv_run.ui.json")) + driver = InducedPolarizationPseudo3DInversionDriver.start( + str(tmp_path / "Inv_run.ui.json") + ) basepath = workpath.parent with open(basepath / "lookup.json", encoding="utf8") as f: From 1d21f9d4bbebc29140cf33b07ed5bade36a6b391 Mon Sep 17 00:00:00 2001 From: benjamink Date: Wed, 5 Feb 2025 09:31:05 -0800 Subject: [PATCH 07/13] Fix pseudo3d usage of 2d params class --- simpeg_drivers/components/data.py | 2 +- simpeg_drivers/electricals/driver.py | 16 ++++++++-------- .../pseudo_three_dimensions/params.py | 6 +++++- simpeg_drivers/electricals/params.py | 9 ++++----- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/simpeg_drivers/components/data.py b/simpeg_drivers/components/data.py index 3facdaa0..3c275b4f 100644 --- a/simpeg_drivers/components/data.py +++ b/simpeg_drivers/components/data.py @@ -110,7 +110,7 @@ def _initialize(self) -> None: self.has_tensor = InversionData.check_tensor(self.components) self.locations = super().get_locations(self.params.data_object) - if getattr(self.params, "line_selection", None) is not None: + if "2d" in self.params.inversion_type: self.mask = ( self.params.line_selection.line_object.values == self.params.line_selection.line_id diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index b0f83ad6..b9f7c0ba 100644 --- a/simpeg_drivers/electricals/driver.py +++ b/simpeg_drivers/electricals/driver.py @@ -112,14 +112,14 @@ def transfer_models(self, mesh: DrapeModel) -> dict[str, uuid.UUID | float]: model_values = model * np.ones(len(xyz_out)) model_object = mesh.add_data({name: {"values": model_values}}) - models[name] = model_object.uid + models[name] = model_object return models def write_files(self, lookup): """Write ui.geoh5 and ui.json files for sweep trials.""" - ifile = self._params_2d_class().input_file + kwargs_2d = {} with self.workspace.open(mode="r+"): self._window = InversionWindow(self.workspace, self.pseudo3d_params) self._inversion_data = InversionData(self.workspace, self.pseudo3d_params) @@ -173,16 +173,16 @@ def write_files(self, lookup): model_parameters = self.transfer_models(mesh) - for key in ifile.data: + for key in self._params_2d_class.model_fields: param = getattr(self.pseudo3d_params, key, None) if key not in ["title", "inversion_type"]: - ifile.data[key] = param + kwargs_2d[key] = param self.pseudo3d_params.topography_object.copy( parent=iter_workspace, copy_children=True ) - ifile.data.update( + kwargs_2d.update( dict( **{ "geoh5": iter_workspace, @@ -195,9 +195,9 @@ def write_files(self, lookup): ) ) - ifile.name = f"{uid}.ui.json" - ifile.path = self.working_directory # pylint: disable=no-member - ifile.write_ui_json() + params = self._params_2d_class.build(kwargs_2d) + params.write_ui_json(Path(self.working_directory) / f"{uid}.ui.json") + lookup[uid]["status"] = "written" _ = self.update_lookup(lookup) # pylint: disable=no-member diff --git a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py index 93112c9b..d6052877 100644 --- a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py @@ -19,7 +19,7 @@ inversion_defaults, validations, ) -from simpeg_drivers.electricals.params import BasePseudo3DParams +from simpeg_drivers.electricals.params import BasePseudo3DParams, LineSelectionData class InducedPolarizationPseudo3DParams(BasePseudo3DParams): @@ -42,6 +42,10 @@ def __init__(self, input_file=None, forward_only=False, **kwargs): super().__init__(input_file=input_file, forward_only=forward_only, **kwargs) + @property + def line_selection(self): + return LineSelectionData(line_object=self.line_object, line_id=1) + @property def conductivity_model(self): return self._conductivity_model diff --git a/simpeg_drivers/electricals/params.py b/simpeg_drivers/electricals/params.py index 994c0de4..da975290 100644 --- a/simpeg_drivers/electricals/params.py +++ b/simpeg_drivers/electricals/params.py @@ -39,12 +39,11 @@ def validate_cell_association(cls, value): raise ValueError("Line identifier must be associated with cells.") return value - @model_validator(mode="before") - @classmethod - def line_id_referenced(cls, model): - if model["line_id"] not in model["line_object"](): + @model_validator(mode="after") + def line_id_referenced(self): + if self.line_id not in self.line_object.values: raise ValueError("Line id isn't referenced in the line object.") - return model + return self class DrapeModelData(BaseModel): From 36e41b5a2cca422de54d618969b20f84e9d4bc5a Mon Sep 17 00:00:00 2001 From: benjamink Date: Wed, 5 Feb 2025 15:14:50 -0800 Subject: [PATCH 08/13] Working pseudo3d runtests --- .../direct_current/pseudo_three_dimensions/params.py | 6 +++++- .../electricals/direct_current/two_dimensions/params.py | 2 ++ simpeg_drivers/electricals/driver.py | 8 +++++++- .../induced_polarization/two_dimensions/params.py | 2 ++ tests/run_tests/driver_dc_2d_test.py | 1 - tests/run_tests/driver_ip_p3d_test.py | 3 +++ 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py index d2987f95..c69e78ff 100644 --- a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py @@ -19,7 +19,7 @@ inversion_defaults, validations, ) -from simpeg_drivers.electricals.params import BasePseudo3DParams +from simpeg_drivers.electricals.params import BasePseudo3DParams, LineSelectionData class DirectCurrentPseudo3DParams(BasePseudo3DParams): @@ -41,6 +41,10 @@ def __init__(self, input_file=None, forward_only=False, **kwargs): super().__init__(input_file=input_file, forward_only=forward_only, **kwargs) + @property + def line_selection(self): + return LineSelectionData(line_object=self.line_object, line_id=1) + @property def potential_channel_bool(self): return self._potential_channel_bool diff --git a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py index 63c3681b..45905548 100644 --- a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py @@ -84,3 +84,5 @@ class DirectCurrent2DInversionParams(BaseInversionData): mesh: DrapeModel | None = None drape_model: DrapeModelData model_type: str = "Conductivity (S/m)" + length_scale_y: None = None + y_norm: None = None diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index b9f7c0ba..df9ed7db 100644 --- a/simpeg_drivers/electricals/driver.py +++ b/simpeg_drivers/electricals/driver.py @@ -29,6 +29,7 @@ from simpeg_drivers.components.topography import InversionTopography from simpeg_drivers.components.windows import InversionWindow from simpeg_drivers.driver import InversionDriver +from simpeg_drivers.electricals.params import LineSelectionData from simpeg_drivers.line_sweep.driver import LineSweepDriver from simpeg_drivers.params import BaseParams from simpeg_drivers.utils.surveys import extract_dcip_survey @@ -188,7 +189,12 @@ def write_files(self, lookup): "geoh5": iter_workspace, "mesh": mesh, "data_object": receiver_entity, - "line_id": trial["line_id"], + "line_selection": LineSelectionData( + line_object=receiver_entity.get_data( + self.pseudo3d_params.line_object.name + )[0], + line_id=trial["line_id"], + ), "out_group": None, }, **model_parameters, diff --git a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py index 3cff1327..1e6187ff 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py @@ -75,3 +75,5 @@ class InducedPolarization2DInversionParams(BaseInversionData): drape_model: DrapeModelData = DrapeModelData() conductivity_model: float | FloatData lower_bound: float | FloatData | None = 0.0 + length_scale_y: None = None + y_norm: None = None diff --git a/tests/run_tests/driver_dc_2d_test.py b/tests/run_tests/driver_dc_2d_test.py index 0869f39a..ef56e606 100644 --- a/tests/run_tests/driver_dc_2d_test.py +++ b/tests/run_tests/driver_dc_2d_test.py @@ -117,7 +117,6 @@ def test_dc_2d_run(tmp_path: Path, max_iterations=1, pytest=True): reference_model=100.0, s_norm=0.0, x_norm=1.0, - y_norm=1.0, z_norm=1.0, gradient_type="components", potential_channel_bool=True, diff --git a/tests/run_tests/driver_ip_p3d_test.py b/tests/run_tests/driver_ip_p3d_test.py index 3687c32d..96576dbd 100644 --- a/tests/run_tests/driver_ip_p3d_test.py +++ b/tests/run_tests/driver_ip_p3d_test.py @@ -110,6 +110,9 @@ def test_ip_p3d_run( x_norm=0.0, y_norm=0.0, z_norm=0.0, + length_scale_x=1.0, + length_scale_y=1.0, + length_scale_z=1.0, gradient_type="components", chargeability_channel_bool=True, z_from_topo=True, From 7080735b00e2b834cd5c2496b962144d9d6d3224 Mon Sep 17 00:00:00 2001 From: benjamink Date: Thu, 6 Feb 2025 11:57:05 -0800 Subject: [PATCH 09/13] Add Pseudo3d BaseDAta params --- .../pseudo_three_dimensions/driver.py | 7 +- .../pseudo_three_dimensions/params.py | 77 ++++++++++++++++++- simpeg_drivers/electricals/driver.py | 23 +++--- simpeg_drivers/electricals/params.py | 14 +++- simpeg_drivers/line_sweep/driver.py | 15 ++-- tests/run_tests/driver_dc_p3d_test.py | 69 +++++++++-------- 6 files changed, 152 insertions(+), 53 deletions(-) diff --git a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py index ef9db5d6..6cd7a61b 100644 --- a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py +++ b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/driver.py @@ -15,7 +15,8 @@ validations, ) from simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.params import ( - DirectCurrentPseudo3DParams, + DirectCurrentPseudo3DForwardParams, + DirectCurrentPseudo3DInversionParams, ) from simpeg_drivers.electricals.direct_current.two_dimensions.params import ( DirectCurrent2DForwardParams, @@ -25,12 +26,12 @@ class DirectCurrentPseudo3DForwardDriver(BasePseudo3DDriver): - _params_class = DirectCurrentPseudo3DParams + _params_class = DirectCurrentPseudo3DForwardParams _params_2d_class = DirectCurrent2DForwardParams _validations = validations class DirectCurrentPseudo3DInversionDriver(BasePseudo3DDriver): - _params_class = DirectCurrentPseudo3DParams + _params_class = DirectCurrentPseudo3DInversionParams _params_2d_class = DirectCurrent2DInversionParams _validations = validations diff --git a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py index c69e78ff..b35e271a 100644 --- a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py @@ -12,14 +12,89 @@ from __future__ import annotations from copy import deepcopy +from typing import ClassVar +from geoh5py.data import FloatData +from geoh5py.objects import Octree, PotentialElectrode + +from simpeg_drivers import assets_path from simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.constants import ( default_ui_json, forward_defaults, inversion_defaults, validations, ) -from simpeg_drivers.electricals.params import BasePseudo3DParams, LineSelectionData +from simpeg_drivers.electricals.params import ( + BasePseudo3DParams, + DrapeModelData, + FileControlData, + LineSelectionData, +) +from simpeg_drivers.params import BaseForwardData, BaseInversionData + + +class DirectCurrentPseudo3DForwardParams(BaseForwardData): + """ + Parameter class for three dimensional direct current forward simulation. + + :param data_object: DC survey object. + :param potential_channel_bool: Potential channel boolean. + :param line_object: Data channel providing line ids. + :param mesh: Optional mesh object if providing a heterogeneous model. + :param drape_model: Drape model parameters common to all 2D simulations. + :param model_type: Specify whether the models are provided in resistivity + or conductivity. + """ + + name: ClassVar[str] = "Direct Current Pseudo 3D Forward" + title: ClassVar[str] = "Direct Current (DC) 2D Batch Forward" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/direct_current_pseudo3d_forward.ui.json" + ) + + inversion_type: str = "direct current pseudo 3d" + physical_property: str = "conductivity" + + data_object: PotentialElectrode + potential_channel_bool: bool = True + line_selection: LineSelectionData + mesh: Octree | None = None + drape_model: DrapeModelData = DrapeModelData() + model_type: str = "Conductivity (S/m)" + file_control: FileControlData = FileControlData() + + +class DirectCurrentPseudo3DInversionParams(BaseInversionData): + """ + Parameter class for three dimensional direct current inversion. + + :param data_object: DC survey object. + :param potential_channel: Potential data channel. + :param potential_uncertainty: Potential data uncertainty channel. + :param line_object: Data channel providing line ids. + :param mesh: Optional mesh object if providing a heterogeneous model. + :param drape_model: Drape model parameters. + """ + + name: ClassVar[str] = "Direct Current Pseudo 3D Inversion" + title: ClassVar[str] = "Direct Current (DC) 2D Batch Inversion" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/direct_current_pseudo3d_inversion.ui.json" + ) + + inversion_type: str = "direct current pseudo 3d" + physical_property: str = "conductivity" + + data_object: PotentialElectrode + potential_channel: FloatData + potential_uncertainty: float | FloatData + line_selection: LineSelectionData + mesh: Octree | None = None + drape_model: DrapeModelData = DrapeModelData() + model_type: str = "Conductivity (S/m)" + file_control: FileControlData = FileControlData() + length_scale_y: None = None + y_norm: None = None class DirectCurrentPseudo3DParams(BasePseudo3DParams): diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index df9ed7db..7bd357af 100644 --- a/simpeg_drivers/electricals/driver.py +++ b/simpeg_drivers/electricals/driver.py @@ -80,7 +80,7 @@ class BasePseudo3DDriver(LineSweepDriver): def __init__(self, params): super().__init__(params) - if params.files_only: + if params.file_control.files_only: sys.exit("Files written") def transfer_models(self, mesh: DrapeModel) -> dict[str, uuid.UUID | float]: @@ -138,13 +138,14 @@ def write_files(self, lookup): if filepath.exists(): warnings.warn( f"File {filepath} already exists but 'status' marked as 'pending'. " - "Over-writting file." + "Over-writing file." ) filepath.unlink() with Workspace.create(filepath) as iter_workspace: cell_mask: np.ndarray = ( - self.pseudo3d_params.line_object.values == trial["line_id"] + self.pseudo3d_params.line_selection.line_object.values + == trial["line_id"] ) if not np.any(cell_mask): @@ -163,13 +164,13 @@ def write_files(self, lookup): "Models", receiver_locs, [ - self.pseudo3d_params.u_cell_size, - self.pseudo3d_params.v_cell_size, + self.pseudo3d_params.drape_model.u_cell_size, + self.pseudo3d_params.drape_model.v_cell_size, ], - self.pseudo3d_params.depth_core, - [self.pseudo3d_params.horizontal_padding] * 2 - + [self.pseudo3d_params.vertical_padding, 1], - self.pseudo3d_params.expansion_factor, + self.pseudo3d_params.drape_model.depth_core, + [self.pseudo3d_params.drape_model.horizontal_padding] * 2 + + [self.pseudo3d_params.drape_model.vertical_padding, 1], + self.pseudo3d_params.drape_model.expansion_factor, )[0] model_parameters = self.transfer_models(mesh) @@ -179,7 +180,7 @@ def write_files(self, lookup): if key not in ["title", "inversion_type"]: kwargs_2d[key] = param - self.pseudo3d_params.topography_object.copy( + self.pseudo3d_params.active_cells.topography_object.copy( parent=iter_workspace, copy_children=True ) @@ -191,7 +192,7 @@ def write_files(self, lookup): "data_object": receiver_entity, "line_selection": LineSelectionData( line_object=receiver_entity.get_data( - self.pseudo3d_params.line_object.name + self.pseudo3d_params.line_selection.line_object.name )[0], line_id=trial["line_id"], ), diff --git a/simpeg_drivers/electricals/params.py b/simpeg_drivers/electricals/params.py index da975290..70283758 100644 --- a/simpeg_drivers/electricals/params.py +++ b/simpeg_drivers/electricals/params.py @@ -30,7 +30,7 @@ class LineSelectionData(BaseModel): ) line_object: ReferencedData - line_id: int + line_id: int = 1 @field_validator("line_object", mode="before") @classmethod @@ -66,6 +66,18 @@ class DrapeModelData(BaseModel): expansion_factor: float = 100.0 +class FileControlData(BaseModel): + """ + File control parameters for pseudo 3D simulations. + + :param files_only: Boolean to only write files. + :param cleanup: Boolean to cleanup files. + """ + + files_only: bool = False + cleanup: bool = True + + class Core2DParams(InversionBaseParams): """ Core parameter class for 2D electrical->conductivity inversion. diff --git a/simpeg_drivers/line_sweep/driver.py b/simpeg_drivers/line_sweep/driver.py index fa7197f6..5987e358 100644 --- a/simpeg_drivers/line_sweep/driver.py +++ b/simpeg_drivers/line_sweep/driver.py @@ -34,7 +34,7 @@ def __init__(self, params): self._out_group = None self.workspace = params.geoh5 self.pseudo3d_params = params - self.cleanup = params.cleanup + self.cleanup = params.file_control.cleanup if ( hasattr(self.pseudo3d_params, "out_group") @@ -78,9 +78,8 @@ def setup_params(self): ) if not (ui_json_path).is_file(): with self.workspace.open(): - self.pseudo3d_params.write_input_file( - name=ui_json_path.name, - path=h5_file_path.parent, + self.pseudo3d_params.write_ui_json( + path=h5_file_path.parent / ui_json_path.name ) generate( ui_json_path, @@ -92,7 +91,7 @@ def setup_params(self): / (re.sub(r"\.ui$", "", h5_file_path.stem) + "_sweep.ui.json") ) with self.workspace.open(mode="r"): - lines = self.pseudo3d_params.line_object.values + lines = self.pseudo3d_params.line_selection.line_object.values ifile.data["line_id_start"] = int(lines.min()) ifile.data["line_id_end"] = int(lines.max()) ifile.data["line_id_n"] = len(np.unique(lines)) @@ -122,7 +121,7 @@ def line_files(path: str | Path): def collect_results(self): path = Path(self.workspace.h5file).parent files = LineSweepDriver.line_files(str(path)) - line_ids = self.pseudo3d_params.line_object.values + line_ids = self.pseudo3d_params.line_selection.line_object.values data = {} drape_models = [] @@ -138,7 +137,9 @@ def collect_results(self): for child in out_group.children if isinstance(child, PotentialElectrode) ) - line_data = survey.get_entity(self.pseudo3d_params.line_object.name) + line_data = survey.get_entity( + self.pseudo3d_params.line_selection.line_object.name + ) if not line_data: raise ValueError(f"Line {line} not found in {survey.name}") diff --git a/tests/run_tests/driver_dc_p3d_test.py b/tests/run_tests/driver_dc_p3d_test.py index f86584e9..f2591922 100644 --- a/tests/run_tests/driver_dc_p3d_test.py +++ b/tests/run_tests/driver_dc_p3d_test.py @@ -22,8 +22,15 @@ DirectCurrentPseudo3DInversionDriver, ) from simpeg_drivers.electricals.direct_current.pseudo_three_dimensions.params import ( - DirectCurrentPseudo3DParams, + DirectCurrentPseudo3DForwardParams, + DirectCurrentPseudo3DInversionParams, ) +from simpeg_drivers.electricals.params import ( + DrapeModelData, + FileControlData, + LineSelectionData, +) +from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output @@ -52,23 +59,23 @@ def test_dc_p3d_fwr_run( drape_height=0.0, flatten=False, ) - params = DirectCurrentPseudo3DParams( - forward_only=True, + params = DirectCurrentPseudo3DForwardParams( geoh5=geoh5, - mesh=model.parent.uid, - u_cell_size=5.0, - v_cell_size=5.0, - depth_core=100.0, - expansion_factor=1.1, - padding_distance=100.0, - topography_object=topography.uid, + mesh=model.parent, + drape_model=DrapeModelData( + u_cell_size=5.0, + v_cell_size=5.0, + depth_core=100.0, + expansion_factor=1.1, + padding_distance=100.0, + ), + active_cells=ActiveCellsData(topography_object=topography), z_from_topo=False, - data_object=survey.uid, - starting_model=model.uid, - line_object=geoh5.get_entity("line_ids")[0].uid, - cleanup=True, + data_object=survey, + starting_model=model, + line_selection=LineSelectionData(line_object=geoh5.get_entity("line_ids")[0]), ) - params.workpath = tmp_path + fwr_driver = DirectCurrentPseudo3DForwardDriver(params) fwr_driver.run() @@ -89,27 +96,29 @@ def test_dc_p3d_run( topography = geoh5.get_entity("topography")[0] # Run the inverse - params = DirectCurrentPseudo3DParams( + params = DirectCurrentPseudo3DInversionParams( geoh5=geoh5, - mesh=mesh.uid, - u_cell_size=5.0, - v_cell_size=5.0, - depth_core=100.0, - expansion_factor=1.1, - padding_distance=100.0, - topography_object=topography.uid, - data_object=potential.parent.uid, - potential_channel=potential.uid, + mesh=mesh, + drape_model=DrapeModelData( + u_cell_size=5.0, + v_cell_size=5.0, + depth_core=100.0, + expansion_factor=1.1, + padding_distance=100.0, + ), + active_cells=ActiveCellsData(topography_object=topography), + data_object=potential.parent, + potential_channel=potential, potential_uncertainty=1e-3, - line_object=geoh5.get_entity("line_ids")[0].uid, + line_selection=LineSelectionData( + line_object=geoh5.get_entity("line_ids")[0] + ), starting_model=1e-2, reference_model=1e-2, s_norm=0.0, x_norm=1.0, - y_norm=1.0, z_norm=1.0, gradient_type="components", - potential_channel_bool=True, z_from_topo=False, max_global_iterations=max_iterations, initial_beta=None, @@ -117,9 +126,9 @@ def test_dc_p3d_run( prctile=100, upper_bound=10, coolingRate=1, - cleanup=False, + file_control=FileControlData(cleanup=False), ) - params.write_input_file(path=tmp_path, name="Inv_run") + params.write_ui_json(path=tmp_path / "Inv_run.ui.json") driver = DirectCurrentPseudo3DInversionDriver.start( str(tmp_path / "Inv_run.ui.json") From 92c46d6d2bb830d32b1fad222055e5100cbf51ab Mon Sep 17 00:00:00 2001 From: benjamink Date: Thu, 6 Feb 2025 12:12:25 -0800 Subject: [PATCH 10/13] fix pseudo3d runtest --- tests/run_tests/driver_dc_p3d_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/run_tests/driver_dc_p3d_test.py b/tests/run_tests/driver_dc_p3d_test.py index f2591922..414bb2c6 100644 --- a/tests/run_tests/driver_dc_p3d_test.py +++ b/tests/run_tests/driver_dc_p3d_test.py @@ -104,7 +104,8 @@ def test_dc_p3d_run( v_cell_size=5.0, depth_core=100.0, expansion_factor=1.1, - padding_distance=100.0, + horizontal_padding=1000.0, + vertical_padding=1000.0, ), active_cells=ActiveCellsData(topography_object=topography), data_object=potential.parent, From 9b108285df9af1186c87eb24d409d0c830f8598d Mon Sep 17 00:00:00 2001 From: benjamink Date: Thu, 6 Feb 2025 13:26:48 -0800 Subject: [PATCH 11/13] Add IP p3d BaseData params --- .../pseudo_three_dimensions/params.py | 10 ++- .../pseudo_three_dimensions/driver.py | 7 +- .../pseudo_three_dimensions/params.py | 82 ++++++++++++++++++- .../two_dimensions/params.py | 8 +- tests/run_tests/driver_dc_p3d_test.py | 3 +- tests/run_tests/driver_ip_p3d_test.py | 73 ++++++++++------- 6 files changed, 145 insertions(+), 38 deletions(-) diff --git a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py index b35e271a..043a47f0 100644 --- a/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/pseudo_three_dimensions/params.py @@ -39,11 +39,12 @@ class DirectCurrentPseudo3DForwardParams(BaseForwardData): :param data_object: DC survey object. :param potential_channel_bool: Potential channel boolean. - :param line_object: Data channel providing line ids. + :param line_selection: Line selection parameters. :param mesh: Optional mesh object if providing a heterogeneous model. :param drape_model: Drape model parameters common to all 2D simulations. :param model_type: Specify whether the models are provided in resistivity or conductivity. + :param file_control: File control parameters. """ name: ClassVar[str] = "Direct Current Pseudo 3D Forward" @@ -71,9 +72,14 @@ class DirectCurrentPseudo3DInversionParams(BaseInversionData): :param data_object: DC survey object. :param potential_channel: Potential data channel. :param potential_uncertainty: Potential data uncertainty channel. - :param line_object: Data channel providing line ids. + :param line_selection: Line selection parameters. :param mesh: Optional mesh object if providing a heterogeneous model. :param drape_model: Drape model parameters. + :param model_type: Specify whether the models are provided in resistivity + or conductivity. + :param file_control: File control parameters. + :param length_scale_y: Inactive length scale for y direction. + :param y_norm: Inactive y normalization factor. """ name: ClassVar[str] = "Direct Current Pseudo 3D Inversion" diff --git a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py index ae017124..03c73c8d 100644 --- a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py +++ b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py @@ -16,7 +16,8 @@ validations, ) from simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.params import ( - InducedPolarizationPseudo3DParams, + InducedPolarizationPseudo3DForwardParams, + InducedPolarizationPseudo3DInversionParams, ) from simpeg_drivers.electricals.induced_polarization.two_dimensions.params import ( InducedPolarization2DForwardParams, @@ -25,14 +26,14 @@ class InducedPolarizationPseudo3DForwardDriver(BasePseudo3DDriver): - _params_class = InducedPolarizationPseudo3DParams + _params_class = InducedPolarizationPseudo3DForwardParams _params_2d_class = InducedPolarization2DForwardParams _validations = validations _model_list = ["conductivity_model"] class InducedPolarizationPseudo3DInversionDriver(BasePseudo3DDriver): - _params_class = InducedPolarizationPseudo3DParams + _params_class = InducedPolarizationPseudo3DInversionParams _params_2d_class = InducedPolarization2DInversionParams _validations = validations _model_list = ["conductivity_model"] diff --git a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py index d6052877..73b4489d 100644 --- a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py @@ -12,14 +12,94 @@ from __future__ import annotations from copy import deepcopy +from typing import ClassVar +from geoh5py.data import FloatData +from geoh5py.objects import Octree, PotentialElectrode + +from simpeg_drivers import assets_path from simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.constants import ( default_ui_json, forward_defaults, inversion_defaults, validations, ) -from simpeg_drivers.electricals.params import BasePseudo3DParams, LineSelectionData +from simpeg_drivers.electricals.params import ( + BasePseudo3DParams, + DrapeModelData, + FileControlData, + LineSelectionData, +) +from simpeg_drivers.params import BaseForwardData, BaseInversionData + + +class InducedPolarizationPseudo3DForwardParams(BaseForwardData): + """ + Parameter class for three dimensional induced polarization forward simulation. + + :param data_object: DC/IP survey object. + :param chargeability_channel_bool: Chargeability channel boolean. + :param line_selection: Line selection parameters. + :param mesh: Optional mesh object if providing a heterogeneous model. + :param drape_model: Drape model parameters common to all 2D simulations. + :param model_type: Specify whether the models are provided in resistivity + or conductivity. + :param file_control: File control parameters. + """ + + name: ClassVar[str] = "Induced Polarization Pseudo 3D Forward" + title: ClassVar[str] = "Induced Polarization (IP) 2D Batch Forward" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/induced_polarization_pseudo3d_forward.ui.json" + ) + + inversion_type: str = "induced polarization pseudo 3d" + physical_property: str = "chargeability" + + data_object: PotentialElectrode + chargeability_channel_bool: bool = True + line_selection: LineSelectionData + mesh: Octree | None = None + conductivity_model: float | FloatData + drape_model: DrapeModelData = DrapeModelData() + file_control: FileControlData = FileControlData() + + +class InducedPolarizationPseudo3DInversionParams(BaseInversionData): + """ + Parameter class for three dimensional induced polarization inversion. + + :param data_object: DC/IP survey object. + :param chargeability_channel: Chargeability data channel. + :param chargeability_uncertainty: Chargeability data uncertainty channel. + :param line_selection: Line selection parameters. + :param mesh: Optional mesh object if providing a heterogeneous model. + :param drape_model: Drape model parameters common to all 2D simulations. + :param conductivity_model: Conductivity model. + :param file_control: File control parameters. + :param length_scale_y: Inactive length scale for y direction. + :param y_norm: Inactive y normalization factor. + """ + + name: ClassVar[str] = "Induced Polarization Pseudo 3D Inversion" + title: ClassVar[str] = "Induced Polarization (IP) 2D Batch Inversion" + default_ui_json: ClassVar[str] = ( + assets_path() / "uijson/induced_polarization_pseudo3d_inversion.ui.json" + ) + + inversion_type: str = "induced polarization pseudo 3d" + physical_property: str = "chargeability" + + data_object: PotentialElectrode + chargeability_channel: FloatData + chargeability_uncertainty: float | FloatData + line_selection: LineSelectionData + mesh: Octree | None = None + drape_model: DrapeModelData = DrapeModelData() + conductivity_model: float | FloatData + file_control: FileControlData = FileControlData() + length_scale_y: None = None + y_norm: None = None class InducedPolarizationPseudo3DParams(BasePseudo3DParams): diff --git a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py index 1e6187ff..5a9e12bd 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py @@ -29,8 +29,10 @@ class InducedPolarization2DForwardParams(BaseForwardData): Parameter class for two dimensional induced polarization forward simulation. :param chargeability_channel_bool: Chargeability channel boolean. - :param line_selection: Line selection parameters. + :param mesh: Optional mesh object if providing a heterogeneous model. :param drape_model: Drape model parameters. + :param line_selection: Line selection parameters. + :param conductivity_model: Conductivity model. """ name: ClassVar[str] = "Induced Polarization 2D Forward" @@ -57,6 +59,10 @@ class InducedPolarization2DInversionParams(BaseInversionData): :param chargeability_uncertainty: Chargeability data uncertainty channel. :param line_selection: Line selection parameters. :param drape_model: Drape model parameters. + :param conductivity_model: Conductivity model. + :param lower_bound: Lower bound for the inversion. + :param length_scale_y: Inactive length scale in the y direction. + :param y_norm: Inactive y normalization factor. """ name: ClassVar[str] = "Induced Polarization 2D Inversion" diff --git a/tests/run_tests/driver_dc_p3d_test.py b/tests/run_tests/driver_dc_p3d_test.py index 414bb2c6..1bf2bcb0 100644 --- a/tests/run_tests/driver_dc_p3d_test.py +++ b/tests/run_tests/driver_dc_p3d_test.py @@ -67,7 +67,8 @@ def test_dc_p3d_fwr_run( v_cell_size=5.0, depth_core=100.0, expansion_factor=1.1, - padding_distance=100.0, + horizontal_padding=1000.0, + vertical_padding=1000.0, ), active_cells=ActiveCellsData(topography_object=topography), z_from_topo=False, diff --git a/tests/run_tests/driver_ip_p3d_test.py b/tests/run_tests/driver_ip_p3d_test.py index 96576dbd..1f0e6819 100644 --- a/tests/run_tests/driver_ip_p3d_test.py +++ b/tests/run_tests/driver_ip_p3d_test.py @@ -21,8 +21,15 @@ InducedPolarizationPseudo3DInversionDriver, ) from simpeg_drivers.electricals.induced_polarization.pseudo_three_dimensions.params import ( - InducedPolarizationPseudo3DParams, + InducedPolarizationPseudo3DForwardParams, + InducedPolarizationPseudo3DInversionParams, ) +from simpeg_drivers.electricals.params import ( + DrapeModelData, + FileControlData, + LineSelectionData, +) +from simpeg_drivers.params import ActiveCellsData from simpeg_drivers.utils.testing import check_target, setup_inversion_workspace from simpeg_drivers.utils.utils import get_inversion_output @@ -52,24 +59,27 @@ def test_ip_p3d_fwr_run( flatten=False, ) - params = InducedPolarizationPseudo3DParams( - forward_only=True, + params = InducedPolarizationPseudo3DForwardParams( geoh5=geoh5, - mesh=model.parent.uid, - u_cell_size=5.0, - v_cell_size=5.0, - depth_core=100.0, - expansion_factor=1.1, - padding_distance=100.0, - topography_object=topography.uid, + mesh=model.parent, + drape_model=DrapeModelData( + u_cell_size=5.0, + v_cell_size=5.0, + depth_core=100.0, + expansion_factor=1.1, + horizontal_padding=100.0, + vertical_padding=100.0, + ), + active_cells=ActiveCellsData( + topography_object=topography, + ), z_from_topo=True, - data_object=survey.uid, + data_object=survey, conductivity_model=1e-2, - starting_model=model.uid, - line_object=geoh5.get_entity("line_ids")[0].uid, - cleanup=True, + starting_model=model, + line_selection=LineSelectionData(line_object=geoh5.get_entity("line_ids")[0]), ) - params.workpath = tmp_path + fwr_driver = InducedPolarizationPseudo3DForwardDriver(params) fwr_driver.run() @@ -90,28 +100,31 @@ def test_ip_p3d_run( topography = geoh5.get_entity("topography")[0] # Run the inverse - params = InducedPolarizationPseudo3DParams( + params = InducedPolarizationPseudo3DInversionParams( geoh5=geoh5, - mesh=mesh.uid, - u_cell_size=5.0, - v_cell_size=5.0, - depth_core=100.0, - expansion_factor=1.1, - padding_distance=100.0, - topography_object=topography.uid, - data_object=chargeability.parent.uid, - chargeability_channel=chargeability.uid, + mesh=mesh, + drape_model=DrapeModelData( + u_cell_size=5.0, + v_cell_size=5.0, + depth_core=100.0, + expansion_factor=1.1, + horizontal_padding=1000.0, + vertical_padding=1000.0, + ), + active_cells=ActiveCellsData(topography_object=topography), + data_object=chargeability.parent, + chargeability_channel=chargeability, chargeability_uncertainty=2e-4, - line_object=geoh5.get_entity("line_ids")[0].uid, + line_selection=LineSelectionData( + line_object=geoh5.get_entity("line_ids")[0], + ), conductivity_model=1e-2, starting_model=1e-6, reference_model=1e-6, s_norm=0.0, x_norm=0.0, - y_norm=0.0, z_norm=0.0, length_scale_x=1.0, - length_scale_y=1.0, length_scale_z=1.0, gradient_type="components", chargeability_channel_bool=True, @@ -122,9 +135,9 @@ def test_ip_p3d_run( prctile=100, upper_bound=0.1, coolingRate=1, - cleanup=False, + file_control=FileControlData(cleanup=False), ) - params.write_input_file(path=tmp_path, name="Inv_run") + params.write_ui_json(path=tmp_path / "Inv_run.ui.json") driver = InducedPolarizationPseudo3DInversionDriver.start( str(tmp_path / "Inv_run.ui.json") From 35e5a8d208eca82c3eeeb20d5d0709af982b23b3 Mon Sep 17 00:00:00 2001 From: benjamink Date: Thu, 6 Feb 2025 14:59:58 -0800 Subject: [PATCH 12/13] dump kwargs directly into 2dparams --- simpeg_drivers/electricals/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index 7bd357af..947dd65d 100644 --- a/simpeg_drivers/electricals/driver.py +++ b/simpeg_drivers/electricals/driver.py @@ -202,7 +202,7 @@ def write_files(self, lookup): ) ) - params = self._params_2d_class.build(kwargs_2d) + params = self._params_2d_class(**kwargs_2d) params.write_ui_json(Path(self.working_directory) / f"{uid}.ui.json") lookup[uid]["status"] = "written" From 4fd6dbe155c75f75b140b8ef3e57e87afdcbcb57 Mon Sep 17 00:00:00 2001 From: benjamink Date: Thu, 6 Feb 2025 15:04:33 -0800 Subject: [PATCH 13/13] add lower_bound=0 on ipp3d params --- .../induced_polarization/pseudo_three_dimensions/params.py | 1 + 1 file changed, 1 insertion(+) diff --git a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py index 73b4489d..9e4a6d7c 100644 --- a/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/params.py @@ -97,6 +97,7 @@ class InducedPolarizationPseudo3DInversionParams(BaseInversionData): mesh: Octree | None = None drape_model: DrapeModelData = DrapeModelData() conductivity_model: float | FloatData + lower_bound: float | FloatData | None = 0.0 file_control: FileControlData = FileControlData() length_scale_y: None = None y_norm: None = None