diff --git a/apf/apf.cc b/apf/apf.cc index 13eca372a..231d0f2fd 100644 --- a/apf/apf.cc +++ b/apf/apf.cc @@ -463,6 +463,13 @@ Field* createUserField(Mesh* m, const char* name, int valueType, FieldShape* s, return makeField(m, name, valueType, 0, s, new UserData(f)); } +void updateUserField(Field* field, Function* newFunc) +{ + UserData* ud = dynamic_cast(field->getData()); + // ud will be null if the data is not user data + if (ud) ud->setFunction(newFunc); +} + void copyData(Field* to, Field* from) { copyFieldData(from->getData(), to->getData()); diff --git a/apf/apf.h b/apf/apf.h index 5921afeaf..44ef417db 100644 --- a/apf/apf.h +++ b/apf/apf.h @@ -668,14 +668,22 @@ struct Function virtual void eval(MeshEntity* e, double* result) = 0; }; -/** \brief Create a Field from a user's analytic function. - \details This field will use no memory, has values on all - nodes, and calls the user Function for all value queries. - Writing to this field does nothing. - */ +/* \brief Create a Field from a user's analytic function. + * \details This field will use no memory, has values on all + * nodes, and calls the user Function for all value queries. + * Writing to this field does nothing. + * \warning if you copy the mesh with apf::convert you may want to use + * apf::updateUserField to update function that this field refers to. This is + * extremely important if the analytic function you use references user fields. + */ Field* createUserField(Mesh* m, const char* name, int valueType, FieldShape* s, Function* f); +/* \brief update the analytic function from a user field + * \details this field updates the apf::Function which the UserField refers to + */ +void updateUserField(Field* field, Function* newFunc); + /** \brief Compute a nodal gradient field from a nodal input field \details given a nodal field, compute approximate nodal gradient values by giving each node a volume-weighted average of the diff --git a/apf/apfArrayData.cc b/apf/apfArrayData.cc index 289afaa28..3d76f7c0a 100644 --- a/apf/apfArrayData.cc +++ b/apf/apfArrayData.cc @@ -79,6 +79,14 @@ class ArrayDataOf : public FieldDataOf T* getDataArray() { return this->dataArray; } + virtual FieldData* clone() { + //FieldData* newData = new TagDataOf(); + FieldData* newData = new ArrayDataOf(); + newData->init(this->field); + copyFieldData(static_cast*>(newData), + static_cast*>(this->field->getData())); + return newData; + } private: /* data variables go here */ diff --git a/apf/apfConvert.cc b/apf/apfConvert.cc index fc4b163cc..5b9ada911 100644 --- a/apf/apfConvert.cc +++ b/apf/apfConvert.cc @@ -7,6 +7,8 @@ #include "apfNumbering.h" #include #include +#include +#include namespace apf { @@ -31,6 +33,9 @@ class Converter convertFields(); convertNumberings(); convertGlobalNumberings(); + // this must be called after anything that might create tags e.g. fields + // or numberings to avoid problems with tag duplication + convertTags(); outMesh->acceptChanges(); } ModelEntity* getNewModelFromOld(ModelEntity* oldC) @@ -210,6 +215,48 @@ class Converter } } } + void convertTag(Mesh* inMesh, MeshTag* in, Mesh* outMesh, MeshTag* out) + { + for (int d = 0; d <= 3; ++d) { + int tagType = inMesh->getTagType(in); + int tagSize = inMesh->getTagSize(in); + PCU_DEBUG_ASSERT(tagType == outMesh->getTagType(out)); + PCU_DEBUG_ASSERT(tagSize == outMesh->getTagSize(out)); + MeshIterator* it = inMesh->begin(d); + MeshEntity* e; + while ((e = inMesh->iterate(it))) { + if(inMesh->hasTag(e, in)) { + // these initializations cannot go into the cases due to compiler + // warnings on gcc 7.3.0 + double* dblData; + int* intData; + long* lngData; + switch (tagType) { + case apf::Mesh::DOUBLE: + dblData = new double[tagSize]; + inMesh->getDoubleTag(e, in, dblData); + outMesh->setDoubleTag(newFromOld[e], out, dblData); + break; + case apf::Mesh::INT: + intData = new int[tagSize]; + inMesh->getIntTag(e, in, intData); + outMesh->setIntTag(newFromOld[e], out, intData); + break; + case apf::Mesh::LONG: + lngData = new long[tagSize]; + inMesh->getLongTag(e, in, lngData); + outMesh->setLongTag(newFromOld[e], out, lngData); + break; + default: + std::cerr << "Tried to convert unknown tag type\n"; + abort(); + break; + } + } + } + inMesh->end(it); + } + } void convertFields() { for (int i = 0; i < inMesh->countFields(); ++i) { @@ -222,8 +269,18 @@ class Converter { for (int i = 0; i < inMesh->countNumberings(); ++i) { Numbering* in = inMesh->getNumbering(i); - Numbering* out = createNumbering(outMesh, - getName(in), getShape(in), countComponents(in)); + Numbering* out; + if (getField(in)) { + // here we assume that the fields have already been copied into the + // mesh + Field* outField = outMesh->findField(getName(getField(in))); + PCU_DEBUG_ASSERT(outField); + out = createNumbering(outField); + } + else { + out = createNumbering(outMesh, getName(in), getShape(in), + countComponents(in)); + } convertNumbering(in, out); } } @@ -231,11 +288,57 @@ class Converter { for (int i = 0; i < inMesh->countGlobalNumberings(); ++i) { GlobalNumbering* in = inMesh->getGlobalNumbering(i); - GlobalNumbering* out = createGlobalNumbering(outMesh, - getName(in), getShape(in), countComponents(in)); + GlobalNumbering* out; + if (getField(in)) { + // here we assume that the fields have already been copied into the + // mesh + Field* outField = outMesh->findField(getName(getField(in))); + PCU_DEBUG_ASSERT(outField); + out = createGlobalNumbering(outField); + } + else { + out = createGlobalNumbering(outMesh, getName(in), getShape(in), + countComponents(in)); + } convertGlobalNumbering(in, out); } } + void convertTags() + { + DynamicArray tags; + inMesh->getTags(tags); + for (std::size_t i = 0; i < tags.getSize(); ++i) { + apf::MeshTag* in = tags[i]; + PCU_DEBUG_ASSERT(in); + // create a new tag on the outMesh + int tagType = inMesh->getTagType(in); + int tagSize = inMesh->getTagSize(in); + const char* tagName = inMesh->getTagName(in); + PCU_DEBUG_ASSERT(tagName); + // need to make sure that the tag wasn't already created by a field or + // numbering + if (!outMesh->findTag(tagName)) { + apf::MeshTag* out = NULL; + switch (tagType) { + case apf::Mesh::DOUBLE: + out = outMesh->createDoubleTag(tagName, tagSize); + break; + case apf::Mesh::INT: + out = outMesh->createIntTag(tagName, tagSize); + break; + case apf::Mesh::LONG: + out = outMesh->createLongTag(tagName, tagSize); + break; + default: + std::cerr << "Tried to convert unknown tag type\n"; + abort(); + } + PCU_DEBUG_ASSERT(out); + // copy the tag on the inMesh to the outMesh + convertTag(inMesh, in, outMesh, out); + } + } + } void convertQuadratic() { if (inMesh->getShape() != getLagrange(2) && inMesh->getShape() != getSerendipity()) diff --git a/apf/apfCoordData.cc b/apf/apfCoordData.cc index 0578540e5..b74031c82 100644 --- a/apf/apfCoordData.cc +++ b/apf/apfCoordData.cc @@ -11,6 +11,14 @@ namespace apf { +FieldData* CoordData::clone() { + FieldData* newData = new CoordData(); + newData->init(field); + copyFieldData(static_cast*>(newData), + static_cast*>(field->getData())); + return newData; + +} void CoordData::init(FieldBase* f) { FieldData::field = f; diff --git a/apf/apfCoordData.h b/apf/apfCoordData.h index 002428f9b..8866b79f1 100644 --- a/apf/apfCoordData.h +++ b/apf/apfCoordData.h @@ -33,6 +33,7 @@ class CoordData : public FieldDataOf virtual void get(MeshEntity* e, double* data); virtual void set(MeshEntity* e, double const* data); virtual bool isFrozen() { return false; } + virtual FieldData* clone(); private: Mesh* mesh; FieldShape* shape; diff --git a/apf/apfFieldData.cc b/apf/apfFieldData.cc index b89b087dc..6bed21628 100644 --- a/apf/apfFieldData.cc +++ b/apf/apfFieldData.cc @@ -10,11 +10,6 @@ FieldData::~FieldData() { } -FieldData* FieldData::clone() -{ - abort(); -} - void FieldData::rename(const char*) { abort(); diff --git a/apf/apfFieldData.h b/apf/apfFieldData.h index 24be9cd0d..2fbca276e 100644 --- a/apf/apfFieldData.h +++ b/apf/apfFieldData.h @@ -22,7 +22,7 @@ class FieldData virtual bool hasEntity(MeshEntity* e) = 0; virtual void removeEntity(MeshEntity* e) = 0; virtual bool isFrozen() = 0; - virtual FieldData* clone(); + virtual FieldData* clone() = 0; virtual void rename(const char* newName); FieldBase* getField() {return field;} protected: @@ -55,6 +55,7 @@ class FieldDataOf : public FieldData void setNodeComponents(MeshEntity* e, int node, T const* components); void getNodeComponents(MeshEntity* e, int node, T* components); int getElementData(MeshEntity* entity, NewArray& data); + virtual FieldData* clone()=0; }; } //namespace apf diff --git a/apf/apfNumbering.cc b/apf/apfNumbering.cc index 1a70b2272..8b05d6c3a 100644 --- a/apf/apfNumbering.cc +++ b/apf/apfNumbering.cc @@ -461,6 +461,14 @@ GlobalNumbering* createGlobalNumbering( return n; } +GlobalNumbering* createGlobalNumbering(Field* f) +{ + GlobalNumbering* n = new GlobalNumbering(); + n->init(f); + f->getMesh()->addGlobalNumbering(n); + return n; +} + FieldShape* getShape(GlobalNumbering* n) { return n->getShape(); @@ -601,5 +609,5 @@ void getNodes(GlobalNumbering* n, DynamicArray& nodes) getFieldNodes(n,nodes); } +Field* getField(GlobalNumbering* n) { return n->getField(); } } - diff --git a/apf/apfNumbering.h b/apf/apfNumbering.h index 35b02c334..f4f339e4d 100644 --- a/apf/apfNumbering.h +++ b/apf/apfNumbering.h @@ -159,6 +159,11 @@ GlobalNumbering* createGlobalNumbering( const char* name, FieldShape* shape, int components=1); + +/** \brief Create a Numbering of degrees of freedom of a Field. + */ +GlobalNumbering* createGlobalNumbering(Field* f); + FieldShape* getShape(GlobalNumbering* n); const char* getName(GlobalNumbering* n); /** \brief get the mesh associated with a global numbering */ @@ -176,6 +181,9 @@ long getNumber(GlobalNumbering* n, MeshEntity* e, int node, int component=0); /** \brief get an element's global node numbers */ int getElementNumbers(GlobalNumbering* n, MeshEntity* e, NewArray& numbers); +/** \brief get the field being numbered + */ +Field* getField(GlobalNumbering* n); /** \brief converts a local numbering into a global numbering. \param destroy Should the input Numbering* be destroyed? diff --git a/apf/apfUserData.cc b/apf/apfUserData.cc index e324f189c..15ee6a310 100644 --- a/apf/apfUserData.cc +++ b/apf/apfUserData.cc @@ -42,4 +42,13 @@ bool UserData::isFrozen() return false; } +FieldData* UserData::clone() +{ + FieldData* newData = new UserData(function); + newData->init(field); + copyFieldData(static_cast*>(newData), + static_cast*>(field->getData())); + return newData; +} + } diff --git a/apf/apfUserData.h b/apf/apfUserData.h index 4802cd822..96980727e 100644 --- a/apf/apfUserData.h +++ b/apf/apfUserData.h @@ -21,11 +21,15 @@ struct UserData : public FieldDataOf void get(MeshEntity* e, double* data); void set(MeshEntity* e, double const* data); bool isFrozen(); + virtual FieldData* clone(); + // using const * const gives an error on gcc/7.3.0 because the return is an + // r-value which cannot be modified anyways + Function const* getFunction() const { return function; } + void setFunction(Function* func) { function = func; } + private: Function* function; }; } #endif - - diff --git a/cmake/FindSimModSuite.cmake b/cmake/FindSimModSuite.cmake index 92c5583f7..ed4de471f 100644 --- a/cmake/FindSimModSuite.cmake +++ b/cmake/FindSimModSuite.cmake @@ -74,7 +74,7 @@ string(REGEX REPLACE "${SIM_VERSION}") set(MIN_VALID_SIM_VERSION 11.0.170826) -set(MAX_VALID_SIM_VERSION 12.0.180713) +set(MAX_VALID_SIM_VERSION 12.0.180811) if( (SIM_DOT_VERSION VERSION_LESS MIN_VALID_SIM_VERSION) OR (SIM_DOT_VERSION VERSION_GREATER MAX_VALID_SIM_VERSION) ) MESSAGE(FATAL_ERROR diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 946be8aba..c3389e565 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,7 @@ util_exe_func(describe describe.cc) test_exe_func(quality quality.cc) test_exe_func(writeVtxPtn writeVtxPtn.cc) test_exe_func(verify_2nd_order_shapes verify_2nd_order_shapes.cc) +test_exe_func(verify_convert verify_convert.cc) # Geometric model utilities if(ENABLE_SIMMETRIX) diff --git a/test/testing.cmake b/test/testing.cmake index 396cf5593..e921e9824 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -33,6 +33,7 @@ mpi_test(integrate 1 ./integrate) mpi_test(qr_test 1 ./qr) mpi_test(base64 1 ./base64) mpi_test(tensor_test 1 ./tensor) +mpi_test(verify_convert 1 ./verify_convert) if(ENABLE_SIMMETRIX) mpi_test(in_closure_of 1 diff --git a/test/verify_convert.cc b/test/verify_convert.cc new file mode 100644 index 000000000..0ce1bb5cf --- /dev/null +++ b/test/verify_convert.cc @@ -0,0 +1,142 @@ +/* + * this test verifies that the convert process properly clones the underlying + * fields, numberings, and tags + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +apf::Mesh2* createEmptyMesh() +{ + gmi_model* mdl = gmi_load(".null"); + return apf::makeEmptyMdsMesh(mdl, 3, false); +} +apf::Mesh2* createMesh() +{ + apf::Mesh2* m = createEmptyMesh(); + apf::MeshEntity* verts[2]; + verts[0] = + m->createVertex(NULL, apf::Vector3(0, 0, 0), apf::Vector3(0, 0, 0)); + verts[1] = + m->createVertex(NULL, apf::Vector3(1, 0, 0), apf::Vector3(1, 0, 0)); + m->createEntity(apf::Mesh::EDGE, NULL, verts); + return m; +} +class twox : public apf::Function { + private: + apf::Field* x; + + public: + virtual void eval(apf::MeshEntity* e, double* result) + { + result[0] = 2 * apf::getScalar(x, e, 0); + } + twox(apf::Field* x) : x(x) {} +}; +int main(int argc, char* argv[]) +{ + MPI_Init(&argc, &argv); + PCU_Comm_Init(); + gmi_register_null(); + apf::Mesh* m1 = createMesh(); + apf::Mesh2* m2 = createEmptyMesh(); + // create field on m1 + apf::Field* f = apf::createLagrangeField(m1, "field1", apf::SCALAR, 1); + apf::Function* func = new twox(f); + apf::Field* uf = + apf::createUserField(m1, "ufield1", apf::SCALAR, apf::getShape(f), func); + // create numbering and global numbering with and without fields to make sure + // they transfer properly + apf::Numbering* numWithField = apf::createNumbering(f); + apf::Numbering* numNoField = apf::createNumbering( + m1, "noField", apf::getShape(f), apf::countComponents(f)); + apf::GlobalNumbering* globalNumWithField = apf::createGlobalNumbering(f); + apf::GlobalNumbering* globalNumNoField = apf::createGlobalNumbering( + m1, "noField", apf::getShape(f), apf::countComponents(f)); + // create an integer tag + apf::MeshTag* intTag = m1->createIntTag("intTag", 1); + // loop over all vertices in mesh and set values + int count = 1; + apf::MeshIterator* it = m1->begin(0); + while (apf::MeshEntity* vert = m1->iterate(it)) { + apf::setScalar(f, vert, 0, count); + // set the numberings + apf::number(numWithField, vert, 0, 0, count); + apf::number(numNoField, vert, 0, 0, count); + apf::number(globalNumWithField, vert, 0, count); + apf::number(globalNumNoField, vert, 0, count); + // set the tag + // int tagData[1] = {count}; + m1->setIntTag(vert, intTag, &count); + ++count; + } + m1->end(it); + // verify the user field works on mesh 1 + it = m1->begin(0); + while (apf::MeshEntity* vert = m1->iterate(it)) { + double val = apf::getScalar(f, vert, 0); + double uval = apf::getScalar(uf, vert, 0); + if (!(std::abs(uval - 2 * double(val)) < 1E-15)) return 1; + } + m1->end(it); + // copy m1 to m2 + apf::convert(m1, m2); + apf::Field* f2 = m2->findField("field1"); + apf::Field* uf2 = m2->findField("ufield1"); + + apf::Numbering* numWithField2 = m2->findNumbering(apf::getName(numWithField)); + apf::Numbering* numNoField2 = m2->findNumbering(apf::getName(numNoField)); + apf::GlobalNumbering* globalNumWithField2 = + m2->findGlobalNumbering(apf::getName(globalNumWithField)); + apf::GlobalNumbering* globalNumNoField2 = + m2->findGlobalNumbering(apf::getName(globalNumNoField)); + // all of these numberings should exist + assert(numWithField2 && numNoField2 && globalNumWithField2 && + globalNumNoField2); + // make sure the fields of the numberings match up properly as they should be + // the new field copied into the new mesh + assert(getField(numWithField2) == f2); + assert(getField(globalNumWithField2) == f2); + // update the user field to reference the field in mesh 2 + apf::updateUserField(uf2, new twox(f2)); + // find the copied tag data + apf::MeshTag* intTag2 = m2->findTag("intTag"); + assert(intTag2); + count = 1; + it = m2->begin(0); + while (apf::MeshEntity* vert = m2->iterate(it)) { + double val = apf::getScalar(f2, vert, 0); + double uval = apf::getScalar(uf2, vert, 0); + assert(std::abs(val - count) < 1E-15); + assert(std::abs(uval - 2 * double(count)) < 1E-15); + apf::setScalar(f2, vert, 0, 18.0); + // make sure that the function updated properly + uval = apf::getScalar(uf2, vert, 0); + assert(std::abs(uval - 36.0) < 1E-15); + // check to make sure the numberings have the correct values + assert(getNumber(numWithField2, vert, 0, 0) == count); + assert(getNumber(numNoField2, vert, 0, 0) == count); + assert(getNumber(globalNumWithField2, vert, 0, 0) == count); + assert(getNumber(globalNumNoField2, vert, 0, 0) == count); + // check that the correct tag data was recovered + int* data = new int[1]; + m2->getIntTag(vert, intTag2, data); + assert(*data == count); + delete[] data; + ++count; + } + m2->end(it); + delete func; + m1->destroyNative(); + m2->destroyNative(); + apf::destroyMesh(m1); + apf::destroyMesh(m2); + PCU_Comm_Free(); + MPI_Finalize(); + return 0; +}