5 #include <unordered_map>
7 #include <Caribou/config.h>
8 #include <Caribou/macros.h>
9 #include <Caribou/constants.h>
10 #include <Caribou/Topology/Domain.h>
11 #include <Caribou/Topology/HashGrid.h>
13 namespace caribou::topology {
27 template <
typename Domain>
31 using ContainerElement =
typename Domain::ElementType;
33 static constexpr INTEGER_TYPE Dimension = ContainerElement::Dimension;
34 using ElementIndex = INTEGER_TYPE;
35 using LocalCoordinates =
typename ContainerElement::LocalCoordinates;
36 using WorldCoordinates =
typename ContainerElement::WorldCoordinates;
46 BarycentricPoint() : element_index(-1), local_coordinates(LocalCoordinates::Zero()) {}
47 BarycentricPoint(
const ElementIndex & index,
const LocalCoordinates & coordinates)
48 : element_index(index), local_coordinates(coordinates) {}
50 ElementIndex element_index;
51 LocalCoordinates local_coordinates;
70 template <
typename Derived>
74 set_embedded_points(embedded_points);
85 : p_container_domain(container_domain) {
87 throw std::runtime_error(
"Trying to create a barycentric container from an empty domain.");
91 WorldCoordinates H_mean = WorldCoordinates::Zero();
92 for (UNSIGNED_INTEGER_TYPE element_id = 0; element_id < container_domain->
number_of_elements(); ++element_id) {
93 const ContainerElement element = container_domain->
element(element_id);
94 const auto element_nodes = element.nodes();
95 const auto min = element_nodes.colwise().minCoeff();
96 const auto max = element_nodes.colwise().maxCoeff();
97 H_mean += (max - min);
102 p_hash_grid = std::make_unique<HashGridT>(H_mean.maxCoeff(), container_domain->
number_of_elements());
104 for (UNSIGNED_INTEGER_TYPE element_id = 0; element_id < container_domain->
number_of_elements(); ++element_id) {
105 p_hash_grid->add(container_domain->
element(element_id), element_id);
122 const auto candidate_element_indices = p_hash_grid->get(p);
125 for (
const auto & element_index : candidate_element_indices) {
126 const ContainerElement e = p_container_domain->
element(element_index);
127 const LocalCoordinates local_coordinates = e.local_coordinates(p);
128 if (e.contains_local(local_coordinates)) {
130 return {
static_cast<ElementIndex
>(element_index), local_coordinates};
135 return {-1, LocalCoordinates::Zero()};
143 const auto candidate_element_indices = p_hash_grid->get(p);
147 for (
const auto & element_index : candidate_element_indices) {
148 const ContainerElement e = p_container_domain->
element(element_index);
149 const LocalCoordinates local_coordinates = e.local_coordinates(p);
150 closest_elements.emplace_back(
static_cast<ElementIndex
>(element_index), local_coordinates);
173 template <
typename Derived1,
typename Derived2>
174 void interpolate(
const Eigen::MatrixBase<Derived1> & container_field_values,
175 Eigen::MatrixBase<Derived2> & embedded_field_values)
const {
178 if (
static_cast<unsigned>(container_field_values.rows()) != p_container_domain->
mesh().number_of_nodes()) {
179 std::stringstream ss;
180 ss <<
"The number of rows of the input matrix must be the same as the number of nodes in the container domain. ";
181 ss <<
"The number of rows is " << container_field_values.rows() <<
" and the number of nodes is ";
182 ss << p_container_domain->
mesh().number_of_nodes();
183 throw std::runtime_error(ss.str());
187 if (
static_cast<unsigned>(embedded_field_values.rows()) != p_barycentric_points.size()) {
188 throw std::runtime_error(
"The number of rows of the output matrix must be the same as the number of embedded nodes.");
191 if (container_field_values.cols() != embedded_field_values.cols()) {
192 throw std::runtime_error(
"The number of columns of the input matrix (container field values) must be the "
193 "same as the number of columns of the output matrix (embedded field values).");
197 const auto number_of_embedded_points =
static_cast<signed >(p_barycentric_points.size());
198 for (Eigen::Index node_id = 0; node_id < number_of_embedded_points; ++node_id) {
199 const auto & element_index = p_barycentric_points[node_id].element_index;
200 const auto & local_coordinates = p_barycentric_points[node_id].local_coordinates;
202 if (element_index < 0) {
206 const auto & element_node_indices = p_container_domain->
element_indices(element_index);
207 const auto element = ContainerElement(p_container_domain->
mesh().positions(element_node_indices));
210 Eigen::Matrix<typename Eigen::MatrixBase<Derived2>::Scalar,
211 ContainerElement::NumberOfNodesAtCompileTime,
212 Eigen::MatrixBase<Derived2>::ColsAtCompileTime>
213 values (element.number_of_nodes(), container_field_values.cols());
215 for (UNSIGNED_INTEGER_TYPE i = 0; i < element.number_of_nodes(); ++i) {
216 values.row(i) = container_field_values.row(element_node_indices[i]);
220 const auto interpolated_value = element.interpolate(local_coordinates, values);
223 if constexpr (Eigen::MatrixBase<Derived2>::ColsAtCompileTime == 1) {
224 embedded_field_values[node_id] = interpolated_value;
226 embedded_field_values.row(node_id) = interpolated_value;
235 return p_barycentric_points;
244 return p_outside_nodes;
257 template <
typename Derived>
258 void set_embedded_points(
const Eigen::MatrixBase<Derived> & embedded_points) {
259 static_assert(Eigen::MatrixBase<Derived>::ColsAtCompileTime == Dimension or Eigen::MatrixBase<Derived>::ColsAtCompileTime == Eigen::Dynamic);
261 if (Eigen::MatrixBase<Derived>::ColsAtCompileTime == Eigen::Dynamic and embedded_points.cols() != Dimension) {
262 throw std::runtime_error(
"Trying to get the barycentric coordinates of " +
263 std::to_string(embedded_points.cols()) +
"D points from a " +
264 std::to_string(Dimension) +
"D domain");
267 p_outside_nodes.resize(0);
268 p_outside_nodes.reserve(std::floor(embedded_points.rows() / 10.));
270 p_barycentric_points.resize(0);
271 p_barycentric_points.reserve(embedded_points.rows());
273 const auto number_of_embedded_points = embedded_points.rows();
274 for (Eigen::Index node_id = 0; node_id < number_of_embedded_points; ++node_id) {
275 const BarycentricPoint bp =
barycentric_point(embedded_points.row(node_id).template cast<typename WorldCoordinates::Scalar>());
276 if (bp.element_index < 0) {
277 p_outside_nodes.emplace_back(node_id);
280 p_barycentric_points.emplace_back(bp);
285 const Domain * p_container_domain;
286 std::vector<BarycentricPoint> p_barycentric_points;
287 std::vector<UNSIGNED_INTEGER_TYPE> p_outside_nodes;
288 std::unique_ptr<HashGridT> p_hash_grid;