From 3b59c2a5b76f645fd63ec3aac4b565c094a7ec04 Mon Sep 17 00:00:00 2001 From: abn Date: Wed, 3 Feb 2016 10:45:51 +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 a53430517..bca5865f2 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 51723e5f4..432610845 100644 --- a/src/MEDCoupling/MEDCouplingMemArray.hxx +++ b/src/MEDCoupling/MEDCouplingMemArray.hxx @@ -522,6 +522,7 @@ namespace MEDCoupling MEDCOUPLING_EXPORT DataArrayInt *buildPermArrPerLevel() const; MEDCOUPLING_EXPORT bool isIota(int sizeExpected) const; MEDCOUPLING_EXPORT bool isUniform(int val) const; + MEDCOUPLING_EXPORT bool hasUniqueValues() const; MEDCOUPLING_EXPORT DataArrayInt *subArray(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 fabd7936e..7584085b5 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 455b6bd3c..1ab220f00 100644 --- a/src/MEDCoupling_Swig/MEDCouplingMemArray.i +++ b/src/MEDCoupling_Swig/MEDCouplingMemArray.i @@ -2622,6 +2622,7 @@ namespace MEDCoupling DataArrayInt *buildPermArrPerLevel() const throw(INTERP_KERNEL::Exception); bool isIota(int sizeExpected) const throw(INTERP_KERNEL::Exception); bool isUniform(int val) const throw(INTERP_KERNEL::Exception); + bool hasUniqueValues() const throw(INTERP_KERNEL::Exception); DataArrayInt *subArray(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 23e752040..0c6c6e287 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 checkSMESHConsistency(). + * \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 checkSMESHConsistency() + */ +void MEDFileUMesh::checkConsistency() const +{ + if(!_coords || !_coords->isAllocated()) + { + if(!_ms.size()) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkConsistency(): coords are null but some mesh parts are present!"); + if (!_fam_coords) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkConsistency(): coords are null but not the internal node family array!"); + if (!_num_coords || !_rev_num_coords) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkConsistency(): coords are null but not the internal node numbering array!"); + } + else + { + int nbCoo = _coords->getNumberOfTuples(); + if (_fam_coords) + _fam_coords->checkNbOfTuplesAndComp(nbCoo,1,"MEDFileUMesh::checkConsistency(): inconsistent internal node family array!"); + if (_num_coords) + { + _num_coords->checkNbOfTuplesAndComp(nbCoo,1,"MEDFileUMesh::checkConsistency(): 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::checkConsistency(): inconsistent internal revert node numbering array!"); + } + if ((_num_coords && !_rev_num_coords) || (!_num_coords && _rev_num_coords)) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkConsistency(): inconsistent internal numbering arrays (one is null)!"); + if (_num_coords && !_num_coords->hasUniqueValues()) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkConsistency(): inconsistent internal node numbering array: duplicates found!"); + if (_name_coords) + _name_coords->checkNbOfTuplesAndComp(nbCoo,MED_SNAME_SIZE,"MEDFileUMesh::checkConsistency(): inconsistent internal coord name array!"); + // Now sub part check: + for (std::vector< MCAuto >::const_iterator it=_ms.begin(); + it != _ms.end(); it++) + (*it)->checkConsistency(); + } +} + +/** + * Same as checkConsistency() 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::checkSMESHConsistency() const +{ + checkConsistency(); + // For all sub-levels, numbering is either always null or with void intersection: + if (_ms.size()) + { + std::vector< MCAuto >::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::checkConsistency(): 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); + MCAuto inter = DataArrayInt::BuildIntersection(v); + if (inter->getNumberOfTuples()) + throw INTERP_KERNEL::Exception("MEDFileUMesh::checkConsistency(): 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< MCAuto >::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 f4676c218..ef17b2e47 100644 --- a/src/MEDLoader/MEDFileMesh.hxx +++ b/src/MEDLoader/MEDFileMesh.hxx @@ -255,6 +255,9 @@ namespace MEDCoupling MEDLOADER_EXPORT MEDFileMesh *deepCopy() const; MEDLOADER_EXPORT MEDFileMesh *shallowCpy() const; MEDLOADER_EXPORT bool isEqual(const MEDFileMesh *other, double eps, std::string& what) const; + MEDLOADER_EXPORT void checkConsistency() const; + MEDLOADER_EXPORT void checkSMESHConsistency() 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 f46f9b0c7..2f6e05dff 100644 --- a/src/MEDLoader/MEDFileMeshLL.cxx +++ b/src/MEDLoader/MEDFileMeshLL.cxx @@ -947,6 +947,29 @@ MEDFileUMeshSplitL1 *MEDFileUMeshSplitL1::deepCopy(DataArrayDouble *coords) cons return ret.retn(); } +void MEDFileUMeshSplitL1::checkConsistency() const +{ + if (!_fam || _fam->getNumberOfTuples() != getSize()) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkConsistency(): internal family array has an invalid size!"); + int nbCells = getSize(); + if (_num) + { + _num->checkNbOfTuplesAndComp(nbCells,1,"MEDFileUMeshSplitL1::checkConsistency(): 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::checkConsistency(): inconsistent internal revert node numbering array!"); + } + if ((_num && !_rev_num) || (!_num && _rev_num)) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkConsistency(): inconsistent internal numbering arrays (one is null)!"); + if (_num && !_num->hasUniqueValues()) + throw INTERP_KERNEL::Exception("MEDFileUMeshSplitL1::checkConsistency(): inconsistent internal node numbering array: duplicates found!"); + if (_names) + _names->checkNbOfTuplesAndComp(nbCells,1,"MEDFileUMeshSplitL1::checkConsistency(): internal cell naming array has an invalid size!"); + + _m_by_types.checkConsistency(); +} + 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::checkConsistency() const +{ + if(_mp_time >= _m_time) + for(std::vector< MCAuto >::const_iterator it=_m_parts.begin(); + it!=_m_parts.end(); it++) + (*it)->checkConsistency(); + else + _m->checkConsistency(); +} + void MEDFileUMeshAggregateCompute::clearNonDiscrAttributes() const { for(std::vector< MCAuto >::const_iterator it=_m_parts.begin();it!=_m_parts.end();it++) diff --git a/src/MEDLoader/MEDFileMeshLL.hxx b/src/MEDLoader/MEDFileMeshLL.hxx index e5e29cf68..565c44318 100644 --- a/src/MEDLoader/MEDFileMeshLL.hxx +++ b/src/MEDLoader/MEDFileMeshLL.hxx @@ -186,6 +186,7 @@ namespace MEDCoupling MEDFileUMeshAggregateCompute deepCopy(DataArrayDouble *coords) const; void shallowCpyMeshes(); bool isEqual(const MEDFileUMeshAggregateCompute& other, double eps, std::string& what) const; + void checkConsistency() const; void clearNonDiscrAttributes() const; void synchronizeTinyInfo(const MEDFileMesh& master) const; bool empty() const; @@ -211,6 +212,7 @@ namespace MEDCoupling 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 MEDCoupling std::vector getDirectChildrenWithNull() const; MEDFileUMeshSplitL1 *shallowCpyUsingCoords(DataArrayDouble *coords) const; MEDFileUMeshSplitL1 *deepCopy(DataArrayDouble *coords) const; + void checkConsistency() 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 bb4a5e658..21dc6559c 100644 --- a/src/MEDLoader/Swig/MEDLoaderCommon.i +++ b/src/MEDLoader/Swig/MEDLoaderCommon.i @@ -1205,6 +1205,9 @@ namespace MEDCoupling ~MEDFileUMesh(); int getSpaceDimension() const throw(INTERP_KERNEL::Exception); int getRelativeLevOnGeoType(INTERP_KERNEL::NormalizedCellType gt) const throw(INTERP_KERNEL::Exception); + void checkConsistency() const throw(INTERP_KERNEL::Exception); + void checkSMESHConsistency() 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 a058b967f..0f93d488d 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.checkConsistency() + mum2 = mum.deepCopy() + + # Nodes + arr = DataArrayInt([2]*4) + mum.setFamilyFieldArr(1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayInt([2]*4) + mum.setRenumFieldArr(1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + mum.setRenumFieldArr(1, DataArrayInt([2]*4)) + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayAsciiChar(['tutu x']*4) + mum.setNameFieldAtLevel(1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + + # 2D + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayInt([2]*2) + mum.setFamilyFieldArr(0, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayInt([2]*2) + mum.setRenumFieldArr(0, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + mum.setRenumFieldArr(0, DataArrayInt([2]*2)) + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayAsciiChar(['tutu x']*2) + mum.setNameFieldAtLevel(0, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + + # 1D + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayInt([2]*5) + mum.setFamilyFieldArr(-1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayInt([2]*5) + mum.setRenumFieldArr(-1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + mum.setRenumFieldArr(-1, DataArrayInt([2]*5)) + self.assertRaises(InterpKernelException, mum.checkConsistency) + mum=mum2; mum2=mum.deepCopy(); + arr = DataArrayAsciiChar(['tutu x']*5) + mum.setNameFieldAtLevel(-1, arr); arr.reAlloc(35); + self.assertRaises(InterpKernelException, mum.checkConsistency) + + def testCheckSMESHConsistency(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.checkConsistency() + mum.checkSMESHConsistency() + 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.checkSMESHConsistency) + mum.setRenumFieldArr(-1, n1+100) + mum.checkSMESHConsistency() + 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.checkConsistency() + 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.checkSMESHConsistency() + pass + pass if __name__ == "__main__": -- 2.39.2