Nektar++
ProcessIsoContour.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: ProcessIsoContour.cpp
4//
5// For more information, please see: http://www.nektar.info/
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Generate isocontours from field data.
32//
33///////////////////////////////////////////////////////////////////////////////
34#include <iostream>
35#include <string>
36
37#include <boost/geometry.hpp>
38#include <boost/geometry/geometries/box.hpp>
39#include <boost/geometry/geometries/point.hpp>
40#include <boost/geometry/index/rtree.hpp>
41
42#include "ProcessIsoContour.h"
43
47
48namespace bg = boost::geometry;
49namespace bgi = boost::geometry::index;
50
51using namespace std;
52
53namespace Nektar::FieldUtils
54{
55
59 "Extract an isocontour of fieldid variable and at "
60 "value fieldvalue, Optionally fieldstr can be "
61 "specified for a string defiition or smooth for "
62 "smoothing");
63
65{
66
67 m_config["fieldstr"] =
68 ConfigOption(false, "NotSet", "string of isocontour to be extracted");
69 m_config["fieldname"] = ConfigOption(false, "isocon",
70 "name for isocontour if fieldstr "
71 "specified, default is isocon");
72
73 m_config["fieldid"] = ConfigOption(false, "NotSet", "field id to extract");
74
75 m_config["fieldvalue"] =
76 ConfigOption(false, "NotSet", "field value to extract");
77
78 m_config["globalcondense"] =
79 ConfigOption(true, "0",
80 "Globally condense contour to unique "
81 "values");
82
83 m_config["smooth"] = ConfigOption(true, "0",
84 "Smooth isocontour (might require "
85 "globalcondense)");
86
87 m_config["smoothiter"] =
88 ConfigOption(false, "100",
89 "Number of smoothing cycle, default = "
90 "100");
91
92 m_config["smoothposdiffusion"] =
93 ConfigOption(false, "0.5",
94 "Postive diffusion coefficient "
95 "(0 < lambda < 1), default = 0.5");
96
97 m_config["smoothnegdiffusion"] =
98 ConfigOption(false, "0.505",
99 "Negative diffusion coefficient "
100 "(0 < mu < 1), default = 0.505");
101
102 m_config["removesmallcontour"] = ConfigOption(
103 false, "0",
104 "Remove contours with less than specified number of triangles."
105 "Only valid with GlobalCondense or Smooth options.");
106}
107
109{
110}
111
112void ProcessIsoContour::v_Process(po::variables_map &vm)
113{
114 m_f->SetUpExp(vm);
115
116 bool verbose = (m_f->m_verbose && m_f->m_comm->TreatAsRankZero());
117
118 vector<IsoSharedPtr> iso;
119
120 ASSERTL0(m_f->m_fieldPts.get(), "Should have m_fieldPts for IsoContour.");
121
122 if (m_f->m_fieldPts->GetPtsType() == LibUtilities::ePtsTriBlock)
123 {
124 // assume we have read .dat file to directly input dat file.
125 if (verbose)
126 {
127 cout << "\t Process read iso from Field Pts" << endl;
128 }
129
131 }
132 else if (m_f->m_fieldPts->GetPtsType() == LibUtilities::ePtsTetBlock)
133 {
134 if (m_config["fieldstr"].m_beenSet)
135 {
136 string fieldName = m_config["fieldname"].as<string>();
137 m_f->m_variables.push_back(fieldName);
138 }
139
140 if (m_f->m_fieldPts->GetNpoints() == 0)
141 {
142 return;
143 }
144
145 int fieldid;
146 NekDouble value;
147
148 if (m_config["fieldstr"].m_beenSet) // generate field of interest
149 {
150 fieldid = m_f->m_fieldPts->GetNFields();
151
152 Array<OneD, NekDouble> pts(m_f->m_fieldPts->GetNpoints());
153
154 // evaluate new function
156 string varstr = "x y z";
157 vector<Array<OneD, const NekDouble>> interpfields;
158
159 for (int i = 0; i < m_f->m_fieldPts->GetDim(); ++i)
160 {
161 interpfields.push_back(m_f->m_fieldPts->GetPts(i));
162 }
163 for (int i = 0; i < m_f->m_fieldPts->GetNFields(); ++i)
164 {
165 varstr += " " + m_f->m_fieldPts->GetFieldName(i);
166 interpfields.push_back(m_f->m_fieldPts->GetPts(i + 3));
167 }
168
169 int ExprId = -1;
170 std::string str = m_config["fieldstr"].as<string>();
171 ExprId = strEval.DefineFunction(varstr.c_str(), str);
172
173 strEval.Evaluate(ExprId, interpfields, pts);
174
175 // set up field name if provided otherwise called "isocon" from
176 // default
177 string fieldName = m_config["fieldname"].as<string>();
178
179 m_f->m_fieldPts->AddField(pts, fieldName);
180 }
181 else
182 {
183 ASSERTL0(m_config["fieldid"].as<string>() != "NotSet",
184 "fieldid must be specified");
185 fieldid = m_config["fieldid"].as<int>();
186 }
187
188 ASSERTL0(m_config["fieldvalue"].as<string>() != "NotSet",
189 "fieldvalue must be specified");
190 value = m_config["fieldvalue"].as<NekDouble>();
191
192 iso = ExtractContour(fieldid, value);
193 }
194 else
195 {
196 ASSERTL0(false, "PtsType not supported for isocontour.");
197 }
198
199 // Process isocontour
200 bool smoothing = m_config["smooth"].as<bool>();
201 bool globalcondense = m_config["globalcondense"].as<bool>();
202 if (globalcondense)
203 {
204 if (verbose)
205 {
206 cout << "\t Process global condense ..." << endl;
207 }
208 int nfields = m_f->m_fieldPts->GetNFields() + m_f->m_fieldPts->GetDim();
210
211 g_iso->GlobalCondense(iso, verbose);
212
213 iso.clear();
214 iso.push_back(g_iso);
215 }
216
217 if (smoothing)
218 {
219 LibUtilities::Timer timersm;
220
221 if (verbose)
222 {
223 cout << "\t Process Contour smoothing ..." << endl;
224 timersm.Start();
225 }
226
227 int niter = m_config["smoothiter"].as<int>();
228 NekDouble lambda = m_config["smoothposdiffusion"].as<NekDouble>();
229 NekDouble mu = m_config["smoothnegdiffusion"].as<NekDouble>();
230 for (int i = 0; i < iso.size(); ++i)
231 {
232 iso[i]->Smooth(niter, lambda, -mu);
233 }
234
235 if (verbose)
236 {
237 timersm.Stop();
238 NekDouble cpuTime = timersm.TimePerTest(1);
239
240 stringstream ss;
241 ss << cpuTime << "s";
242 cout << "\t Process smooth CPU Time: " << setw(8) << left
243 << ss.str() << endl;
244 cpuTime = 0.0;
245 }
246 }
247
248 int mincontour = 0;
249 if ((mincontour = m_config["removesmallcontour"].as<int>()))
250 {
251 vector<IsoSharedPtr> new_iso;
252
253 if (verbose)
254 {
255 cout << "\t Identifying separate regions [." << flush;
256 }
257 for (int i = 0; i < iso.size(); ++i)
258 {
259 iso[i]->SeparateRegions(new_iso, mincontour, m_f->m_verbose);
260 }
261
262 if (verbose)
263 {
264 cout << "]" << endl << flush;
265 }
266
267 // reset iso to new_iso;
268 iso = new_iso;
269 }
270
271 ResetFieldPts(iso);
272}
273
274/*********************/
275/* Function TwoPairs */
276/*********************/
277
279 Array<OneD, NekDouble> &cz, int &pr)
280/* The logic of the following SWITCH statement may only be
281 understood with a "peusdo truth-table" */
282{
283 if (((cx[0] - cx[1]) == 0.0) && ((cy[0] - cy[1]) == 0.0) &&
284 ((cz[0] - cz[1]) == 0.0))
285 {
286 if (((cx[2] - cx[3]) == 0.0) && ((cy[2] - cy[3]) == 0.0) &&
287 ((cz[2] - cz[3]) == 0.0))
288 {
289 pr = 4;
290 }
291 else
292 {
293 pr = 3;
294 }
295 }
296 else
297 {
298 pr = 1;
299 }
300}
301
302/*************************/
303/* Function ThreeSimilar */
304/*************************/
305void ThreeSimilar(const int i, const int j, const int k, int &pr, int &ps)
306/* The logic of the following SWITCH statement may be
307 understood with a "peusdo truth-table" */
308{
309 switch (i + j + k)
310 {
311 case (3):
312 pr = 3;
313 ps = 4;
314 break;
315 case (4):
316 pr = 2;
317 ps = 4;
318 break;
319 case (5):
320 if (j == 1)
321 {
322 pr = 2;
323 ps = 3;
324 }
325 else
326 {
327 pr = 1;
328 ps = 4;
329 }
330 break;
331 case (6):
332 if (i == 0)
333 {
334 pr = 1;
335 ps = 3;
336 }
337 else
338 {
339 pr = 0;
340 ps = 4;
341 }
342 break;
343 case (7):
344 if (i == 0)
345 {
346 pr = 1;
347 ps = 2;
348 }
349 else
350 {
351 pr = 0;
352 ps = 3;
353 }
354 break;
355 case (8):
356 pr = 0;
357 ps = 2;
358 break;
359 case (9):
360 pr = 0;
361 ps = 1;
362 break;
363 default:
364 ASSERTL0(false, "Error in 5-point triangulation in ThreeSimilar");
365 break;
366 }
367}
368
369vector<IsoSharedPtr> ProcessIsoContour::ExtractContour(const int fieldid,
370 const NekDouble val)
371{
372 vector<IsoSharedPtr> returnval;
373
374 int coordim = m_f->m_fieldPts->GetDim();
375 int nfields = m_f->m_fieldPts->GetNFields() + coordim;
376
377 ASSERTL0(m_f->m_fieldPts->GetPtsType() == LibUtilities::ePtsTetBlock,
378 "This methods is currently only set up for 3D fields");
379 ASSERTL1(coordim + fieldid < nfields,
380 "field id is larger than number contained in FieldPts");
382 m_f->m_fieldPts->GetPts(fields);
383
384 Array<OneD, NekDouble> c = fields[coordim + fieldid];
385
386 int i, j, k, ii, jj, kk, r, s, n, counter, boolean;
387 Array<OneD, Array<OneD, NekDouble>> intfields(nfields);
388 intfields[0] = Array<OneD, NekDouble>(5 * nfields);
389 for (i = 1; i < nfields; ++i)
390 {
391 intfields[i] = intfields[i - 1] + 5;
392 }
393 Array<OneD, NekDouble> cx = intfields[0];
394 Array<OneD, NekDouble> cy = intfields[1];
395 Array<OneD, NekDouble> cz = intfields[2];
396
397 vector<Array<OneD, int>> ptsConn;
398 m_f->m_fieldPts->GetConnectivity(ptsConn);
399
400 for (int zone = 0; zone < ptsConn.size(); ++zone)
401 {
402 IsoSharedPtr iso;
403
404 iso = MemoryManager<Iso>::AllocateSharedPtr(nfields - 3);
405
406 int nelmt = ptsConn[zone].size() / (coordim + 1);
407
408 Array<OneD, int> conn = ptsConn[zone];
409
410 for (n = 0, i = 0; i < nelmt; ++i)
411 {
412 // check to see if val is between vertex values
413 if (!(((c[conn[i * 4]] > val) && (c[conn[i * 4 + 1]] > val) &&
414 (c[conn[i * 4 + 2]] > val) && (c[conn[i * 4 + 3]] > val)) ||
415 ((c[conn[i * 4]] < val) && (c[conn[i * 4 + 1]] < val) &&
416 (c[conn[i * 4 + 2]] < val) && (c[conn[i * 4 + 3]] < val))))
417 {
418
419 // loop over all edges and interpolate if
420 // contour is between vertex values
421 for (counter = 0, j = 0; j <= 2; j++)
422 {
423 for (k = j + 1; k <= 3; k++)
424 {
425 if (((c[conn[i * 4 + j]] >= val) &&
426 (val >= c[conn[i * 4 + k]])) ||
427 ((c[conn[i * 4 + j]] <= val) &&
428 (val <= c[conn[i * 4 + k]])))
429 {
430 // linear interpolation of fields
431 // (and coords).
432 NekDouble cj = c[conn[i * 4 + j]];
433 NekDouble ck = c[conn[i * 4 + k]];
434 NekDouble factor = (val - cj) / (ck - cj);
435
436 if (fabs(cj - ck) > 1e-12)
437 {
438 // interpolate coordinates and fields
439 for (int f = 0; f < nfields; ++f)
440 {
441 if (counter == 5)
442 {
443 ASSERTL0(false, "Counter is 5");
444 }
445 intfields[f][counter] =
446 fields[f][conn[4 * i + j]] +
447 factor * (fields[f][conn[4 * i + k]] -
448 fields[f][conn[4 * i + j]]);
449 }
450 ++counter;
451 }
452 }
453 }
454 }
455
456 switch (counter)
457 {
458 case 3:
459 n += 1;
460 iso->ResizeFields(3 * n);
461
462 for (j = 0; j < 3; ++j)
463 {
464 iso->SetFields(3 * (n - 1) + j, intfields, j);
465 }
466 break;
467 case 4:
468 n += 2;
469 iso->ResizeFields(3 * n);
470
471 for (j = 0; j < 3; ++j)
472 {
473 iso->SetFields(3 * (n - 2) + j, intfields, j);
474 iso->SetFields(3 * (n - 1) + j, intfields, j + 1);
475 }
476 break;
477 case 5:
478 n += 1;
479 iso->ResizeFields(3 * n);
480
481 boolean = 0;
482 for (ii = 0; ii <= 2; ii++)
483 {
484 for (jj = ii + 1; jj <= 3; jj++)
485 {
486 for (kk = jj + 1; kk <= 4; kk++)
487 {
488 if ((((cx[ii] - cx[jj]) == 0.0) &&
489 ((cy[ii] - cy[jj]) == 0.0) &&
490 ((cz[ii] - cz[jj]) == 0.0)) &&
491 (((cx[ii] - cx[kk]) == 0.0) &&
492 ((cy[ii] - cy[kk]) == 0.0) &&
493 ((cz[ii] - cz[kk]) == 0.0)))
494 {
495 boolean += 1;
496 ThreeSimilar(ii, jj, kk, r, s);
497
498 iso->SetFields(3 * (n - 1), intfields,
499 ii);
500 iso->SetFields(3 * (n - 1) + 1,
501 intfields, r);
502 iso->SetFields(3 * (n - 1) + 2,
503 intfields, s);
504 }
505 else
506 {
507 boolean += 0;
508 }
509 }
510 }
511 }
512
513 if (boolean == 0)
514 {
515 TwoPairs(cx, cy, cz, r);
516
517 iso->SetFields(3 * (n - 1), intfields, 0);
518 iso->SetFields(3 * (n - 1) + 1, intfields, 2);
519 iso->SetFields(3 * (n - 1) + 2, intfields, r);
520 }
521 break;
522 }
523 }
524 }
525
526 if (n)
527 {
528 iso->SetNTris(n);
529
530 // condense the information in this elemental extraction.
531 iso->Condense();
532
533 returnval.push_back(iso);
534 }
535 }
536
537 return returnval;
538}
539
540// reset m_fieldPts with values from iso;
541void ProcessIsoContour::ResetFieldPts(vector<IsoSharedPtr> &iso)
542{
543 int nfields = m_f->m_fieldPts->GetNFields() + m_f->m_fieldPts->GetDim();
544
545 // set output to triangle block.
546 m_f->m_fieldPts->SetPtsType(LibUtilities::ePtsTriBlock);
547
548 Array<OneD, Array<OneD, NekDouble>> newfields(nfields);
549
550 // count up number of points
551 int npts = 0;
552 for (int i = 0; i < iso.size(); ++i)
553 {
554 npts += iso[i]->GetNVert();
555 }
556
557 // set up coordinate in new field
558 newfields[0] = Array<OneD, NekDouble>(npts);
559 newfields[1] = Array<OneD, NekDouble>(npts);
560 newfields[2] = Array<OneD, NekDouble>(npts);
561
562 int cnt = 0;
563 for (int i = 0; i < iso.size(); ++i)
564 {
565 for (int j = 0; j < iso[i]->GetNVert(); ++j, ++cnt)
566 {
567 newfields[0][cnt] = iso[i]->GetX(j);
568 newfields[1][cnt] = iso[i]->GetY(j);
569 newfields[2][cnt] = iso[i]->GetZ(j);
570 }
571 }
572
573 // set up fields
574 for (int f = 0; f < nfields - 3; ++f)
575 {
576 newfields[f + 3] = Array<OneD, NekDouble>(npts);
577
578 cnt = 0;
579 for (int i = 0; i < iso.size(); ++i)
580 {
581 for (int j = 0; j < iso[i]->GetNVert(); ++j, ++cnt)
582 {
583 newfields[f + 3][cnt] = iso[i]->GetFields(f, j);
584 }
585 }
586 }
587
588 m_f->m_fieldPts->SetPts(newfields);
589
590 // set up connectivity data.
591 vector<Array<OneD, int>> ptsConn;
592 m_f->m_fieldPts->GetConnectivity(ptsConn);
593 cnt = 0;
594 ptsConn.clear();
595 for (int i = 0; i < iso.size(); ++i)
596 {
597 int ntris = iso[i]->GetNTris();
598 Array<OneD, int> conn(ntris * 3);
599
600 for (int j = 0; j < 3 * ntris; ++j)
601 {
602 conn[j] = cnt + iso[i]->GetVId(j);
603 }
604 ptsConn.push_back(conn);
605 cnt += iso[i]->GetNVert();
606 }
607 m_f->m_fieldPts->SetConnectivity(ptsConn);
608}
609
610// reset m_fieldPts with values from iso;
611void ProcessIsoContour::SetupIsoFromFieldPts(vector<IsoSharedPtr> &isovec)
612{
613 ASSERTL0(m_f->m_fieldPts->GetPtsType() == LibUtilities::ePtsTriBlock,
614 "Assume input is from ePtsTriBlock");
615
616 // get information from PtsField
617 int dim = m_f->m_fieldPts->GetDim();
618 int nfields = m_f->m_fieldPts->GetNFields() + dim;
620 m_f->m_fieldPts->GetPts(fieldpts);
621 vector<Array<OneD, int>> ptsConn;
622 m_f->m_fieldPts->GetConnectivity(ptsConn);
623
624 int cnt = 0;
625 for (int c = 0; c < ptsConn.size(); ++c)
626 {
627 // set up single iso with all the information from PtsField
629
630 int nelmt = 0;
631 nelmt = ptsConn[c].size() / 3;
632
633 iso->SetNTris(nelmt);
634 iso->ResizeVId(3 * nelmt);
635
636 // fill in connectivity values.
637 int nvert = 0;
638 for (int i = 0; i < ptsConn[c].size(); ++i)
639 {
640 int cid = ptsConn[c][i] - cnt;
641 iso->SetVId(i, cid);
642 nvert = max(cid, nvert);
643 }
644 nvert++;
645
646 iso->SetNVert(nvert);
647 iso->ResizeFields(nvert);
648
649 // fill in points values (including coordinates)
650 for (int i = 0; i < nvert; ++i)
651 {
652 iso->SetFields(i, fieldpts, i + cnt);
653 }
654 cnt += nvert;
655 isovec.push_back(iso);
656 }
657}
658
660{
661 int i, j, cnt;
662 IsoVertex v;
663 vector<IsoVertex> vert;
664
665 if (!m_ntris)
666 {
667 return;
668 }
669
670 if (m_condensed)
671 {
672 return;
673 }
674 m_condensed = true;
675
676 vert.reserve(m_ntris / 6);
677
679
680 // fill first 3 points and initialise fields
681 v.m_fields.resize(m_fields.size());
682 for (cnt = 0, i = 0; i < 3; ++i)
683 {
684 v.m_x = m_x[i];
685 v.m_y = m_y[i];
686 v.m_z = m_z[i];
687 for (int f = 0; f < m_fields.size(); ++f)
688 {
689 v.m_fields[f] = m_fields[f][i];
690 }
691 v.m_id = cnt;
692 vert.push_back(v);
693 m_vid[i] = v.m_id;
694 ++cnt;
695 }
696
697 for (i = 1; i < m_ntris; ++i)
698 {
699 for (j = 0; j < 3; ++j)
700 {
701 v.m_x = m_x[3 * i + j];
702 v.m_y = m_y[3 * i + j];
703 v.m_z = m_z[3 * i + j];
704
705 auto pt = find(vert.begin(), vert.end(), v);
706 if (pt != vert.end())
707 {
708 m_vid[3 * i + j] = pt[0].m_id;
709 }
710 else
711 {
712 v.m_id = cnt;
713
714 for (int f = 0; f < m_fields.size(); ++f)
715 {
716 v.m_fields[f] = m_fields[f][3 * i + j];
717 }
718
719 vert.push_back(v);
720
721 m_vid[3 * i + j] = v.m_id;
722 ++cnt;
723 }
724 }
725 }
726
727 // remove elements with multiple vertices
728 for (i = 0, cnt = 0; i < m_ntris;)
729 {
730 if ((m_vid[3 * i] == m_vid[3 * i + 1]) ||
731 (m_vid[3 * i] == m_vid[3 * i + 2]) ||
732 (m_vid[3 * i + 1] == m_vid[3 * i + 2]))
733 {
734 cnt++;
735 for (j = 3 * i; j < 3 * (m_ntris - 1); ++j)
736 {
737 m_vid[j] = m_vid[j + 3];
738 }
739 m_ntris--;
740 }
741 else
742 {
743 ++i;
744 }
745 }
746
747 m_nvert = vert.size();
748
749 m_x.resize(m_nvert);
750 m_y.resize(m_nvert);
751 m_z.resize(m_nvert);
752
753 for (int f = 0; f < m_fields.size(); ++f)
754 {
755 m_fields[f].resize(m_nvert);
756 }
757
758 for (i = 0; i < m_nvert; ++i)
759 {
760 m_x[i] = vert[i].m_x;
761 m_y[i] = vert[i].m_y;
762 m_z[i] = vert[i].m_z;
763 for (int f = 0; f < m_fields.size(); ++f)
764 {
765 m_fields[f][i] = vert[i].m_fields[f];
766 }
767 }
768}
769
770void Iso::GlobalCondense(vector<IsoSharedPtr> &iso, bool verbose)
771{
772 typedef bg::model::point<NekDouble, 3, bg::cs::cartesian> BPoint;
773 typedef std::pair<BPoint, unsigned int> PointPair;
774
775 int i, j, n;
776 int nelmt;
777 int niso = iso.size();
778 int id1, id2;
780
781 if (m_condensed)
782 {
783 return;
784 }
785 m_condensed = true;
786
787 vidmap = Array<OneD, Array<OneD, int>>(niso);
788
789 m_ntris = 0;
790 for (i = 0; i < niso; ++i)
791 {
792 if (iso[i]->m_ntris)
793 {
794 m_ntris += iso[i]->m_ntris;
795 }
796 }
797
799
800 m_nvert = 0;
801 for (i = 0; i < niso; ++i)
802 {
803 if (iso[i]->m_ntris)
804 {
805 m_nvert += iso[i]->m_nvert;
806 }
807 }
808
809 vector<vector<int>> isocon;
810 isocon.resize(niso);
811 vector<int> global_to_unique_map;
812 global_to_unique_map.resize(m_nvert);
813
814 vector<pair<int, int>> global_to_iso_map;
815 global_to_iso_map.resize(m_nvert);
816
817 // Fill vertex array into rtree format
818 id2 = 0;
819 std::vector<PointPair> inPoints;
820 for (i = 0; i < niso; ++i)
821 {
822 for (id1 = 0; id1 < iso[i]->m_nvert; ++id1)
823 {
824 inPoints.push_back(PointPair(
825 BPoint(iso[i]->m_x[id1], iso[i]->m_y[id1], iso[i]->m_z[id1]),
826 id2));
827 global_to_unique_map[id2] = id2;
828 global_to_iso_map[id2] = make_pair(i, id1);
829 id2++;
830 }
831 }
832
833 if (verbose)
834 {
835 cout << "\t Process building tree ..." << endl;
836 }
837
838 // Build tree
839 bgi::rtree<PointPair, bgi::rstar<16>> rtree;
840 rtree.insert(inPoints.begin(), inPoints.end());
841
842 // Find neighbours
843 int unique_index = 0;
844 int prog = 0;
845 for (i = 0; i < m_nvert; ++i)
846 {
847 if (verbose)
848 {
849 prog = LibUtilities::PrintProgressbar(i, m_nvert, "Nearest verts",
850 prog);
851 }
852
853 BPoint queryPoint = inPoints[i].first;
854
855 // check to see if point has been already reset to lower than
856 // unique value
857 if (global_to_unique_map[i] < unique_index) // do nothing
858 {
859 }
860 else
861 {
862 // find nearest 10 points within the distance box
863 std::vector<PointPair> result;
864 rtree.query(bgi::nearest(queryPoint, 10),
865 std::back_inserter(result));
866
867 // see if any values have unique value already
868 set<int> samept;
869 int new_index = -1;
870 for (id1 = 0; id1 < result.size(); ++id1)
871 {
872 NekDouble dist = bg::distance(queryPoint, result[id1].first);
873 if (dist * dist < NekConstants::kNekZeroTol) // same point
874 {
875 id2 = result[id1].second;
876 samept.insert(id2);
877
878 if (global_to_unique_map[id2] < unique_index)
879 {
880 new_index = global_to_unique_map[id2];
881 }
882 }
883 }
884 if (new_index == -1)
885 {
886 new_index = unique_index;
887 unique_index++;
888 }
889
890 // reset all same values to new_index
891 global_to_unique_map[i] = new_index;
892 for (auto &it : samept)
893 {
894 global_to_unique_map[it] = new_index;
895 }
896 }
897 }
898
899 if (verbose)
900 {
901 cout << endl;
902 }
903
904 m_nvert = unique_index;
905
906 nelmt = 0;
907 // reset m_vid;
908 int cnt = 0;
909 for (n = 0; n < niso; ++n)
910 {
911 for (i = 0; i < iso[n]->m_ntris; ++i, nelmt++)
912 {
913 for (j = 0; j < 3; ++j)
914 {
915 m_vid[3 * nelmt + j] =
916 global_to_unique_map[iso[n]->m_vid[3 * i + j] + cnt];
917 }
918 }
919 cnt += iso[n]->m_nvert;
920 }
921
922 m_ntris = nelmt;
923
924 m_x.resize(m_nvert);
925 m_y.resize(m_nvert);
926 m_z.resize(m_nvert);
927
928 m_fields.resize(iso[0]->m_fields.size());
929 for (i = 0; i < iso[0]->m_fields.size(); ++i)
930 {
931 m_fields[i].resize(m_nvert);
932 }
933
934 for (n = 0; n < global_to_unique_map.size(); ++n)
935 {
936
937 m_x[global_to_unique_map[n]] = bg::get<0>(inPoints[n].first);
938 m_y[global_to_unique_map[n]] = bg::get<1>(inPoints[n].first);
939 m_z[global_to_unique_map[n]] = bg::get<2>(inPoints[n].first);
940
941 int isoid = global_to_iso_map[n].first;
942 int ptid = global_to_iso_map[n].second;
943 for (j = 0; j < m_fields.size(); ++j)
944 {
945 m_fields[j][global_to_unique_map[n]] =
946 iso[isoid]->m_fields[j][ptid];
947 }
948 }
949}
950
951// define == if point is within 1e-8
952bool operator==(const IsoVertex &x, const IsoVertex &y)
953{
954 return ((x.m_x - y.m_x) * (x.m_x - y.m_x) +
955 (x.m_y - y.m_y) * (x.m_y - y.m_y) +
956 (x.m_z - y.m_z) * (x.m_z - y.m_z) <
958 ? true
959 : false;
960}
961
962// define != if point is outside 1e-8
963bool operator!=(const IsoVertex &x, const IsoVertex &y)
964{
965 return ((x.m_x - y.m_x) * (x.m_x - y.m_x) +
966 (x.m_y - y.m_y) * (x.m_y - y.m_y) +
967 (x.m_z - y.m_z) * (x.m_z - y.m_z) <
969 ? 0
970 : 1;
971}
972
973void Iso::Smooth(int n_iter, NekDouble lambda, NekDouble mu)
974{
975 int iter, i, j, k;
976 NekDouble del_v[3];
977 NekDouble w;
978 Array<OneD, NekDouble> xtemp, ytemp, ztemp;
979 vector<vector<int>> adj, vertcon;
980 vector<vector<NekDouble>> wght;
981
982 // determine elements around each vertex
983 vertcon.resize(m_nvert);
984 for (i = 0; i < m_ntris; ++i)
985 {
986 for (j = 0; j < 3; ++j)
987 {
988 vertcon[m_vid[3 * i + j]].push_back(i);
989 }
990 }
991
992 // determine vertices and weights around each vertex
993 adj.resize(m_nvert);
994 wght.resize(m_nvert);
995
996 for (i = 0; i < m_nvert; ++i)
997 {
998 // loop over surrounding elements
999 for (auto &ipt : vertcon[i])
1000 {
1001 for (j = 0; j < 3; ++j)
1002 {
1003 // make sure not adding own vertex
1004 if (m_vid[3 * ipt + j] != i)
1005 {
1006 // check to see if vertex has already been added
1007 for (k = 0; k < adj[i].size(); ++k)
1008 {
1009 if (adj[i][k] == m_vid[3 * ipt + j])
1010 {
1011 break;
1012 }
1013 }
1014
1015 if (k == adj[i].size())
1016 {
1017 adj[i].push_back(m_vid[3 * ipt + j]);
1018 }
1019 }
1020 }
1021 }
1022 }
1023
1024 // Currently set weights up as even distribution
1025 for (i = 0; i < m_nvert; ++i)
1026 {
1027 w = 1.0 / ((NekDouble)adj[i].size());
1028 for (j = 0; j < adj[i].size(); ++j)
1029 {
1030 wght[i].push_back(w);
1031 }
1032 }
1033
1037
1038 // smooth each point
1039 for (iter = 0; iter < n_iter; iter++)
1040 {
1041 // compute first weighted average
1042 for (i = 0; i < m_nvert; ++i)
1043 {
1044 del_v[0] = del_v[1] = del_v[2] = 0.0;
1045 for (j = 0; j < adj[i].size(); ++j)
1046 {
1047 del_v[0] = del_v[0] + (m_x[adj[i][j]] - m_x[i]) * wght[i][j];
1048 del_v[1] = del_v[1] + (m_y[adj[i][j]] - m_y[i]) * wght[i][j];
1049 del_v[2] = del_v[2] + (m_z[adj[i][j]] - m_z[i]) * wght[i][j];
1050 }
1051
1052 xtemp[i] = m_x[i] + del_v[0] * lambda;
1053 ytemp[i] = m_y[i] + del_v[1] * lambda;
1054 ztemp[i] = m_z[i] + del_v[2] * lambda;
1055 }
1056
1057 // compute second weighted average
1058 for (i = 0; i < m_nvert; ++i)
1059 {
1060 del_v[0] = del_v[1] = del_v[2] = 0;
1061 for (j = 0; j < adj[i].size(); ++j)
1062 {
1063 del_v[0] =
1064 del_v[0] + (xtemp[adj[i][j]] - xtemp[i]) * wght[i][j];
1065 del_v[1] =
1066 del_v[1] + (ytemp[adj[i][j]] - ytemp[i]) * wght[i][j];
1067 del_v[2] =
1068 del_v[2] + (ztemp[adj[i][j]] - ztemp[i]) * wght[i][j];
1069 }
1070
1071 m_x[i] = xtemp[i] + del_v[0] * mu;
1072 m_y[i] = ytemp[i] + del_v[1] * mu;
1073 m_z[i] = ztemp[i] + del_v[2] * mu;
1074 }
1075 }
1076}
1077
1078void Iso::SeparateRegions(vector<IsoSharedPtr> &sep_iso, int minsize,
1079 bool verbose)
1080{
1081 int i, j, k, id;
1083 list<int> tocheck;
1084
1085 Array<OneD, bool> viddone(m_nvert, false);
1086
1087 // make list of connecting tris around each vertex
1088 for (i = 0; i < m_ntris; ++i)
1089 {
1090 for (j = 0; j < 3; ++j)
1091 {
1092 vertcon[m_vid[3 * i + j]].push_back(i);
1093 }
1094 }
1095
1096 Array<OneD, int> vidregion(m_nvert, -1);
1097
1098 int nregions = -1;
1099
1100 // check all points are assigned to a region
1101 int progcnt = -1;
1102 for (k = 0; k < m_nvert; ++k)
1103 {
1104 if (verbose)
1105 {
1107 k, m_nvert, "Separating regions", progcnt);
1108 }
1109
1110 if (vidregion[k] == -1)
1111 {
1112 vidregion[k] = ++nregions;
1113
1114 // find all elmts around this.. vertex that need to be checked
1115 for (auto &ipt : vertcon[k])
1116 {
1117 for (i = 0; i < 3; ++i)
1118 {
1119 if (vidregion[id = m_vid[3 * ipt + i]] == -1)
1120 {
1121 tocheck.push_back(id);
1122 vidregion[id] = nregions;
1123 }
1124 }
1125 }
1126 viddone[k] = 1;
1127
1128 // check all other neighbouring vertices
1129 while (tocheck.size())
1130 {
1131 auto cid = tocheck.begin();
1132 while (cid != tocheck.end())
1133 {
1134 if (!viddone[*cid])
1135 {
1136 for (auto &ipt : vertcon[*cid])
1137 {
1138 for (i = 0; i < 3; ++i)
1139 {
1140 if (vidregion[id = m_vid[3 * ipt + i]] == -1)
1141 {
1142 tocheck.push_back(id);
1143 vidregion[id] = nregions;
1144 }
1145 }
1146 }
1147 viddone[*cid] = 1;
1148 ++cid;
1149 tocheck.pop_front();
1150 }
1151 }
1152 }
1153 }
1154 }
1155 nregions++;
1156
1157 Array<OneD, int> nvert(nregions, 0);
1158 Array<OneD, int> nelmt(nregions, 0);
1159
1160 // count nverts
1161 for (i = 0; i < m_nvert; ++i)
1162 {
1163 nvert[vidregion[i]] += 1;
1164 }
1165
1166 // count nelmts
1167 for (i = 0; i < m_ntris; ++i)
1168 {
1169 nelmt[vidregion[m_vid[3 * i]]] += 1;
1170 }
1171
1172 Array<OneD, int> vidmap(m_nvert);
1173 // generate new list of isocontour
1174 for (int n = 0; n < nregions; ++n)
1175 {
1176 if (nelmt[n] > minsize)
1177 {
1178 int nfields = m_fields.size();
1180
1181 iso->m_ntris = nelmt[n];
1182 iso->m_vid = Array<OneD, int>(3 * nelmt[n]);
1183
1184 iso->m_nvert = nvert[n];
1185 iso->m_x.resize(nvert[n]);
1186 iso->m_y.resize(nvert[n]);
1187 iso->m_z.resize(nvert[n]);
1188
1189 iso->m_fields.resize(nfields);
1190 for (i = 0; i < nfields; ++i)
1191 {
1192 iso->m_fields[i].resize(nvert[n]);
1193 }
1194
1195 int cnt = 0;
1196 // generate vid map;
1197 Vmath::Zero(m_nvert, vidmap, 1);
1198 for (i = 0; i < m_nvert; ++i)
1199 {
1200 if (vidregion[i] == n)
1201 {
1202 vidmap[i] = cnt++;
1203 }
1204 }
1205
1206 cnt = 0;
1207 for (i = 0; i < m_ntris; ++i)
1208 {
1209 if (vidregion[m_vid[3 * i]] == n)
1210 {
1211 for (j = 0; j < 3; ++j)
1212 {
1213 iso->m_vid[3 * cnt + j] = vidmap[m_vid[3 * i + j]];
1214
1215 iso->m_x[vidmap[m_vid[3 * i + j]]] =
1216 m_x[m_vid[3 * i + j]];
1217 iso->m_y[vidmap[m_vid[3 * i + j]]] =
1218 m_y[m_vid[3 * i + j]];
1219 iso->m_z[vidmap[m_vid[3 * i + j]]] =
1220 m_z[m_vid[3 * i + j]];
1221
1222 for (k = 0; k < nfields; ++k)
1223 {
1224 iso->m_fields[k][vidmap[m_vid[3 * i + j]]] =
1225 m_fields[k][m_vid[3 * i + j]];
1226 }
1227 }
1228 cnt++;
1229 }
1230 }
1231
1232 WARNINGL0(cnt == nelmt[n], "Number of elements do not match");
1233 sep_iso.push_back(iso);
1234 }
1235 }
1236}
1237
1238} // namespace Nektar::FieldUtils
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:242
void SeparateRegions(std::vector< std::shared_ptr< Iso > > &iso, int minsize, bool verbose)
std::vector< NekDouble > m_z
void GlobalCondense(std::vector< std::shared_ptr< Iso > > &iso, bool verbose)
std::vector< std::vector< NekDouble > > m_fields
Array< OneD, int > m_vid
std::vector< NekDouble > m_y
std::vector< NekDouble > m_x
void Smooth(int n_iter, NekDouble lambda, NekDouble mu)
std::vector< NekDouble > m_fields
FieldSharedPtr m_f
Field object.
Definition: Module.h:239
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:272
std::vector< IsoSharedPtr > ExtractContour(const int fieldid, const NekDouble val)
void SetupIsoFromFieldPts(std::vector< IsoSharedPtr > &isovec)
void ResetFieldPts(std::vector< IsoSharedPtr > &iso)
static std::shared_ptr< Module > create(FieldSharedPtr f)
Creates an instance of this class.
void v_Process(po::variables_map &vm) override
Write mesh to output file.
Abstract base class for processing modules.
Definition: Module.h:301
Interpreter class for the evaluation of mathematical expressions.
Definition: Interpreter.h:76
int DefineFunction(const std::string &vlist, const std::string &expr)
Defines a function for the purposes of evaluation.
NekDouble Evaluate(const int id)
Evaluate a function which depends only on constants and/or parameters.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:197
NekDouble TimePerTest(unsigned int n)
Returns amount of seconds per iteration in a test with n iterations.
Definition: Timer.cpp:65
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:990
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:180
bool operator==(TriFaceIDs const &p1, TriFaceIDs const &p2)
void ThreeSimilar(const int i, const int j, const int k, int &pr, int &ps)
bool operator!=(const IsoVertex &x, const IsoVertex &y)
void TwoPairs(Array< OneD, NekDouble > &cx, Array< OneD, NekDouble > &cy, Array< OneD, NekDouble > &cz, int &pr)
std::shared_ptr< Iso > IsoSharedPtr
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:47
int PrintProgressbar(const int position, const int goal, const std::string message, int lastprogress=-1)
Prints a progressbar.
Definition: Progressbar.hpp:65
static const NekDouble kNekZeroTol
InputIterator find(InputIterator first, InputIterator last, InputIterator startingpoint, const EqualityComparable &value)
Definition: StdRegions.hpp:447
std::vector< double > w(NPUPPER)
double NekDouble
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.hpp:273
Represents a command-line configuration option.
Definition: Module.h:129