diff --git a/Plugin/CMakeLists.txt b/Plugin/CMakeLists.txt index b93ce557b7ed5973db18ef7ee3a390a6a5ed1c9b..4af19054c1cb0fbb97c9d82f5d7961ad3ed6b6b4 100644 --- a/Plugin/CMakeLists.txt +++ b/Plugin/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRC CutMesh.cpp NewView.cpp SimplePartition.cpp Crack.cpp + FaultZone.cpp ) file(GLOB HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) diff --git a/Plugin/FaultZone.cpp b/Plugin/FaultZone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ae32aa5dc42a70f58c81eb78a9cee50cc236408 --- /dev/null +++ b/Plugin/FaultZone.cpp @@ -0,0 +1,763 @@ +// Gmsh - Copyright (C) 1997-2013 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to the public mailing list <gmsh@geuz.org>. + +#include <assert.h> +#include <sstream> +#include <stdlib.h> +#include "Numeric.h" +#include "Context.h" +#include "GmshMessage.h" +#include "FaultZone.h" + +#include "Geo.h" +#include "GModel.h" +#include "SVector3.h" +#include "GEdge.h" +#include "GFace.h" +#include "discreteFace.h" +#include "MVertex.h" +#include "MElement.h" +#include "MQuadrangle.h" + +StringXNumber FaultZoneOptions_Number[] = { + {GMSH_FULLRC, "SurfaceTag", NULL, 1}, + {GMSH_FULLRC, "Thickness", NULL, 0.}, +}; + +StringXString FaultZoneOptions_String[] = { + {GMSH_FULLRC, "Prefix", NULL, "FAMI_"}, +}; + +extern "C" +{ + GMSH_Plugin *GMSH_RegisterFaultZonePlugin() + { + return new GMSH_FaultZonePlugin(); + } +} + +std::string GMSH_FaultZonePlugin::getHelp() const +{ + return "Plugin(FaultZone) convert all the embedded lines of an existing surfacic mesh to flat quadrangles. " + "Flat quadrangles represent joint elements suitable to model a fault zone with Code_Aster." + "\n\n" + "`SurfaceTag' must be an existing plane surface containing embedded lines. " + "Embedded lines must have been added to the surface via the command Line {} In Surface {}. " + "The surface must be meshed with quadratic incomplete elements." + "\n\n" + "`Thickness' is the thichness of the flat quadrangles. " + "Set a value different to zero can be helpfull to check the connectivity. " + "\n\n" + "`Prefix' is the prefix of the name of physicals containing the new embedded. " + "All physicals containing embedded lines are replaced by physicals containing the coresponding joint elements."; +} + +int GMSH_FaultZonePlugin::getNbOptions() const +{ + return sizeof(FaultZoneOptions_Number) / sizeof(StringXNumber); +} + +StringXNumber *GMSH_FaultZonePlugin::getOption(int iopt) +{ + return &FaultZoneOptions_Number[iopt]; +} + +int GMSH_FaultZonePlugin::getNbOptionsStr() const +{ + return sizeof(FaultZoneOptions_String) / sizeof(StringXString); +} + +StringXString *GMSH_FaultZonePlugin::getOptionStr(int iopt) +{ + return &FaultZoneOptions_String[iopt]; +} + +//============================================================================= +/*! + * \brief Execute the pluggin FaultZone + */ +//============================================================================= +PView *GMSH_FaultZonePlugin::execute(PView *view) +{ + int tag = (int)FaultZoneOptions_Number[0].def; + double eps = (double)FaultZoneOptions_Number[1].def; + std::string prefix = FaultZoneOptions_String[0].def; + + // controls + + GModel *gModel = GModel::current(); + + GFace *gFace = gModel->getFaceByTag(tag); + + if (!gFace){ + Msg::Error("Face %d does not exist or was not meshed",tag); + return view; + } + + if (!gFace->embeddedEdges().size()){ + Msg::Error("No line to treat in this surface"); + return view; + } + + std::list< GEdge* > embeddedEdges = gFace->embeddedEdges(); + std::list<GEdge*>::const_iterator itl; + for(itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl) + if ((*itl)->length() != 0) break; + if (itl == embeddedEdges.end()){ + Msg::Error("No line to treat in this surface"); + Msg::Error("The plugin FaultZone may have been already run for this surface. Reload your geometry"); + return view; + } + + for(itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl) + if ((*itl)->geomType() != GEntity::Line) break; + if (itl != embeddedEdges.end()){ + Msg::Error("All the embedded edges must be straight lines"); + return view; + } + + for(itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl){ + Curve* c = FindCurve((*itl)->tag()); + if (c && List_Nbr(c->Control_Points) > 2) break; + } + if (itl != embeddedEdges.end()){ + Msg::Error("The embedded edges must not contain control points"); + return view; + } + + for(itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl){ + GEdge* gEdge = *itl; + unsigned int i = 0; + for(; i < gEdge->getNumMeshElements(); i++) + if (gEdge->getMeshElement(i)->getNumVertices() == 3) break; + if (i == gEdge->getNumMeshElements()) break; + } + if (itl != embeddedEdges.end()){ + Msg::Error("The mesh must be order 2"); + return view; + } + + // end of controls + + GMSH_FaultZoneMesher faultZoneMesher; + + faultZoneMesher.RetriveFissuresInfos(gFace); + + faultZoneMesher.DuplicateNodes(); + + faultZoneMesher.ComputeHeavisideFunction(); + + faultZoneMesher.CreateJointElements(gModel, gFace, prefix); + + faultZoneMesher.ModifyElementsConnectivity(gFace); + + if (eps) + faultZoneMesher.ModifyJointNodePosition(eps/2); + + CTX::instance()->mesh.changed = ENT_ALL; + + return view; +} + +//================================================================================ +/*! + * \brief retirive fissures (embedded edges) infos in maps. + * this function fills in the following set and maps : + * _jointElements, + * _fissureByHeavNode, + * _vectNormByFissure, + * _fissuresByJunctionNode, + * _vectsTanByJunctionNode, + * _connectedElements + */ +//================================================================================ +void GMSH_FaultZoneMesher::RetriveFissuresInfos(GFace* gFace){ + + std::list< GEdge* > embeddedEdges = gFace->embeddedEdges(); + std::list< GEdge* > edges = gFace->edges(); + + // set with all the MVertex of the fissures + std::set < MVertex* > allFissuresVertices; + for(std::list<GEdge*>::const_iterator itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl){ + GEdge *gEdge = *itl; + for (unsigned int i = 0; i < gEdge->getNumMeshVertices(); i++){ + allFissuresVertices.insert(gEdge->getMeshVertex(i)); + } + allFissuresVertices.insert(gEdge->getBeginVertex()->getMeshVertex(0)); + allFissuresVertices.insert(gEdge->getEndVertex()->getMeshVertex(0)); + } + + // set with all quadratic MVertex of the fissures connected to the surface + std::set < MVertex* > allConnectedQuadraticVertices; + // fill _connectedElements + for(unsigned int i = 0; i < gFace->getNumMeshElements(); i++){ + MElement *mElem = gFace->getMeshElement(i); + for (int j = 0; j < mElem->getNumVertices(); j++){ + MVertex *mVert = mElem->getVertex(j); + std::set < MVertex* >::iterator its = allFissuresVertices.find( mVert ); + if (its != allFissuresVertices.end()){ + _connectedElements.insert(mElem); + if (mVert->getPolynomialOrder() == 2) + allConnectedQuadraticVertices.insert( mVert ); + } + } + } + // for the generatrices + for(std::list<GEdge*>::const_iterator itl = edges.begin();itl != edges.end(); ++itl){ + GEdge *gEdge = *itl; + for(unsigned int i = 0; i < gEdge->getNumMeshElements(); i++){ + MElement *mElem = gEdge->getMeshElement(i); + for (int j = 0; j < mElem->getNumVertices(); j++){ + MVertex *mVert = mElem->getVertex(j); + std::set < MVertex* >::iterator its = allFissuresVertices.find( mVert ); + if (its != allFissuresVertices.end()){ + _connectedElements.insert(mElem); + } + } + } + } + + for(std::list<GEdge*>::const_iterator itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl){ + GEdge *gEdge = *itl; + if (gEdge->length() == 0)// nothing to do if there is no element + continue; + + MVertex *mVertexBegin = gEdge->getBeginVertex()->getMeshVertex(0); + MVertex *mVertexEnd = gEdge->getEndVertex()->getMeshVertex(0); + + SPoint3 pointBegin = gEdge->getBeginVertex()->xyz(); + SPoint3 pointEnd = gEdge->getEndVertex()->xyz(); + + SVector3 vectTanBegin = SVector3(pointEnd, pointBegin); + SVector3 vectTanEnd = SVector3(pointBegin, pointEnd); + SVector3 vectNorm = crossprod(vectZ , vectTanEnd); + + vectTanBegin.normalize(); + vectTanEnd.normalize(); + double norm = vectNorm.normalize(); + assert(norm); + + // fill _jointElements and _fissureByHeavNode + for(unsigned int i = 0; i < gEdge->getNumMeshElements(); i++){ + MElement *mElem = gEdge->getMeshElement(i); + assert(mElem->getNumVertices() == 3); + elementsIt its; + for (its = _jointElements.begin(); its != _jointElements.end(); its++){ + assert((*its)->getNumVertices() == 3); + // the MElements are considered the same if the quadratic node is the same + if ((*its)->getVertex(2) == mElem->getVertex(2)){ + break; + } + } + + if (its != _jointElements.end()){ + Msg::Warning("Element edge %d appears in a second GEntity", mElem->getNum()); + continue; + } + + // the MElements are considered connected if the quadratic node is connected + std::set < MVertex* >::iterator its2 = allConnectedQuadraticVertices.find( mElem->getVertex(2) ); + if (its2 == allConnectedQuadraticVertices.end()){ + Msg::Warning("Element edge %d seams to be not connected, it will be ignored", mElem->getNum()); + continue; + } + + _jointElements.insert(mElem); + for(int j = 0; j < mElem->getNumVertices(); j++){ + MVertex *mVert = mElem->getVertex(j); + _fissureByHeavNode[mVert] = gEdge; + } + } + + // fill _vectNormByFissure + _vectNormByFissure[gEdge] = vectNorm; + + // fill _fissuresByJunctionNode and _vectsTanByJunctionNode + std::map < MVertex* , GEdge* >::iterator itm = _fissureByHeavNode.find(mVertexBegin); + if (itm != _fissureByHeavNode.end()){ + _fissuresByJunctionNode[mVertexBegin].push_back(gEdge); + _vectsTanByJunctionNode[mVertexBegin].push_back( vectTanBegin ); + _fissureByHeavNode.erase(mVertexBegin); + } + itm = _fissureByHeavNode.find(mVertexEnd); + if (itm != _fissureByHeavNode.end()){ + _fissuresByJunctionNode[mVertexEnd].push_back( gEdge ); + _vectsTanByJunctionNode[mVertexEnd].push_back( vectTanEnd ); + _fissureByHeavNode.erase(mVertexEnd); + } + } +} + +//================================================================================ +/*! + * \brief vectZ + */ +const SVector3 GMSH_FaultZoneMesher::vectZ = SVector3(0,0,1); + + +//================================================================================ +/*! + * \brief duplicates nodes of the embedded edges to model the joints + * this function fills in the following maps : + * _vectsTanByTipNode; + * _nodeByHeavNode; + * _nodeJointByHeavOrJunctionNode; + * _nodesByJunctionNode; + * + * The new nodes are not yet associated to a GEntity. The association + * will be done in the CreateJointElements function. + */ +//================================================================================ +void GMSH_FaultZoneMesher::DuplicateNodes(){ + + // fill _nodeJointByHeavOrJunctionNode and _nodesByJunctionNode + for(std::map<MVertex*,std::vector<GEdge*> >::iterator itm=_fissuresByJunctionNode.begin();itm != _fissuresByJunctionNode.end();){ + MVertex *mVert = itm->first; + std::vector < GEdge* > fissures = itm->second; + + unsigned int nbFiss = fissures.size(); + if (nbFiss == 1){ // if only one fissure, the node will be treated in _fissureByHeavNode + _fissureByHeavNode[mVert] = fissures.front(); + _vectsTanByTipNode[mVert] = _vectsTanByJunctionNode[mVert][0]; + _vectsTanByJunctionNode.erase(mVert); + _fissuresByJunctionNode.erase(itm++); + } + else{ + std::vector < MVertex* > mVertices; + mVertices.push_back(mVert); + for (unsigned int i = 0; i < nbFiss-1; i++){ + MVertex *mVertJonc = new MVertex(mVert->x(), mVert->y(), mVert->z()); + mVertices.push_back(mVertJonc); + } + _nodesByJunctionNode[mVert] = mVertices; + MVertex *mVertJoint = new MVertex(mVert->x(), mVert->y(), mVert->z()); + mVertJoint->setPolynomialOrder(2); + _nodeJointByHeavOrJunctionNode[mVert] = mVertJoint; + itm++; + } + } + + // fill _nodeJointByHeavOrJunctionNode and _nodeByHeavNode + for(std::map<MVertex*,GEdge*>::iterator itm=_fissureByHeavNode.begin();itm != _fissureByHeavNode.end(); itm++){ + MVertex *mVert = itm->first; + if (mVert->getPolynomialOrder() == 1){ + MVertex *mVertJoint; + mVertJoint = new MVertex(mVert->x(), mVert->y(), mVert->z()); + mVertJoint->setPolynomialOrder(2); + _nodeJointByHeavOrJunctionNode[mVert] = mVertJoint; + } + MVertex *mVertHeav; + mVertHeav = new MVertex(mVert->x(), mVert->y(), mVert->z()); + _nodeByHeavNode[mVert] = mVertHeav; + } + +} + +//================================================================================ +/*! + * \brief compute Heaviside function for junction nodes + * this function fills in the following maps : + * _heavFuncByJunctionNode; + * + * The _heavFuncByJunctionNode map is used to determinate from which + * domain the junction nodes contained in _nodesByJunctionNode are + * associated. A domain is characterised by the Heaviside values of + * the corresponding level set where the iso-zeros describe the fissures. + * Each term can be +1 (upper the fissure), -1 (under the fissure), + * 0 (undeterminate, can be upper or under). + */ +//================================================================================ +void GMSH_FaultZoneMesher::ComputeHeavisideFunction(){ + + for (std::map<MVertex*,std::vector<GEdge*> >::iterator itm = _fissuresByJunctionNode.begin(); itm != _fissuresByJunctionNode.end(); itm++){ + MVertex *mVert = itm->first; + std::vector < GEdge* > fissures = itm->second; + unsigned int size = fissures.size(); + assert(size >= 2); + std::vector < SVector3 > vectsTan = _vectsTanByJunctionNode[mVert]; + assert(vectsTan.size() == size); + + std::vector < SVector3 > vectsNor; + for (std::vector<GEdge*>::iterator itl = fissures.begin(); itl != fissures.end(); itl++){ + vectsNor.push_back(_vectNormByFissure[*itl]); + } + assert(vectsNor.size() == size); + std::vector < std::vector< int> > heavFunc; + + for (unsigned int i=0; i < size; i++){ + std::vector < int > heav(size, 0); + + if (i == 0){ + heavFunc.insert(heavFunc.begin(), heav); + continue; + } + // found from which side of fissure i, the precedent fissures are placed + // upper = 1, under = -1, both = 0 + bool upper = false; + bool under = false; + for (unsigned int j=0; j<i; j++){ + double lsn = -dot(vectsNor[i], vectsTan[j]); + upper = (upper || lsn > 0); + under = (under || lsn < 0); + } + if (!under) heav[i] = 1; + if (under && !upper) heav[i] = -1; + + // compute the heaviside functions of the precedent fissures for a point located on fissure i + for (unsigned int j=0; j < i;j++){ + double lsn = -dot(vectsNor[j], vectsTan[i]); + if (lsn == 0){ + lsn = dot(vectsNor[j], vectsNor[i])*heav[i]; + } + heav[j] = sign(lsn); + } + + if (heav[i] != 0){ + // add the new domain on the side where the precedent fissure are + // + // Fissure2 + // Fissure1 / + // \ / + // \ / + // \ / + // new domain `. / + // \ / + // \ / + // Fissure3 -----------------------+ + // + heavFunc.insert(heavFunc.begin(), heav); + } + else{ + // find the domain where the fissure i is and duplicates it with value -1 and +1 for the fissure i + // + // Fissure1 + // / + // found domain / + // with value +1 / + // / + // / + // + / + // Fissure3 -----------------------; + // - `._ + // `. + // `. + // found domain `._ + // with value -1 `. + // Fissure2 + // + bool isDomain = false; + for (unsigned int j=0; j < i;j++){ + isDomain = compareHeav(heavFunc[j], heav); + if (isDomain){ + heavFunc.insert(heavFunc.begin()+j, heavFunc[j]); + heavFunc[j][i] = -1; + heavFunc[j+1][i] = 1; + break; + } + } + assert(isDomain); + } + } + _heavFuncByJunctionNode[mVert] = heavFunc; + } +} + +//================================================================================ +/*! + * \brief Return the value of the heaviside function associated to mVert evaluated + * on point sPoint. + */ +//================================================================================ +std::vector < int > GMSH_FaultZoneMesher::HeavisideFunc(MVertex* mVert, SPoint3& sPoint){ + + SVector3 vectPoint = SVector3(mVert->point(), sPoint); + double norm = vectPoint.normalize(); + assert(norm); + + if (_nodeByHeavNode.find( mVert ) != _nodeByHeavNode.end()){// if it is a pure heaviside node + std::vector < int > heav(1, 0); + SVector3 vectNorm = _vectNormByFissure[_fissureByHeavNode[mVert]]; + double lsn = dot(vectPoint, vectNorm); + if (lsn == 0){ + // verify the point is outside the fissure + assert(_vectsTanByTipNode.find( mVert ) != _vectsTanByTipNode.end()); + SVector3 vectTan = _vectsTanByTipNode[mVert]; + assert(dot(vectPoint, vectTan) > 0); + } + heav[0] = sign(lsn); + return heav; + } + else if (_nodesByJunctionNode.find( mVert ) != _nodesByJunctionNode.end()){ // if it is a junction node + std::vector < GEdge* > fissures = _fissuresByJunctionNode[mVert]; + unsigned int size = fissures.size(); + + std::vector < int > heav(size, 0); + for (unsigned int i=0; i < size; i++){ + SVector3 vectNorm = _vectNormByFissure[fissures[i]]; + double lsn = dot(vectPoint, vectNorm); + if (fabs(lsn) > 1e-12) // tolerance seem to be ok + heav[i] = sign(lsn); + } + return heav; + } + else// if it is not a heaviside node + assert(false); +} + +//================================================================================ +/*! + * \brief create joint Elements. For each element in the embedded edges, + * a corresponding face element is created, based on the nodes of the edge element. + * additionnal nodes are picked in the _nodeJointByHeavOrJunctionNode, + * _nodeByHeavNode and _nodesByJunctionNode maps. In case of _nodesByJunctionNode, + * the _heavFuncByJunctionNode map is used to determinate the corresponding node. + * + * this function also replace physical edges containning the embedded edges by + * new corresponding physical faces + */ + //================================================================================ +void GMSH_FaultZoneMesher::CreateJointElements(GModel* gModel, GFace* gFace, std::string prefix){ + + std::list< GEdge * > embeddedEdges = gFace->embeddedEdges(); + std::map < int , int > discreteFaceEntityByEmbeddedEdgeEntity; + for(std::list<GEdge*>::const_iterator itl = embeddedEdges.begin();itl != embeddedEdges.end(); ++itl){ + GEdge *gEdge = *itl; + if (gEdge->length() == 0)// nothing to do if there is no element + continue; + + // creation of a new GFace + GFace *jointFace = new discreteFace(gModel, gModel->getMaxElementaryNumber(2) + 1); + discreteFaceEntityByEmbeddedEdgeEntity[gEdge->tag()] = jointFace->tag(); + gModel->add(jointFace); + + // for each MElement in the GEdge, a new MElement is created and inserted in GFace + for(unsigned int i = 0; i < gEdge->getNumMeshElements(); i++){ + MElement *mElem = gEdge->getMeshElement(i); + elementsIt its = _jointElements.find(mElem); + if (its == _jointElements.end()) + continue; + + SPoint3 bary = mElem->barycenter(); + std::vector <MVertex*> mVerts(8, NULL); + + // retriving MVertices to create the new MElement + for(int j = 0; j < mElem->getNumVertices(); j++){ + MVertex *mVert = mElem->getVertex(j); + + if (j<2) // adding intern node + mVerts[_numNodeJoint[j]] = _nodeJointByHeavOrJunctionNode[mVert]; + + if (_nodeByHeavNode.find( mVert ) != _nodeByHeavNode.end()){ + // adding upper and under nodes + mVerts[_numNodeHeavInf[j]] = mVert; + mVerts[_numNodeHeavSup[j]] = _nodeByHeavNode[mVert]; + } + else{ + std::vector < int > heav = HeavisideFunc(mVert, bary); + unsigned int size = heav.size(); + assert(size > 1); + std::vector < GEdge* > fissures = _fissuresByJunctionNode[mVert]; + assert (fissures.size() == size); + std::vector <std::vector< int > > heavFunc = _heavFuncByJunctionNode[mVert]; + assert (heavFunc.size() == size); + std::vector < MVertex*> nodes = _nodesByJunctionNode[mVert]; + assert (nodes.size() == size); + + unsigned int k; + for (k=0; k < size; k++){ + if (fissures[k]->tag() == gEdge->tag()) + break; + } + assert(k < size); + + // adding under node + heav[k] = -1; + mVerts[_numNodeHeavInf[j]] = nodes[findMatchingHeav(heavFunc, heav)]; + + // adding upper node + heav[k] = 1; + mVerts[_numNodeHeavSup[j]] = nodes[findMatchingHeav(heavFunc, heav)]; + } + } + // association of the MVertices (created in function DuplicateNodes) to the new GEntity (created here) + for(int j =0; j<8; j++){ + MVertex *mVert = mVerts[j]; + assert(mVert!= NULL); + if (mVert->onWhat() == 0){ + mVert->setEntity(jointFace); + jointFace->addMeshVertex(mVert); + } + } + // creation of the new MElement and push into the new GEntity + MQuadrangle8* mQuad8 = new MQuadrangle8(mVerts[0], mVerts[1], mVerts[2], mVerts[3], + mVerts[4], mVerts[5], mVerts[6], mVerts[7]); + jointFace->quadrangles.push_back(mQuad8); + } + // hide gEdge and set lenght to zero to detect that this edge was treated + gEdge->setVisibility(0); + gEdge->setLength(0); + } + + // replace physical edges by physical surfaces + for(int i = 0; i < List_Nbr(gModel->getGEOInternals()->PhysicalGroups); i++){ + PhysicalGroup *p = *(PhysicalGroup**)List_Pointer + (GModel::current()->getGEOInternals()->PhysicalGroups, i); + if(p->Typ == MSH_PHYSICAL_LINE){ + List_T *faceEntities = List_Create(2, 1, sizeof(int)); + for(int j = 0; j < List_Nbr(p->Entities); j++){ + int num; + List_Read(p->Entities, j, &num); + std::map < int ,int >::iterator itm = discreteFaceEntityByEmbeddedEdgeEntity.find(num); + if (itm != discreteFaceEntityByEmbeddedEdgeEntity.end()){ + List_Add(faceEntities, &itm->second); + } + } + if (List_Nbr(faceEntities) > 0){ + std::stringstream sufix; + sufix << p->Num; + int num = gModel->setPhysicalName(prefix+sufix.str(), 2, ++GModel::current()->getGEOInternals()->MaxPhysicalNum); + PhysicalGroup *pnew = Create_PhysicalGroup(num, MSH_PHYSICAL_SURFACE, faceEntities); + List_Add(gModel->getGEOInternals()->PhysicalGroups, &pnew); + for(int j = 0; j < List_Nbr(faceEntities); j++){ + int num; + List_Read(faceEntities, j, &num); + GFace* gFace = gModel->getFaceByTag(num); + gFace->physicals.push_back(pnew->Num); + } + List_Remove(gModel->getGEOInternals()->PhysicalGroups, i); + gModel->deletePhysicalGroup(1, p->Num); + i-=1; + } + List_Delete(faceEntities); + } + } +} + +//================================================================================ +/*! + * \brief corresponding quad8 node number to seg3 node number for under side + */ +const int GMSH_FaultZoneMesher::_numNodeHeavInf[3] = {0, 1, 4}; + +/*! + * \brief corresponding quad8 node number to seg3 node number for upper side + */ +const int GMSH_FaultZoneMesher::_numNodeHeavSup[3] = {3, 2, 6}; + +/*! + * \brief corresponding quad8 node number to seg3 node number for middle side + */ +const int GMSH_FaultZoneMesher::_numNodeJoint[2] = {7, 5}; + +//================================================================================ +/*! + * \brief modify the connectivity of face elements connected to the joint elements + * to take into account the duplicates nodes. Nodes are picked in the _nodeByHeavNode + * and _nodesByJunctionNode maps. In case of _nodesByJunctionNode, the + * _heavFuncByJunctionNode map is used to determinate the corresponding node. + */ + //================================================================================ +void GMSH_FaultZoneMesher::ModifyElementsConnectivity(GFace* gFace){ + + // modif connectivity in _connectedElements + for(elementsIt its = _connectedElements.begin();its != _connectedElements.end(); ++its){ + MElement *mElem = *its; + SPoint3 bary = mElem->barycenter(); + std::vector<MVertex*> mVerts; + mElem->getVertices(mVerts); + for (unsigned j = 0; j < mVerts.size(); j++){ + MVertex *mVert = mVerts[j]; + if (_nodeByHeavNode.find( mVert ) == _nodeByHeavNode.end() && _nodesByJunctionNode.find( mVert ) == _nodesByJunctionNode.end()) + continue; + + std::vector < int > heav = HeavisideFunc(mVert, bary); + unsigned int size = heav.size(); + if (size == 1){ // if it is a pure heaviside node + if (heav[0] == 1) // modifying connectivity only if upper side + mElem->setVertex(j, _nodeByHeavNode[mVert]); + } + else{ // if it is a junction node + std::vector <std::vector< int > > heavFunc = _heavFuncByJunctionNode[mVert]; + assert (heavFunc.size() == size); + int i = findMatchingHeav(heavFunc, heav); + std::vector < MVertex*> nodes = _nodesByJunctionNode[mVert]; + assert (nodes.size() == size); + assert(nodes[i]->onWhat() != 0); + mElem->setVertex(j, nodes[i]); + } + } + } +} + +//================================================================================ +/*! + * \brief modify node positions of the joint elements with distance eps. This function + * is optional, it give a thickness to the joint elements that helps to view the joint + * elements appear on the mesh. + * Nodes in the _nodeByHeavNode and _nodesByJunctionNode maps are moved. + */ +//================================================================================ +void GMSH_FaultZoneMesher::ModifyJointNodePosition(double eps){ + + // for pure heaviside nodes + for (std::map<MVertex*,MVertex*>::iterator itm = _nodeByHeavNode.begin(); itm != _nodeByHeavNode.end(); itm++){ + // under side + MVertex *mVert = itm->first; + SVector3 vectNorm = _vectNormByFissure[_fissureByHeavNode[mVert]]; + vectNorm *= eps; + mVert->x() -= vectNorm.x(); + mVert->y() -= vectNorm.y(); + mVert->z() -= vectNorm.z(); + // upper side + mVert = itm->second; + mVert->x() += vectNorm.x(); + mVert->y() += vectNorm.y(); + mVert->z() += vectNorm.z(); + } + + // for junction nodes + std::map < MVertex*, std::set < MElement* > > connectedElementsByJunctionNode; + for (std::map<MVertex*,std::vector<MVertex*> >::iterator itm = _nodesByJunctionNode.begin(); itm != _nodesByJunctionNode.end(); itm++){ + std::vector < MVertex* > nodes = itm->second; + for (unsigned int i=0; i< nodes.size(); i++){ + std::set < MElement* > mElements; + connectedElementsByJunctionNode[nodes[i]] = mElements; + } + } + + std::map<MVertex*,std::set<MElement*> >::iterator itm; + for(elementsIt its = _connectedElements.begin();its != _connectedElements.end(); ++its){ + MElement *mElem = *its; + std::vector<MVertex*> mVerts; + mElem->getVertices(mVerts); + for (unsigned j = 0; j < mVerts.size(); j++){ + itm = connectedElementsByJunctionNode.find( mVerts[j] ); + if (itm != connectedElementsByJunctionNode.end()){ + itm->second.insert(mElem); + } + } + } + + for (itm = connectedElementsByJunctionNode.begin(); itm != connectedElementsByJunctionNode.end(); itm++){ + MVertex *mVert = itm->first; + SPoint3 point = mVert->point(); + + std::set < MElement* > mElements = itm->second; + assert(mElements.size() > 0); + SVector3 vect = SVector3(0,0,0); + for (std::set < MElement* >::iterator its = mElements.begin(); its != mElements.end(); its++){ + MElement *mElem = *its; + SPoint3 bary = mElem->barycenter(); + SVector3 vectPointBary = SVector3(point, bary)*fabs(mElem->getVolume()); + vect += vectPointBary; + } + vect.normalize(); + vect *= eps; + mVert->x() += vect.x(); + mVert->y() += vect.y(); + mVert->z() += vect.z(); + } +} + diff --git a/Plugin/FaultZone.h b/Plugin/FaultZone.h new file mode 100644 index 0000000000000000000000000000000000000000..a25eef0364991420cb5480771b08f5aaf66a0bc7 --- /dev/null +++ b/Plugin/FaultZone.h @@ -0,0 +1,102 @@ +// Gmsh - Copyright (C) 1997-2013 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to the public mailing list <gmsh@geuz.org>. + +#ifndef _MESH_FAULTZONE_H_ +#define _MESH_FAULTZONE_H_ + +#include <assert.h> +#include "Plugin.h" + +class SVector3; +class GEdge; +class GFace; +class MVertex; +class MElement; + + +extern "C" +{ + GMSH_Plugin *GMSH_RegisterFaultZonePlugin(); +} + +class GMSH_FaultZonePlugin : public GMSH_PostPlugin{ + public: + GMSH_FaultZonePlugin(){} + std::string getName() const { return "FaultZone"; } + std::string getShortHelp() const{ return "FaultZone generator"; } + std::string getHelp() const; + int getNbOptions() const; + StringXNumber* getOption(int iopt); + int getNbOptionsStr() const; + StringXString *getOptionStr(int iopt); + PView* execute(PView*); +}; + +//============================================================================= +/*! + * \brief This class creates joint elements over the embedded edges of a GFace + */ +//============================================================================= +class GMSH_FaultZoneMesher{ + public: + GMSH_FaultZoneMesher(){} + void RetriveFissuresInfos(GFace* gFace); + void DuplicateNodes(); + void ComputeHeavisideFunction(); + void CreateJointElements(GModel* gModel, GFace* gFace, const std::string prefix); + void ModifyElementsConnectivity(GFace* gFace); + void ModifyJointNodePosition(const double eps); + private: + std::set < MElement* > _jointElements; + std::map < GEdge* , SVector3 > _vectNormByFissure; + std::map < MVertex* , GEdge* > _fissureByHeavNode; + std::map < MVertex* , std::vector < GEdge* > > _fissuresByJunctionNode; + std::map < MVertex* , std::vector < SVector3 > > _vectsTanByJunctionNode; + std::set < MElement* > _connectedElements; + typedef std::set<MElement*>::iterator elementsIt; + static const SVector3 vectZ; + + std::map < MVertex* , SVector3 > _vectsTanByTipNode; + std::map < MVertex*, MVertex* > _nodeByHeavNode; + std::map < MVertex*, MVertex* > _nodeJointByHeavOrJunctionNode; + std::map < MVertex*, std::vector < MVertex*> > _nodesByJunctionNode; + + std::map < MVertex*, std::vector <std::vector< int > > > _heavFuncByJunctionNode; + + std::vector < int > HeavisideFunc(MVertex* mVert, SPoint3& sPoint); + + static const int _numNodeHeavInf[3]; + static const int _numNodeHeavSup[3]; + static const int _numNodeJoint[2]; +}; + +//============================================================================= +/*! + * \brief Compare two heaviside functions + */ +//============================================================================= +inline bool compareHeav(const std::vector< int > heav1, const std::vector< int > heav2){ + assert(heav1.size() >= heav2.size()); + for (unsigned int i=0; i< heav2.size(); i++){ + if (heav1[i] != 0 and heav1[i] != heav2[i] and heav2[i] != 0){ + return false; + } + } + return true; +} + +//============================================================================= +/*! + * \brief Find the matching heaviside function heav, in the vector heavFunc + */ +//============================================================================= +inline int findMatchingHeav(const std::vector< std::vector < int > >& heavFunc, const std::vector< int >& heav){ + for (unsigned int i=0; i < heavFunc.size();i++){ + if(compareHeav(heavFunc[i], heav)) + return i; + } + assert(false); +} +#endif diff --git a/Plugin/PluginManager.cpp b/Plugin/PluginManager.cpp index 1afbfa3757814d3d95365889d26096aa1bcf2bc1..8f7d763a966481cd50b1daa50a495eada62293af 100644 --- a/Plugin/PluginManager.cpp +++ b/Plugin/PluginManager.cpp @@ -58,6 +58,7 @@ #include "Scal2Vec.h" #include "CutMesh.h" #include "NewView.h" +#include "FaultZone.h" // for testing purposes only :-) #undef HAVE_DLOPEN @@ -248,6 +249,8 @@ void PluginManager::registerDefaultPlugins() ("SimplePartition", GMSH_RegisterSimplePartitionPlugin())); allPlugins.insert(std::pair<std::string, GMSH_Plugin*> ("Crack", GMSH_RegisterCrackPlugin())); + allPlugins.insert(std::pair<std::string, GMSH_Plugin*> + ("FaultZone", GMSH_RegisterFaultZonePlugin())); #if defined(HAVE_TETGEN) allPlugins.insert(std::pair<std::string, GMSH_Plugin*> ("Tetrahedralize", GMSH_RegisterTetrahedralizePlugin())); diff --git a/benchmarks/2d/faultzone.geo b/benchmarks/2d/faultzone.geo new file mode 100644 index 0000000000000000000000000000000000000000..17530537af69bdb587cc3c4558068ed8bce97804 --- /dev/null +++ b/benchmarks/2d/faultzone.geo @@ -0,0 +1,92 @@ +Mesh.ElementOrder = 2; +Mesh.CharacteristicLengthFactor = 0.1; +Mesh.RecombinationAlgorithm = 0; +Mesh.SecondOrderIncomplete = 1; + +Point(1) = {-10,-8,0.000000}; +Point(2) = {4, 6,0.000000}; + +Point(3) = {-11,-4,0.000000}; +Point(4) = {-3, 4,0.000000}; + +Point(5) = {0,-7,0.000000}; +Point(6) = {10, 3,0.000000}; + +Point(7) = {-8,-9,0.000000}; +Point(8) = {3,2,0.000000}; + +Point(9) = {0,-9,0.000000}; +Point(10)= {4,-5,0.000000}; + + +Point(11) = {-9,-9,0.000000}; +Point(12) = { 6,-9,0.000000}; + +Point(13) = { 1,-5 ,0.000000}; +Point(14) = {11,-5 ,0.000000}; + +Point(15) = {-13, 0 ,0.000000}; +Point(16) = { 3, 0 ,0.000000}; + +Point(17) = {-6 ,5 ,.000000}; +Point(18) = {9 ,5 ,.000000}; + +Point(19) = {-4 ,9 ,.000000}; +Point(20) = {4 ,9 ,.000000}; + + +Line(1) = {1,2}; +Line(2) = {3,4}; +Line(3) = {5,6}; +Line(4) = {7,8}; +Line(5) = {9,10}; + + +Line(6) = {11,12}; +Line(7) = {13,14}; +Line(8) = {15,16}; +Line(9) = {17,18}; +Line(10)= {19,20}; + + +Point(288) = {-12.941176,-10.000000,0.000000}; +Point(289) = {-12.941176,0.000000,0.000000}; +Point(290) = {-12.941176,10.000000,0.000000}; +Point(286) = {12.941176,10.000000,0.000000}; +Point(287) = {12.941176,-10.000000,0.000000}; + + +Line(145) = {286,287}; +Line(146) = {287,288}; +Line(147) = {288,289}; +Line(148) = {289,290}; +Line(149) = {290,286}; + +Line Loop(150) = {145,146,147,148,149}; +Plane Surface(1) = {150}; + +group1[] = {1, 2, 3, 4, 5}; +group2[] = {6, 7, 8, 9,10}; + +Physical Line ( 1 ) = { group1[] } ; +Physical Line ( 2 ) = { group2[] } ; + + +Physical Line ( "DROITE" ) = { 145 } ; +Physical Line ( "BAS" ) = { 146 } ; +Physical Line ( "GAUCHE" ) = { 147, 148 } ; +Physical Line ( "HAUT" ) = { 149 } ; + + +Physical Surface ( "MASSIF" ) = { 1 } ; + + +Line { group1[] } In Surface {1}; +Line { group2[] } In Surface {1}; + +Mesh 2; + +Plugin(FaultZone).SurfaceTag = 1; +Plugin(FaultZone).Thickness = 0.05; +Plugin(FaultZone).Prefix = "FAMI_"; +Plugin(FaultZone).Run;