From e437601e2d12de528b5834197371e0c0f2ad821e Mon Sep 17 00:00:00 2001 From: Ihar Date: Fri, 25 Sep 2026 17:00:49 +0200 Subject: [PATCH 1/3] save util improve --- .../add_custom_sensors_to_python.cpp | 6 ++-- .../custom_sensors/displacement_sensor.cpp | 34 +++++++++++++++---- .../custom_sensors/displacement_sensor.h | 13 +++++++ .../custom_sensors/strain_sensor.cpp | 24 ++++++++++++- .../custom_sensors/strain_sensor.h | 13 +++++++ .../sensor_generator_analysis.py | 8 ++--- .../python_scripts/utilities/sensor_utils.py | 25 +++++++++++++- 7 files changed, 109 insertions(+), 14 deletions(-) diff --git a/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp b/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp index 3035f5373183..24c24525d31f 100644 --- a/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp +++ b/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp @@ -71,7 +71,8 @@ void AddCustomSensorsToPython(pybind11::module& m) py::arg("weight"), py::arg("error_threshold") = Sensor::DefaultErrorThreshold) .def_static("GetDefaultParameters", &DisplacementSensor::GetDefaultParameters) - .def_static("Create", &DisplacementSensor::Create, py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters")) + .def_static("Create", py::overload_cast(&DisplacementSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters")) + .def_static("Create", py::overload_cast(&DisplacementSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters"), py::arg("element_id")) ; auto strain_sensor = py::class_(sensor_module, "StrainSensor"); @@ -93,7 +94,8 @@ void AddCustomSensorsToPython(pybind11::module& m) py::arg("weight"), py::arg("error_threshold") = Sensor::DefaultErrorThreshold) .def_static("GetDefaultParameters", &StrainSensor::GetDefaultParameters) - .def_static("Create", &StrainSensor::Create, py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters")) + .def_static("Create", py::overload_cast(&StrainSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters")) + .def_static("Create", py::overload_cast(&StrainSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters"), py::arg("element_id")) ; } diff --git a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp index 9de915670db2..d89f2b0a70ef 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp +++ b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp @@ -110,11 +110,6 @@ Sensor::Pointer DisplacementSensor::Create( SensorParameters.ValidateAndAssignDefaults(DisplacementSensor::GetDefaultParameters()); - const auto& direction = SensorParameters["direction"].GetVector(); - KRATOS_ERROR_IF_NOT(direction.size() == 3) - << "Direction of the sensor \"" << SensorParameters["name"].GetString() - << "\" should have 3 components. [ direction = " << direction << " ].\n"; - const auto& location = SensorParameters["location"].GetVector(); KRATOS_ERROR_IF_NOT(location.size() == 3) << "Location of the sensor \"" << SensorParameters["name"].GetString() @@ -125,7 +120,34 @@ Sensor::Pointer DisplacementSensor::Create( Vector dummy_shape_functions; const auto element_id = BruteForcePointLocator(rDomainModelPart).FindElement(loc, dummy_shape_functions); - const auto& r_element = rDomainModelPart.GetElement(element_id); + + return DisplacementSensor::Create(rDomainModelPart, rSensorModelPart, Id, SensorParameters, element_id); + + KRATOS_CATCH(""); +} + +Sensor::Pointer DisplacementSensor::Create( + ModelPart& rDomainModelPart, + ModelPart& rSensorModelPart, + const IndexType Id, + Parameters SensorParameters, + const IndexType ElementId) +{ + KRATOS_TRY + + SensorParameters.ValidateAndAssignDefaults(DisplacementSensor::GetDefaultParameters()); + + const auto& direction = SensorParameters["direction"].GetVector(); + KRATOS_ERROR_IF_NOT(direction.size() == 3) + << "Direction of the sensor \"" << SensorParameters["name"].GetString() + << "\" should have 3 components. [ direction = " << direction << " ].\n"; + + const auto& location = SensorParameters["location"].GetVector(); + KRATOS_ERROR_IF_NOT(location.size() == 3) + << "Location of the sensor \"" << SensorParameters["name"].GetString() + << "\" should have 3 components. [ location = " << location << " ].\n"; + + const auto& r_element = rDomainModelPart.GetElement(ElementId); auto p_node = rSensorModelPart.CreateNewNode(Id, location[0], location[1], location[2]); diff --git a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h index 1e7d84e18410..ec0844ec91e8 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h +++ b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h @@ -70,6 +70,19 @@ class KRATOS_API(SYSTEM_IDENTIFICATION_APPLICATION) DisplacementSensor : public const IndexType Id, Parameters SensorParameters); + /** + * @brief Creates the sensor in an already located element. + * @details Same as the overload without ElementId, but skips the + * point location. The element with ElementId in rDomainModelPart + * must contain the sensor location. + */ + static Sensor::Pointer Create( + ModelPart& rDomainModelPart, + ModelPart& rSensorModelPart, + const IndexType Id, + Parameters SensorParameters, + const IndexType ElementId); + static Parameters GetDefaultParameters(); ///@} diff --git a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp index 2a802a13fcbb..b6a0f849fce7 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp +++ b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp @@ -91,7 +91,29 @@ Sensor::Pointer StrainSensor::Create( Vector dummy_shape_functions; const auto element_id = BruteForcePointLocator(rDomainModelPart).FindElement(loc, dummy_shape_functions); - const auto& r_element = rDomainModelPart.GetElement(element_id); + + return StrainSensor::Create(rDomainModelPart, rSensorModelPart, Id, SensorParameters, element_id); + + KRATOS_CATCH(""); +} + +Sensor::Pointer StrainSensor::Create( + ModelPart& rDomainModelPart, + ModelPart& rSensorModelPart, + const IndexType Id, + Parameters SensorParameters, + const IndexType ElementId) +{ + KRATOS_TRY + + SensorParameters.ValidateAndAssignDefaults(StrainSensor::GetDefaultParameters()); + + const auto& location = SensorParameters["location"].GetVector(); + KRATOS_ERROR_IF_NOT(location.size() == 3) + << "Location of the sensor \"" << SensorParameters["name"].GetString() + << "\" should have 3 components. [ location = " << location << " ].\n"; + + const auto& r_element = rDomainModelPart.GetElement(ElementId); auto p_node = rSensorModelPart.CreateNewNode(Id, location[0], location[1], location[2]); diff --git a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h index 8b310cc80d52..d69aed3b1c87 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h +++ b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h @@ -84,6 +84,19 @@ class KRATOS_API(SYSTEM_IDENTIFICATION_APPLICATION) StrainSensor : public Sensor const IndexType Id, Parameters SensorParameters); + /** + * @brief Creates the sensor in an already located element. + * @details Same as the overload without ElementId, but skips the + * point location. The element with ElementId in rDomainModelPart + * must contain the sensor location. + */ + static Sensor::Pointer Create( + ModelPart& rDomainModelPart, + ModelPart& rSensorModelPart, + const IndexType Id, + Parameters SensorParameters, + const IndexType ElementId); + static Parameters GetDefaultParameters(); ///@} diff --git a/applications/SystemIdentificationApplication/python_scripts/sensor_generator_analysis.py b/applications/SystemIdentificationApplication/python_scripts/sensor_generator_analysis.py index addff98d8224..4847102dfeb2 100644 --- a/applications/SystemIdentificationApplication/python_scripts/sensor_generator_analysis.py +++ b/applications/SystemIdentificationApplication/python_scripts/sensor_generator_analysis.py @@ -63,7 +63,8 @@ def __GenerationBoundingSurfaceBased(self, sensors_dict, sensor_group_params: Kr }""") sensor_group_params.ValidateAndAssignDefaults(defaults) - point_locator = Kratos.BruteForcePointLocator(self.model_part) + # spatial search built once for all generated points (mesh is in its initial configuration here) + bins = Kratos.GeometricalObjectsBins(self.model_part.Elements, 1e-8) corner_1 = sensor_group_params["bounding_surface_corner_1"].GetVector() corner_2 = sensor_group_params["bounding_surface_corner_2"].GetVector() @@ -83,9 +84,8 @@ def __GenerationBoundingSurfaceBased(self, sensors_dict, sensor_group_params: Kr for i_z in range(number_of_sensors[2]): z_coord = corner_1[2] + distance[2] * (i_z + 1) / (number_of_sensors[2] + 1) loc = Kratos.Point(x_coord, y_coord, z_coord) - shape_funcs = Kratos.Vector() - elem_id = point_locator.FindElement(loc, shape_funcs, Kratos.Configuration.Initial, 1e-8) - if elem_id != -1 and KratosSI.SensorUtils.IsPointInGeometry(loc, self.model_part.GetElement(elem_id).GetGeometry()): + result = bins.SearchIsInside(loc) + if result.IsObjectFound() and KratosSI.SensorUtils.IsPointInGeometry(loc, self.model_part.GetElement(result.Get().Id).GetGeometry()): current_params = sensor_params.Clone() current_params["location"].SetVector(loc) json_dict = json.loads(current_params.WriteJsonString().replace("", str(index))) diff --git a/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py b/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py index 8f998e332ba1..2b8b070dd83a 100644 --- a/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py +++ b/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py @@ -38,6 +38,12 @@ def CreateSensors(sensor_model_part: Kratos.ModelPart, domain_model_part: Kratos except: pass + # The sensor elements are located with one spatial search over the domain elements + # instead of a brute force search per sensor. Bins only see the local elements, hence + # distributed domains keep using the brute force search within each sensor's Create. + use_bins = not domain_model_part.IsDistributed() and domain_model_part.NumberOfElements() > 0 + bins: 'typing.Optional[Kratos.GeometricalObjectsBins]' = None + list_of_sensors: 'list[KratosSI.Sensors.Sensor]' = [] for parameters in list_of_parameters: if not parameters.Has("type"): @@ -47,7 +53,24 @@ def CreateSensors(sensor_model_part: Kratos.ModelPart, domain_model_part: Kratos if not sensor_type_name in dict_of_sensor_types.keys(): raise RuntimeError(f"Unsupported sensor type = \"{sensor_type_name}\" requested. Followings are supported:\n\t" + "\n\t".join(dict_of_sensor_types.keys())) - sensor: KratosSI.Sensors.Sensor = dict_of_sensor_types[sensor_type_name].Create(domain_model_part, sensor_model_part, len(list_of_sensors) + 1, parameters) + sensor_type = dict_of_sensor_types[sensor_type_name] + sensor_id = len(list_of_sensors) + 1 + + element_id = None + if use_bins and parameters.Has("location") and parameters["location"].IsVector() and parameters["location"].GetVector().Size() == 3: + if bins is None: + # same local coordinate tolerance as the brute force search + bins = Kratos.GeometricalObjectsBins(domain_model_part.Elements, 1e-6) + location = parameters["location"].GetVector() + result = bins.SearchIsInside(Kratos.Point(location[0], location[1], location[2])) + if result.IsObjectFound(): + element_id = result.Get().Id + + if element_id is None: + # not located (or not locatable) by the bins, use the brute force search + sensor: KratosSI.Sensors.Sensor = sensor_type.Create(domain_model_part, sensor_model_part, sensor_id, parameters) + else: + sensor: KratosSI.Sensors.Sensor = sensor_type.Create(domain_model_part, sensor_model_part, sensor_id, parameters, element_id) list_of_sensors.append(sensor) return list_of_sensors From 8ff9185a7cff700fae79b2f885788e358fa6f4a3 Mon Sep 17 00:00:00 2001 From: Ihar Date: Mon, 28 Sep 2026 14:07:15 +0200 Subject: [PATCH 2/3] use Geo Bins instead of const elem id --- .../add_custom_sensors_to_python.cpp | 6 +-- .../custom_sensors/displacement_sensor.cpp | 37 ++++--------------- .../custom_sensors/displacement_sensor.h | 16 +++----- .../custom_sensors/strain_sensor.cpp | 35 ++++-------------- .../custom_sensors/strain_sensor.h | 16 +++----- .../python_scripts/utilities/sensor_utils.py | 33 +++++------------ 6 files changed, 37 insertions(+), 106 deletions(-) diff --git a/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp b/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp index 24c24525d31f..4105cfb54128 100644 --- a/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp +++ b/applications/SystemIdentificationApplication/custom_python/add_custom_sensors_to_python.cpp @@ -71,8 +71,7 @@ void AddCustomSensorsToPython(pybind11::module& m) py::arg("weight"), py::arg("error_threshold") = Sensor::DefaultErrorThreshold) .def_static("GetDefaultParameters", &DisplacementSensor::GetDefaultParameters) - .def_static("Create", py::overload_cast(&DisplacementSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters")) - .def_static("Create", py::overload_cast(&DisplacementSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters"), py::arg("element_id")) + .def_static("Create", &DisplacementSensor::Create, py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters"), py::arg("domain_bins")) ; auto strain_sensor = py::class_(sensor_module, "StrainSensor"); @@ -94,8 +93,7 @@ void AddCustomSensorsToPython(pybind11::module& m) py::arg("weight"), py::arg("error_threshold") = Sensor::DefaultErrorThreshold) .def_static("GetDefaultParameters", &StrainSensor::GetDefaultParameters) - .def_static("Create", py::overload_cast(&StrainSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters")) - .def_static("Create", py::overload_cast(&StrainSensor::Create), py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters"), py::arg("element_id")) + .def_static("Create", &StrainSensor::Create, py::arg("domain_model_part"), py::arg("sensor_model_part"), py::arg("sensor_id"), py::arg("sensor_parameters"), py::arg("domain_bins")) ; } diff --git a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp index d89f2b0a70ef..3e14d4908380 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp +++ b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp @@ -16,7 +16,6 @@ // External includes // Project includes -#include "utilities/brute_force_point_locator.h" // Application includes #include "custom_utilities/sensor_utils.h" @@ -100,38 +99,12 @@ DisplacementSensor::DisplacementSensor( this->GetNode()->SetValue(SENSOR_ELEMENT_ID, static_cast(mElementId)); } -Sensor::Pointer DisplacementSensor::Create( - ModelPart& rDomainModelPart, - ModelPart& rSensorModelPart, - const IndexType Id, - Parameters SensorParameters) -{ - KRATOS_TRY - - SensorParameters.ValidateAndAssignDefaults(DisplacementSensor::GetDefaultParameters()); - - const auto& location = SensorParameters["location"].GetVector(); - KRATOS_ERROR_IF_NOT(location.size() == 3) - << "Location of the sensor \"" << SensorParameters["name"].GetString() - << "\" should have 3 components. [ location = " << location << " ].\n"; - - Point loc(location[0], location[1], location[2]); - - Vector dummy_shape_functions; - - const auto element_id = BruteForcePointLocator(rDomainModelPart).FindElement(loc, dummy_shape_functions); - - return DisplacementSensor::Create(rDomainModelPart, rSensorModelPart, Id, SensorParameters, element_id); - - KRATOS_CATCH(""); -} - Sensor::Pointer DisplacementSensor::Create( ModelPart& rDomainModelPart, ModelPart& rSensorModelPart, const IndexType Id, Parameters SensorParameters, - const IndexType ElementId) + GeometricalObjectsBins& rDomainBins) { KRATOS_TRY @@ -147,7 +120,13 @@ Sensor::Pointer DisplacementSensor::Create( << "Location of the sensor \"" << SensorParameters["name"].GetString() << "\" should have 3 components. [ location = " << location << " ].\n"; - const auto& r_element = rDomainModelPart.GetElement(ElementId); + const auto search_result = rDomainBins.SearchIsInside(Point(location[0], location[1], location[2])); + KRATOS_ERROR_IF_NOT(search_result.GetIsObjectFound()) + << "Location of the sensor \"" << SensorParameters["name"].GetString() + << "\" is not inside any element of " << rDomainModelPart.FullName() + << ". [ location = " << location << " ].\n"; + + const auto& r_element = rDomainModelPart.GetElement(search_result.Get()->Id()); auto p_node = rSensorModelPart.CreateNewNode(Id, location[0], location[1], location[2]); diff --git a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h index ec0844ec91e8..efa29c58f395 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h +++ b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.h @@ -19,6 +19,7 @@ // Project includes #include "includes/ublas_interface.h" #include "includes/element.h" +#include "spatial_containers/geometrical_objects_bins.h" // Application includes #include "sensor.h" @@ -64,24 +65,17 @@ class KRATOS_API(SYSTEM_IDENTIFICATION_APPLICATION) DisplacementSensor : public ///@name Static operations ///@{ - static Sensor::Pointer Create( - ModelPart& rDomainModelPart, - ModelPart& rSensorModelPart, - const IndexType Id, - Parameters SensorParameters); - /** - * @brief Creates the sensor in an already located element. - * @details Same as the overload without ElementId, but skips the - * point location. The element with ElementId in rDomainModelPart - * must contain the sensor location. + * @brief Creates the sensor at the location given in SensorParameters. + * @details rDomainBins must be built from the elements of rDomainModelPart. It is + * shared by all sensors, so each sensor is located without a linear search. */ static Sensor::Pointer Create( ModelPart& rDomainModelPart, ModelPart& rSensorModelPart, const IndexType Id, Parameters SensorParameters, - const IndexType ElementId); + GeometricalObjectsBins& rDomainBins); static Parameters GetDefaultParameters(); diff --git a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp index b6a0f849fce7..9209abf41f19 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp +++ b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp @@ -16,7 +16,6 @@ // Project includes #include "includes/kratos_components.h" -#include "utilities/brute_force_point_locator.h" // Application includes #include "custom_utilities/sensor_utils.h" @@ -75,7 +74,8 @@ Sensor::Pointer StrainSensor::Create( ModelPart& rDomainModelPart, ModelPart& rSensorModelPart, const IndexType Id, - Parameters SensorParameters) + Parameters SensorParameters, + GeometricalObjectsBins& rDomainBins) { KRATOS_TRY @@ -86,34 +86,13 @@ Sensor::Pointer StrainSensor::Create( << "Location of the sensor \"" << SensorParameters["name"].GetString() << "\" should have 3 components. [ location = " << location << " ].\n"; - Point loc(location[0], location[1], location[2]); - - Vector dummy_shape_functions; - - const auto element_id = BruteForcePointLocator(rDomainModelPart).FindElement(loc, dummy_shape_functions); - - return StrainSensor::Create(rDomainModelPart, rSensorModelPart, Id, SensorParameters, element_id); - - KRATOS_CATCH(""); -} - -Sensor::Pointer StrainSensor::Create( - ModelPart& rDomainModelPart, - ModelPart& rSensorModelPart, - const IndexType Id, - Parameters SensorParameters, - const IndexType ElementId) -{ - KRATOS_TRY - - SensorParameters.ValidateAndAssignDefaults(StrainSensor::GetDefaultParameters()); - - const auto& location = SensorParameters["location"].GetVector(); - KRATOS_ERROR_IF_NOT(location.size() == 3) + const auto search_result = rDomainBins.SearchIsInside(Point(location[0], location[1], location[2])); + KRATOS_ERROR_IF_NOT(search_result.GetIsObjectFound()) << "Location of the sensor \"" << SensorParameters["name"].GetString() - << "\" should have 3 components. [ location = " << location << " ].\n"; + << "\" is not inside any element of " << rDomainModelPart.FullName() + << ". [ location = " << location << " ].\n"; - const auto& r_element = rDomainModelPart.GetElement(ElementId); + const auto& r_element = rDomainModelPart.GetElement(search_result.Get()->Id()); auto p_node = rSensorModelPart.CreateNewNode(Id, location[0], location[1], location[2]); diff --git a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h index d69aed3b1c87..4f7255e518c1 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h +++ b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.h @@ -18,6 +18,7 @@ // Project includes #include "includes/element.h" +#include "spatial_containers/geometrical_objects_bins.h" // Application includes #include "sensor.h" @@ -78,24 +79,17 @@ class KRATOS_API(SYSTEM_IDENTIFICATION_APPLICATION) StrainSensor : public Sensor ///@name Static operations ///@{ - static Sensor::Pointer Create( - ModelPart& rDomainModelPart, - ModelPart& rSensorModelPart, - const IndexType Id, - Parameters SensorParameters); - /** - * @brief Creates the sensor in an already located element. - * @details Same as the overload without ElementId, but skips the - * point location. The element with ElementId in rDomainModelPart - * must contain the sensor location. + * @brief Creates the sensor at the location given in SensorParameters. + * @details rDomainBins must be built from the elements of rDomainModelPart. It is + * shared by all sensors, so each sensor is located without a linear search. */ static Sensor::Pointer Create( ModelPart& rDomainModelPart, ModelPart& rSensorModelPart, const IndexType Id, Parameters SensorParameters, - const IndexType ElementId); + GeometricalObjectsBins& rDomainBins); static Parameters GetDefaultParameters(); diff --git a/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py b/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py index 2b8b070dd83a..07084a44cf67 100644 --- a/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py +++ b/applications/SystemIdentificationApplication/python_scripts/utilities/sensor_utils.py @@ -38,11 +38,9 @@ def CreateSensors(sensor_model_part: Kratos.ModelPart, domain_model_part: Kratos except: pass - # The sensor elements are located with one spatial search over the domain elements - # instead of a brute force search per sensor. Bins only see the local elements, hence - # distributed domains keep using the brute force search within each sensor's Create. - use_bins = not domain_model_part.IsDistributed() and domain_model_part.NumberOfElements() > 0 - bins: 'typing.Optional[Kratos.GeometricalObjectsBins]' = None + # One spatial search over the domain elements, shared by all sensors, so each sensor's Create + # locates its element(s) without a linear search. Built lazily since empty bins are not searchable. + domain_bins: 'typing.Optional[Kratos.GeometricalObjectsBins]' = None list_of_sensors: 'list[KratosSI.Sensors.Sensor]' = [] for parameters in list_of_parameters: @@ -53,24 +51,13 @@ def CreateSensors(sensor_model_part: Kratos.ModelPart, domain_model_part: Kratos if not sensor_type_name in dict_of_sensor_types.keys(): raise RuntimeError(f"Unsupported sensor type = \"{sensor_type_name}\" requested. Followings are supported:\n\t" + "\n\t".join(dict_of_sensor_types.keys())) - sensor_type = dict_of_sensor_types[sensor_type_name] - sensor_id = len(list_of_sensors) + 1 - - element_id = None - if use_bins and parameters.Has("location") and parameters["location"].IsVector() and parameters["location"].GetVector().Size() == 3: - if bins is None: - # same local coordinate tolerance as the brute force search - bins = Kratos.GeometricalObjectsBins(domain_model_part.Elements, 1e-6) - location = parameters["location"].GetVector() - result = bins.SearchIsInside(Kratos.Point(location[0], location[1], location[2])) - if result.IsObjectFound(): - element_id = result.Get().Id - - if element_id is None: - # not located (or not locatable) by the bins, use the brute force search - sensor: KratosSI.Sensors.Sensor = sensor_type.Create(domain_model_part, sensor_model_part, sensor_id, parameters) - else: - sensor: KratosSI.Sensors.Sensor = sensor_type.Create(domain_model_part, sensor_model_part, sensor_id, parameters, element_id) + if domain_bins is None: + if domain_model_part.NumberOfElements() == 0: + raise RuntimeError(f"The domain model part \"{domain_model_part.FullName()}\" has no elements to locate the sensors in.") + # same local coordinate tolerance as the former brute force point locator + domain_bins = Kratos.GeometricalObjectsBins(domain_model_part.Elements, 1e-6) + + sensor: KratosSI.Sensors.Sensor = dict_of_sensor_types[sensor_type_name].Create(domain_model_part, sensor_model_part, len(list_of_sensors) + 1, parameters, domain_bins) list_of_sensors.append(sensor) return list_of_sensors From b7f2f645420054badf3ee52866f367ed0138f708 Mon Sep 17 00:00:00 2001 From: Ihar Date: Wed, 30 Sep 2026 07:37:13 +0200 Subject: [PATCH 3/3] fix miss tolerances between check and search --- .../custom_sensors/displacement_sensor.cpp | 4 +++- .../custom_sensors/strain_sensor.cpp | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp index 3e14d4908380..ee42653f0279 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp +++ b/applications/SystemIdentificationApplication/custom_sensors/displacement_sensor.cpp @@ -47,8 +47,10 @@ DisplacementSensor::DisplacementSensor( const auto& r_geometry = rElement.GetGeometry(); const auto& current_sensor_location = *(this->GetNode()); + // same local coordinate tolerance as the domain bins used to find rElement, otherwise a + // point lying on a face shared by two elements may be rejected by the element the bins found. Point local_point; - if (r_geometry.IsInside(current_sensor_location, local_point)) { + if (r_geometry.IsInside(current_sensor_location, local_point, 1e-6)) { // point is within the geometry. Use shape function evaluations // from the element geometry to get the shape function values. r_geometry.ShapeFunctionsValues(mNs, local_point); diff --git a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp index 9209abf41f19..949a43e60212 100644 --- a/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp +++ b/applications/SystemIdentificationApplication/custom_sensors/strain_sensor.cpp @@ -40,7 +40,8 @@ StrainSensor::StrainSensor( mStrainType(rStrainType), mrStrainVariable(rStrainVariable) { - KRATOS_ERROR_IF_NOT(rElement.GetGeometry().IsInside(*(this->GetNode()), mLocalPoint)) + // same local coordinate tolerance as the domain bins used to find rElement + KRATOS_ERROR_IF_NOT(rElement.GetGeometry().IsInside(*(this->GetNode()), mLocalPoint, 1e-6)) << "The point " << this->GetNode()->Coordinates() << " is not inside or on the boundary of the geometry of element with id " << mElementId << ".";