diff --git a/kratos/python/add_tensor_adaptors_to_python.cpp b/kratos/python/add_tensor_adaptors_to_python.cpp index a7f10537a5a1..36a875eedb83 100644 --- a/kratos/python/add_tensor_adaptors_to_python.cpp +++ b/kratos/python/add_tensor_adaptors_to_python.cpp @@ -37,6 +37,7 @@ #include "tensor_adaptors/geometries_tensor_adaptor.h" #include "tensor_adaptors/tensor_adaptor.h" #include "tensor_adaptors/variable_tensor_adaptor.h" +#include "tensor_adaptors/geometry_metrics_tensor_adaptor.h" // Include base h #include "add_tensor_adaptors_to_python.h" @@ -303,6 +304,27 @@ void AddTensorAdaptorsToPython(pybind11::module& m) .def(py::init(), py::arg("tensor_adaptor"), py::arg("copy") = true); + + py::class_ geometric_tensor_adaptor(tensor_adaptor_sub_module, "GeometryMetricsTensorAdaptor"); + + py::enum_(geometric_tensor_adaptor,"Metric") + .value("DomainSize", GeometryMetricsTensorAdaptor::Metric::DomainSize) + .export_values(); + + geometric_tensor_adaptor + .def(py::init(), + py::arg("tensor_adaptor"), + py::arg("datum"), + py::arg("copy") = true) + .def(py::init(), + py::arg("container"), + py::arg("datum")) + .def(py::init(), + py::arg("container"), + py::arg("datum")) + .def(py::init(), + py::arg("container"), + py::arg("datum")); } } // namespace Kratos::Python. \ No newline at end of file diff --git a/kratos/tensor_adaptors/geometry_metrics_tensor_adaptor.cpp b/kratos/tensor_adaptors/geometry_metrics_tensor_adaptor.cpp new file mode 100644 index 000000000000..b623bded8312 --- /dev/null +++ b/kratos/tensor_adaptors/geometry_metrics_tensor_adaptor.cpp @@ -0,0 +1,158 @@ +// | / | +// ' / __| _` | __| _ \ __| +// . \ | ( | | ( |\__ ` +// _|\_\_| \__,_|\__|\___/ ____/ +// Multi-Physics +// +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Suneth Warnakulasuriya +// + +// System includes +#include + +// External includes + +// Project includes +#include "utilities/atomic_utilities.h" +#include "utilities/data_type_traits.h" + +// Include base h +#include "geometry_metrics_tensor_adaptor.h" + +namespace Kratos { + +namespace GeometryMetricsTensorAdaptorHelperUtils { + +DenseVector GetShape( + const unsigned int NumberOfEntities, + const GeometryMetricsTensorAdaptor::Metric CurrentMetric) +{ + switch (CurrentMetric) { + case GeometryMetricsTensorAdaptor::DomainSize: + return DenseVector(1, NumberOfEntities); + } + + return DenseVector(0); +} + +template +const ModelPart::GeometryType& GetGeometry(const TEntityType& rEntity) +{ + if constexpr(std::is_same_v) { + return rEntity; + } else { + return rEntity.GetGeometry(); + } +} + +template +void FillDomainSize( + Kratos::span DataSpan, + const TContainerType& rContainer) +{ + IndexPartition(rContainer.size()).for_each([&rContainer, &DataSpan](const auto Index) { + DataSpan[Index] = GetGeometry(*(rContainer.begin() + Index)).DomainSize(); + }); +} + +template +void FillData( + Kratos::span DataSpan, + const GeometryMetricsTensorAdaptor::Metric CurrentMetric, + const TContainerType& rContainer) +{ + switch (CurrentMetric) { + case GeometryMetricsTensorAdaptor::DomainSize: + FillDomainSize(DataSpan, rContainer); + break; + } +} + +} // namespace GeometryMetricsTensorAdaptorHelperUtils + +template +GeometryMetricsTensorAdaptor::GeometryMetricsTensorAdaptor( + TContainerPointerType pContainer, + const Metric CurrentMetric) + : mMetric(CurrentMetric) +{ + this->mpContainer = pContainer; + this->mpStorage = Kratos::make_shared(DenseVector(1, pContainer->size())); +} + +GeometryMetricsTensorAdaptor::GeometryMetricsTensorAdaptor( + const TensorAdaptor& rOther, + const Metric CurrentMetric, + const bool Copy) + : BaseType(rOther, Copy), + mMetric(CurrentMetric) +{ + KRATOS_TRY + + if (!HoldsAlternative::Evaluate(this->GetContainer())) { + KRATOS_ERROR << "GeometryMetricsTensorAdaptor can only be used with tensor data " + "having geometry, condition or element containers."; + } + + const auto& current_shape = this->Shape(); + + KRATOS_ERROR_IF(current_shape.size() < 1) << "Tensor data's first dimension should represent number of entities [ tensor adaptor = " << *this << " ].\n"; + + const auto& required_shape = GeometryMetricsTensorAdaptorHelperUtils::GetShape(current_shape[0], CurrentMetric); + + for (IndexType i = 0; i < required_shape.size(); ++i) { + KRATOS_ERROR_IF_NOT(current_shape[i] == required_shape[i]) + << "Incompatible tensor data shape [ required tensor data shape = " << required_shape + << ", current tensor data shape = " << current_shape << ", tensor adaptor = " << *this << " ].\n"; + } + + KRATOS_CATCH(""); +} + +TensorAdaptor::Pointer GeometryMetricsTensorAdaptor::Clone() const +{ + return Kratos::make_shared(*this); +} + +void GeometryMetricsTensorAdaptor::CollectData() +{ + KRATOS_TRY + + std::visit([this](auto pContainer) { + using container_type = BareType; + if constexpr(IsInList) { + GeometryMetricsTensorAdaptorHelperUtils::FillData(this->ViewData(), this->mMetric, *pContainer); + } + + }, this->GetContainer()); + + KRATOS_CATCH(""); +} + +void GeometryMetricsTensorAdaptor::StoreData() +{ + KRATOS_ERROR << "StoreData method is not implemented."; +} + +std::string GeometryMetricsTensorAdaptor::Info() const +{ + std::stringstream info; + + info << "GeometryMetricsTensorAdaptor:"; + info << " Metric = "; + switch (mMetric) { + case DomainSize: info << "DomainSize"; break; + } + info << ", " << BaseType::Info(); + return info.str(); +} + +template KRATOS_API(KRATOS_CORE) GeometryMetricsTensorAdaptor::GeometryMetricsTensorAdaptor(ModelPart::GeometryContainerType::Pointer, GeometryMetricsTensorAdaptor::Metric); +template KRATOS_API(KRATOS_CORE) GeometryMetricsTensorAdaptor::GeometryMetricsTensorAdaptor(ModelPart::ConditionsContainerType::Pointer, GeometryMetricsTensorAdaptor::Metric); +template KRATOS_API(KRATOS_CORE) GeometryMetricsTensorAdaptor::GeometryMetricsTensorAdaptor(ModelPart::ElementsContainerType::Pointer, GeometryMetricsTensorAdaptor::Metric); + +} // namespace Kratos \ No newline at end of file diff --git a/kratos/tensor_adaptors/geometry_metrics_tensor_adaptor.h b/kratos/tensor_adaptors/geometry_metrics_tensor_adaptor.h new file mode 100644 index 000000000000..af3f36534985 --- /dev/null +++ b/kratos/tensor_adaptors/geometry_metrics_tensor_adaptor.h @@ -0,0 +1,122 @@ +// | / | +// ' / __| _` | __| _ \ __| +// . \ | ( | | ( |\__ ` +// _|\_\_| \__,_|\__|\___/ ____/ +// Multi-Physics +// +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Suneth Warnakulasuriya +// + +#pragma once + +// System includes +#include + +// External includes + +// Project includes +#include "tensor_adaptors/tensor_adaptor.h" + +namespace Kratos { + +///@name Kratos Classes +///@{ + +/** + * @ingroup TensorAdaptors + * @brief Adaptor class for calculating geometry metrics. + * + * @details This class provides an interface to calculate geometry metrics for given entity container pointer @p pContainer . + * This @ref TensorAdaptor only implements the @ref CollectData method. Following geometry metrics are supported. + * - @ref GeometryMetricsTensorAdaptor::DomainSize + * + * @section GeometryMetricsTensorAdaptor_supported_container Supported entity container types + * - @ref ModelPart::GeometryContainerType + * - @ref ModelPart::ConditionsContainerType + * - @ref ModelPart::ElementsContainerType + * + * @section GeometryMetricsTensorAdaptor_usage Usage + * - Use @ref CollectData to fill internal data with the metric from each entity given by the container pointer @p pContainer . + * + * @author Suneth Warnakulasuriya + * @see @ref TensorAdaptor Base class. + */ +class KRATOS_API(KRATOS_CORE) GeometryMetricsTensorAdaptor: public TensorAdaptor { +public: + ///@name Enums + ///@{ + + enum class Metric + { + DomainSize + }; + + ///@} + ///@name Type definitions + ///@{ + + KRATOS_CLASS_POINTER_DEFINITION(GeometryMetricsTensorAdaptor); + + using BaseType = TensorAdaptor; + + using enum Metric; + + ///@} + ///@name Life cycle + ///@{ + + template + GeometryMetricsTensorAdaptor( + TContainerPointerType pContainer, + const Metric Datum); + + GeometryMetricsTensorAdaptor( + const TensorAdaptor& rOther, + const Metric Datum, + const bool Copy = true); + + // Destructor + ~GeometryMetricsTensorAdaptor() override = default; + + ///@} + ///@name Public operations + ///@{ + + /** + * @brief Clones the existing tensor adaptor. + */ + TensorAdaptor::Pointer Clone() const override; + + /** + * @brief Fill the internal data with metric of the entities in the container. + */ + void CollectData() override; + + /** + * @brief Does not do anything + * @throws std::runtime_error always. + */ + void StoreData() override; + + ///@} + ///@name Input and output + ///@{ + + std::string Info() const override; + + ///@} + +private: + ///@name Private member variables + ///@{ + + Metric mMetric; + + ///@} +}; + +/// @} +} // namespace Kratos \ No newline at end of file diff --git a/kratos/tests/test_tensor_adaptors.py b/kratos/tests/test_tensor_adaptors.py index 1f70bb45b8a6..b9fa3857c7e2 100644 --- a/kratos/tests/test_tensor_adaptors.py +++ b/kratos/tests/test_tensor_adaptors.py @@ -736,6 +736,32 @@ def test_NodalNeighboursTensorAdaptorElement(self): with self.assertRaises(RuntimeError): ta.StoreData() + def testGeometryMetricsTensorAdaptorDomainSizeCondition(self): + ta = Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor(self.model_part.Conditions, Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor.DomainSize) + ta.CollectData() + for i, condition in enumerate(self.model_part.Conditions): + self.assertEqual(ta.data[i], condition.GetGeometry().DomainSize()) + + def testGeometryMetricsTensorAdaptorDomainSizeElement(self): + ta = Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor(self.model_part.Elements, Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor.DomainSize) + ta.CollectData() + for i, element in enumerate(self.model_part.Elements): + self.assertEqual(ta.data[i], element.GetGeometry().DomainSize()) + + def testGeometryMetricsTensorAdaptorDomainSizeCondition_Empty(self): + model = Kratos.Model() + model_part = model.CreateModelPart("test") + ta = Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor(model_part.Conditions, Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor.DomainSize) + ta.CollectData() + self.assertEqual(ta.data.shape, (0,)) + + def testGeometryMetricsTensorAdaptorDomainSizeElement_Empty(self): + model = Kratos.Model() + model_part = model.CreateModelPart("test") + ta = Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor(model_part.Elements, Kratos.TensorAdaptors.GeometryMetricsTensorAdaptor.DomainSize) + ta.CollectData() + self.assertEqual(ta.data.shape, (0,)) + def __TestCopyTensorAdaptor(self, tensor_adaptor_type, value_getter): var_ta_orig = tensor_adaptor_type(self.model_part.Nodes, Kratos.VELOCITY, data_shape=[2]) var_ta_orig.Check()