|
7 | 7 | of the SCOREC Non-Commercial License this program is distributed under. |
8 | 8 | |
9 | 9 | *******************************************************************************/ |
| 10 | +/* |
| 11 | +This file contains two coarsening alogrithms. |
| 12 | + 1. coarsenMultiple: will repeatedly create independent sets and collapse the mesh |
| 13 | + until there are no more short edges left. This code is currently only used for testing |
| 14 | + since more work needs to be done to achieve better performance and have the code work in |
| 15 | + parallel, at which point the other coarsen algorithm can be deleted. |
| 16 | + 2. coarsen: The first will create one independent set and then collapse all of the edges |
| 17 | + in that independent set. |
| 18 | +*/ |
10 | 19 | #include "maCoarsen.h" |
11 | 20 | #include "maAdapt.h" |
12 | 21 | #include "maCollapse.h" |
13 | 22 | #include "maMatchedCollapse.h" |
14 | 23 | #include "maOperator.h" |
| 24 | +#include "maDBG.h" |
15 | 25 | #include <pcu_util.h> |
| 26 | +#include "apfShape.h" |
| 27 | +#include <vector> |
| 28 | +#include <list> |
| 29 | +#include <algorithm> |
16 | 30 |
|
17 | 31 | namespace ma { |
18 | 32 |
|
| 33 | +namespace { |
| 34 | + |
| 35 | +//Measures edge length and stores the result so it doesn't have to be calculated again |
| 36 | +double getLength(Adapt* a, Tag* lengthTag, Entity* edge) |
| 37 | +{ |
| 38 | + double length = 0; |
| 39 | + if (a->mesh->hasTag(edge, lengthTag)) |
| 40 | + a->mesh->getDoubleTag(edge, lengthTag, &length); |
| 41 | + else { |
| 42 | + length = a->sizeField->measure(edge); |
| 43 | + a->mesh->setDoubleTag(edge, lengthTag, &length); |
| 44 | + } |
| 45 | + return length; |
| 46 | +} |
| 47 | + |
| 48 | +//Make sure that a collapse will not create an edge longer than the max |
| 49 | +bool collapseSizeCheck(Adapt* a, Entity* vertex, Entity* edge, apf::Up& adjacent) |
| 50 | +{ |
| 51 | + Entity* vCollapse = getEdgeVertOppositeVert(a->mesh, edge, vertex); |
| 52 | + for (int i=0; i<adjacent.n; i++) { |
| 53 | + Entity* newEdgeVerts[2]{vertex, getEdgeVertOppositeVert(a->mesh, adjacent.e[i], vCollapse)}; |
| 54 | + Entity* newEdge = a->mesh->createEntity(apf::Mesh::EDGE, 0, newEdgeVerts); |
| 55 | + double length = a->sizeField->measure(newEdge); |
| 56 | + destroyElement(a, newEdge); |
| 57 | + if (length > MAXLENGTH) return false; |
| 58 | + } |
| 59 | + return true; |
| 60 | +} |
| 61 | + |
| 62 | +bool tryCollapseEdge(Adapt* a, Entity* edge, Entity* keep, Collapse& collapse, apf::Up& adjacent) |
| 63 | +{ |
| 64 | + PCU_ALWAYS_ASSERT(a->mesh->getType(edge) == apf::Mesh::EDGE); |
| 65 | + bool alreadyFlagged = true; |
| 66 | + if (keep) alreadyFlagged = getFlag(a, keep, DONT_COLLAPSE); |
| 67 | + if (!alreadyFlagged) setFlag(a, keep, DONT_COLLAPSE); |
| 68 | + |
| 69 | + double quality = a->input->shouldForceAdaptation ? a->input->validQuality |
| 70 | + : a->input->goodQuality; |
| 71 | + |
| 72 | + bool result = false; |
| 73 | + if (collapse.setEdge(edge) && |
| 74 | + collapse.checkClass() && |
| 75 | + collapse.checkTopo() && |
| 76 | + collapseSizeCheck(a, keep, edge, adjacent) && |
| 77 | + collapse.tryBothDirections(quality)) { |
| 78 | + result = true; |
| 79 | + } |
| 80 | + if (!alreadyFlagged) clearFlag(a, keep, DONT_COLLAPSE); |
| 81 | + return result; |
| 82 | +} |
| 83 | +} |
| 84 | + |
19 | 85 | class CollapseChecker : public apf::CavityOp |
20 | 86 | { |
21 | 87 | public: |
@@ -253,4 +319,153 @@ bool coarsen(Adapt* a) |
253 | 319 | return true; |
254 | 320 | } |
255 | 321 |
|
| 322 | +/* |
| 323 | + To be used after collapsing a vertex to flag adjacent vertices as NEED_NOT_COLLAPSE. This will create a |
| 324 | + set of collapses where no two adjacent are vertices collapsed. Will also clear adjacent vertices for |
| 325 | + collapse in next independent set since they might succeed after a collapse. |
| 326 | +*/ |
| 327 | +void flagIndependentSet(Adapt* a, apf::Up& adjacent, size_t& checked) |
| 328 | +{ |
| 329 | + for (int adj=0; adj < adjacent.n; adj++) { |
| 330 | + Entity* vertices[2]; |
| 331 | + a->mesh->getDownward(adjacent.e[adj],0, vertices); |
| 332 | + for (int v = 0; v < 2; v++) { |
| 333 | + setFlag(a, vertices[v], NEED_NOT_COLLAPSE); |
| 334 | + if (getFlag(a, vertices[v], CHECKED)){ |
| 335 | + clearFlag(a, vertices[v], CHECKED); //needs to be checked again in next independent set |
| 336 | + checked--; |
| 337 | + } |
| 338 | + } |
| 339 | + } |
| 340 | +} |
| 341 | + |
| 342 | +struct EdgeLength |
| 343 | +{ |
| 344 | + Entity* edge; |
| 345 | + double length; |
| 346 | + bool operator<(const EdgeLength& other) const { |
| 347 | + return length < other.length; |
| 348 | + } |
| 349 | +}; |
| 350 | + |
| 351 | +/* |
| 352 | + Given an iterator pointing to a vertex we will collapse the shortest adjacent edge and try the next |
| 353 | + shorted until one succeeds and then it will expand independent set. In Li's thesis it only attempts |
| 354 | + to collapse the shortest edge, but this gave us better results. |
| 355 | +*/ |
| 356 | +bool collapseShortest(Adapt* a, Collapse& collapse, std::list<Entity*>& shortEdgeVerts, std::list<Entity*>::iterator& itr, size_t& checked, apf::Up& adjacent, Tag* lengthTag) |
| 357 | +{ |
| 358 | + Entity* vertex = *itr; |
| 359 | + std::vector<EdgeLength> sorted; |
| 360 | + for (int i=0; i < adjacent.n; i++) { |
| 361 | + double length = getLength(a, lengthTag, adjacent.e[i]); |
| 362 | + EdgeLength measured{adjacent.e[i], length}; |
| 363 | + if (measured.length > MINLENGTH) continue; |
| 364 | + sorted.push_back(measured); |
| 365 | + } |
| 366 | + if (sorted.size() == 0) { //performance optimization, will rarely result in a missed edge |
| 367 | + itr = shortEdgeVerts.erase(itr); |
| 368 | + return false; |
| 369 | + } |
| 370 | + std::sort(sorted.begin(), sorted.end()); |
| 371 | + for (size_t i=0; i < sorted.size(); i++) { |
| 372 | + Entity* keepVertex = getEdgeVertOppositeVert(a->mesh, sorted[i].edge, vertex); |
| 373 | + if (!tryCollapseEdge(a, sorted[i].edge, keepVertex, collapse, adjacent)) continue; |
| 374 | + flagIndependentSet(a, adjacent, checked); |
| 375 | + itr = shortEdgeVerts.erase(itr); |
| 376 | + collapse.destroyOldElements(); |
| 377 | + return true; |
| 378 | + } |
| 379 | + setFlag(a, vertex, CHECKED); |
| 380 | + checked++; |
| 381 | + return false; |
| 382 | +} |
| 383 | + |
| 384 | +/* |
| 385 | + Iterates through shortEdgeVerts until it finds a vertex that is adjacent to an |
| 386 | + independent set. We want our collapses to touch the independent set in order to |
| 387 | + reduce adjacent collapses, since collapsing adjacent vertices will result in a |
| 388 | + lower quality mesh. |
| 389 | +*/ |
| 390 | +bool getAdjIndependentSet(Adapt* a, std::list<Entity*>& shortEdgeVerts, std::list<Entity*>::iterator& itr, bool& independentSetStarted, apf::Up& adjacent) |
| 391 | +{ |
| 392 | + size_t numItr=0; |
| 393 | + do { |
| 394 | + numItr++; |
| 395 | + if (itr == shortEdgeVerts.end()) itr = shortEdgeVerts.begin(); |
| 396 | + Entity* vertex = *itr; |
| 397 | + if (getFlag(a, vertex, CHECKED)) {itr++; continue;} //Already tried to collapse |
| 398 | + a->mesh->getUp(vertex, adjacent); |
| 399 | + if (!independentSetStarted) return true; |
| 400 | + if (getFlag(a, vertex, NEED_NOT_COLLAPSE)) {itr++; continue;} //Too close to last collapse |
| 401 | + for (int i=0; i < adjacent.n; i++) |
| 402 | + { |
| 403 | + Entity* opposite = getEdgeVertOppositeVert(a->mesh, adjacent.e[i], vertex); |
| 404 | + if (getFlag(a, opposite, NEED_NOT_COLLAPSE)) return true; //Touching independent set |
| 405 | + } |
| 406 | + itr++; |
| 407 | + } while (numItr < shortEdgeVerts.size()); |
| 408 | + for (auto v : shortEdgeVerts) clearFlag(a, v, NEED_NOT_COLLAPSE); |
| 409 | + independentSetStarted = false; |
| 410 | + return false; |
| 411 | +} |
| 412 | + |
| 413 | +//returns a list of vertices that bound a short edge and flags them |
| 414 | +std::list<Entity*> getShortEdgeVerts(Adapt* a, Tag* lengthTag) |
| 415 | +{ |
| 416 | + std::list<Entity*> shortEdgeVerts; |
| 417 | + Iterator* it = a->mesh->begin(1); |
| 418 | + Entity* edge; |
| 419 | + while ((edge = a->mesh->iterate(it))) |
| 420 | + { |
| 421 | + double length = getLength(a, lengthTag, edge); |
| 422 | + if (length > MINLENGTH) continue; |
| 423 | + Entity* vertices[2]; |
| 424 | + a->mesh->getDownward(edge,0,vertices); |
| 425 | + for (int i = 0; i < 2; i++) { |
| 426 | + if (getFlag(a, vertices[i], CHECKED)) continue; |
| 427 | + setFlag(a, vertices[i], CHECKED); |
| 428 | + shortEdgeVerts.push_back(vertices[i]); |
| 429 | + } |
| 430 | + } |
| 431 | + for (auto v : shortEdgeVerts) clearFlag(a, v, CHECKED); |
| 432 | + a->mesh->end(it); |
| 433 | + return shortEdgeVerts; |
| 434 | +} |
| 435 | + |
| 436 | +/* |
| 437 | + Follows the alogritm in Li's thesis in order to coarsen all short edges |
| 438 | + in a mesh while maintaining a decent quality mesh. |
| 439 | +*/ |
| 440 | +bool coarsenMultiple(Adapt* a) |
| 441 | +{ |
| 442 | + if (!a->input->shouldCoarsen) |
| 443 | + return false; |
| 444 | + double t0 = pcu::Time(); |
| 445 | + Tag* lengthTag = a->mesh->createDoubleTag("edge_length", 1); |
| 446 | + std::list<Entity*> shortEdgeVerts = getShortEdgeVerts(a, lengthTag); |
| 447 | + |
| 448 | + Collapse collapse; |
| 449 | + collapse.Init(a); |
| 450 | + int success = 0; |
| 451 | + size_t checked = 0; |
| 452 | + bool independentSetStarted = false; |
| 453 | + std::list<Entity*>::iterator itr = shortEdgeVerts.begin(); |
| 454 | + while (checked < shortEdgeVerts.size()) |
| 455 | + { |
| 456 | + apf::Up adjacent; |
| 457 | + if (!getAdjIndependentSet(a, shortEdgeVerts, itr, independentSetStarted, adjacent)) continue; |
| 458 | + if (collapseShortest(a, collapse, shortEdgeVerts, itr, checked, adjacent, lengthTag)) { |
| 459 | + independentSetStarted=true; |
| 460 | + success++; |
| 461 | + } |
| 462 | + } |
| 463 | + ma::clearFlagFromDimension(a, NEED_NOT_COLLAPSE | CHECKED, 0); |
| 464 | + a->mesh->destroyTag(lengthTag); |
| 465 | + double t1 = pcu::Time(); |
| 466 | + print(a->mesh->getPCU(), "coarsened %d edges in %f seconds", success, t1-t0); |
| 467 | + return true; |
| 468 | +} |
| 469 | + |
| 470 | + |
256 | 471 | } |
0 commit comments