diff --git a/gempy_engine/API/interp_single/_aux_faults_ops.py b/gempy_engine/API/interp_single/_aux_faults_ops.py index 87fb9278..e33c8300 100644 --- a/gempy_engine/API/interp_single/_aux_faults_ops.py +++ b/gempy_engine/API/interp_single/_aux_faults_ops.py @@ -76,10 +76,13 @@ def _modify_faults_values_output( points=projected_points, normal=gradient_matrix[center_index], ) - finite_fault_scalar = BackendTensor.t.array( - finite_fault_scalar_np, - dtype=shifted_vals.dtype, - ) + if isinstance(shifted_vals, np.ndarray): + finite_fault_scalar = finite_fault_scalar_np.astype(shifted_vals.dtype, copy=False) + else: + finite_fault_scalar = BackendTensor.t.array( + finite_fault_scalar_np, + dtype=shifted_vals.dtype, + ) if include_raw_scalar_fields(): output.finite_fault_scalar = finite_fault_scalar return shifted_vals * finite_fault_scalar diff --git a/gempy_engine/core/backend_tensor.py b/gempy_engine/core/backend_tensor.py index f3f04c69..47e8768a 100644 --- a/gempy_engine/core/backend_tensor.py +++ b/gempy_engine/core/backend_tensor.py @@ -207,8 +207,8 @@ def _sum(tensor, axis=None, dtype=None, keepdims=False): if isinstance(dtype, str): dtype = getattr(torch, dtype) if isinstance(tensor, torch.Tensor): - return _true_torch_sum(tensor, axis, dtype=dtype) - return tensor.sum(axis) + return _true_torch_sum(tensor, axis, dtype=dtype, keepdim=keepdims) + return tensor.sum(axis, keepdims=keepdims) def _repeat(tensor, n_repeats, axis=None): if not isinstance(tensor, torch.Tensor): @@ -228,6 +228,8 @@ def _array(array_like, dtype=None): # Resolve string dtypes safely if isinstance(dtype, str): dtype = getattr(torch, dtype) + elif dtype is not None and not isinstance(dtype, torch.dtype): + dtype = torch.from_numpy(numpy.empty((), dtype=dtype)).dtype # 1. Fast Path: It's already a Tensor if isinstance(array_like, torch.Tensor): diff --git a/gempy_engine/core/data/internal_structs.py b/gempy_engine/core/data/internal_structs.py index f8b29ebc..6d1149a8 100644 --- a/gempy_engine/core/data/internal_structs.py +++ b/gempy_engine/core/data/internal_structs.py @@ -25,7 +25,7 @@ def __init__(self, sp_internal: SurfacePointsInternals, ori_internal: Orientatio self.sp_internal = sp_internal self.ori_internal = ori_internal if xyz_to_interpolate is not None and xyz_to_interpolate.dtype != BackendTensor.dtype_obj: - self.xyz_to_interpolate = xyz_to_interpolate.astype(BackendTensor.dtype) + self.xyz_to_interpolate = BackendTensor.t.array(xyz_to_interpolate, dtype=BackendTensor.dtype_obj) else: self.xyz_to_interpolate = xyz_to_interpolate self._fault_internal = fault_internal diff --git a/gempy_engine/modules/faults/finite_faults.py b/gempy_engine/modules/faults/finite_faults.py index 15bede70..8db9ced8 100644 --- a/gempy_engine/modules/faults/finite_faults.py +++ b/gempy_engine/modules/faults/finite_faults.py @@ -126,6 +126,7 @@ def project_points_onto_surface( """ points = np.asarray(points) scalar_field_values = np.asarray(scalar_field_values) + target_scalar_value = np.asarray(target_scalar_value) gx, gy, gz = gradient_fields grad = np.stack([np.asarray(gx), np.asarray(gy), np.asarray(gz)], axis=-1) diff --git a/tests/conftest.py b/tests/conftest.py index dc42d1b3..a33a4bd2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,10 @@ import enum import os +# Tests are headless by default. Set GEMPY_TEST_PLOTS=True for interactive plotting. +if os.getenv("GEMPY_TEST_PLOTS", "False") != "True": + os.environ.setdefault("MPLBACKEND", "Agg") + # Allow overriding backend via DEFAULT_BACKEND env var (for CI matrix builds) _backend_name = os.getenv('DEFAULT_BACKEND', 'numpy') diff --git a/tests/test_common/test_api/test_faults/test_finite_fault_stack_wiring.py b/tests/test_common/test_api/test_faults/test_finite_fault_stack_wiring.py index 9d5c7967..7a16c7aa 100644 --- a/tests/test_common/test_api/test_faults/test_finite_fault_stack_wiring.py +++ b/tests/test_common/test_api/test_faults/test_finite_fault_stack_wiring.py @@ -149,8 +149,9 @@ def test_finite_fault_is_wired_into_dependent_stack(one_fault_model, monkeypatch ) fault_points = interpolation_input.surface_points.sp_coords[:9] + fault_points_np = np.asarray(BackendTensor.t.to_numpy(fault_points)) finite_fault = FiniteFault( - center=tuple(np.mean(fault_points, axis=0)), + center=tuple(np.mean(fault_points_np, axis=0)), strike_radius=0.75, dip_radius=0.75, ) @@ -189,11 +190,12 @@ def test_finite_fault_flat_stack_matches_serial(one_fault_model, monkeypatch): options.evaluation_options.compute_scalar_gradient = False fault_points = interpolation_input.surface_points.sp_coords[:9] + fault_points_np = np.asarray(BackendTensor.t.to_numpy(fault_points)) data_descriptor.stack_structure.faults_input_data = [ FaultsData.from_user_input( thickness=None, finite_fault=FiniteFault( - center=tuple(np.mean(fault_points, axis=0)), + center=tuple(np.mean(fault_points_np, axis=0)), strike_radius=0.75, dip_radius=0.75, ), diff --git a/tests/test_common/test_modules/test_evaluator/test_micro_anisotropic_macro_integration.py b/tests/test_common/test_modules/test_evaluator/test_micro_anisotropic_macro_integration.py index ceef8c70..982cdc13 100644 --- a/tests/test_common/test_modules/test_evaluator/test_micro_anisotropic_macro_integration.py +++ b/tests/test_common/test_modules/test_evaluator/test_micro_anisotropic_macro_integration.py @@ -1,7 +1,11 @@ +import copy import os import numpy as np +import pytest +from gempy_engine.config import AvailableBackends +from gempy_engine.core.backend_tensor import BackendTensor from gempy_engine.core.data import Orientations from gempy_engine.core.data.internal_structs import SolverInput from gempy_engine.API.interp_single._interp_scalar_field import ( @@ -17,7 +21,7 @@ solve_micro_weights, ) -PLOT = os.getenv("GEMPY_PLOT_MICRO", "1") == "1" +PLOT = os.getenv("GEMPY_PLOT_MICRO", "0") == "1" _MICRO_SURFACE_COLORS = {0: "#00bfff", 1: "#ff6b35"} _MACRO_SURFACE_COLORS = {0: "#0099cc", 1: "#cc5500"} @@ -36,8 +40,12 @@ def _eval_at_points(sp_internal, ori_internal, options, weights, xyz): return _evaluate_sys_eq(eval_in, weights, options) +@pytest.mark.skipif( + BackendTensor.engine_backend is not AvailableBackends.numpy, + reason="Dense NumPy reference implementation", +) def test_micro_correction_moves_contacts_closer_to_target(simple_model_2): - sp, orientations, options, data_descriptor = simple_model_2 + sp, orientations, options, data_descriptor = copy.deepcopy(simple_model_2) orientations.dip_positions = np.array([[ 0., 4.], [ 4., 1.]]) orientations.dip_gradients = np.array([[ -.2, .8], [ 0, 1.]]) options.kernel_options.range = 20 @@ -328,6 +336,10 @@ def _build_grid_3d(x_range, y_range, z_range, nx, ny, nz): return np.column_stack([xv.ravel(), yv.ravel(), zv.ravel()]) +@pytest.mark.skipif( + BackendTensor.engine_backend is not AvailableBackends.numpy, + reason="Dense NumPy reference implementation", +) def test_micro_correction_moves_3d_contacts_closer_to_target(simple_model): """3D analog of the 2D integration test. @@ -339,7 +351,7 @@ def test_micro_correction_moves_3d_contacts_closer_to_target(simple_model): The dense NumPy micro solve here is a reference implementation. Production path for 3D is intended to be PyKeOps matvec + CG, not dense K assembly. """ - sp, orientations, options, data_descriptor = simple_model + sp, orientations, options, data_descriptor = copy.deepcopy(simple_model) options.evaluation_options.compute_scalar_gradient = True diff --git a/tests/test_common/test_modules/test_kernel_constructor/test_kernel_constructor.py b/tests/test_common/test_modules/test_kernel_constructor/test_kernel_constructor.py index f2b0ad66..f1858249 100644 --- a/tests/test_common/test_modules/test_kernel_constructor/test_kernel_constructor.py +++ b/tests/test_common/test_modules/test_kernel_constructor/test_kernel_constructor.py @@ -53,7 +53,7 @@ def test_covariance_cubic_kernel(simple_model_2): sol = BackendTensor.tfnp.sum(cov, axis=1, keepdims=True) - gempy_verify_array(sol, "axis=1") + gempy_verify_array(sol, "axis=1", rtol=1e-4) def test_b_vector(simple_model_2): diff --git a/tests/verify_helper.py b/tests/verify_helper.py index a11bb8a8..c64e1380 100644 --- a/tests/verify_helper.py +++ b/tests/verify_helper.py @@ -8,7 +8,7 @@ def gempy_verify_array(item, name: str, rtol: float = 1e-5, atol: float = 1e-5, ): import os - if os.environ.get('CI'): + if os.environ.get('CI') or os.environ.get('TEAMCITY_VERSION'): from approvaltests.reporters import PythonNativeReporter reporter = PythonNativeReporter() else: