#include "ElementLevelSet.h" #include "ElInfo.h" int ElementLevelSet::createElementLevelSet(const ElInfo *elInfo_, const bool doCalcIntersecPts_) { Element *el = elInfo_->getElement(); if (elInfo == NULL || el != lastEl) { /** * Element has changed. New calculation. */ // Set new element. lastEl = el; // Set information for element and reset data. setElement(elInfo_); // Calculate level set function values for each vertex of element. calculateElementLevelSetVal(); // Check whether level set function values are not too small. checkElementLevelSetVal(); // Calculate status of element and element vertices. calculateElementStatus(); // Calculate intersection points with zero level set if necessary. if (doCalcIntersecPts_ && elStatus == LEVEL_SET_BOUNDARY) { calculateIntersecPoints(); // dim == 3: sort intersection points if necessary if (dim == 3 && numIntersecPoints == 4) sortIntersecPoints_4IP3D(); } } // else { /** * LevelSet-Status for element has already been calculated. */ // } return elStatus; } void ElementLevelSet::calculateElementLevelSetVal() { DimVec<double> lambda(dim, DEFAULT_VALUE, 0.0); for (int i = 0; i <= dim; i++) { lambda[i] = 1.0; lSFct->setElInfo(elInfo); elVertexLevelSetVec[i] = (*lSFct)((const DimVec<double>) lambda); lambda[i] = 0.0; } } int ElementLevelSet::calculateElementStatus() { for (int i=0; i<=dim; ++i) { if (elVertexLevelSetVec[i] < 0) { elVertexStatusVec[i] = LEVEL_SET_INTERIOR; numElVertexInterior++; } else { elVertexStatusVec[i] = LEVEL_SET_EXTERIOR; numElVertexExterior++; } } /** * Calculate level set status of element. */ if (numElVertexInterior == dim+1) { elStatus = LEVEL_SET_INTERIOR; return LEVEL_SET_INTERIOR; } if (numElVertexExterior == dim+1) { elStatus = LEVEL_SET_EXTERIOR; return LEVEL_SET_EXTERIOR; } elStatus = LEVEL_SET_BOUNDARY; return LEVEL_SET_BOUNDARY; } void ElementLevelSet::calculateIntersecPoints() { //************************************************************************** // The level set function is linearly approximated by a hyperplane through // the points of the graph of the level set function in the vertices // of the element. // This routine calculates the intersection points of the hyperplane // with the edges of the element. //************************************************************************** DimVec<double> tempPoint(dim); DimVec<double> zeroVec(dim, DEFAULT_VALUE, 0.0); int i, j; /** * Get intersection points (in barycentric coordinates). * * An edge is intersected if its vertices have level sets with * different sign. */ for (i = 0; i <= dim; i++) { for (j = i+1; j <= dim; j++) { if (elVertexStatusVec[i] * elVertexStatusVec[j] < 0.0) { tempPoint = zeroVec; tempPoint[j] = elVertexLevelSetVec[i] / (elVertexLevelSetVec[i] - elVertexLevelSetVec[j]); checkIntersecBary(tempPoint[j]); tempPoint[i] = 1 - tempPoint[j]; (*elIntersecPoints)[numIntersecPoints] = tempPoint; ++numIntersecPoints; } } // for(j ... } // for(i ... } int ElementLevelSet::checkElementLevelSetVal() { int changed = 0; double abs_grd_val = 0.0; double abs_min_val = 0.0; double comp = 0.0; for (int i=0; i<dim; ++i) { comp = elVertexLevelSetVec[i]-elVertexLevelSetVec[dim]; abs_grd_val += comp*comp; } abs_grd_val = sqrt(abs_grd_val); abs_min_val = LS_VAL_TOL*abs_grd_val; abs_min_val = (abs_min_val > LS_VAL_MIN) ? abs_min_val : LS_VAL_MIN; for (int i=0; i<=dim; ++i) { if (fabs(elVertexLevelSetVec[i]) < abs_min_val) { // elVertexLevelSetVec[i] = abs_min_val; elVertexLevelSetVec[i] = (elVertexLevelSetVec[i] < 0) ? -abs_min_val : abs_min_val; ++changed; } } return changed; } bool ElementLevelSet::checkIntersecBary(double &bary) { if (bary < SP_BARY_TOL) { bary = SP_BARY_TOL; return true; } if (bary > 1-SP_BARY_TOL) { bary = 1-SP_BARY_TOL; return true; } return false; } void ElementLevelSet::sortIntersecPoints_4IP3D() { FUNCNAME("sortIntersecPoints_4IP3D"); int indexFace1 = 0; int indexFace2 = 0; int indexOpVert = 0; DimVec<double> tempPoint(dim); int i,j; /** * Consider the 4 intersection points S0, S1, S2 and S3. If the components * of S0 indexFace1 and indexFace2 are not zero, i.e. S0 lies in * the element faces indexFace1 and indexFace2, the intersection * point with zero components indexFace1 and indexFace2 is * opposite to S0 in the intersection plane. Move this vertex to the end * of the intersection point list elIntersecPoints. */ // Get indexFace1 and indexFace2. for (i = 0; i < numIntersecPoints; i++) { if (fabs((*elIntersecPoints)[0][i]) > 1.e-15) { indexFace1 = i; break; } } for (j = i+1; j < numIntersecPoints; j++) { if (fabs((*elIntersecPoints)[0][j]) > 1.e-15) { indexFace2 = j; break; } } // Get index of vertex opposite to S0. for (i = 1; i < numIntersecPoints; i++) { if (fabs((*elIntersecPoints)[i][indexFace1]) <= 1.e-15 && fabs((*elIntersecPoints)[i][indexFace2]) <= 1.e-15) { indexOpVert = i; break; } } // Move vertex to the end of \ref elIntersecPoints. if (indexOpVert != numIntersecPoints-1) { tempPoint = (*elIntersecPoints)[indexOpVert]; (*elIntersecPoints)[indexOpVert] = (*elIntersecPoints)[numIntersecPoints-1]; (*elIntersecPoints)[numIntersecPoints-1] = tempPoint; } return; } void ElementLevelSet::calcIntersecNormal(WorldVector<double> &normalVec) { FUNCNAME("ElementLevelSet::calcIntersecNormal"); switch(dim) { case 2: calcIntersecNormal_2d(normalVec); break; case 3: calcIntersecNormal_3d(normalVec); break; default: ERROR_EXIT("illegal dimension !\n"); } }; void ElementLevelSet::calcIntersecNormal_2d(WorldVector<double> &normalVec) { FUNCNAME("ElementLevelSet::calcIntersecNormal_2d"); TEST_EXIT(numIntersecPoints == 2)("illegal number of intersection points !\n"); // ===== Get world coordinates of intersection points. ===== WorldVector<double> sP1; WorldVector<double> sP2; elInfo->coordToWorld((*elIntersecPoints)[0], sP1); elInfo->coordToWorld((*elIntersecPoints)[1], sP2); // ===== Calculate normal vector. ===== double norm2 = 0.0; double val; for (int i=0; i<dim; ++i){ val = sP1[i] - sP2[i]; norm2 += val*val; normalVec[dim-1-i] = val; } normalVec[0] *= -1; double norm = sqrt(norm2); for(int i=0; i<dim; ++i) { normalVec[i] = 1/norm * normalVec[i]; } // ===== Set correct orientation (exterior normal vector). ===== // Calculate barycenter. WorldVector<double> baryc; for (int i=0; i<dim; ++i) { baryc[i] = 0.5*sP1[i] + 0.5*sP2[i]; } double d = sqrt((sP1-sP2)*(sP1-sP2)); // Barycenter + factor*normalVec. double elSize = sqrt(fabs(elInfo->getDet())); double factor = 0.01*d/elSize; WorldVector<double> tmpPoint; int cntr = 0; DimVec<double> lambda(dim, NO_INIT); while (1) { ++cntr; for(int i=0; i<dim; ++i) { tmpPoint[i] = baryc[i] + factor*normalVec[i]; } elInfo->worldToCoord(tmpPoint, &lambda); for (int i=0; i<=dim; ++i) { if (lambda[i] < 0) { factor *= 0.1; if (cntr == 10) { WARNING("inefficient normal vector calculation !\n"); } if (cntr == 100) { ERROR_EXIT("infinite loop !\n"); } continue; } } break; } if (ElementLevelSet::calcLevelSetFct(lambda) < 0) { for (int i=0; i<dim; ++i) { normalVec[i] *= -1; } } }; void ElementLevelSet::calcIntersecNormal_3d(WorldVector<double> &normalVec) { FUNCNAME("ElementLevelSet::calcIntersecNormal_3d"); TEST_EXIT(numIntersecPoints == 3 || numIntersecPoints == 4)("illegal number of intersection points !\n"); // ===== Get world coordinates of intersection points. ===== WorldVector<double> sP1; WorldVector<double> sP2; WorldVector<double> sP3; elInfo->coordToWorld((*elIntersecPoints)[0], sP1); elInfo->coordToWorld((*elIntersecPoints)[1], sP2); elInfo->coordToWorld((*elIntersecPoints)[2], sP3); // ===== Calculate normal vector. ===== WorldVector<double> A = sP2-sP1; WorldVector<double> B = sP3-sP1; vectorProduct(A, B, normalVec); double norm = sqrt(normalVec * normalVec); for(int i=0; i<dim; ++i) { normalVec[i] = 1/(norm+1.e-4) * normalVec[i]; } // ===== Set correct orientation (exterior normal vector). ===== // Calculate barycenter. WorldVector<double> baryc; for (int i=0; i<dim; ++i) { baryc[i] = 1.0/3*sP1[i] + 1.0/3*sP2[i] + 1.0/3*sP3[i]; } double d = sqrt((sP1-sP2)*(sP1-sP2)); // Barycenter + factor*normalVec. double elSize = pow(fabs(elInfo->getDet()), 1.0/3); double factor = 0.01*d/elSize; WorldVector<double> tmpPoint; int cntr = 0; DimVec<double> lambda(dim, NO_INIT); while (1) { ++cntr; for(int i=0; i<dim; ++i) { tmpPoint[i] = baryc[i] + factor*normalVec[i]; } elInfo->worldToCoord(tmpPoint, &lambda); for (int i=0; i<=dim; ++i) { if (lambda[i] < 0) { factor *= 0.1; if (cntr == 10) { WARNING("inefficient normal vector calculation !\n"); } if (cntr == 100) { ERROR_EXIT("infinite loop !\n"); } continue; } } break; } if (ElementLevelSet::calcLevelSetFct(lambda) < 0) { for (int i=0; i<dim; ++i) { normalVec[i] *= -1; } } }; int ElementLevelSet::getVertexPos(const DimVec<double> barCoords) { double vertex_val; for (int i=0; i<=dim; ++i) { if (barCoords[i] > 1-1.e-15) { vertex_val = elVertexLevelSetVec[i]; break; } } if (vertex_val > 0) { return LEVEL_SET_EXTERIOR; } else { return LEVEL_SET_INTERIOR; } } int ElementLevelSet::getElPos(const DimVec<double> barCoords) { double ls_val = calcLevelSetFct(barCoords); if (ls_val > 0) { return LEVEL_SET_EXTERIOR; } else { return LEVEL_SET_INTERIOR; } }