From 8492a4ad09178a763e74dfa5fe02dd370990bd6b Mon Sep 17 00:00:00 2001 From: abn Date: Fri, 29 Jan 2016 14:34:59 +0100 Subject: [PATCH] Introduced check functions in MEDFileUMesh: + checkCoherency(), + checkSMESHCoherency() + and clearNodeAndCellNumbers() Also added hasUniqueValues() in DataArrayInt --- src/MEDCoupling/MEDCouplingMemArray.cxx | 18 ++++ src/MEDCoupling/MEDCouplingMemArray.hxx | 1 + .../MEDCouplingBasicsTest2.py | 13 +++ src/MEDCoupling_Swig/MEDCouplingMemArray.i | 1 + src/MEDLoader/MEDFileMesh.cxx | 95 ++++++++++++++++++ src/MEDLoader/MEDFileMesh.hxx | 3 + src/MEDLoader/MEDFileMeshLL.cxx | 33 +++++++ src/MEDLoader/MEDFileMeshLL.hxx | 3 + src/MEDLoader/Swig/MEDLoaderCommon.i | 3 + src/MEDLoader/Swig/MEDLoaderTest3.py | 97 +++++++++++++++++++ 10 files changed, 267 insertions(+) diff --git a/src/MEDCoupling/MEDCouplingMemArray.cxx b/src/MEDCoupling/MEDCouplingMemArray.cxx index 337c822e0..3df9fb6c6 100644 --- a/src/MEDCoupling/MEDCouplingMemArray.cxx +++ b/src/MEDCoupling/MEDCouplingMemArray.cxx @@ -7820,6 +7820,24 @@ bool DataArrayInt::isUniform(int val) const return true; } +/*! + * Checks if all values in \a this array are unique. + * \return bool - \a true if condition above is true + * \throw If \a this is not allocated. + * \throw If \a this->getNumberOfComponents() != 1 + */ +bool DataArrayInt::hasUniqueValues() const +{ + checkAllocated(); + if(getNumberOfComponents()!=1) + throw INTERP_KERNEL::Exception("DataArrayInt::hasOnlyUniqueValues: must be applied on DataArrayInt with only one component, you can call 'rearrange' method before !"); + int nbOfTuples(getNumberOfTuples()); + std::set s(begin(),end()); // in C++11, should use unordered_set (O(1) complexity) + if (s.size() != nbOfTuples) + return false; + return true; +} + /*! * Creates a new DataArrayDouble and assigns all (textual and numerical) data of \a this * array to the new one. diff --git a/src/MEDCoupling/MEDCouplingMemArray.hxx b/src/MEDCoupling/MEDCouplingMemArray.hxx index 93de8ade0..02857f144 100644 --- a/src/MEDCoupling/MEDCouplingMemArray.hxx +++ b/src/MEDCoupling/MEDCouplingMemArray.hxx @@ -522,6 +522,7 @@ namespace ParaMEDMEM MEDCOUPLING_EXPORT DataArrayInt *buildPermArrPerLevel() const; MEDCOUPLING_EXPORT bool isIdentity2(int sizeExpected) const; MEDCOUPLING_EXPORT bool isUniform(int val) const; + MEDCOUPLING_EXPORT bool hasUniqueValues() const; MEDCOUPLING_EXPORT DataArrayInt *substr(int tupleIdBg, int tupleIdEnd=-1) const; MEDCOUPLING_EXPORT void rearrange(int newNbOfCompo); MEDCOUPLING_EXPORT void transpose(); diff --git a/src/MEDCoupling_Swig/MEDCouplingBasicsTest2.py b/src/MEDCoupling_Swig/MEDCouplingBasicsTest2.py index db3777022..f40daab8c 100644 --- a/src/MEDCoupling_Swig/MEDCouplingBasicsTest2.py +++ b/src/MEDCoupling_Swig/MEDCouplingBasicsTest2.py @@ -1870,6 +1870,19 @@ class MEDCouplingBasicsTest2(unittest.TestCase): da2.setIJ(1,0,1.+1.e-11); self.assertTrue(not da2.isUniform(1.,1.e-12)); pass + + def testDAHasUniqueValues1(self): + da=DataArrayInt([1,2,3,4,5]) + self.assertTrue(da.hasUniqueValues()) + da[1,0] = 5 + self.assertFalse(da.hasUniqueValues()) + da=DataArrayInt([]) + self.assertTrue(da.hasUniqueValues()) + da=DataArrayInt([(1,2), (2,3)]) # wrong num of compo + self.assertRaises(InterpKernelException, da.hasUniqueValues) + da=DataArrayInt() # non allocated array + self.assertRaises(InterpKernelException, da.hasUniqueValues) + pass def testDADFromPolarToCart1(self): tab1=[2.,0.2,2.5,0.7] diff --git a/src/MEDCoupling_Swig/MEDCouplingMemArray.i b/src/MEDCoupling_Swig/MEDCouplingMemArray.i index e13e8eec1..663e15f92 100644 --- a/src/MEDCoupling_Swig/MEDCouplingMemArray.i +++ b/src/MEDCoupling_Swig/MEDCouplingMemArray.i @@ -2622,6 +2622,7 @@ namespace ParaMEDMEM DataArrayInt *buildPermArrPerLevel() const throw(INTERP_KERNEL::Exception); bool isIdentity2(int sizeExpected) const throw(INTERP_KERNEL::Exception); bool isUniform(int val) const throw(INTERP_KERNEL::Exception); + bool hasUniqueValues() const throw(INTERP_KERNEL::Exception); DataArrayInt *substr(int tupleIdBg, int tupleIdEnd=-1) const throw(INTERP_KERNEL::Exception); void transpose() throw(INTERP_KERNEL::Exception); DataArrayInt *changeNbOfComponents(int newNbOfComp, int dftValue) const throw(INTERP_KERNEL::Exception); diff --git a/src/MEDLoader/MEDFileMesh.cxx b/src/MEDLoader/MEDFileMesh.cxx index c5c9f58d0..a6f24be56 100644 --- a/src/MEDLoader/MEDFileMesh.cxx +++ b/src/MEDLoader/MEDFileMesh.cxx @@ -2488,6 +2488,97 @@ bool MEDFileUMesh::isEqual(const MEDFileMesh *other, double eps, std::string& wh return pd0->isEqual(pd1,what); } +/*! + * Check that the current object MEDFileUMesh is consistent. This does not check the optional renumbering of + * nodes and cells. This last item is important for SMESH, see checkSMESHCoherency(). + * \throw if any internal part (i.e. mesh sub-levels and single geometric-type meshes) are inconsistent + * \throw if internal family array is inconsistent + * \sa checkSMESHCoherency() + */ +void MEDFileUMesh::checkCoherency() const +{ + if(!_coords || !_coords->isAllocated()) + { + if(!_ms.size()) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): coords are null but some mesh parts are present!"); + if (!_fam_coords) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): coords are null but not the internal node family array!"); + if (!_num_coords || !_rev_num_coords) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): coords are null but not the internal node numbering array!"); + } + else + { + int nbCoo = _coords->getNumberOfTuples(); + if (_fam_coords) + _fam_coords->checkNbOfTuplesAndComp(nbCoo,1,"MEDFileUMesh::checkCoherency(): inconsistent internal node family array!"); + if (_num_coords) + { + _num_coords->checkNbOfTuplesAndComp(nbCoo,1,"MEDFileUMesh::checkCoherency(): inconsistent internal node numbering array!"); + int pos; + int maxValue=_num_coords->getMaxValue(pos); + if (!_rev_num_coords || _rev_num_coords->getNumberOfTuples() != (maxValue+1)) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): inconsistent internal revert node numbering array!"); + } + if ((_num_coords && !_rev_num_coords) || (!_num_coords && _rev_num_coords)) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): inconsistent internal numbering arrays (one is null)!"); + if (_num_coords && !_num_coords->hasUniqueValues()) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): inconsistent internal node numbering array: duplicates found!"); + if (_name_coords) + _name_coords->checkNbOfTuplesAndComp(nbCoo,MED_SNAME_SIZE,"MEDFileUMesh::checkCoherency(): inconsistent internal coord name array!"); + // Now sub part check: + for (std::vector< MEDCouplingAutoRefCountObjectPtr >::const_iterator it=_ms.begin(); + it != _ms.end(); it++) + (*it)->checkCoherency(); + } +} + +/** + * Same as checkCoherency() but also checks that optional entities (edges, faces, volumes) numbers are + * consistent, i.e. the numbering is either set to null for all sub-levels (thus letting SMESH numbers the + * entities as it likes), or non overlapping between all sub-levels. + * \throw if the condition above is not respected + */ +void MEDFileUMesh::checkSMESHCoherency() const +{ + checkCoherency(); + // For all sub-levels, numbering is either always null or with void intersection: + if (_ms.size()) + { + std::vector< MEDCouplingAutoRefCountObjectPtr >::const_iterator it=_ms.begin(); + std::vector< const DataArrayInt * > v; + bool voidOrNot = ((*it)->_num == 0); + for (it++; it != _ms.end(); it++) + if( ((*it)->_num == 0) != voidOrNot ) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): inconsistent numbering between mesh sub-levels!"); + else if (!voidOrNot) + v.push_back((*it)->_num); + if (!voidOrNot) + { + // don't forget the 1st one: + v.push_back(_ms[0]->_num); + MEDCouplingAutoRefCountObjectPtr inter = DataArrayInt::BuildIntersection(v); + if (inter->getNumberOfTuples()) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkCoherency(): overlapping entity numbering between mesh sub-levels!"); + } + } +} + +/** + * Reset optional node and cell numbering for all sub levels in this. This particularly useful to make + * sure SMESH will handle the mesh correctly, as it tries to use those numbers if given. + */ +void MEDFileUMesh::clearNodeAndCellNumbers() +{ + _num_coords = 0; + _rev_num_coords = 0; + for (std::vector< MEDCouplingAutoRefCountObjectPtr >::iterator it=_ms.begin(); + it != _ms.end(); it++) + { + (*it)->_num = 0; + (*it)->_rev_num = 0; + } +} + /*! * Clears redundant attributes of incorporated data arrays. */ @@ -3694,12 +3785,15 @@ void MEDFileUMesh::optimizeFamilies() * other side of the group is no more a neighbor) * - finally, the connectivity of (part of) the top level-cells bordering the group is also modified so that some cells * bordering the newly created boundary use the newly computed nodes. + * Finally note that optional cell numbers are also affected by this method and might become invalid for SMESH. + * Use clearNodeAndCellNumbers() afterwards to ensure a proper SMESH loading. * * \param[in] grpNameM1 name of the (-1)-level group defining the boundary * \param[out] nodesDuplicated ids of the initial nodes which have been duplicated (and whose copy is put at the end of * the coord array) * \param[out] cellsModified ids of the cells whose connectivity has been modified (to use the newly created nodes) * \param[out] cellsNotModified ids of the rest of cells bordering the new boundary whose connectivity remains unchanged. + * \sa clearNodeAndCellNumbers() */ void MEDFileUMesh::buildInnerBoundaryAlongM1Group(const std::string& grpNameM1, DataArrayInt *&nodesDuplicated, DataArrayInt *&cellsModified, DataArrayInt *&cellsNotModified) @@ -3793,6 +3887,7 @@ void MEDFileUMesh::buildInnerBoundaryAlongM1Group(const std::string& grpNameM1, newFam->setPartOfValuesSimple1(0,nbNodes,newNbOfNodes,1,0,1,1); _fam_coords=newFam; } + nodesDuplicated=nodeIdsToDuplicate.retn(); cellsModified=cellsToModifyConn0.retn(); cellsNotModified=cellsToModifyConn1.retn(); diff --git a/src/MEDLoader/MEDFileMesh.hxx b/src/MEDLoader/MEDFileMesh.hxx index b29871d36..bf8fb184e 100644 --- a/src/MEDLoader/MEDFileMesh.hxx +++ b/src/MEDLoader/MEDFileMesh.hxx @@ -255,6 +255,9 @@ namespace ParaMEDMEM MEDLOADER_EXPORT MEDFileMesh *deepCpy() const; MEDLOADER_EXPORT MEDFileMesh *shallowCpy() const; MEDLOADER_EXPORT bool isEqual(const MEDFileMesh *other, double eps, std::string& what) const; + MEDLOADER_EXPORT void checkCoherency() const; + MEDLOADER_EXPORT void checkSMESHCoherency() const; + MEDLOADER_EXPORT void clearNodeAndCellNumbers(); MEDLOADER_EXPORT void clearNonDiscrAttributes() const; MEDLOADER_EXPORT void setName(const std::string& name); // diff --git a/src/MEDLoader/MEDFileMeshLL.cxx b/src/MEDLoader/MEDFileMeshLL.cxx index ff1afac61..4acb98932 100644 --- a/src/MEDLoader/MEDFileMeshLL.cxx +++ b/src/MEDLoader/MEDFileMeshLL.cxx @@ -947,6 +947,29 @@ MEDFileUMeshSplitL1 *MEDFileUMeshSplitL1::deepCpy(DataArrayDouble *coords) const return ret.retn(); } +void MEDFileUMeshSplitL1::checkCoherency() const +{ + if (!_fam || _fam->getNumberOfTuples() != getSize()) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkCoherency(): internal family array has an invalid size!"); + int nbCells = getSize(); + if (_num) + { + _num->checkNbOfTuplesAndComp(nbCells,1,"MEDFileUMeshSplitL1::checkCoherency(): inconsistent internal node numbering array!"); + int pos; + int maxValue=_num->getMaxValue(pos); + if (!_rev_num || _rev_num->getNumberOfTuples() != (maxValue+1)) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkCoherency(): inconsistent internal revert node numbering array!"); + } + if ((_num && !_rev_num) || (!_num && _rev_num)) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkCoherency(): inconsistent internal numbering arrays (one is null)!"); + if (_num && !_num->hasUniqueValues()) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkCoherency(): inconsistent internal node numbering array: duplicates found!"); + if (_names) + _names->checkNbOfTuplesAndComp(nbCells,1,"MEDFileUMeshSplitL1::checkCoherency(): internal cell naming array has an invalid size!"); + + _m_by_types.checkCoherency(); +} + bool MEDFileUMeshSplitL1::isEqual(const MEDFileUMeshSplitL1 *other, double eps, std::string& what) const { if(!_m_by_types.isEqual(other->_m_by_types,eps,what)) @@ -1836,6 +1859,16 @@ bool MEDFileUMeshAggregateCompute::isEqual(const MEDFileUMeshAggregateCompute& o return true; } +void MEDFileUMeshAggregateCompute::checkCoherency() const +{ + if(_mp_time >= _m_time) + for(std::vector< MEDCouplingAutoRefCountObjectPtr >::const_iterator it=_m_parts.begin(); + it!=_m_parts.end(); it++) + (*it)->checkCoherency1(); + else + _m->checkCoherency1(); +} + void MEDFileUMeshAggregateCompute::clearNonDiscrAttributes() const { for(std::vector< MEDCouplingAutoRefCountObjectPtr >::const_iterator it=_m_parts.begin();it!=_m_parts.end();it++) diff --git a/src/MEDLoader/MEDFileMeshLL.hxx b/src/MEDLoader/MEDFileMeshLL.hxx index f40ccf1ab..29c943a00 100644 --- a/src/MEDLoader/MEDFileMeshLL.hxx +++ b/src/MEDLoader/MEDFileMeshLL.hxx @@ -186,6 +186,7 @@ namespace ParaMEDMEM MEDFileUMeshAggregateCompute deepCpy(DataArrayDouble *coords) const; void shallowCpyMeshes(); bool isEqual(const MEDFileUMeshAggregateCompute& other, double eps, std::string& what) const; + void checkCoherency() const; void clearNonDiscrAttributes() const; void synchronizeTinyInfo(const MEDFileMesh& master) const; bool empty() const; @@ -211,6 +212,7 @@ namespace ParaMEDMEM class MEDFileUMeshSplitL1 : public RefCountObject { friend class MEDFileUMeshPermCompute; + friend class MEDFileUMesh; public: MEDFileUMeshSplitL1(const MEDFileUMeshSplitL1& other); MEDFileUMeshSplitL1(const MEDFileUMeshL2& l2, const std::string& mName, int id); @@ -222,6 +224,7 @@ namespace ParaMEDMEM std::vector getDirectChildrenWithNull() const; MEDFileUMeshSplitL1 *shallowCpyUsingCoords(DataArrayDouble *coords) const; MEDFileUMeshSplitL1 *deepCpy(DataArrayDouble *coords) const; + void checkCoherency() const; void setCoords(DataArrayDouble *coords); bool isEqual(const MEDFileUMeshSplitL1 *other, double eps, std::string& what) const; void clearNonDiscrAttributes() const; diff --git a/src/MEDLoader/Swig/MEDLoaderCommon.i b/src/MEDLoader/Swig/MEDLoaderCommon.i index 8e0ef6a21..73b7803d8 100644 --- a/src/MEDLoader/Swig/MEDLoaderCommon.i +++ b/src/MEDLoader/Swig/MEDLoaderCommon.i @@ -1179,6 +1179,9 @@ namespace ParaMEDMEM ~MEDFileUMesh(); int getSpaceDimension() const throw(INTERP_KERNEL::Exception); int getRelativeLevOnGeoType(INTERP_KERNEL::NormalizedCellType gt) const throw(INTERP_KERNEL::Exception); + void checkCoherency() const throw(INTERP_KERNEL::Exception); + void checkSMESHCoherency() const throw(INTERP_KERNEL::Exception); + void clearNodeAndCellNumbers(); // MEDCouplingUMesh *getGroup(int meshDimRelToMaxExt, const std::string& grp, bool renum=false) const throw(INTERP_KERNEL::Exception); MEDCouplingUMesh *getGroups(int meshDimRelToMaxExt, const std::vector& grps, bool renum=false) const throw(INTERP_KERNEL::Exception); diff --git a/src/MEDLoader/Swig/MEDLoaderTest3.py b/src/MEDLoader/Swig/MEDLoaderTest3.py index 9f8a81965..84fcb203d 100644 --- a/src/MEDLoader/Swig/MEDLoaderTest3.py +++ b/src/MEDLoader/Swig/MEDLoaderTest3.py @@ -4852,6 +4852,103 @@ class MEDLoaderTest3(unittest.TestCase): self.assertTrue(mm.getHiddenCppPointer()==mm2.getHiddenCppPointer()) # optimization pass + def testCheckCoherency(self): + m2 = MEDCouplingUMesh("2d", 2) + m2.setCoords(DataArrayDouble([(0.0, 1.0)] * 4, 4,2)) # whatever + m2.setConnectivity(DataArrayInt([NORM_TRI3, 0,1,2,NORM_TRI3, 1,2,3]), DataArrayInt(([0,4,8]))) + m1 , _, _ , _, _ = m2.buildDescendingConnectivity() + mum = MEDFileUMesh() + mum.setMeshAtLevel(0, m2) + mum.setMeshAtLevel(-1, m1) + mum.checkCoherency() + mum2 = mum.deepCpy() + + # Nodes + arr = DataArrayInt([2]*4) + mum.setFamilyFieldArr(1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayInt([2]*4) + mum.setRenumFieldArr(1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + mum.setRenumFieldArr(1, DataArrayInt([2]*4)) + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayAsciiChar(['tutu x']*4) + mum.setNameFieldAtLevel(1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + + # 2D + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayInt([2]*2) + mum.setFamilyFieldArr(0, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayInt([2]*2) + mum.setRenumFieldArr(0, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + mum.setRenumFieldArr(0, DataArrayInt([2]*2)) + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayAsciiChar(['tutu x']*2) + mum.setNameFieldAtLevel(0, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + + # 1D + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayInt([2]*5) + mum.setFamilyFieldArr(-1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayInt([2]*5) + mum.setRenumFieldArr(-1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + mum.setRenumFieldArr(-1, DataArrayInt([2]*5)) + self.assertRaises(InterpKernelException, mum.checkCoherency) + mum=mum2; mum2=mum.deepCpy(); + arr = DataArrayAsciiChar(['tutu x']*5) + mum.setNameFieldAtLevel(-1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkCoherency) + + def testCheckSMESHCoherency(self): + m2 = MEDCouplingUMesh("2d", 2) + m2.setCoords(DataArrayDouble([(0.0, 1.0)] * 4, 4,2)) # whatever + m2.setConnectivity(DataArrayInt([NORM_TRI3, 0,1,2,NORM_TRI3, 1,2,3]), DataArrayInt(([0,4,8]))) + m1 , _, _ , _, _ = m2.buildDescendingConnectivity() + mum = MEDFileUMesh() + mum.setMeshAtLevel(0, m2) + mum.setMeshAtLevel(-1, m1) + mum.checkCoherency() + mum.checkSMESHCoherency() + n2 = DataArrayInt(m2.getNumberOfCells(), 1); n2.iota(1) + n1 = DataArrayInt(m1.getNumberOfCells(), 1); n1.iota(1) + mum.setRenumFieldArr(0, n2) + mum.setRenumFieldArr(-1, n1) + self.assertRaises(InterpKernelException, mum.checkSMESHCoherency) + mum.setRenumFieldArr(-1, n1+100) + mum.checkSMESHCoherency() + pass + + def testClearNodeAndCellNumbers(self): + m2 = MEDCouplingUMesh("2d", 2) + m2.setCoords(DataArrayDouble([(0.0, 1.0)] * 4, 4,2)) # whatever + m2.setConnectivity(DataArrayInt([NORM_TRI3, 0,1,2,NORM_TRI3, 1,2,3]), DataArrayInt(([0,4,8]))) + m1 , _, _ , _, _ = m2.buildDescendingConnectivity() + mum = MEDFileUMesh() + mum.setMeshAtLevel(0, m2) + mum.setMeshAtLevel(-1, m1) + mum.checkCoherency() + n2 = DataArrayInt(m2.getNumberOfCells(), 1); n2.iota(1) + n1 = DataArrayInt(m1.getNumberOfCells(), 1); n1.iota(1) + mum.setRenumFieldArr(0, n2) + mum.setRenumFieldArr(-1, n1) + mum.clearNodeAndCellNumbers() + mum.checkSMESHCoherency() + pass + pass if __name__ == "__main__": -- 2.39.2