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