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..dfaeddd5 100644 --- a/simpeg_drivers/__init__.py +++ b/simpeg_drivers/__init__.py @@ -45,27 +45,45 @@ 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", - {"inversion": "DirectCurrent2DDriver"}, + { + "forward": "DirectCurrent2DForwardDriver", + "inversion": "DirectCurrent2DInversionDriver", + }, ), "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", - {"inversion": "InducedPolarization3DDriver"}, + { + "forward": "InducedPolarization3DForwardDriver", + "inversion": "InducedPolarization3DInversionDriver", + }, ), "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", - {"inversion": "InducedPolarizationPseudo3DDriver"}, + { + "forward": "InducedPolarizationPseudo3DForwardDriver", + "inversion": "InducedPolarizationPseudo3DInversionDriver", + }, ), "joint surveys": ( "simpeg_drivers.joint.joint_surveys.driver", diff --git a/simpeg_drivers/components/data.py b/simpeg_drivers/components/data.py index b49b5220..7cfa5a8e 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 "2d" in self.params.inversion_type: + 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) @@ -471,11 +471,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/__init__.py b/simpeg_drivers/electricals/__init__.py index 2d06129e..a5a4b0b4 100644 --- a/simpeg_drivers/electricals/__init__.py +++ b/simpeg_drivers/electricals/__init__.py @@ -11,8 +11,14 @@ from __future__ import annotations -from .direct_current.three_dimensions import DirectCurrent3DParams -from .induced_polarization.three_dimensions.params import InducedPolarization3DParams +from .direct_current.three_dimensions import ( + DirectCurrent3DForwardParams, + DirectCurrent3DInversionParams, +) +from .induced_polarization.three_dimensions.params import ( + InducedPolarization3DForwardParams, + InducedPolarization3DInversionParams, +) # pylint: disable=unused-import # flake8: noqa 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..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,15 +15,23 @@ 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 ( - DirectCurrent2DParams, + DirectCurrent2DForwardParams, + DirectCurrent2DInversionParams, ) from simpeg_drivers.electricals.driver import BasePseudo3DDriver -class DirectCurrentPseudo3DDriver(BasePseudo3DDriver): - _params_class = DirectCurrentPseudo3DParams - _params_2d_class = DirectCurrent2DParams +class DirectCurrentPseudo3DForwardDriver(BasePseudo3DDriver): + _params_class = DirectCurrentPseudo3DForwardParams + _params_2d_class = DirectCurrent2DForwardParams + _validations = validations + + +class DirectCurrentPseudo3DInversionDriver(BasePseudo3DDriver): + _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 d2987f95..043a47f0 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,95 @@ 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 +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_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" + 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_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" + 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): @@ -41,6 +122,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/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/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..45905548 100644 --- a/simpeg_drivers/electricals/direct_current/two_dimensions/params.py +++ b/simpeg_drivers/electricals/direct_current/two_dimensions/params.py @@ -11,59 +11,78 @@ from __future__ import annotations -from copy import deepcopy +from typing import ClassVar -from simpeg_drivers.electricals.params import Base2DParams +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, + DrapeModelData, + LineSelectionData, +) +from simpeg_drivers.params import BaseForwardData, BaseInversionData from .constants import ( - default_ui_json, - forward_defaults, - inversion_defaults, validations, ) -class DirectCurrent2DParams(Base2DParams): +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)" + length_scale_y: None = None + y_norm: None = None diff --git a/simpeg_drivers/electricals/driver.py b/simpeg_drivers/electricals/driver.py index f0a87d78..947dd65d 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 @@ -59,13 +60,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 @@ -79,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]: @@ -112,16 +113,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.""" - forward_only = self.pseudo3d_params.forward_only - ifile = self._params_2d_class(forward_only=forward_only).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) @@ -139,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): @@ -164,42 +164,47 @@ 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) - 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( + self.pseudo3d_params.active_cells.topography_object.copy( parent=iter_workspace, copy_children=True ) - ifile.data.update( + kwargs_2d.update( dict( **{ "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_selection.line_object.name + )[0], + line_id=trial["line_id"], + ), "out_group": None, }, **model_parameters, ) ) - ifile.name = f"{uid}.ui.json" - ifile.path = self.working_directory # pylint: disable=no-member - ifile.write_ui_json() + params = self._params_2d_class(**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/driver.py b/simpeg_drivers/electricals/induced_polarization/pseudo_three_dimensions/driver.py index f41ab6a7..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,15 +16,24 @@ 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 ( - InducedPolarization2DParams, + InducedPolarization2DForwardParams, + InducedPolarization2DInversionParams, ) -class InducedPolarizationPseudo3DDriver(BasePseudo3DDriver): - _params_class = InducedPolarizationPseudo3DParams - _params_2d_class = InducedPolarization2DParams +class InducedPolarizationPseudo3DForwardDriver(BasePseudo3DDriver): + _params_class = InducedPolarizationPseudo3DForwardParams + _params_2d_class = InducedPolarization2DForwardParams + _validations = validations + _model_list = ["conductivity_model"] + + +class InducedPolarizationPseudo3DInversionDriver(BasePseudo3DDriver): + _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 93112c9b..9e4a6d7c 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,95 @@ 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 +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 + lower_bound: float | FloatData | None = 0.0 + file_control: FileControlData = FileControlData() + length_scale_y: None = None + y_norm: None = None class InducedPolarizationPseudo3DParams(BasePseudo3DParams): @@ -42,6 +123,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/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/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..5a9e12bd 100644 --- a/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py +++ b/simpeg_drivers/electricals/induced_polarization/two_dimensions/params.py @@ -11,77 +11,75 @@ 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 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" + 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. + :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. """ - _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 + chargeability_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 + length_scale_y: None = None + y_norm: None = None diff --git a/simpeg_drivers/electricals/params.py b/simpeg_drivers/electricals/params.py index e2b55a95..70283758 100644 --- a/simpeg_drivers/electricals/params.py +++ b/simpeg_drivers/electricals/params.py @@ -11,11 +11,71 @@ from __future__ import annotations -import numpy as np -from geoh5py.data import Data, DataAssociationEnum +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 = 1 + + @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="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 self + + +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 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): 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/simpeg_drivers/params.py b/simpeg_drivers/params.py index 66e88739..7e5d9c99 100644 --- a/simpeg_drivers/params.py +++ b/simpeg_drivers/params.py @@ -144,7 +144,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/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/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_dc_2d_test.py b/tests/run_tests/driver_dc_2d_test.py index 26ff19a0..ef56e606 100644 --- a/tests/run_tests/driver_dc_2d_test.py +++ b/tests/run_tests/driver_dc_2d_test.py @@ -14,14 +14,19 @@ 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, ) +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 @@ -54,24 +59,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,26 +94,29 @@ 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, x_norm=1.0, - y_norm=1.0, z_norm=1.0, gradient_type="components", potential_channel_bool=True, @@ -116,9 +128,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 diff --git a/tests/run_tests/driver_dc_p3d_test.py b/tests/run_tests/driver_dc_p3d_test.py index f5cf8015..1bf2bcb0 100644 --- a/tests/run_tests/driver_dc_p3d_test.py +++ b/tests/run_tests/driver_dc_p3d_test.py @@ -18,11 +18,19 @@ 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, + 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 @@ -51,24 +59,25 @@ 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, + horizontal_padding=1000.0, + vertical_padding=1000.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 = DirectCurrentPseudo3DDriver(params) + + fwr_driver = DirectCurrentPseudo3DForwardDriver(params) fwr_driver.run() @@ -88,27 +97,30 @@ 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, + horizontal_padding=1000.0, + vertical_padding=1000.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, @@ -116,11 +128,13 @@ 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 = 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_dc_test.py b/tests/run_tests/driver_dc_test.py index e43764f3..bf3d9674 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, @@ -106,9 +109,8 @@ 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.uid, + potential_channel=potential, potential_uncertainty=1e-3, max_global_iterations=max_iterations, initial_beta=None, @@ -122,9 +124,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 +159,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) diff --git a/tests/run_tests/driver_ip_2d_test.py b/tests/run_tests/driver_ip_2d_test.py index 688a3077..0f3aba9b 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 diff --git a/tests/run_tests/driver_ip_p3d_test.py b/tests/run_tests/driver_ip_p3d_test.py index 53f2a7bf..1f0e6819 100644 --- a/tests/run_tests/driver_ip_p3d_test.py +++ b/tests/run_tests/driver_ip_p3d_test.py @@ -17,11 +17,19 @@ 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, + 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 @@ -51,25 +59,28 @@ 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 = InducedPolarizationPseudo3DDriver(params) + + fwr_driver = InducedPolarizationPseudo3DForwardDriver(params) fwr_driver.run() @@ -89,26 +100,32 @@ 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_z=1.0, gradient_type="components", chargeability_channel_bool=True, z_from_topo=True, @@ -118,11 +135,13 @@ 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 = 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: 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 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(