@@ -84,10 +84,13 @@ def create_mesh_by_line_id(
8484 """
8585 drape_models = []
8686 temp_work = Workspace ()
87+
88+ relief = get_max_line_relief (line_ids , drape_options .v_cell_size )
89+
8790 for line_id in np .unique (line_ids .values ):
8891 poles = get_poles_by_line_id (line_ids , line_id )
8992 poles = np .unique (poles , axis = 0 )
90- poles = normalize_vertically (poles , line_ids . parent , drape_options . v_cell_size )
93+ poles = normalize_vertically (poles , relief )
9194
9295 drape_model = get_drape_model (
9396 temp_work ,
@@ -108,31 +111,38 @@ def create_mesh_by_line_id(
108111 return entity
109112
110113
111- def normalize_vertically (
112- poles : np .ndarray , survey : PotentialElectrode , z_cell_size
113- ) -> np .ndarray :
114+ def get_max_line_relief (line_ids , z_cell_size ) -> float :
115+ """
116+ Get the maximum relief across all survey lines, rounded to the nearest cell thickness.
117+
118+ :param line_ids: IntegerData object containing the line IDs for each vertex.
119+ :param z_cell_size: Cell size in the vertical direction for the drape mesh.
120+ """
121+ max_relief = 0
122+ for line_id in np .unique (line_ids .values ):
123+ poles = get_poles_by_line_id (line_ids , line_id )
124+ max_relief = np .maximum (poles [:, 2 ].max () - poles [:, 2 ].min (), max_relief )
125+
126+ return (max_relief // z_cell_size + 2 ) * z_cell_size
127+
128+
129+ def normalize_vertically (poles : np .ndarray , relief : float ) -> np .ndarray :
114130 """
115- Given a set of pole locations, normalize the vertical component to the minimum
116- and maximum elevations of the survey electrodes, rounded to the nearest cell thickness.
131+ Given a set of pole locations, normalize the vertical component to the maximum relief across all lines.
117132
118133 This ensures that the drape mesh has uniform vertical discretization across all survey lines.
119134
120135 :param poles: Array of pole locations to normalize.
121- :param survey: PotentialElectrode object containing the survey electrode locations.
122- :param z_cell_size: Cell size in the vertical direction for rounding the minimum and maximum
136+ :param relief: Maximum relief across all survey lines, rounded to the nearest cell thickness.
123137
124138 :return: Array of pole locations with normalized vertical component.
125139 """
126- min_elev = np .min (np .r_ [survey .vertices [:, 2 ], survey .complement .vertices [:, 2 ]])
127- max_elev = np .max (np .r_ [survey .vertices [:, 2 ], survey .complement .vertices [:, 2 ]])
128-
129- delta = ((max_elev - min_elev ) // z_cell_size + 2 ) * z_cell_size
130140 min_poles_z = poles [:, 2 ].min ()
131141 poles [:, 2 ] -= min_poles_z
132- poles [:, 2 ] *= delta / poles [:, 2 ].max ()
142+ poles [:, 2 ] *= relief / poles [:, 2 ].max ()
133143
134- # Shift back vertically and round to the nearest cell size to align
135- poles [:, 2 ] += ( min_poles_z // z_cell_size - 1 ) * z_cell_size
144+ # Shift back vertically
145+ poles [:, 2 ] += min_poles_z
136146
137147 return poles
138148
@@ -153,177 +163,3 @@ def get_poles_by_line_id(line_ids: IntegerData, uid: int) -> np.ndarray:
153163 ],
154164 ]
155165 )
156-
157-
158- # class BaseBatch2DDriver(LineSweepDriver):
159- # """Base class for batch 2D DC and IP forward and inversion drivers."""
160- #
161- # _params_class: type[BaseForwardOptions | BaseInversionOptions]
162- # _params_2d_class: type[BaseForwardOptions | BaseInversionOptions]
163- #
164- # _model_list: list[str] = []
165- #
166- # def __init__(self, params):
167- # super().__init__(params)
168- # if params.file_control.files_only:
169- # sys.exit("Files written")
170- #
171- # def transfer_models(self, mesh: DrapeModel) -> dict[str, uuid.UUID | float]:
172- # """
173- # Transfer models from the input parameters to the output drape mesh.
174- #
175- # :param mesh: Destination DrapeModel object.
176- # """
177- # models = {"starting_model": self.batch2d_params.models.starting_model}
178- #
179- # for model in self._model_list:
180- # models[model] = getattr(self.batch2d_params, model, None)
181- #
182- # if not self.batch2d_params.forward_only:
183- # for model in ["reference_model", "lower_bound", "upper_bound"]:
184- # models[model] = getattr(self.batch2d_params.models, model)
185- #
186- # if self.batch2d_params.models.gradient_rotation is not None:
187- # group_properties = {}
188- # for prop in self.batch2d_params.models.gradient_rotation.properties:
189- # model = self.batch2d_params.mesh.get_data(prop)[0]
190- # group_properties[model.name] = model
191- #
192- # models.update(group_properties)
193- #
194- # if self.batch2d_params.mesh is not None:
195- # xyz_in = get_locations(self.workspace, self.batch2d_params.mesh)
196- # xyz_out = mesh.centroids
197- #
198- # for name, model in models.items():
199- # if model is None:
200- # continue
201- # elif isinstance(model, Data):
202- # model_values = weighted_average(
203- # xyz_in, xyz_out, [model.values], n=1
204- # )[0]
205- # else:
206- # model_values = model * np.ones(len(xyz_out))
207- #
208- # model_object = mesh.add_data({name: {"values": model_values}})
209- # models[name] = model_object
210- #
211- # if (
212- # not self.batch2d_params.forward_only
213- # and self.batch2d_params.models.gradient_rotation is not None
214- # ):
215- # pg = PropertyGroup(
216- # mesh,
217- # properties=[models[prop] for prop in group_properties],
218- # property_group_type=self.batch2d_params.models.gradient_rotation.property_group_type,
219- # )
220- # models["gradient_rotation"] = pg
221- # del models["azimuth"]
222- # del models["dip"]
223- #
224- # return models
225- #
226- # def write_files(self, lookup):
227- # """Write ui.geoh5 and ui.json files for sweep trials."""
228- #
229- # kwargs_2d = {}
230- # with fetch_active_workspace(self.workspace, mode="r+"):
231- # for uid, trial in lookup.items():
232- # if trial["status"] != "pending":
233- # continue
234- #
235- # filepath = Path(self.working_directory) / f"{uid}.ui.geoh5"
236- #
237- # if filepath.exists():
238- # warnings.warn(
239- # f"File {filepath} already exists but 'status' marked as 'pending'. "
240- # "Over-writing file."
241- # )
242- # filepath.unlink()
243- #
244- # with Workspace.create(filepath) as iter_workspace:
245- # cell_mask: np.ndarray = (
246- # self.batch2d_params.line_selection.line_object.values
247- # == trial["line_id"]
248- # )
249- #
250- # if not np.any(cell_mask):
251- # continue
252- #
253- # receiver_entity = extract_dcip_survey(
254- # iter_workspace, self.inversion_data.entity, cell_mask
255- # )
256- # current_entity = receiver_entity.current_electrodes
257- # receiver_locs = np.vstack(
258- # [receiver_entity.vertices, current_entity.vertices]
259- # )
260- #
261- # mesh = get_drape_model(
262- # iter_workspace,
263- # "Models",
264- # receiver_locs,
265- # [
266- # self.batch2d_params.drape_model.u_cell_size,
267- # self.batch2d_params.drape_model.v_cell_size,
268- # ],
269- # self.batch2d_params.drape_model.depth_core,
270- # [self.batch2d_params.drape_model.horizontal_padding] * 2
271- # + [self.batch2d_params.drape_model.vertical_padding, 1],
272- # self.batch2d_params.drape_model.expansion_factor,
273- # )[0]
274- #
275- # model_parameters = self.transfer_models(mesh)
276- #
277- # for key in self._params_2d_class.model_fields:
278- # param = getattr(self.batch2d_params, key, None)
279- # if key not in ["title", "inversion_type"]:
280- # kwargs_2d[key] = param
281- #
282- # self.batch2d_params.active_cells.topography_object.copy(
283- # parent=iter_workspace, copy_children=True
284- # )
285- #
286- # kwargs_2d.update(
287- # dict(
288- # **{
289- # "geoh5": iter_workspace,
290- # "mesh": mesh,
291- # "data_object": receiver_entity,
292- # "line_selection": LineSelectionOptions(
293- # line_object=receiver_entity.get_data(
294- # self.batch2d_params.line_selection.line_object.name
295- # )[0],
296- # line_id=trial["line_id"],
297- # ),
298- # "out_group": None,
299- # },
300- # **model_parameters,
301- # )
302- # )
303- #
304- # params = self._params_2d_class(**kwargs_2d)
305- # params.write_ui_json(Path(self.working_directory) / f"{uid}.ui.json")
306- #
307- # lookup[uid]["status"] = "written"
308- #
309- # _ = self.update_lookup(lookup) # pylint: disable=no-member
310- #
311- # @property
312- # def inversion_data(self) -> InversionData:
313- # """Inversion data"""
314- # if getattr(self, "_inversion_data", None) is None:
315- # with fetch_active_workspace(self.workspace, mode="r+"):
316- # self._inversion_data = InversionData(
317- # self.workspace, self.batch2d_params
318- # )
319- #
320- # return self._inversion_data
321- #
322- # @property
323- # def inversion_topography(self):
324- # """Inversion topography"""
325- # if getattr(self, "_inversion_topography", None) is None:
326- # self._inversion_topography = InversionTopography(
327- # self.workspace, self.batch2d_params
328- # )
329- # return self._inversion_topography
0 commit comments