TTK
Loading...
Searching...
No Matches
ttkPersistenceDiagramApproximation.cpp
Go to the documentation of this file.
1#include <vtkCellData.h>
2#include <vtkDataSet.h>
3#include <vtkDoubleArray.h>
4#include <vtkFloatArray.h>
5#include <vtkIdTypeArray.h>
6#include <vtkInformation.h>
7#include <vtkNew.h>
8#include <vtkPointData.h>
9#include <vtkSmartPointer.h>
10
11#include <ttkMacros.h>
14#include <ttkUtils.h>
15
17
19 SetNumberOfInputPorts(1);
20 SetNumberOfOutputPorts(3);
21}
22
24 int port, vtkInformation *info) {
25 if(port == 0) {
26 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
27 return 1;
28 }
29 return 0;
30}
31
33 int port, vtkInformation *info) {
34 if(port == 0) {
35 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
36 return 1;
37 } else if(port == 1) {
39 return 1;
40 } else if(port == 2) {
41 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
42 return 1;
43 }
44 return 0;
45}
46
47template <typename scalarType, typename triangulationType>
48int ttkPersistenceDiagramApproximation::dispatch(
49 vtkUnstructuredGrid *outputCTPersistenceDiagram,
50 vtkUnstructuredGrid *outputBounds,
51 vtkDataArray *const inputScalarsArray,
52 const scalarType *const inputScalars,
53 scalarType *outputScalars,
54 SimplexId *outputOffsets,
55 int *outputMonotonyOffsets,
56 const SimplexId *const inputOrder,
57 const triangulationType *triangulation) {
58
59 int status{};
60 std::vector<ttk::PersistencePair> CTDiagram{};
61
63
64 double *range = inputScalarsArray->GetRange(0);
65 this->setDeltaApproximate(range[1] - range[0]);
66 this->setOutputScalars(outputScalars);
67 this->setOutputOffsets(outputOffsets);
68 this->setOutputMonotonyOffsets(outputMonotonyOffsets);
69
70 if(!std::is_same<ttk::ImplicitWithPreconditions, triangulationType>::value
71 && !std::is_same<ttk::ImplicitNoPreconditions, triangulationType>::value) {
72 this->printErr("Explicit, Compact or Periodic triangulation detected.");
73 this->printErr("Approximation only works on regular grids.");
74 return 0;
75 }
76
77 ttk::Timer tm{};
78
79 status
80 = this->executeApproximateTopology(CTDiagram, inputScalars, triangulation);
81
82 this->printMsg("Complete", 1.0, tm.getElapsedTime(), this->threadNumber_);
83
84 // fill missing data in CTDiagram (critical points coordinates & value)
85 augmentPersistenceDiagram(CTDiagram, outputScalars, triangulation);
86
87 // finally sort the diagram
88 sortPersistenceDiagram(CTDiagram, inputOrder);
89
91
92 // something wrong in baseCode
93 if(status != 0) {
94 this->printErr("PersistenceDiagram::execute() error code : "
95 + std::to_string(status));
96 return 0;
97 }
98
99 // convert CTDiagram to vtkUnstructuredGrid
100 vtkNew<vtkUnstructuredGrid> const vtu{};
101 DiagramToVTU(vtu, CTDiagram, inputScalarsArray, *this,
102 triangulation->getDimensionality(), this->ShowInsideDomain);
103
104 outputCTPersistenceDiagram->ShallowCopy(vtu);
105
107 drawBottleneckBounds(outputBounds, CTDiagram, inputScalarsArray,
108 outputScalars, inputScalars, triangulation);
109 }
110
111 return 1;
112}
113
115 vtkInformation *ttkNotUsed(request),
116 vtkInformationVector **inputVector,
117 vtkInformationVector *outputVector) {
118
119 vtkDataSet *input = vtkDataSet::GetData(inputVector[0]);
120 vtkUnstructuredGrid *outputCTPersistenceDiagram
121 = vtkUnstructuredGrid::GetData(outputVector, 0);
122 vtkDataSet *outputField = vtkDataSet::GetData(outputVector, 1);
123 vtkUnstructuredGrid *outputBounds
124 = vtkUnstructuredGrid::GetData(outputVector, 2);
125
127#ifndef TTK_ENABLE_KAMIKAZE
128 if(!triangulation) {
129 this->printErr("Wrong triangulation");
130 return 0;
131 }
132#endif
133
134 this->preconditionTriangulation(triangulation);
135
136 vtkDataArray *inputScalars = this->GetInputArrayToProcess(0, inputVector);
137#ifndef TTK_ENABLE_KAMIKAZE
138 if(!inputScalars) {
139 this->printErr("Wrong input scalars");
140 return 0;
141 }
142#endif
143
144 vtkDataArray *offsetField = this->GetOrderArray(
145 input, 0, triangulation, false, 1, ForceInputOffsetScalarField);
146
147#ifndef TTK_ENABLE_KAMIKAZE
148 if(!offsetField) {
149 this->printErr("Wrong input offsets");
150 return 0;
151 }
152 if(offsetField->GetDataType() != VTK_INT
153 and offsetField->GetDataType() != VTK_ID_TYPE) {
154 this->printErr("Input offset field type not supported");
155 return 0;
156 }
157#endif
158
159 vtkNew<ttkSimplexIdTypeArray> outputOffsets{};
160 outputOffsets->SetNumberOfComponents(1);
161 outputOffsets->SetNumberOfTuples(inputScalars->GetNumberOfTuples());
162 outputOffsets->SetName("outputOffsets");
163
164 vtkNew<vtkIntArray> outputMonotonyOffsets{};
165 outputMonotonyOffsets->SetNumberOfComponents(1);
166 outputMonotonyOffsets->SetNumberOfTuples(inputScalars->GetNumberOfTuples());
167 outputMonotonyOffsets->SetName("outputMonotonyffsets");
168 outputMonotonyOffsets->FillComponent(0, 0);
169
170 vtkSmartPointer<vtkDataArray> const outputScalars
171 = vtkSmartPointer<vtkDataArray>::Take(inputScalars->NewInstance());
172 outputScalars->SetNumberOfComponents(1);
173 outputScalars->SetNumberOfTuples(inputScalars->GetNumberOfTuples());
174 outputScalars->DeepCopy(inputScalars);
175
176 std::stringstream ss;
177 ss << inputScalars->GetName() << "_Approximated";
178 outputScalars->SetName(ss.str().c_str());
179
180 int status{};
182 inputScalars->GetDataType(), triangulation->getType(),
183 status = this->dispatch(
184 outputCTPersistenceDiagram, outputBounds, inputScalars,
185 static_cast<VTK_TT *>(ttkUtils::GetVoidPointer(inputScalars)),
186 static_cast<VTK_TT *>(ttkUtils::GetVoidPointer(outputScalars)),
187 static_cast<SimplexId *>(ttkUtils::GetVoidPointer(outputOffsets)),
188 static_cast<int *>(ttkUtils::GetVoidPointer(outputMonotonyOffsets)),
189 static_cast<SimplexId *>(ttkUtils::GetVoidPointer(offsetField)),
190 static_cast<TTK_TT *>(triangulation->getData())));
191
192 // shallow copy input Field Data
193 outputCTPersistenceDiagram->GetFieldData()->ShallowCopy(
194 input->GetFieldData());
195
197 outputField->ShallowCopy(input);
198 outputField->GetPointData()->AddArray(outputScalars);
199 outputField->GetPointData()->AddArray(outputOffsets);
200 outputField->GetPointData()->AddArray(outputMonotonyOffsets);
201 } else {
202 printWrn("The exact Persistence Diagram was computed");
203 printWrn("Other outputs are empty");
204 }
205
206 return status;
207}
208
209template <typename scalarType, typename triangulationType>
210int ttkPersistenceDiagramApproximation::drawBottleneckBounds(
211 vtkUnstructuredGrid *outputBounds,
212 const std::vector<ttk::PersistencePair> &diagram,
213 vtkDataArray *inputScalarsArray,
214 const scalarType *const outputScalars,
215 const scalarType *const inputScalars,
216 const triangulationType *triangulation) const {
217
218 if(diagram.empty()) {
219 return 1;
220 }
221
222 vtkNew<vtkUnstructuredGrid> bounds{};
223 double *range = inputScalarsArray->GetRange(0);
224 double const delta = (range[1] - range[0]) * Epsilon;
225 // std::cout << "DELTA for BOUNDS " << delta << " " << getEpsilon() <<
226 // std::endl;
227
228 vtkNew<ttkSimplexIdTypeArray> BirthId{};
229 BirthId->SetNumberOfComponents(1);
230 BirthId->SetName("BirthId");
231 vtkNew<ttkSimplexIdTypeArray> DeathId{};
232 DeathId->SetNumberOfComponents(1);
233 DeathId->SetName("DeathId");
234
235 vtkNew<ttkSimplexIdTypeArray> pairIdentifierScalars{};
236 pairIdentifierScalars->SetNumberOfComponents(1);
237 pairIdentifierScalars->SetName("PairIdentifier");
238
239 vtkNew<vtkDoubleArray> persistenceScalars{};
240 persistenceScalars->SetNumberOfComponents(1);
241 persistenceScalars->SetName("Persistence");
242
243 vtkNew<vtkIntArray> unfolded{};
244 unfolded->SetNumberOfComponents(1);
245 unfolded->SetName("TrueValues");
246
247 vtkNew<vtkIntArray> extremumIndexScalars{};
248 extremumIndexScalars->SetNumberOfComponents(1);
249 extremumIndexScalars->SetName("PairType");
250
251 const ttk::SimplexId minIndex = 0;
252 const ttk::SimplexId saddleSaddleIndex = 1;
253 const ttk::SimplexId maxIndex = triangulation->getCellVertexNumber(0) - 2;
254
255 vtkNew<vtkPoints> points{};
256
257 for(size_t i = 0; i < diagram.size(); ++i) {
258 const auto a = diagram[i].birth.id;
259 const auto b = diagram[i].death.id;
260
261 const auto sa = outputScalars[a];
262 const auto sb = outputScalars[b];
263 if(sb - sa >= 2 * delta) {
264 vtkIdType const p0 = points->InsertNextPoint(sa - delta, sb - delta, 0);
265 vtkIdType const p1 = points->InsertNextPoint(sa + delta, sb - delta, 0);
266 vtkIdType const p2 = points->InsertNextPoint(sa - delta, sb + delta, 0);
267 vtkIdType const p3 = points->InsertNextPoint(sa + delta, sb + delta, 0);
268
269 vtkNew<vtkIdList> quad{};
270 quad->InsertNextId(p0);
271 quad->InsertNextId(p1);
272 quad->InsertNextId(p3);
273 quad->InsertNextId(p2);
274 bounds->InsertNextCell(VTK_QUAD, quad);
275
276 pairIdentifierScalars->InsertNextTuple1(i);
277 BirthId->InsertNextTuple1(a);
278 DeathId->InsertNextTuple1(b);
279 persistenceScalars->InsertNextTuple1(sb - sa);
280 if(i == 0) {
281 extremumIndexScalars->InsertNextTuple1(-1);
282 } else {
283 const auto type = diagram[i].dim;
284 if(type == 0) {
285 extremumIndexScalars->InsertNextTuple1(minIndex);
286 } else if(type == 1) {
287 extremumIndexScalars->InsertNextTuple1(saddleSaddleIndex);
288 } else if(type == 2) {
289 extremumIndexScalars->InsertNextTuple1(maxIndex);
290 }
291 }
292 if((abs((double)(outputScalars[a] - inputScalars[a])) > 1e-6)
293 or (abs((double)(outputScalars[b] - inputScalars[b])) > 1e-6)) {
294 unfolded->InsertNextTuple1(0);
295 } else {
296 unfolded->InsertNextTuple1(1);
297 }
298 }
299 }
300
301 // ____________________________________________
302 // Case of the uncertain zone, near the diagonal
303 // ____________________________________________
304 const auto s0 = inputScalars[diagram[0].birth.id];
305 const auto s2 = inputScalars[diagram[0].death.id];
306 auto s1 = inputScalars[diagram.back().birth.id];
307 s1 = s1 > s2 / 2 ? s1 : s2 / 2;
308 vtkIdType const p0 = points->InsertNextPoint(s0, s0, 0);
309 vtkIdType const p1 = points->InsertNextPoint(s0, s0 + 2 * delta, 0);
310 vtkIdType const p2 = points->InsertNextPoint(s1, s1, 0);
311 vtkIdType const p3 = points->InsertNextPoint(s1, s1 + 2 * delta, 0);
312
313 vtkNew<vtkIdList> quad{};
314 quad->InsertNextId(p0);
315 quad->InsertNextId(p1);
316 quad->InsertNextId(p3);
317 quad->InsertNextId(p2);
318 bounds->InsertNextCell(VTK_QUAD, quad);
319 pairIdentifierScalars->InsertNextTuple1(-1);
320 BirthId->InsertNextTuple1(-1);
321 DeathId->InsertNextTuple1(-1);
322 persistenceScalars->InsertNextTuple1(Epsilon);
323 extremumIndexScalars->InsertNextTuple1(-1);
324 unfolded->InsertNextTuple1(-1);
325 // ____________________________________________
326
327 bounds->SetPoints(points);
328
329 bounds->GetCellData()->AddArray(BirthId);
330 bounds->GetCellData()->AddArray(DeathId);
331 bounds->GetCellData()->AddArray(persistenceScalars);
332 bounds->GetCellData()->AddArray(pairIdentifierScalars);
333 bounds->GetCellData()->AddArray(extremumIndexScalars);
334 bounds->GetCellData()->AddArray(unfolded);
335
336 outputBounds->ShallowCopy(bounds);
337
338 return 0;
339}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
static vtkInformationIntegerKey * SAME_DATA_TYPE_AS_INPUT_PORT()
ttk::Triangulation * GetTriangulation(vtkDataSet *dataSet)
vtkDataArray * GetOrderArray(vtkDataSet *const inputData, const int scalarArrayIdx, ttk::Triangulation *triangulation, const bool getGlobalOrder=false, const int orderArrayIdx=0, const bool enforceOrderArrayIdx=false)
TTK VTK-filter for the computation of an approximation of a persistence diagram.
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
int FillOutputPortInformation(int port, vtkInformation *info) override
int FillInputPortInformation(int port, vtkInformation *info) override
static void * GetVoidPointer(vtkDataArray *array, vtkIdType start=0)
Definition ttkUtils.cpp:226
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
void setOutputMonotonyOffsets(void *data)
void sortPersistenceDiagram(std::vector< PersistencePair > &diagram, const SimplexId *const offsets) const
void preconditionTriangulation(AbstractTriangulation *triangulation)
void setDeltaApproximate(double data)
void setOutputScalars(void *data)
void setOutputOffsets(void *data)
void augmentPersistenceDiagram(std::vector< PersistencePair > &persistencePairs, const scalarType *const scalars, const triangulationType *triangulation)
Complete a ttk::DiagramType instance with scalar field values (useful for persistence) and 3D coordin...
int executeApproximateTopology(std::vector< PersistencePair > &CTDiagram, const scalarType *inputScalars, const triangulationType *triangulation)
Triangulation is a class that provides time and memory efficient traversal methods on triangulations ...
AbstractTriangulation * getData()
Triangulation::Type getType() const
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
#define ttkVtkTemplateMacro(dataType, triangulationType, call)
Definition ttkMacros.h:69
vtkStandardNewMacro(ttkPersistenceDiagramApproximation)
int DiagramToVTU(vtkUnstructuredGrid *vtu, const ttk::DiagramType &diagram, vtkDataArray *const inputScalars, const ttk::Debug &dbg, const int dim, const bool embedInDomain)
Converts a Persistence Diagram in the ttk::DiagramType format to the VTK Unstructured Grid format (as...
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)