diff --git a/Cassiopee/CPlot/apps/tkCADFix.py b/Cassiopee/CPlot/apps/tkCADFix.py index 8017f6313..a684c960c 100644 --- a/Cassiopee/CPlot/apps/tkCADFix.py +++ b/Cassiopee/CPlot/apps/tkCADFix.py @@ -161,14 +161,15 @@ def removeFaces(event=None): CTK.setCursor(2, WIDGETS['frame']) CTK.setCursor(2, WIDGETS['removeFacesButton']) - OCC._removeFaces(hook, faces) + edgeMap = []; faceMap = [] + OCC._removeFaces(hook, faces, edgeMap, faceMap) # remesh CAD and redisplay edges = Internal.getNodeFromName1(CTK.t, 'EDGES') edges[2] = [] faces = Internal.getNodeFromName1(CTK.t, 'FACES') faces[2] = [] - OCC._meshAllEdges(hook, CTK.t, hmax=hmax, hausd=hausd) # loose manual remeshing... + OCC._meshAllEdges(hook, CTK.t, hmax=hmax, hausd=hausd) OCC._meshAllFacesTri(hook, CTK.t, hmax=hmax, hausd=hausd) CTK.setCursor(0, WIDGETS['frame']) CTK.setCursor(0, WIDGETS['removeFacesButton']) diff --git a/Cassiopee/CPlot/apps/tkCADMesh.py b/Cassiopee/CPlot/apps/tkCADMesh.py index f71749eab..74c4abc64 100644 --- a/Cassiopee/CPlot/apps/tkCADMesh.py +++ b/Cassiopee/CPlot/apps/tkCADMesh.py @@ -17,6 +17,7 @@ def meshCADEdges(event=None): import OCC.PyTree as OCC hmax = CTK.varsFromWidget(VARS[0].get(), 1)[0] hausd = CTK.varsFromWidget(VARS[1].get(), 1)[0] + CTK.saveTree() CTK.setCursor(2, WIDGETS['frame']) # remesh CAD and redisplay edges = Internal.getNodeFromName1(CTK.t, 'EDGES') @@ -38,10 +39,9 @@ def meshCADFaces(event=None): import OCC.PyTree as OCC hmax = CTK.varsFromWidget(VARS[0].get(), 1)[0] hausd = CTK.varsFromWidget(VARS[1].get(), 1)[0] - + CTK.saveTree() CTK.setCursor(2, WIDGETS['frame']) CTK.setCursor(2, WIDGETS['meshFaceButton']) - faces = Internal.getNodeFromName1(CTK.t, 'FACES') if faces is not None: faces[2] = [] if mtype == 'TRI': diff --git a/Cassiopee/CPlot/apps/tkMapEdge.py b/Cassiopee/CPlot/apps/tkMapEdge.py index bb0816e27..c390c607c 100644 --- a/Cassiopee/CPlot/apps/tkMapEdge.py +++ b/Cassiopee/CPlot/apps/tkMapEdge.py @@ -1155,6 +1155,7 @@ def enforceLocal(event=None): imax = ind+1+delta CAD = Internal.getNodeFromName1(z, 'CAD') + render = Internal.getNodeFromName1(z, '.RenderInfo') #print("imin=",imin,"imax",imax,"ind",ind,"npts",npts) if imin > 1: z0 = T.subzone(z, (1,1,1), (imin,-1,-1)) @@ -1220,6 +1221,7 @@ def enforceLocal(event=None): if z1 is not None: zo = T.join(zo, z1) zo[0] = z[0] # keep orig name and CAD zo[2].append(CAD) + if render: zo[2].append(render) CTK.replace(CTK.t, nob, noz, zo) (CTK.Nb, CTK.Nz) = CPlot.updateCPlotNumbering(CTK.t) diff --git a/Cassiopee/CPlot/apps/tkView.py b/Cassiopee/CPlot/apps/tkView.py index 301d5b968..aec65e993 100644 --- a/Cassiopee/CPlot/apps/tkView.py +++ b/Cassiopee/CPlot/apps/tkView.py @@ -396,14 +396,14 @@ def loadSlot(): pos = Internal.getNodesFromName(slot, 'isoScales*') if pos != []: updateIsoWidgets(); updateIsoPyTree() - scales = [] - for p in pos: - name = p[0] - name = name.replace('isoScales[', '') - name = name[0:-2] - out = [name]+p[1].tolist() - scales.append(out) - if scales != []: CPlot.setState(isoScales=scales) + scales = [] + for p in pos: + name = p[0] + name = name.replace('isoScales[', '') + name = name[0:-2] + out = [name]+p[1].tolist() + scales.append(out) + if scales != []: CPlot.setState(isoScales=scales) pos = Internal.getNodeFromName1(slot, 'mode') if pos is not None: diff --git a/Cassiopee/Converter/Converter/PyTree.py b/Cassiopee/Converter/Converter/PyTree.py index fb4020a87..50386cc8c 100755 --- a/Cassiopee/Converter/Converter/PyTree.py +++ b/Cassiopee/Converter/Converter/PyTree.py @@ -1017,13 +1017,14 @@ def convertFile2PyTree(fileName, format=None, nptsCurve=20, nptsLine=2, CAD = Internal.getNodeFromName1(t, 'CAD') if CAD is not None: # reload CAD file = Internal.getNodeFromName1(CAD, 'file') - file = Internal.getValue(file) + if file is not None: file = Internal.getValue(file) fmt = Internal.getNodeFromName1(CAD, 'format') - fmt = Internal.getValue(fmt) - import OCC.PyTree as OCC - import CPlot.Tk as CTK - hook = OCC.readCAD(file, fmt) - CTK.CADHOOK = hook + if fmt is not None: fmt = Internal.getValue(fmt) + if file is not None and fmt is not None: + import OCC.PyTree as OCC + import CPlot.Tk as CTK + hook = OCC.readCAD(file, fmt) + CTK.CADHOOK = hook return t except: if format == 'bin_cgns' or format == 'bin_adf': diff --git a/Cassiopee/Envs/env_Cassiopee_local b/Cassiopee/Envs/env_Cassiopee_local index 4a375be0a..d763d9db8 100644 --- a/Cassiopee/Envs/env_Cassiopee_local +++ b/Cassiopee/Envs/env_Cassiopee_local @@ -127,6 +127,7 @@ else if ($MAC == "ld") then @ ncpu++ setenv OMP_NUM_THREADS $ncpu setenv ASAN_OPTIONS verify_asan_link_order=false + setenv LSAN_OPTIONS suppressions="$CASSIOPEE"/Dist/bin/"$ELSAPROD"/asan.supp:print_suppressions=0 setenv ASAN_LIB /opt/tools/gcc/10.2.0-gnu831/lib64/libasan.so else if ($MAC == "eos814") then @@ -228,6 +229,9 @@ else if ($MAC == "ubuntu") then setenv PYTHONEXE=python3 setenv PRODMODE=1 setenv PIP_DISABLE_PIP_VERSION_CHECK=1 + setenv ASAN_OPTIONS verify_asan_link_order=false + setenv LSAN_OPTIONS suppressions="$CASSIOPEE"/Dist/bin/"$ELSAPROD"/asan.supp:print_suppressions=0 + setenv ASAN_LIB /usr/lib/gcc/x86_64-linux-gnu/13/libasan.so else if ($MAC == "fulvio") then #------------------------------- fulvio --------------------------------------- @@ -700,6 +704,7 @@ else if ($MAC == "juno_gcc") then unsetenv SLURM* unsetenv OMP_PLACES setenv ASAN_OPTIONS verify_asan_link_order=false + setenv LSAN_OPTIONS suppressions="$CASSIOPEE"/Dist/bin/"$ELSAPROD"/asan.supp:print_suppressions=0 setenv ASAN_LIB /opt/tools/gcc/12.1.0-gnu831/lib64/libasan.so else if ($MAC == "juno_gpu") then diff --git a/Cassiopee/Envs/sh_Cassiopee_local b/Cassiopee/Envs/sh_Cassiopee_local index 51c126f3c..6becf0b1b 100644 --- a/Cassiopee/Envs/sh_Cassiopee_local +++ b/Cassiopee/Envs/sh_Cassiopee_local @@ -157,6 +157,9 @@ elif [ "$MAC" = "ubuntu" ]; then export PYTHONEXE=python3 export PRODMODE=1 export PIP_DISABLE_PIP_VERSION_CHECK=1 + export ASAN_OPTIONS=verify_asan_link_order=false + export LSAN_OPTIONS=suppressions=$CASSIOPEE/Dist/bin/"$ELSAPROD"/asan.supp:print_suppressions=0 + export ASAN_LIB=/usr/lib/gcc/x86_64-linux-gnu/13/libasan.so elif [ "$MAC" = "azure" ]; then #------------------------ azure: ubuntu - github actions ---------------------- diff --git a/Cassiopee/KCore/KCore/Nuga/include/metric.hxx b/Cassiopee/KCore/KCore/Nuga/include/metric.hxx index 0ddf9e76a..a79fe20cc 100755 --- a/Cassiopee/KCore/KCore/Nuga/include/metric.hxx +++ b/Cassiopee/KCore/KCore/Nuga/include/metric.hxx @@ -22,12 +22,9 @@ #ifndef NUGA_METRIC_HXX #define NUGA_METRIC_HXX - namespace NUGA { - enum eMetricType { ISO_MIN=0, ISO_MEAN=1, ISO_MAX=2,/*, ANISO*/ }; - } #endif diff --git a/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp b/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp index 620f21b87..18ac76e3d 100644 --- a/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp +++ b/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp @@ -118,6 +118,7 @@ E_Int __getParamHausd(const TopoDS_Edge& E, E_Float hausd, E_Int& nbPoints, E_Fl return 0; } +// ============================================================================ // Geom distrib entre u0 et u1, h0 et h1 (interieurs) void geom1(E_Float u0, E_Float u1, E_Float h0, E_Float h1, E_Int& N, E_Float*& ue) { diff --git a/Cassiopee/OCC/OCC/Atomic/removeFaces.cpp b/Cassiopee/OCC/OCC/Atomic/removeFaces.cpp index 0afc18699..ec7330282 100644 --- a/Cassiopee/OCC/OCC/Atomic/removeFaces.cpp +++ b/Cassiopee/OCC/OCC/Atomic/removeFaces.cpp @@ -20,6 +20,7 @@ #include "occ.h" #include "TopoDS.hxx" +#include "TopoDS_Edge.hxx" #include "BRep_Tool.hxx" #include "TopExp.hxx" #include "TopExp_Explorer.hxx" @@ -27,13 +28,69 @@ #include "ShapeBuild_ReShape.hxx" #include "BRep_Builder.hxx" +//============================================================= +void getEdgeMap(TopTools_IndexedMapOfShape& oldEdges, TopTools_IndexedMapOfShape& newEdges, PyObject*& edgeMap) +{ + bool found; + E_Int neold = oldEdges.Extent(); + E_Int nenew = newEdges.Extent(); + PyList_SetSlice(edgeMap, 0, PyList_Size(edgeMap), NULL); + for (E_Int i = 1; i <= nenew; i++) + { + const TopoDS_Edge& E = TopoDS::Edge(newEdges(i)); + + found = false; + for (E_Int j = 1; j <= neold; j++) + { + const TopoDS_Edge& EO = TopoDS::Edge(oldEdges(j)); + if (E.IsSame(EO)) + { + //printf("edge identified %d %d\n", i, j); + PyList_Append(edgeMap, PyLong_FromLong(j)); + found = true; + break; + } + } + if (not found) PyList_Append(edgeMap, PyLong_FromLong(-1)); + //printf("%d -> %p\n", i, (void*)&E); + } +} + +//============================================================= +// Get new->old for faces +void getFaceMap(TopTools_IndexedMapOfShape& oldFaces, TopTools_IndexedMapOfShape& newFaces, PyObject*& faceMap) +{ + bool found; + E_Int neold = oldFaces.Extent(); + E_Int nenew = newFaces.Extent(); + PyList_SetSlice(faceMap, 0, PyList_Size(faceMap), NULL); + for (E_Int i = 1; i <= nenew; i++) + { + const TopoDS_Face& F = TopoDS::Face(newFaces(i)); + found = false; + for (E_Int j = 1; j <= neold; j++) + { + const TopoDS_Face& FO = TopoDS::Face(oldFaces(j)); + if (F.IsSame(FO)) + { + //printf("face identified %d %d\n", i, j); + PyList_Append(faceMap, PyLong_FromLong(j)); + found = true; + break; + } + } + if (not found) PyList_Append(faceMap, PyLong_FromLong(-1)); + } +} + //===================================================================== // Remove some faces and rebuild compound +// output edgeMap et faceMap //===================================================================== PyObject* K_OCC::removeFaces(PyObject* self, PyObject* args) { - PyObject* hook; PyObject* listFaces; - if (!PYPARSETUPLE_(args, OO_, &hook, &listFaces)) return NULL; + PyObject* hook; PyObject* listFaces; PyObject* edgeMap; PyObject* faceMap; + if (!PYPARSETUPLE_(args, OO_ OO_, &hook, &listFaces, &edgeMap, &faceMap)) return NULL; void** packet = NULL; #if (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 7) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 1) @@ -44,10 +101,15 @@ PyObject* K_OCC::removeFaces(PyObject* self, PyObject* args) // get top shape TopoDS_Shape* shp = (TopoDS_Shape*)packet[0]; - //TopTools_IndexedMapOfShape& edges = *(TopTools_IndexedMapOfShape*)packet[2]; + TopTools_IndexedMapOfShape& edges = *(TopTools_IndexedMapOfShape*)packet[2]; TopTools_IndexedMapOfShape& surfaces = *(TopTools_IndexedMapOfShape*)packet[1]; E_Int nbFaces = surfaces.Extent(); + //Handle(TDF_Data) data = new TDF_Data(); + //TDF_Label label = data->Root(); + //TDataStd_Name::Set(label, "MyFaceTag"); + //TDF_Tool::AddShape(label, F); + ShapeBuild_ReShape reshaper; for (E_Int no = 0; no < PyList_Size(listFaces); no++) { @@ -64,21 +126,22 @@ PyObject* K_OCC::removeFaces(PyObject* self, PyObject* args) TopoDS_Shape shc = reshaper.Apply(*shp); // export - delete shp; TopoDS_Shape* newshp = new TopoDS_Shape(shc); - - // Export - packet[0] = newshp; - TopTools_IndexedMapOfShape* ptr = (TopTools_IndexedMapOfShape*)packet[1]; - delete ptr; TopTools_IndexedMapOfShape* sf = new TopTools_IndexedMapOfShape(); TopExp::MapShapes(*newshp, TopAbs_FACE, *sf); - packet[1] = sf; - TopTools_IndexedMapOfShape* ptr2 = (TopTools_IndexedMapOfShape*)packet[2]; - delete ptr2; TopTools_IndexedMapOfShape* se = new TopTools_IndexedMapOfShape(); TopExp::MapShapes(*newshp, TopAbs_EDGE, *se); + getFaceMap(surfaces, *sf, faceMap); + getEdgeMap(edges, *se, edgeMap); + delete shp; + TopTools_IndexedMapOfShape* ptr = (TopTools_IndexedMapOfShape*)packet[1]; + delete ptr; + TopTools_IndexedMapOfShape* ptr2 = (TopTools_IndexedMapOfShape*)packet[2]; + delete ptr2; + packet[0] = newshp; + packet[1] = sf; packet[2] = se; + printf("INFO: after removeFaces: Nb edges=%d\n", se->Extent()); printf("INFO: after removeFaces: Nb faces=%d\n", sf->Extent()); diff --git a/Cassiopee/OCC/OCC/OCCSurface.cpp b/Cassiopee/OCC/OCC/OCCSurface.cpp index 0739637fb..761c45ff0 100644 --- a/Cassiopee/OCC/OCC/OCCSurface.cpp +++ b/Cassiopee/OCC/OCC/OCCSurface.cpp @@ -463,7 +463,6 @@ void K_OCC::OCCSurface::__normalize(E_Float& u, E_Float& v) const v = (v - _V0) / (_V1 - _V0); } - void K_OCC::OCCSurface::__denormalize(E_Float& u, E_Float& v) const { if (!_normalize_domain) return; @@ -510,7 +509,6 @@ void K_OCC::OCCSurface::__traverse_face_edges(const TopoDS_Face& F, TopExp_Explo void K_OCC::OCCSurface::__get_params_and_type (const TopoDS_Face& F, E_Float& U0, E_Float& U1, E_Float& V0, E_Float& V1, bool& isUClosed, bool& isVClosed) { - E_Float paramtol = 1.e-6; ShapeAnalysis::GetFaceUVBounds(F, U0, U1, V0, V1); @@ -530,4 +528,4 @@ void K_OCC::OCCSurface::__get_params_and_type isUClosed = (U0 == paramtol) & (U1 == (twoPI - paramtol)); isVClosed = (V0 == paramtol) & (V1 == (twoPI - paramtol)); - } +} diff --git a/Cassiopee/OCC/OCC/PyTree.py b/Cassiopee/OCC/OCC/PyTree.py index 332ebd742..93c084cf3 100644 --- a/Cassiopee/OCC/OCC/PyTree.py +++ b/Cassiopee/OCC/OCC/PyTree.py @@ -462,12 +462,14 @@ def getNo(e): return no # return the position of entities in base baseName by number -def getPos(t, baseName): +def getPos(t, baseName=None): pos = {}; posi = {} - b = Internal.getNodeFromName1(t, baseName) + if baseName is not None: + b = Internal.getNodeFromName1(t, baseName) + else: b = t # suppose t is already chosen base for c, e in enumerate(b[2]): cad = Internal.getNodeFromName1(e, 'CAD') - if cad is not None: # this is a CAD edge + if cad is not None: # this is a CAD edge or face no = Internal.getNodeFromName1(cad, 'no') no = Internal.getValue(no) pos[no] = c @@ -804,7 +806,7 @@ def _meshAllFacesTri(hook, t, metric=True, faceList=None, hList=[], hmax=-1, hau dedges = [] for z in Internal.getZones(b): pf = Internal.getNodeFromName2(z, 'u') - if pf is None: print("u field missing in edges.") + if pf is None: print("Error: meshAllFacesTri: u field missing in edges.") e = C.getFields([Internal.__GridCoordinates__, Internal.__FlowSolutionNodes__], z, api=2)[0] dedges.append(e) @@ -817,7 +819,8 @@ def _meshAllFacesTri(hook, t, metric=True, faceList=None, hList=[], hmax=-1, hau faceList = range(nstart+1, nend+1) if hList is None or hList == []: - if hausd < 0: hList = [(hmax,hmax,hausd)]*len(faceList) + if hausd < 0 and hmax > 0: hList = [(hmax,hmax,hausd)]*len(faceList) + elif hausd > 0 and hmax < 0: hList = [(1.e-5,10000.,hausd)]*len(faceList) else: hList = [(hmax*0.8,hmax*1.2,hausd)]*len(faceList) faces = OCC.meshAllFacesTri(hook, dedges, metric, faceList, hList) @@ -1174,11 +1177,129 @@ def _addFillet(hook, edges, radius): OCC.occ.addFillet(hook, edges, radius) return None -def _removeFaces(hook, faces): - OCC.occ.removeFaces(hook, faces) +# edgeMap and faceMap are new2old maps +def _removeFaces(hook, faces, new2OldEdgeMap=[], new2OldFaceMap=[]): + OCC.occ.removeFaces(hook, faces, new2OldEdgeMap, new2OldFaceMap) return None # edges: edge list no must be ordered def _fillHole(hook, edges): OCC.occ.fillHole(hook, edges) - return None + return None + +def getNbEdges(hook): + """Return the number of edges in CAD hook.""" + return OCC.occ.getNbEdges(hook) + +def getNbFaces(hook): + """Return the number of faces in CAD hook.""" + return OCC.occ.getNbFaces(hook) + +# IN: new2old: new2old map +# IN: Nold: size of old entities +# OUT: odl2new array +def getOld2NewMap(Nold, new2old): + old2new = numpy.zeros( (Nold), dtype=Internal.E_NpyInt) + old2new[:] = -1 + Nnew = len(new2old) + for n in range(Nnew): + v = new2old[n] + if v > 0: old2new[v-1] = n+1 + return old2new + +# update numbering in persistent zones +def _updateCADNumbering__(b, old2new, name): + zones = Internal.getZones(b) + for z in zones: + CAD = Internal.getNodeFromName1(z, 'CAD') + if CAD is not None: + n = Internal.getNodeFromName1(CAD, 'name') + no = Internal.getValue(n) + no = no.replace(name, '') + no = int(no) + no = old2new[no-1] + print("updating ",no) + Internal._setValue(n, name+'%03d'%no) + z[0] = name+'%03d'%no + n = Internal.getNodeFromName1(CAD, 'no') + no = Internal.getValue(n) + no = old2new[no-1] + Internal._setValue(n, no) + return None + +# update numbering first step +def _updateEdgesNumbering__(t, old2NewEdgeMap, old2NewFaceMap): + b = Internal.getNodeFromName1(t, 'EDGES') + _updateCADNumbering__(b, old2NewEdgeMap, 'edge') + for z in Internal.getZones(b): + CAD = Internal.getNodeFromName1(z, 'CAD') + if CAD is not None: + n = Internal.getNodeFromName1(CAD, 'faceList') + if n is not None: + pn = n[1]; index = [] + for i in range(pn.size): + pn[i] = old2NewFaceMap[pn[i]-1] + if pn[i] == -1: index.append(i) + n[1] = numpy.delete(pn, index) + return None + +def _updateFacesNumbering__(t, old2NewEdgeMap, old2NewFaceMap): + b = Internal.getNodeFromName1(t, 'FACES') + _updateCADNumbering__(b, old2NewFaceMap, 'face') + for z in Internal.getZones(b): + CAD = Internal.getNodeFromName1(z, 'CAD') + if CAD is not None: + n = Internal.getNodeFromName1(CAD, 'edgeList') + if n is not None: + pn = n[1]; index = [] + for i in range(pn.size): + pn[i] = old2NewEdgeMap[pn[i]-1] + if pn[i] == -1: index.append(i) + n[1] = numpy.delete(pn, index) + return None + +def _updateNumbering(t, old2NewEdgeMap, old2NewFaceMap): + _updateEdgesNumbering__(t, old2NewEdgeMap, old2NewFaceMap) + _updateFacesNumbering__(t, old2NewEdgeMap, old2NewFaceMap) + return None + +# suppress edge or face depending on old2new +def _suppressZones__(b, old2new): + rme = [] + for i in range(old2new.size): + if old2new[i] == -1: rme.append(i+1) + pos, posi = getPos(b) + rme = list(reversed(rme)) + for c, f in enumerate(rme): + cd = pos[f] + del b[2][cd] + return None + +# add zones depending on new2old +def _addZones__(b, new2old): + addme = [] + for i in range(new2old.size): + if new2old[i] == -1: addme.append(i+1) + # mesh and add zone... + return None + +# update tree when CAD hook is modified +def _updateTree(t, oldNbEdges, oldNbFaces, new2OldEdgeMap, new2OldFaceMap): + """Update tree when CAD is mmodified.""" + old2NewEdgeMap = getOld2NewMap(oldNbEdges, new2OldEdgeMap) + old2NewFaceMap = getOld2NewMap(oldNbFaces, new2OldFaceMap) + + # remove zones that are no longer in CAD + b = Internal.getNodeFromName1(t, 'EDGES') + _suppressZones__(b, old2NewEdgeMap) + b = Internal.getNodeFromName1(t, 'FACES') + _suppressZones__(b, old2NewFaceMap) + + # update numbering of persistent zones + _updateNumbering(t, old2NewEdgeMap, old2NewFaceMap) + + # add new edges and faces... + + # recolor + _setLonelyEdgesColor(t) + return None \ No newline at end of file