OGS
ComputeNaturalCoordsAlgorithm.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <vtkCell.h>
7#include <vtkLine.h>
8#include <vtkPointData.h>
9
10#include <algorithm>
11
14#include "FindCellsForPoints.h"
18
20{
21
23{
24public:
25 void append(Eigen::Vector3d const& natural_coords,
26 Eigen::Vector3d const& real_coords,
27 vtkIdType bulk_element_id,
28 vtkIdType point_cloud_node_id)
29 {
30 natural_coordss.push_back(natural_coords);
31 real_coordss.push_back(real_coords);
32 bulk_element_ids.push_back(bulk_element_id);
33 point_cloud_node_ids.push_back(point_cloud_node_id);
34 }
35
36 void fail() { success = false; }
37
39 Eigen::VectorXd const& initial_anchor_stress,
40 Eigen::VectorXd const& maximum_anchor_stress,
41 Eigen::VectorXd const& residual_anchor_stress,
42 Eigen::VectorXd const& anchor_cross_sectional_area,
43 Eigen::VectorXd const& anchor_stiffness)
44 {
45 auto bei = Eigen::Map<Eigen::VectorX<vtkIdType>>(
46 bulk_element_ids.data(), bulk_element_ids.size());
47 auto pcni = Eigen::Map<Eigen::VectorX<vtkIdType>>(
49 return {conv(natural_coordss),
51 initial_anchor_stress,
52 maximum_anchor_stress,
53 residual_anchor_stress,
54 anchor_cross_sectional_area,
55 anchor_stiffness,
56 bei,
57 pcni,
58 success};
59 }
60
61private:
62 static Eigen::MatrixXd conv(std::vector<Eigen::Vector3d> const& in)
63 {
64 Eigen::MatrixXd out(in.size(), 3);
65 for (std::size_t i = 0; i < in.size(); ++i)
66 {
67 out.row(i).noalias() = in[i].transpose();
68 }
69 return out;
70 }
71
72 std::vector<Eigen::Vector3d> natural_coordss;
73 std::vector<Eigen::Vector3d> real_coordss;
74 std::vector<vtkIdType> bulk_element_ids;
75 std::vector<vtkIdType> point_cloud_node_ids;
76 bool success = true;
77};
78
80 vtkUnstructuredGrid* const bulk_mesh,
81 ComputeNaturalCoordsResult const& json_data, double const tolerance,
82 int const max_iter)
83{
84 // Check inputs ------------------------------------------------------------
85
86 if (json_data.real_coords.cols() != 3)
87 {
88 OGS_FATAL("Wrong number of input coordinates");
89 }
90
91 // General definitions -----------------------------------------------------
92 using SolverRegistry =
95
96 double const real_coords_tolerance = tolerance;
97
98 // Initialization ----------------------------------------------------------
99 FindCellsForPoints findCellsForPoints;
100 findCellsForPoints.initialize(bulk_mesh);
101
103
104 // Do computation for each point ------------------------------------------
105 for (Eigen::Index rc_idx = 0; rc_idx < json_data.real_coords.rows();
106 ++rc_idx)
107 {
108 Eigen::RowVector3d const real_coords_single_point =
109 json_data.real_coords.row(rc_idx);
110
111 // Find cells for point
112 auto const filtered_bulk_mesh =
113 findCellsForPoints.find(real_coords_single_point, tolerance);
114
115 // Check multiplicity
116 int const actual_multiplicity = filtered_bulk_mesh->GetNumberOfCells();
117 if (actual_multiplicity == 0)
118 {
119 OGS_FATAL(
120 "Did not find any cell for the point {} (coordinates: {})",
121 rc_idx, real_coords_single_point);
122 }
123 assert(actual_multiplicity > 0);
124 if (actual_multiplicity != 1)
125 {
126 INFO(
127 "Found more then one cell (namely {}) for point #{} "
128 "(coordinates: {})",
129 actual_multiplicity, rc_idx, real_coords_single_point);
130 }
131
132 // Convert to OGS
133 std::unique_ptr<MeshLib::Mesh> ogs_mesh(
135 filtered_bulk_mesh, "filtered_bulk_mesh"));
136
137 auto const* bulk_element_ids = MeshLib::bulkElementIDs(*ogs_mesh);
138
139 auto const& elements = ogs_mesh->getElements();
140
141 // Compute natural coordinates
142 // Found a solution, no need to test other elements ignoring
143 // multiplicity.
144 constexpr std::size_t elt_idx = 0;
145 auto const& element = *elements[elt_idx];
146 auto const bulk_element_id = bulk_element_ids->getComponent(elt_idx, 0);
147
148 auto const& solver = SolverRegistry::getFor(element);
149 auto const opt_natural_coords = solver.solve(
150 element, real_coords_single_point, max_iter, real_coords_tolerance);
151
152 result.append(opt_natural_coords, real_coords_single_point,
153 bulk_element_id, rc_idx);
154 }
155
156 return result.finished(
157 json_data.initial_anchor_stress, json_data.maximum_anchor_stress,
159 json_data.anchor_stiffness);
160}
161
162template <typename T>
163void addPointData(vtkUnstructuredGrid* grid, std::string const& name,
164 Eigen::MatrixX<T> const& data)
165{
166 INFO("converting {}", name);
167 vtkNew<vtkAOSDataArrayTemplate<T>> array;
168 array->SetName(name.c_str());
169 array->SetNumberOfComponents(data.cols());
170 array->SetNumberOfTuples(grid->GetNumberOfPoints());
171
172 if (grid->GetNumberOfPoints() != data.rows())
173 {
174 OGS_FATAL(
175 "Got {} rows in the table but expected {} rows, same as number of "
176 "points in the grid.",
177 data.rows(), grid->GetNumberOfPoints());
178 }
179 for (Eigen::Index i = 0; i < data.rows(); ++i)
180 {
181 // copy to contiguous storage
182 Eigen::RowVectorX<T> const row = data.row(i);
183
184 array->SetTypedTuple(i, row.data());
185 DBUG("> {}", row);
186 }
187
188 grid->GetPointData()->AddArray(array);
189}
190
191template <typename T>
192void addCellData(vtkUnstructuredGrid* grid, std::string const& name,
193 Eigen::MatrixX<T> const& data)
194{
195 INFO("converting {}", name);
196 vtkNew<vtkAOSDataArrayTemplate<T>> array;
197 array->SetName(name.c_str());
198 array->SetNumberOfComponents(data.cols());
199 array->SetNumberOfTuples(grid->GetNumberOfCells());
200
201 if (grid->GetNumberOfCells() != data.rows())
202 {
203 OGS_FATAL(
204 "Got {} rows in the table but expected {} rows, same as number of "
205 "cells in the grid.",
206 data.rows(), grid->GetNumberOfCells());
207 }
208 for (Eigen::Index i = 0; i < data.rows(); ++i)
209 {
210 // copy to contiguous storage
211 Eigen::RowVectorX<T> const row = data.row(i);
212
213 array->SetTypedTuple(i, row.data());
214 DBUG("> {}", row);
215 }
216
217 grid->GetCellData()->AddArray(array);
218}
219
226vtkSmartPointer<vtkUnstructuredGrid> toVTKGrid(
227 ComputeNaturalCoordsResult const& result)
228{
229 INFO("converting points");
230 auto const n_pts = result.natural_coords.rows();
231 if (n_pts == 0)
232 {
233 OGS_FATAL(
234 "No point was found in the mesh. Please check whether all anchors "
235 "are within the model region.");
236 }
237 else if (n_pts == 1)
238 {
239 OGS_FATAL(
240 "Only one point was found in the mesh. Please check whether all "
241 "anchors are within the model region.");
242 }
243 else if (n_pts % 2 != 0)
244 {
245 // number should be even if multiplicity is ignored
246 OGS_FATAL(
247 "Number of points is not even. Anchors are believed to consist of "
248 "a start and an end point.");
249 }
250
251 vtkNew<vtkUnstructuredGrid> grid;
252
253 // Points ------------------------------------------------------------------
254 vtkNew<vtkPoints> points;
255 points->SetDataTypeToDouble();
256 points->SetNumberOfPoints(n_pts);
257
258 auto const& css = result.real_coords;
259 for (Eigen::Index i = 0; i < css.rows(); ++i)
260 {
261 points->SetPoint(i, css(i, 0), css(i, 1), css(i, 2));
262
263 if ((i % 2) == 1)
264 {
265 vtkNew<vtkLine> line;
266 line->GetPointIds()->SetId(0, i - 1);
267 line->GetPointIds()->SetId(1, i);
268 grid->InsertNextCell(line->GetCellType(), line->GetPointIds());
269 }
270 }
271
272 grid->SetPoints(points);
273
274 // Point data --------------------------------------------------------------
275 addPointData<double>(grid, "natural_coordinates", result.natural_coords);
276 addPointData<std::size_t>(grid, "bulk_element_ids",
277 result.bulk_element_ids.cast<std::size_t>());
278 addPointData<std::size_t>(grid, "point_cloud_node_ids",
279 result.point_cloud_node_ids.cast<std::size_t>());
280 addCellData<double>(grid, "initial_anchor_stress",
281 result.initial_anchor_stress);
282 addCellData<double>(grid, "maximum_anchor_stress",
283 result.maximum_anchor_stress);
284 addCellData<double>(grid, "residual_anchor_stress",
286 addCellData<double>(grid, "anchor_cross_sectional_area",
288 addCellData<double>(grid, "anchor_stiffness", result.anchor_stiffness);
289 return grid;
290}
291} // namespace ApplicationUtils
#define OGS_FATAL(...)
Definition Error.h:19
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
ComputeNaturalCoordsResult finished(Eigen::VectorXd const &initial_anchor_stress, Eigen::VectorXd const &maximum_anchor_stress, Eigen::VectorXd const &residual_anchor_stress, Eigen::VectorXd const &anchor_cross_sectional_area, Eigen::VectorXd const &anchor_stiffness)
static Eigen::MatrixXd conv(std::vector< Eigen::Vector3d > const &in)
void append(Eigen::Vector3d const &natural_coords, Eigen::Vector3d const &real_coords, vtkIdType bulk_element_id, vtkIdType point_cloud_node_id)
vtkSmartPointer< vtkUnstructuredGrid > find(Eigen::Vector3d const &coords, double const tolerance)
void initialize(vtkUnstructuredGrid *const bulk_mesh)
static MeshLib::Mesh * convertUnstructuredGrid(vtkUnstructuredGrid *grid, bool const compute_element_neighbors=false, std::string const &mesh_name="vtkUnstructuredGrid")
Converts a vtkUnstructuredGrid object to a Mesh.
void addPointData(vtkUnstructuredGrid *grid, std::string const &name, Eigen::MatrixX< T > const &data)
void addCellData(vtkUnstructuredGrid *grid, std::string const &name, Eigen::MatrixX< T > const &data)
vtkSmartPointer< vtkUnstructuredGrid > toVTKGrid(ComputeNaturalCoordsResult const &result)
ComputeNaturalCoordsResult computeNaturalCoords(vtkUnstructuredGrid *const bulk_mesh, ComputeNaturalCoordsResult const &json_data, double const tolerance, int const max_iter)
PropertyVector< std::size_t > const * bulkElementIDs(Mesh const &mesh)
Definition Mesh.cpp:290