diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..0938c8d80 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Title of Pull Request + +Pull requests should target the `develop` branch. + +In your PR description please include: + +- a brief description +- references to existing issues this PR fixes (if applicable) +- for new functionality, please describe the test cases added to exercise it + +More information on PUMI development is here: + +https://github.com/SCOREC/core/wiki/Developer-Guide + +Thank you for your contribution. diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 000000000..39ed4a701 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,46 @@ +name: CMake test matrix +on: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] +jobs: + build: + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + compiler: + - { compiler: GNU, CC: gcc-10, CXX: g++-10 } + - { compiler: LLVM, CC: clang, CXX: clang++ } + build_type: [Debug, Release] + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: install mpich and gcc + run: | + sudo apt update + sudo apt install gcc-10 g++-10 mpich + + - name: Configure CMake + env: + MPICH_CXX: ${{matrix.compiler.CXX}} + MPICH_CC: ${{matrix.compiler.CC}} + run: cmake -S ${{github.workspace}} -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=mpicxx -DCMAKE_C_COMPILER=mpicc -DCMAKE_VERBOSE_MAKEFILE=ON -DMESHES=${{github.workspace}}/pumi-meshes -DIS_TESTING=ON -DSCOREC_CXX_WARNINGS=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} + + - name: Build + env: + MPICH_CXX: ${{matrix.compiler.CXX}} + MPICH_CC: ${{matrix.compiler.CC}} + run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}} -j + + - name: Test + env: + MPICH_CXX: ${{matrix.compiler.CXX}} + MPICH_CC: ${{matrix.compiler.CC}} + working-directory: ${{github.workspace}}/build + run: ctest --output-on-failure -C ${{matrix.build_type}} diff --git a/.gitmodules b/.gitmodules index c0cc81b3e..5be9b0df6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "pumi-meshes"] path = pumi-meshes - url = https://github.com/SCOREC/pumi-meshes.git + url = ../../SCOREC/pumi-meshes.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2075c0f1b..000000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ -dist: trusty -language: cpp -compiler: gcc -sudo: true - -branches: - only: - - develop - -addons: - apt: - update: true - sources: - - sourceline: 'ppa:ubuntu-toolchain-r/test' - packages: - - gcc-7 - - g++-7 - - mpich - - libmpich-dev - - clang - -before_install: - - export DEVROOT=/home/travis/build - - test -n $CC && unset CC - - test -n $CXX && unset CXX - - pwd - - export NP=`grep -c ^processor /proc/cpuinfo` - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7 - - sudo update-alternatives --config gcc - - cd ${DEVROOT} - - wget https://cmake.org/files/v3.12/cmake-3.12.1.tar.gz - - tar xzf cmake-3.12.1.tar.gz - - cd ${DEVROOT}/cmake-3.12.1 - - ./bootstrap - - make -j ${NP} && sudo make install - - which cmake - - cmake --version - - export PATH=/usr/local/bin:${PATH} - -script: - - cd ${DEVROOT}/SCOREC/core - - mkdir -p ${DEVROOT}/SCOREC/core/build - - cd build - - cmake .. -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx -DCMAKE_INSTALL_PREFIX=${DEVROOT}/install/core -DIS_TESTING=ON -DBUILD_EXES=ON -DCMAKE_BUILD_TYPE=Release -DMESHES=${DEVROOT}/SCOREC/core/pumi-meshes - - make -j ${NP} - - ctest - - cmake .. -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx -DCMAKE_INSTALL_PREFIX=${DEVROOT}/install/core -DIS_TESTING=ON -DBUILD_EXES=ON -DCMAKE_BUILD_TYPE=Debug -DMESHES=${DEVROOT}/SCOREC/core/pumi-meshes - - make -j ${NP} - - ctest - diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..b7e904134 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,45 @@ +cff-version: 1.1.0 +title: "PUMI" +message: "If you use this software, please cite it as below." +authors: + - family-names: "Ibanez" + given-names: "Daniel A." + - family-names: "Smith" + given-names: "Cameron W." + orcid: "https://orcid.org/0000-0001-9258-5226" + - family-names: "Granzow" + given-names: "Brian" + - family-names: "Zaide" + given-names: "Daniel" + - family-names: "Hakimi" + given-names: "Morteza" + - family-names: "Seol" + given-names: "E. Seegyoung" +date-released: 2021-06-25 +version: 2.2.6 +keywords: + - "unstructured meshes" + - "parallel" + - "mesh adaptation" + - "load balancing" + - "C++" + - "finite elements" +references: + - type: article + authors: + - family-names: "Ibanez" + given-names: "Daniel A." + - family-names: "Seol" + given-names: "E. Seegyoung" + - family-names: "Smith" + given-names: "Cameron W." + orcid: "https://orcid.org/0000-0001-9258-5226" + - family-names: "Shephard" + given-names: "Mark S." + title: "PUMI: Parallel Unstructured Mesh Infrastructure" + doi: "10.1145/2814935" + month: 6 + year: 2016 + volume: 42 + issue: "3" + journal: "ACM Trans. Math. Softw" diff --git a/CMakeLists.txt b/CMakeLists.txt index 21880f4e1..7648e54d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,15 +6,20 @@ endif() # This is the top level CMake file for the SCOREC build cmake_minimum_required(VERSION 3.0) -project(SCOREC VERSION 2.2.2 LANGUAGES CXX C) +project(SCOREC VERSION 2.2.7 LANGUAGES CXX C) include(cmake/bob.cmake) include(cmake/xsdk.cmake) -option(SCOREC_ENABLE_CXX11 "enable compilation with c++11 support" NO) - option(USE_XSDK_DEFAULTS "enable the XDSK v0.3.0 default configuration" NO) +#requre c++11 without extensions +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSION OFF) +if(NOT ENABLE_CGNS) + set(CMAKE_CXX_STANDARD 11) +endif() + xsdk_begin_package() bob_begin_package() @@ -32,16 +37,11 @@ if(NOT USE_XSDK_DEFAULTS) bob_begin_cxx_flags() bob_end_cxx_flags() set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS}") - if(SCOREC_ENABLE_CXX11) - if(ENABLE_CGNS) - bob_cxx14_flags() - else() - bob_cxx14_flags() - endif() - else() - if(ENABLE_CGNS) - bob_cxx14_flags() - endif() + if(ENABLE_CGNS) #takes precedence over SCOREC_ENABLE_CXX11 + message(STATUS "enabling cxx14") + bob_cxx14_flags() + elseif(SCOREC_ENABLE_CXX11) + bob_cxx11_flags() endif() endif() message(STATUS "CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}") @@ -58,6 +58,9 @@ message(STATUS "CMAKE_C_FLAGS = ${CMAKE_C_FLAGS}") option(IS_TESTING "Build for CTest" OFF) message(STATUS "IS_TESTING: ${IS_TESTING}") +set(MESHES "${CMAKE_SOURCE_DIR}/pumi-meshes" CACHE STRING "Directory of test meshes") +message(STATUS "MESHES: ${MESHES}") + option(BUILD_EXES "Build executables" ON) message(STATUS "BUILD_EXES: ${BUILD_EXES}") @@ -78,7 +81,7 @@ set(VALGRIND_ARGS "" CACHE STRING "the command line arguments to VALGRIND") # smoke test target - a few tests are defined later with the 'SMOKE_TEST' label -add_custom_target(test_install +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -L SMOKE_TEST COMMENT "running a smoke test on the installed binaries") @@ -102,6 +105,7 @@ option(SKIP_SIMMETRIX_VERSION_CHECK "enable at your own risk; it may result in u option(ENABLE_SIMMETRIX "Build with Simmetrix support" OFF) message(STATUS "ENABLE_SIMMETRIX: ${ENABLE_SIMMETRIX}") option(ENABLE_OMEGA_H "Enable the Omega_h interface" OFF) +option(PUMI_USE_OMEGA_H_VERSION "Specify the Omega_h version PUMI should use" 10.0.0) message(STATUS "ENABLE_OMEGA_H: ${ENABLE_OMEGA_H}") if(ENABLE_SIMMETRIX) @@ -126,16 +130,17 @@ endif() if(ENABLE_OMEGA_H) # find the omega_h library set(SCOREC_USE_Omega_h_DEFAULT ${ENABLE_OMEGA_H}) - set(Omega_h_REQUIRED_VERSION 9.0.0) + set(Omega_h_REQUIRED_VERSION ${PUMI_USE_OMEGA_H_VERSION}) bob_public_dep(Omega_h) endif() if(ENABLE_CGNS) - find_package(CGNS) + set(SCOREC_USE_CGNS_DEFAULT ${ENABLE_CGNS}) + bob_public_dep(CGNS) + #CGNS does not provide cmake targets :( include_directories(SYSTEM ${CGNS_INCLUDE_DIR}) - # - find_package(HDF5) - # + set(SCOREC_USE_HDF5_DEFAULT ${ENABLE_CGNS}) + bob_public_dep(HDF5) add_definitions(-DHAVE_CGNS) endif() @@ -155,6 +160,7 @@ add_subdirectory(pumi) add_subdirectory(ma) add_subdirectory(crv) add_subdirectory(spr) +add_subdirectory(ree) add_subdirectory(sam) add_subdirectory(phasta) add_subdirectory(stk) @@ -165,10 +171,23 @@ add_subdirectory(omega_h) add_library(core INTERFACE) target_link_libraries(core INTERFACE ${SCOREC_EXPORTED_TARGETS}) if(ENABLE_CGNS) - target_link_libraries(core INTERFACE ${CGNS_LIBRARIES} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) + target_link_libraries(core INTERFACE ${CMAKE_DL_LIBS}) #HDF5 uses dlopen + target_compile_features(core INTERFACE cxx_std_14) +else() + target_compile_features(core INTERFACE cxx_std_11) endif() scorec_export_library(core) +#check for mallinfo2 +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + include(CheckCXXSymbolExists) + check_cxx_symbol_exists(mallinfo2 "malloc.h" PUMI_HAS_MALLINFO2) + if(PUMI_HAS_MALLINFO2) + target_compile_definitions(core INTERFACE -DPUMI_HAS_MALLINFO2) + target_compile_definitions(pcu PRIVATE -DPUMI_HAS_MALLINFO2) + endif() +endif() + if(BUILD_EXES) add_subdirectory(test) endif() diff --git a/README.md b/README.md index 992c62876..0c0f0c714 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # SCOREC Core # -[![Coverity Scan Build Status](https://scan.coverity.com/projects/6698/badge.svg)](https://scan.coverity.com/projects/scorec-core) - The SCOREC Core is a set of C/C++ libraries for unstructured mesh simulations on supercomputers. @@ -26,6 +24,7 @@ For more information, start at our * MTH: Math containers and routines * CRV: Support for curved meshes with Bezier Shapes * PYCORE: Python Wrappers (see python_wrappers/README.md for build instructions) +* REE: Residual based implicit error estimator ### How do I get set up? ### @@ -53,3 +52,11 @@ For more information, start at our * If you have a usage question or have found a bug please post an [issue](https://github.com/SCOREC/core/issues). * Otherwise, email [Cameron Smith](https://www.scorec.rpi.edu/~cwsmith) and pumi@scorec.rpi.edu. + +### Citing PUMI + +If you use these tools, please cite the following paper: + +Daniel A. Ibanez, E. Seegyoung Seol, Cameron W. Smith, and Mark S. Shephard. 2016. PUMI: Parallel Unstructured Mesh Infrastructure. ACM Trans. Math. Softw. 42, 3, Article 17 (May 2016), 28 pages. DOI: https://doi.org/10.1145/2814935 + +We would be happy to provide feedback on journal submissions using PUMI prior to publication. diff --git a/apf/CMakeLists.txt b/apf/CMakeLists.txt index ed2bb2140..7eb3a5d8e 100644 --- a/apf/CMakeLists.txt +++ b/apf/CMakeLists.txt @@ -25,14 +25,21 @@ set(SOURCES apfShape.cc apfIPShape.cc apfHierarchic.cc + apfPolyBasis1D.cc + apfNedelec.cc + apfL2Shapes.cc + apfH1Shapes.cc apfVector.cc apfVectorElement.cc apfVectorField.cc + apfMixedVectorElement.cc + apfMixedVectorField.cc apfPackedField.cc apfNumbering.cc apfMixedNumbering.cc apfAdjReorder.cc apfVtk.cc + apfVtkPieceWiseFields.cc apfFieldData.cc apfTagData.cc apfCoordData.cc @@ -98,12 +105,6 @@ target_link_libraries(apf mth ) - -if(ENABLE_CGNS) - message(STATUS ${CGNS_LIBRARIES}) - target_link_libraries(apf PRIVATE ${CGNS_LIBRARIES} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES}) -endif(ENABLE_CGNS) - scorec_export_library(apf) bob_end_subdir() diff --git a/apf/apf.cc b/apf/apf.cc index bd7259483..662a69709 100644 --- a/apf/apf.cc +++ b/apf/apf.cc @@ -4,12 +4,13 @@ * This work is open source software, licensed under the terms of the * BSD license as described in the LICENSE file in the top-level directory. */ - #include "apf.h" #include "apfScalarField.h" #include "apfScalarElement.h" #include "apfVectorField.h" #include "apfVectorElement.h" +#include "apfMixedVectorField.h" +#include "apfMixedVectorElement.h" #include "apfMatrixField.h" #include "apfMatrixElement.h" #include "apfPackedField.h" @@ -25,6 +26,9 @@ #include #include +#include "mth.h" +#include "mth_def.h" + namespace apf { void destroyMesh(Mesh* m) @@ -71,16 +75,24 @@ Field* makeField( { PCU_ALWAYS_ASSERT_VERBOSE( ! m->findField(name), name); Field* f = 0; - if (valueType == SCALAR) - f = new ScalarField(); - else if (valueType == VECTOR) - f = new VectorField(); - else if (valueType == MATRIX) - f = new MatrixField(); - else if (valueType == PACKED) - f = new PackedField(components); - else - fail("invalid valueType in field construction\n"); + // Cases with Vector shape functions + if (shape->isVectorShape()) { + PCU_ALWAYS_ASSERT(valueType == SCALAR); + f = new MixedVectorField(); + } + // Cases with Scalar shahpe funtions + else { + if (valueType == SCALAR) + f = new ScalarField(); + else if (valueType == VECTOR) + f = new VectorField(); + else if (valueType == MATRIX) + f = new MatrixField(); + else if (valueType == PACKED) + f = new PackedField(components); + else + fail("invalid valueType in field construction\n"); + } f->init(name,m,shape,data); m->addField(f); return f; @@ -166,16 +178,29 @@ void destroyField(Field* f) void setScalar(Field* f, MeshEntity* e, int node, double value) { - ScalarField* field = static_cast(f); - double tmp[1] = {value}; - field->setNodeValue(e,node,tmp); + if (f->getShape()->isVectorShape()) { + MixedVectorField* field = static_cast(f); + double tmp[1] = {value}; + field->setNodeValue(e,node,tmp); + } + else { + ScalarField* field = static_cast(f); + double tmp[1] = {value}; + field->setNodeValue(e,node,tmp); + } } double getScalar(Field* f, MeshEntity* e, int node) { - ScalarField* field = static_cast(f); double value[1]; - field->getNodeValue(e,node,value); + if (f->getShape()->isVectorShape()) { + MixedVectorField* field = static_cast(f); + field->getNodeValue(e,node,value); + } + else { + ScalarField* field = static_cast(f); + field->getNodeValue(e,node,value); + } return value[0]; } @@ -258,24 +283,46 @@ void getGrad(Element* e, Vector3 const& param, Vector3& g) void getVector(Element* e, Vector3 const& param, Vector3& value) { - VectorElement* element = static_cast(e); - value = element->getValue(param); + // Cases with vector shape functions first + if (e->getFieldShape()->isVectorShape()) { + MixedVectorElement* element = static_cast(e); + value = element->getValue(param); + } + // Cases with scalar shape functions + else { + VectorElement* element = static_cast(e); + value = element->getValue(param); + } } double getDiv(Element* e, Vector3 const& param) { + // Make sure this in not called for cases with vector shapes + PCU_ALWAYS_ASSERT_VERBOSE(!e->getFieldShape()->isVectorShape(), + "Not implemented for fields with vector shape functions."); VectorElement* element = static_cast(e); return element->div(param); } void getCurl(Element* e, Vector3 const& param, Vector3& c) { - VectorElement* element = static_cast(e); - return element->curl(param,c); + // Cases with vector shape functions first + if (e->getFieldShape()->isVectorShape()) { + MixedVectorElement* element = static_cast(e); + return element->curl(param,c); + } + // Cases with scalar shape functions + else { + VectorElement* element = static_cast(e); + return element->curl(param,c); + } } void getVectorGrad(Element* e, Vector3 const& param, Matrix3x3& deriv) { + // Make sure this in not called for cases with vector shapes + PCU_ALWAYS_ASSERT_VERBOSE(!e->getFieldShape()->isVectorShape(), + "Not implemented for fields with vector shape functions."); VectorElement* element = static_cast(e); return element->grad(param,deriv); } @@ -410,6 +457,69 @@ void getShapeGrads(Element* e, Vector3 const& local, e->getGlobalGradients(local,grads); } +void getVectorShapeValues(Element* e, Vector3 const& local, + NewArray& values) +{ + NewArray vvals(values.size()); + e->getShape()->getVectorValues(e->getMesh(), e->getEntity(), local, vvals); + + apf::Matrix3x3 Jinv; + apf::getJacobianInv( e->getParent(), local, Jinv ); + apf::Matrix3x3 JinvT = apf::transpose(Jinv); + + // Perform Piola transformation - u(x_hat) * J(x_hat)^{-1} + int d = 0; + (e->getDimension() == e->getMesh()->getDimension()) ? d = 3 : d = 2; + for( size_t i = 0; i < values.size(); i++ ) { + for ( int j = 0; j < 3; j++ ) { + values[i][j] = 0.; + for ( int k = 0; k < d; k++ ) + values[i][j] += vvals[i][k] * JinvT[k][j]; + } + } +} + +void getCurlShapeValues(Element* e, Vector3 const& local, + NewArray& values) +{ + NewArray cvals(values.size()); + e->getShape()->getLocalVectorCurls(e->getMesh(), e->getEntity(), local, cvals); + + // Perform Piola transformation + if (e->getDimension() == 3) + { + apf::Matrix3x3 J; + apf::getJacobian( e->getParent(), local, J); + double jdet = apf::getJacobianDeterminant(J, e->getDimension() ); + + // mult J * cvals^T and divide by jdet + mth::Matrix cvalsT(e->getDimension(), cvals.size()); // cvals transpose + for (int i = 0; i < e->getDimension(); i++) + for (size_t j = 0; j < cvals.size(); j++) + cvalsT(i,j) = cvals[j][i]; + + mth::Matrix JT(e->getDimension(), e->getDimension()); // J transpose + for (int i = 0; i < e->getDimension(); i++) + for (int j = 0; j < e->getDimension(); j++) + JT(i,j) = J[j][i]; + + mth::Matrix physCurlShapes(e->getDimension(), cvals.size()); + mth::multiply(JT, cvalsT, physCurlShapes); + physCurlShapes *= 1./jdet; + + for (size_t i = 0; i < values.size(); i++) + for (int j = 0; j < e->getDimension(); j++) + values[i][j] = physCurlShapes(j,i); + } + else + { + // TODO when ref dim != mesh space dim. Pseudo-inverse needed. + PCU_ALWAYS_ASSERT_VERBOSE(false, + "not yet implemented for 3D surface meshes (i.e., manifolds)!"); + } + + +} FieldShape* getShape(Field* f) { return f->getShape(); diff --git a/apf/apf.h b/apf/apf.h index d036d1a1f..7db3f37a2 100644 --- a/apf/apf.h +++ b/apf/apf.h @@ -45,7 +45,7 @@ template class ReductionOp; template class ReductionSum; /** \brief Base class for applying operations to make a Field consistent - * in parallel + * in parallel * \details This function gets applied pairwise to the Field values * from every partition, resulting in a single unique value. No guarantees * are made about the order in which this function is applied to the @@ -605,6 +605,19 @@ void getShapeValues(Element* e, Vector3 const& local, void getShapeGrads(Element* e, Vector3 const& local, NewArray& grads); +/** \brief Returns the vector shape function values at a point + * \details used only for Nedelec shapes + * (Piola transformation used to map from parent to physical coordinates) + */ +void getVectorShapeValues(Element* e, Vector3 const& local, + NewArray& values); + +/** \brief Returns the vector curl shape function values at a point + * \details used only for Nedelec shapes + * (Piola transformation used to map from parent to physical coordinates) + */ +void getCurlShapeValues(Element* e, Vector3 const& local, + NewArray& values); /** \brief Retrieve the apf::FieldShape used by a field */ @@ -690,6 +703,12 @@ void writeASCIIVtkFiles(const char* prefix, Mesh* m); void writeASCIIVtkFiles(const char* prefix, Mesh* m, std::vector writeFields); +/** \brief Output .vtk files with ASCII encoding for this part. + \details this function is useful for debugging meshes with Nedelec + fields on them. + */ +void writeNedelecVtkFiles(const char* prefix, Mesh* m); + /** \brief Return the location of a gaussian integration point. \param type the element type, from apf::Mesh::getType \param order the order of the integration rule diff --git a/apf/apfConstruct.cc b/apf/apfConstruct.cc index 8a96b2b00..40fdbbeb0 100644 --- a/apf/apfConstruct.cc +++ b/apf/apfConstruct.cc @@ -1,23 +1,29 @@ #include +#include "pcu_util.h" #include "apfConvert.h" #include "apfMesh2.h" #include "apf.h" #include "apfNumbering.h" #include +#include namespace apf { -typedef int Gid; - static void constructVerts( Mesh2* m, const Gid* conn, int nelem, int etype, GlobalToVert& result) { ModelEntity* interior = m->findModelEntity(m->getDimension(), 0); int end = nelem * apf::Mesh::adjacentCount[etype][0]; + int self2 = PCU_Comm_Self(); for (int i = 0; i < end; ++i) - if ( ! result.count(conn[i])) + if ( ! result.count(conn[i])) { result[conn[i]] = m->createVert_(interior); +// if(conn[i] < 0 || conn[i] > 4305368187 ) { // for whatever reason max is not stored but is found and checked later + if(conn[i] < 0 ) { + lion_eprint(1, "constructVerts building globalToVert: self=%d,gid=%ld,i=%d,nelem=%ld \n",self2,conn[i],i,nelem); + } + } } static NewElements constructElements( @@ -42,7 +48,7 @@ static Gid getMax(const GlobalToVert& globalToVert) Gid max = -1; APF_CONST_ITERATE(GlobalToVert, globalToVert, it) max = std::max(max, it->first); - return PCU_Max_Int(max); // this is type-dependent + return PCU_Max_Long(max); // this is type-dependent } @@ -52,34 +58,64 @@ static Gid getMax(const GlobalToVert& globalToVert) I didn't think to use it here, so credit is given. */ static void constructResidence(Mesh2* m, GlobalToVert& globalToVert) { - Gid max = getMax(globalToVert); + Gid ifirst=0; + int self2 = PCU_Comm_Self(); + APF_ITERATE(GlobalToVert, globalToVert, it) { + Gid gid = it->first; + if(ifirst==0 || ifirst==13437400 ) { + lion_eprint(1, "constructResidence: self=%d,gid=%ld,ifirst=%ld \n",self2,gid,ifirst); + } + ifirst++; + } + Gid max = getMax(globalToVert); // seems like we read this and know it already on every rank so why compute with global comm? + ifirst=0; + APF_ITERATE(GlobalToVert, globalToVert, it) { + Gid gid = it->first; + if(gid < 0 || gid > max ){ + lion_eprint(1, "constructResidence cgTV2: self=%d,gid=%ld,ifirst=%ld \n",self2,gid,ifirst); + } + if(ifirst==0 || ifirst==13437400 ) { + lion_eprint(1, "constructResidence: self=%d,gid=%ld,ifirst=%ld,max=%ld \n",self2,gid,ifirst,max); + } + ifirst++; + } Gid total = max + 1; int peers = PCU_Comm_Peers(); - int quotient = total / peers; - int remainder = total % peers; + Gid quotientL = total / peers; + int quotient = quotientL; + Gid remainderL = total % peers; + int remainder = remainderL; int mySize = quotient; int self = PCU_Comm_Self(); if (self == (peers - 1)) mySize += remainder; + if (self == (peers - 1)) lion_eprint(1, "CR1 mysize=%d \n",mySize); typedef std::vector< std::vector > TmpParts; TmpParts tmpParts(mySize); /* if we have a vertex, send its global id to the broker for that global id */ PCU_Comm_Begin(); APF_ITERATE(GlobalToVert, globalToVert, it) { - int gid = it->first; - int to = std::min(peers - 1, gid / quotient); + Gid gid = it->first; + if(gid < 0 || gid > max ){ + lion_eprint(1, "constructResidence cgTV3: self=%d,gid=%ld \n",self2,gid); + } + Gid tmpL=gid / quotient; + int tmpI=tmpL; int to = std::min(peers - 1,tmpI); PCU_COMM_PACK(to, gid); } PCU_Comm_Send(); - int myOffset = self * quotient; + Gid myOffset = (long)self * quotient; + if (self == (peers - 1)) lion_eprint(1, "CR5: self=%d,myOffset=%ld,quotient=%d \n",self,myOffset,quotient); /* brokers store all the part ids that sent messages for each global id */ while (PCU_Comm_Receive()) { - int gid; + Gid gid; PCU_COMM_UNPACK(gid); int from = PCU_Comm_Sender(); - tmpParts.at(gid - myOffset).push_back(from); + Gid tmpL=gid - myOffset; // forcing 64 bit difference until we know it is safe + int tmpI=tmpL; + tmpParts.at(tmpI).push_back(from); } /* for each global id, send all associated part ids to all associated parts */ @@ -88,7 +124,7 @@ static void constructResidence(Mesh2* m, GlobalToVert& globalToVert) std::vector& parts = tmpParts[i]; for (size_t j = 0; j < parts.size(); ++j) { int to = parts[j]; - int gid = i + myOffset; + Gid gid = i + myOffset; int nparts = parts.size(); PCU_COMM_PACK(to, gid); PCU_COMM_PACK(to, nparts); @@ -101,7 +137,7 @@ static void constructResidence(Mesh2* m, GlobalToVert& globalToVert) lookup the vertex and classify it on the partition model entity for that set of parts */ while (PCU_Comm_Receive()) { - int gid; + Gid gid; PCU_COMM_UNPACK(gid); int nparts; PCU_COMM_UNPACK(nparts); @@ -124,7 +160,9 @@ static void constructRemotes(Mesh2* m, GlobalToVert& globalToVert) int self = PCU_Comm_Self(); PCU_Comm_Begin(); APF_ITERATE(GlobalToVert, globalToVert, it) { - int gid = it->first; + Gid gid = it->first; + if(gid < 0 ) + lion_eprint(1, "constructRemotes cgTV4: self=%d,gid=%ld \n",self,gid); MeshEntity* vert = it->second; Parts residence; m->getResidence(vert, residence); @@ -136,7 +174,7 @@ static void constructRemotes(Mesh2* m, GlobalToVert& globalToVert) } PCU_Comm_Send(); while (PCU_Comm_Receive()) { - int gid; + Gid gid; PCU_COMM_UNPACK(gid); MeshEntity* remote; PCU_COMM_UNPACK(remote); @@ -144,9 +182,11 @@ static void constructRemotes(Mesh2* m, GlobalToVert& globalToVert) MeshEntity* vert = globalToVert[gid]; m->addRemote(vert, from, remote); } + // who is not stuck? + lion_eprint(1, "%d done inside remotes \n",PCU_Comm_Self()); } -NewElements assemble(Mesh2* m, const int* conn, int nelem, int etype, +NewElements assemble(Mesh2* m, const Gid* conn, int nelem, int etype, GlobalToVert& globalToVert) { constructVerts(m, conn, nelem, etype, globalToVert); @@ -156,12 +196,13 @@ NewElements assemble(Mesh2* m, const int* conn, int nelem, int etype, void finalise(Mesh2* m, GlobalToVert& globalToVert) { constructResidence(m, globalToVert); + lion_eprint(1, "%d after residence \n",PCU_Comm_Self()); constructRemotes(m, globalToVert); stitchMesh(m); m->acceptChanges(); } -NewElements construct(Mesh2* m, const int* conn, int nelem, int etype, +NewElements construct(Mesh2* m, const Gid* conn, int nelem, int etype, GlobalToVert& globalToVert) { const auto& newElements = assemble(m, conn, nelem, etype, globalToVert); @@ -181,17 +222,22 @@ void setCoords(Mesh2* m, const double* coords, int nverts, int self = PCU_Comm_Self(); if (self == (peers - 1)) mySize += remainder; - int myOffset = self * quotient; + Gid myOffset = (long)self * quotient; /* Force each peer to have exactly mySize verts. This means we might need to send and recv some coords */ double* c = new double[mySize*3]; - int start = PCU_Exscan_Int(nverts); + Gid start = PCU_Exscan_Long(nverts); + + PCU_Comm_Begin(); // the forced 64 bit math below may not be necessary + Gid tmpL=start / quotient; + int tmpInt=tmpL; + int to = std::min(peers - 1, tmpInt); + tmpL=(to+1)*(long)quotient-start; + tmpInt=tmpL; + int n = std::min(tmpInt, nverts); - PCU_Comm_Begin(); - int to = std::min(peers - 1, start / quotient); - int n = std::min((to+1)*quotient-start, nverts); while (nverts > 0) { PCU_COMM_PACK(to, start); PCU_COMM_PACK(to, n); @@ -206,7 +252,7 @@ void setCoords(Mesh2* m, const double* coords, int nverts, PCU_Comm_Send(); while (PCU_Comm_Receive()) { PCU_COMM_UNPACK(start); - PCU_COMM_UNPACK(n); + PCU_COMM_UNPACK(n); // |||||| more in-place 64 bit math PCU_Comm_Unpack(&c[(start - myOffset) * 3], n*3*sizeof(double)); } @@ -215,32 +261,36 @@ void setCoords(Mesh2* m, const double* coords, int nverts, TmpParts tmpParts(mySize); PCU_Comm_Begin(); APF_CONST_ITERATE(GlobalToVert, globalToVert, it) { - int gid = it->first; - int to = std::min(peers - 1, gid / quotient); + Gid gid = it->first; + Gid tmpL=gid / quotient; + int tmpInt=tmpL; + int to = std::min(peers - 1, tmpInt); PCU_COMM_PACK(to, gid); } PCU_Comm_Send(); while (PCU_Comm_Receive()) { - int gid; + Gid gid; PCU_COMM_UNPACK(gid); - int from = PCU_Comm_Sender(); - tmpParts.at(gid - myOffset).push_back(from); + Gid from = PCU_Comm_Sender(); + Gid tmpL=gid - myOffset; + int tmpInt=tmpL; + tmpParts.at(tmpInt).push_back(from); } - + /* Send the coords to everybody who want them */ PCU_Comm_Begin(); for (int i = 0; i < mySize; ++i) { std::vector& parts = tmpParts[i]; for (size_t j = 0; j < parts.size(); ++j) { int to = parts[j]; - int gid = i + myOffset; + Gid gid = i + myOffset; PCU_COMM_PACK(to, gid); PCU_Comm_Pack(to, &c[i*3], 3*sizeof(double)); } } PCU_Comm_Send(); while (PCU_Comm_Receive()) { - int gid; + Gid gid; PCU_COMM_UNPACK(gid); double v[3]; PCU_Comm_Unpack(v, sizeof(v)); @@ -251,7 +301,213 @@ void setCoords(Mesh2* m, const double* coords, int nverts, delete [] c; } -void destruct(Mesh2* m, int*& conn, int& nelem, int &etype, int cellDim) +void setMatches(Mesh2* m, const Gid* matches, int nverts, + GlobalToVert& globalToVert) +{ + Gid max = getMax(globalToVert); + Gid total = max + 1; + int peers = PCU_Comm_Peers(); + int quotient = total / peers; + int remainder = total % peers; + int mySize = quotient; + int self = PCU_Comm_Self(); + if (self == (peers - 1)) + mySize += remainder; + Gid myOffset = (long)self * quotient; + + /* Force each peer to have exactly mySize verts. + This means we might need to send and recv some matches */ + Gid* c = new Gid[mySize]; + Gid start = PCU_Exscan_Long(nverts); + + PCU_Comm_Begin(); + + Gid tmpL=start / quotient; + int tmpInt=tmpL; + int to = std::min(peers - 1, tmpInt); + tmpL=(to+1)*(long)quotient-start; + tmpInt=tmpL; + int n = std::min(tmpInt, nverts); + while (nverts > 0) { + PCU_COMM_PACK(to, start); + PCU_COMM_PACK(to, n); + PCU_Comm_Pack(to, matches, n*sizeof(Gid)); + + nverts -= n; + start += n; + matches += n; + to = std::min(peers - 1, to + 1); + n = std::min(quotient, nverts); + } + PCU_Comm_Send(); + while (PCU_Comm_Receive()) { + PCU_COMM_UNPACK(start); + PCU_COMM_UNPACK(n); /// in-place 64 + PCU_Comm_Unpack(&c[(start - myOffset)], n*sizeof(Gid)); + } + + + /* Tell all the owners of the matches what we need */ + typedef std::vector< std::vector > TmpParts; + TmpParts tmpParts(mySize); + PCU_Comm_Begin(); + APF_CONST_ITERATE(GlobalToVert, globalToVert, it) { + Gid gid = it->first; + int tmpI=gid / quotient; + int to = std::min(peers - 1,tmpI); + PCU_COMM_PACK(to, gid); + } + PCU_Comm_Send(); + while (PCU_Comm_Receive()) { + Gid gid; + PCU_COMM_UNPACK(gid); + int from = PCU_Comm_Sender(); + tmpParts.at(gid - myOffset).push_back(from); + } + + MeshTag* matchGidTag = m->createLongTag("matchGids", 1); + /* Send the matches to everybody who wants them */ + PCU_Comm_Begin(); + for (int i = 0; i < mySize; ++i) { + std::vector& parts = tmpParts[i]; + for (size_t j = 0; j < parts.size(); ++j) { + int to = parts[j]; + Gid gid = i + myOffset; + Gid matchGid = c[i]; + PCU_COMM_PACK(to, gid); + PCU_COMM_PACK(to, matchGid); + } + } + PCU_Comm_Send(); + while (PCU_Comm_Receive()) { + Gid gid; + PCU_COMM_UNPACK(gid); + Gid match; + PCU_COMM_UNPACK(match); + PCU_ALWAYS_ASSERT(gid != match); + PCU_ALWAYS_ASSERT(globalToVert.count(gid)); + m->setLongTag(globalToVert[gid], matchGidTag, &match); + } + + /* Use the 1D partitioning of global ids to distribute the + * entity pointers and their owning ranks. Process 0 will hold + * the entity pointers and owners for mesh vertex gid [0..quotient), + * process 1 holds gids [quotient..2*quotient), ... + */ + PCU_Comm_Begin(); + APF_CONST_ITERATE(GlobalToVert, globalToVert, it) { + MeshEntity* e = it->second; // KEJ does not follow this + Gid gid = it->first; + int tmpI=gid / quotient; + int to = std::min(peers - 1,tmpI); + PCU_COMM_PACK(to, gid); + PCU_COMM_PACK(to, e); + } + PCU_Comm_Send(); + typedef std::pair< int, apf::MeshEntity* > EntOwnerPtrs; + typedef std::map< Gid, std::vector< EntOwnerPtrs > > GidPtrs; + GidPtrs gidPtrs; + while (PCU_Comm_Receive()) { + Gid gid; + PCU_COMM_UNPACK(gid); + MeshEntity* vert; + PCU_COMM_UNPACK(vert); + int owner = PCU_Comm_Sender(); + gidPtrs[gid-myOffset].push_back(EntOwnerPtrs(owner,vert)); + } + + /* Tell the brokers of the matches we need */ + typedef std::pair MatchingPair; + typedef std::map< MatchingPair, std::vector > MatchMap; + MatchMap matchParts; + PCU_Comm_Begin(); + APF_CONST_ITERATE(GlobalToVert, globalToVert, it) { //loop over local verts + Gid gid = it->first; + Gid matchGid; + m->getLongTag(it->second, matchGidTag, &matchGid); //get the matched ent gid + if( matchGid != -1 ) { // marker for an unmatched vertex + int tmpI=matchGid / quotient; + int to = std::min(peers - 1,tmpI); //broker + PCU_COMM_PACK(to, gid); // send the local vert gid + PCU_COMM_PACK(to, matchGid); // and the match gid needed + } + } + PCU_Comm_Send(); + while (PCU_Comm_Receive()) { + Gid gid; + PCU_COMM_UNPACK(gid); // request from entity gid + Gid matchGid; + PCU_COMM_UNPACK(matchGid); // requesting matched entity gid + MatchingPair mp(gid,matchGid); + int from = PCU_Comm_Sender(); + matchParts[mp].push_back(from); // store a list of the proceses that need the pair (entity gid, match gid) + } + + /* Send the match pointer and owner process to everybody + * who wants them */ + PCU_Comm_Begin(); + APF_ITERATE(MatchMap,matchParts,it) { + MatchingPair mp = it->first; + Gid gid = mp.first; + Gid matchGid = mp.second; + std::vector parts = it->second; + for(size_t i=0; iaddMatch(partner, owner, match); + } + } + + APF_CONST_ITERATE(GlobalToVert, globalToVert, it) { //loop over local verts + apf::MeshEntity* left = it->second; + Gid matchGid; + m->getLongTag(left, matchGidTag, &matchGid); //get the matched ent gid + if( matchGid != -1 ) { // a matched vtx + apf::Copies copies; + m->getRemotes(left,copies); + APF_ITERATE(apf::Copies, copies, cp) { + int rightPart = cp->first; + apf::MeshEntity* right = cp->second; + m->addMatch(left, rightPart, right); + } + } + } + + delete [] c; + + apf::removeTagFromDimension(m, matchGidTag, 0); + m->destroyTag(matchGidTag); +} + +void destruct(Mesh2* m, Gid*& conn, int& nelem, int &etype, int cellDim) { if(cellDim == -1) cellDim = m->getDimension(); //int dim = m->getDimension(); diff --git a/apf/apfConvert.h b/apf/apfConvert.h index fcee947f8..a045b8abc 100644 --- a/apf/apfConvert.h +++ b/apf/apfConvert.h @@ -18,6 +18,7 @@ namespace apf { class Mesh; class Mesh2; +class MeshTag; //Extra? class ModelEntity; class MeshEntity; using NewElements = std::vector; @@ -30,15 +31,17 @@ using NewElements = std::vector; void convert(Mesh *in, Mesh2 *out, MeshEntity** nodes=NULL, MeshEntity** elems=NULL, bool copy_data=true); +typedef long Gid; + /** \brief a map from global ids to vertex objects */ -typedef std::map GlobalToVert; +typedef std::map GlobalToVert; /** \brief assemble a mixed-cell-type mesh from just a connectivity array \details construct is now split into two functions, assemble and finalise. The premise of assemble being that it is called multiple times for a given cell type, across several different cell types in the input mesh. */ -NewElements assemble(Mesh2* m, const int* conn, int nelem, int etype, +NewElements assemble(Mesh2* m, const apf::Gid* conn, int nelem, int etype, GlobalToVert& globalToVert); /** \brief finalise construction of a mixed-cell-type mesh from just a connectivity array @@ -65,7 +68,7 @@ void finalise(Mesh2* m, GlobalToVert& globalToVert); Note that all vertices will have zero coordinates, so it is often good to use apf::setCoords after this. */ -NewElements construct(Mesh2* m, const int* conn, int nelem, int etype, +NewElements construct(Mesh2* m, const apf::Gid* conn, int nelem, int etype, GlobalToVert& globalToVert); /** \brief Assign coordinates to the mesh @@ -79,6 +82,19 @@ NewElements construct(Mesh2* m, const int* conn, int nelem, int etype, void setCoords(Mesh2* m, const double* coords, int nverts, GlobalToVert& globalToVert); +/** \brief Assign matching to the mesh + * \details + * Each peer provides a set of the matched entity global ids. An id set + * to -1 indicates that the vertex is not matched. The ids most be ordered + * according to the global ids of the vertices. Peer 0 provides the ids + * for vertices 0 to m-1, peer to for m to n-1, ... + * After this call, all vertices in the apf::Mesh2 object have correct + * coordinates assigned. + */ +void setMatches(Mesh2* m, const Gid* matches, int nverts, + GlobalToVert& globalToVert); + + /** \brief convert an apf::Mesh2 object into a connectivity array \details this is useful for debugging the apf::convert function \param mesh the apf mesh @@ -86,7 +102,7 @@ void setCoords(Mesh2* m, const double* coords, int nverts, \param etype apf::Mesh::Type \param cellDim dimension of elements (if embedded in a higher dimension manifold) */ -void destruct(Mesh2* m, int*& conn, int& nelem, int &etype, int cellDim = -1); +void destruct(Mesh2* m, Gid*& conn, int& nelem, int &etype, int cellDim = -1); /** \brief get a contiguous set of global vertex coordinates \details this is used for debugging apf::setCoords */ diff --git a/apf/apfConvertTags.h b/apf/apfConvertTags.h new file mode 100644 index 000000000..069caa43b --- /dev/null +++ b/apf/apfConvertTags.h @@ -0,0 +1,163 @@ +#ifndef APF_CONVERT_TAGS_H +#define APF_CONVERT_TAGS_H + +#include +#include "apf.h" +#include "apfConvert.h" + +namespace { + static apf::Gid getMax(const apf::GlobalToVert& globalToVert) + { + apf::Gid max = -1; + APF_CONST_ITERATE(apf::GlobalToVert, globalToVert, it) + max = std::max(max, it->first); + return PCU_Max_Long(max); // this is type-dependent + } + + template inline + apf::MeshTag* createTag(apf::Mesh*, + const char*, const int) { + exit(EXIT_FAILURE); + return 0; + } + + template <> inline + apf::MeshTag* createTag(apf::Mesh* m, + const char* name, const int entries) { + return m->createIntTag(name,entries); + } + + template <> inline + apf::MeshTag* createTag(apf::Mesh* m, + const char* name, const int entries) { + return m->createLongTag(name,entries); + } + + template <> inline + apf::MeshTag* createTag(apf::Mesh* m, + const char* name, const int entries) { + return m->createDoubleTag(name,entries); + } + + inline void setEntTag(apf::Mesh* m, apf::MeshTag* t, + apf::MeshEntity* e, long* vals) { + return m->setLongTag(e,t,vals); + } + + inline void setEntTag(apf::Mesh* m, apf::MeshTag* t, + apf::MeshEntity* e, int* vals) { + return m->setIntTag(e,t,vals); + } + + inline void setEntTag(apf::Mesh* m, apf::MeshTag* t, + apf::MeshEntity* e, double* vals) { + return m->setDoubleTag(e,t,vals); + } +} + +namespace apf { +/** \brief Assign tag values to the mesh + * \param m (in) mesh + * \param tagName (in) name of returned tag + * \param vals (in) T array of length nverts + * \param entries (in) number of values per vertex + * \param nverts (in) number of vertices for this process + * \param globalToVert (in) map from global mesh vertex ids + * to local vertex * pointers + * \details + * See 'setCoords' for distribution details + */ +template +apf::MeshTag* setMappedTag(Mesh2* m, const char* tagName, + const T* vals, const int entries, + int nverts, GlobalToVert& globalToVert) +{ + apf::Gid max = getMax(globalToVert); + apf::Gid total = max + 1; + int peers = PCU_Comm_Peers(); + int quotient = total / peers; + int remainder = total % peers; + int mySize = quotient; + int self = PCU_Comm_Self(); + if (self == (peers - 1)) + mySize += remainder; + apf::Gid myOffset = (long)self * quotient; + + /* Force each peer to have exactly mySize verts. + This means we might need to send and recv some coords */ + T* c = new T[mySize*entries]; + + apf::Gid start = PCU_Exscan_Long(nverts); + + PCU_Comm_Begin(); + apf::Gid tmpL=start / quotient; + int tmpI=tmpL; + int to = std::min(peers - 1, tmpI); + tmpL=(to+1)*(long)quotient-start; + tmpI=tmpL; + int n = std::min(tmpI, nverts); + while (nverts > 0) { + PCU_COMM_PACK(to, start); + PCU_COMM_PACK(to, n); + PCU_Comm_Pack(to, vals, n*entries*sizeof(T)); + + nverts -= n; + start += n; + vals += n*entries; + to = std::min(peers - 1, to + 1); + n = std::min(quotient, nverts); + } + PCU_Comm_Send(); + while (PCU_Comm_Receive()) { + PCU_COMM_UNPACK(start); + PCU_COMM_UNPACK(n); + PCU_Comm_Unpack(&c[(start - myOffset) * entries], n*entries*sizeof(T)); + } + + /* Tell all the owners of the data what we need */ + typedef std::vector< std::vector > TmpParts; + TmpParts tmpParts(mySize); + PCU_Comm_Begin(); + APF_CONST_ITERATE(GlobalToVert, globalToVert, it) { + apf::Gid gid = it->first; + tmpL=gid / quotient; + tmpI=tmpL; + int to = std::min(peers - 1, tmpI); +// replaced int to = std::min(peers - 1, gid / quotient); + PCU_COMM_PACK(to, gid); + } + PCU_Comm_Send(); + while (PCU_Comm_Receive()) { + apf::Gid gid; + PCU_COMM_UNPACK(gid); + int from = PCU_Comm_Sender(); + tmpParts.at(gid - myOffset).push_back(from); + } + + /* Send the data to everybody who want them */ + PCU_Comm_Begin(); + for (int i = 0; i < mySize; ++i) { + std::vector& parts = tmpParts[i]; + for (size_t j = 0; j < parts.size(); ++j) { + int to = parts[j]; + apf::Gid gid = i + myOffset; + PCU_COMM_PACK(to, gid); + PCU_Comm_Pack(to, &c[i*entries], entries*sizeof(T)); + } + } + PCU_Comm_Send(); + apf::MeshTag* t = createTag(m,tagName,entries); + T* v = new T[entries]; + while (PCU_Comm_Receive()) { + apf::Gid gid; + PCU_COMM_UNPACK(gid); + PCU_Comm_Unpack(v, entries*sizeof(T)); + setEntTag(m,t,globalToVert[gid],v); + } + delete [] v; + delete [] c; + return t; +} + +} +#endif diff --git a/apf/apfElement.cc b/apf/apfElement.cc index 82c89c602..161de8df1 100644 --- a/apf/apfElement.cc +++ b/apf/apfElement.cc @@ -5,6 +5,8 @@ * BSD license as described in the LICENSE file in the top-level directory. */ +#include + #include "apfElement.h" #include "apfShape.h" #include "apfMesh.h" @@ -75,6 +77,8 @@ Matrix3x3 getJacobianInverse(Matrix3x3 J, int dim) void Element::getGlobalGradients(Vector3 const& local, NewArray& globalGradients) { + PCU_ALWAYS_ASSERT_VERBOSE(!field->getShape()->isVectorShape(), + "Not implemented for vector shape functions!"); Matrix3x3 J; parent->getJacobian(local,J); Matrix3x3 jinv = getJacobianInverse(J, getDimension()); @@ -87,13 +91,27 @@ void Element::getGlobalGradients(Vector3 const& local, void Element::getComponents(Vector3 const& xi, double* c) { - NewArray shapeValues; - shape->getValues(mesh, entity, xi, shapeValues); - for (int ci = 0; ci < nc; ++ci) - c[ci] = 0; - for (int ni = 0; ni < nen; ++ni) + // handle cases with vector shape functions + if (field->getShape()->isVectorShape()) { + NewArray shapeValues; + shapeValues.allocate(nen); + getVectorShapeValues(this, xi, shapeValues); + for (int ci = 0; ci < 3; ci++) + c[ci] = 0.; + for (int ni = 0; ni < nen; ni++) + for (int ci = 0; ci < 3; ci++) + c[ci] += nodeData[ni] * shapeValues[ni][ci]; + } + // handle cases with scalar shape functions + else { + NewArray shapeValues; + shape->getValues(mesh, entity, xi, shapeValues); for (int ci = 0; ci < nc; ++ci) - c[ci] += nodeData[ni * nc + ci] * shapeValues[ni]; + c[ci] = 0; + for (int ni = 0; ni < nen; ++ni) + for (int ci = 0; ci < nc; ++ci) + c[ci] += nodeData[ni * nc + ci] * shapeValues[ni]; + } } void Element::getNodeData() @@ -101,4 +119,11 @@ void Element::getNodeData() field->getData()->getElementData(entity,nodeData); } +void Element::getElementNodeData(NewArray& d) +{ + d.allocated() ? d.resize(nen) : d.allocate(nen); + for (int i = 0; i < nen; i++) + d[i] = nodeData[i]; +} + }//namespace apf diff --git a/apf/apfElement.h b/apf/apfElement.h index f100d42e8..b54cd5901 100644 --- a/apf/apfElement.h +++ b/apf/apfElement.h @@ -15,6 +15,7 @@ namespace apf { class EntityShape; +class FieldShape; class VectorElement; class Element @@ -32,7 +33,9 @@ class Element MeshEntity* getEntity() {return entity;} Mesh* getMesh() {return mesh;} EntityShape* getShape() {return shape;} + FieldShape* getFieldShape() {return field->getShape();} void getComponents(Vector3 const& xi, double* c); + void getElementNodeData(NewArray& d); protected: void init(Field* f, MeshEntity* e, VectorElement* p); void getNodeData(); diff --git a/apf/apfElementOf.h b/apf/apfElementOf.h index 9b20565d7..afedf6111 100644 --- a/apf/apfElementOf.h +++ b/apf/apfElementOf.h @@ -14,22 +14,22 @@ namespace apf { -template +template class ElementOf : public Element { public: - ElementOf(FieldOf* f, MeshEntity* e): + ElementOf(FieldOf* f, MeshEntity* e): Element(f,e) { } - ElementOf(FieldOf* f, VectorElement* p): + ElementOf(FieldOf* f, VectorElement* p): Element(f,p) { } virtual ~ElementOf() {} - T* getNodeValues() + S* getNodeValues() { - return reinterpret_cast(&(this->nodeData[0])); + return reinterpret_cast(&(this->nodeData[0])); } T getValue(Vector3 const& local) { @@ -37,10 +37,10 @@ class ElementOf : public Element getComponents(local, reinterpret_cast(value)); return value[0]; } - void getValues(NewArray& values) + void getValues(NewArray& values) { values.allocate(nen); - T* nodeValues = getNodeValues(); + S* nodeValues = getNodeValues(); for (int i=0; i < nen; ++i) values[i] = nodeValues[i]; } diff --git a/apf/apfField.h b/apf/apfField.h index c68fc50a8..39b31718c 100644 --- a/apf/apfField.h +++ b/apf/apfField.h @@ -25,7 +25,7 @@ class FieldBase FieldData* d); virtual ~FieldBase(); /* returns the number of components per node: - 1 for a scalar field + 1 for a scalar field and Nedelec type fields 3 for a vector field 9 for a matrix field, etc. */ virtual int countComponents() const = 0; @@ -54,7 +54,15 @@ class Field : public FieldBase { public: virtual Element* getElement(VectorElement* e) = 0; + // Think of this as the dof types, that is: + // ScalarField will have 1 dof per node + // VectorField will have 3 dof per noed + // MixedVectorField (eg Nedelec) will have 1 dof per node virtual int getValueType() const = 0; + // Think of this as the number of components for the shape, that is: + // Regular shape function will have 1 value per node, but Nedelec type + // shapes will have 3 values per node + virtual int getShapeType() const = 0; virtual int getScalarType() {return Mesh::DOUBLE;} FieldDataOf* getData(); virtual void project(Field* from) = 0; diff --git a/apf/apfFieldData.cc b/apf/apfFieldData.cc index 2cc1a2c8b..2c3f1156a 100644 --- a/apf/apfFieldData.cc +++ b/apf/apfFieldData.cc @@ -3,6 +3,7 @@ #include "apfShape.h" #include #include +#include namespace apf { @@ -208,6 +209,45 @@ void reorderData(T const dataIn[], T dataOut[], int const order[], int nc, int n } } +// This is only used to reorder the data for interior face nodes on a face of +// a Nedelec tet, where each node on the face contains 2 dof values. +template +void reorderDataNedelec( + T const dataIn[], + T dataOut[], + int const order[], + int nc, + int nn, + int type) +{ + if (type == Mesh::TRIANGLE) + for (int i = 0; i < nn; ++i) { + if(order[2*nn+i]) + { + dataOut[i*nc] = (order[i] >= 0) ? + dataIn[ order[i] ] : -dataIn[ -(order[i]+1) ]; + } + else + dataOut[i*nc] = 0.; + + if(order[3*nn+i]) + { + dataOut[i*nc] += (order[1*nn+i] >= 0) ? + dataIn[ order[1*nn+i] ] : -dataIn[ -(order[1*nn+i]+1) ]; + } + } + else if (type == Mesh::EDGE) + for (int i = 0; i < nn; ++i) { + int oi = order[i] >= 0 ? order[i] : -(order[i]+1); + for (int j = 0; j < nc; ++j) + dataOut[oi * nc + j] = + order[i] >= 0 ? dataIn[i * nc + j] : -dataIn[i * nc + j]; + } + else + PCU_ALWAYS_ASSERT_VERBOSE(0, + "type has to be Mesh::EDGE or Mesh::TRIANGLE!"); +} + template int FieldDataOf::getElementData(MeshEntity* entity, NewArray& data) { @@ -227,23 +267,51 @@ int FieldDataOf::getElementData(MeshEntity* entity, NewArray& data) apf::DynamicArray adata; int n = 0; for (int d = 0; d <= ed; ++d) { - if (fs->hasNodesIn(d)) { - Downward a; - int na = mesh->getDownward(entity,d,a); - for (int i = 0; i < na; ++i) { - int nan = fs->countNodesOn(mesh->getType(a[i])); - if (nan > 1 && ed != d) { /* multiple shared nodes, check alignment */ - order.setSize(nen); /* nen >= nan */ - adata.setSize(nen); /* setSize is no-op for the same size */ + if (!fs->hasNodesIn(d)) continue; + Downward a; + int na = mesh->getDownward(entity,d,a); + for (int i = 0; i < na; ++i) { + int nan = fs->countNodesOn(mesh->getType(a[i])); + // for vector shapes (i.e., nedelec) direction matters for nan>=1 + if (fs->isVectorShape()) { + if (nan >= 1 && ed != d) { + // The 1st nen ints is the 1st contribution + // The 2nd nen ints is the 2nd contribution + // The 3rd nen ints tells whether to add 1st contribution + // The 4th nen ints tells whether to add 2st contribution + order.setSize(4*nen); + adata.setSize(nen); es->alignSharedNodes(mesh, entity, a[i], &order[0]); get(a[i], &adata[0]); - reorderData(&adata[0], &data[n], &order[0], nc, nan); - } else if (nan) { /* non-zero set of nodes, either one - or not shared */ - get(a[i], &data[n]); + // We would want to have a different reorder here to handle + // the fact that order now includes some extra info + int dtype = mesh->getType(a[i]); + reorderDataNedelec(&adata[0], &data[n], &order[0], nc, nan, dtype); } - n += nc * nan; + // this else is required to add the dofs associated with the tet + else + get(a[i], &data[n]); + } + // for non vector shapes direction matters for nan>1 + else { + if (nan > 1 && ed != d) { /* multiple shared nodes, check alignment */ + order.setSize(nen); /* nen >= nan */ + adata.setSize(nen); /* setSize is no-op for the same size */ + // Note: The above efficiency consideration does not account for the + // fact that nc might be very large (e.g. nc = 9 for matrix fields) + // and for such cases setting the size of adata to "nen" is not enough. + // Hence the need for the following line. + if (nan*nc > nen) + adata.setSize(nan*nc); + es->alignSharedNodes(mesh, entity, a[i], &order[0]); + get(a[i], &adata[0]); + reorderData(&adata[0], &data[n], &order[0], nc, nan); + } else if (nan) { /* non-zero set of nodes, either one + or not shared */ + get(a[i], &data[n]); + } } + n += nc * nan; } } PCU_ALWAYS_ASSERT(n == nc * nen); diff --git a/apf/apfH1Shapes.cc b/apf/apfH1Shapes.cc new file mode 100644 index 000000000..673b2b781 --- /dev/null +++ b/apf/apfH1Shapes.cc @@ -0,0 +1,696 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "apfShape.h" +#include "apfMesh.h" +#include "apfFieldOf.h" +#include "apfElement.h" +#include "apfVectorElement.h" +#include "apfPolyBasis1D.h" +#include +#include +#include +#include +#include + + +namespace apf { + +// This is used for static tables only. +// The following implementation are for general orders but template classes +// are only instantiated up to order 10 for now. +static const int MAX_ORDER = 10; + +static inline int countEdgeNodes(int P) +{ + return (P+1); +} + +static inline int countTriNodes(int P) +{ + return (P+1)*(P+2)/2; +} + +static inline int countTetNodes(int P) +{ + return (P+1)*(P+2)*(P+3)/6; +} + +static inline int countInternalEdgeNodes(int P) +{ + return (P-1); +} + +static inline int countInternalTriNodes(int P) +{ + return (P-1)*(P-2)/2; +} + +static inline int countInternalTetNodes(int P) +{ + return (P-1)*(P-2)*(P-3)/6; +} + +static void computeTriangleTi( + int P, /*order*/ + mth::Matrix& Q, /*Q in QR factorization of Ti*/ + mth::Matrix& R) /*R in QR factorization of Ti*/ +{ + int non = countTriNodes(P); + + apf::NewArray cp; + getClosedPoints(P, cp); + + + const int p = P; + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_l(p+1); + + apf::DynamicArray nodes (non); + + int o = 0; + + // vertices + nodes[o][0] = cp[0]; nodes[o][1] = cp[0]; nodes[o][2] = 0.; + o++; + nodes[o][0] = cp[p]; nodes[o][1] = cp[0]; nodes[o][2] = 0.; + o++; + nodes[o][0] = cp[0]; nodes[o][1] = cp[p]; nodes[o][2] = 0.; + o++; + + // edges + for (int i = 1; i < p; i++) // (0,1) + { + nodes[o][0] = cp[i]; nodes[o][1] = cp[0]; nodes[o][2] = 0.; + o++; + } + + for (int i = 1; i < p; i++) // (1,2) + { + nodes[o][0] = cp[p-i]; nodes[o][1] = cp[i]; nodes[o][2] = 0.; + o++; + } + + for (int i = 1; i < p; i++) // (2,0) + { + nodes[o][0] = cp[0]; nodes[o][1] = cp[p-i]; nodes[o][2] = 0.; + o++; + } + + + // face + for (int j = 1; j < p; j++) { + for (int i = 1; i + j < p; i++) + { + double w = cp[i] + cp[j] + cp[p-i-j]; + nodes[o][0] = cp[i]/w; nodes[o][1] = cp[j]/w; nodes[o][2] = 0.; + o++; + } + } + + // Populate T + mth::Matrix T(non,non); + for (int m = 0; m < non; m++) + { + o = 0; + + double x = nodes[m][0]; double y = nodes[m][1]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, 1. - x - y, &shape_l[0]); + + for (int j = 0; j <= p; j++) + for (int i = 0; i + j <= p; i++) + T(o++, m) = shape_x[i]*shape_y[j]*shape_l[p-i-j]; + } + mth::decomposeQR(T, Q, R); +} + +static void computeTetTi( + int P, /*order*/ + mth::Matrix& Q, /*Q in QR factorization of Ti*/ + mth::Matrix& R) /*R in QR factorization of Ti*/ +{ + int non = countTetNodes(P); + + apf::NewArray cp; + getClosedPoints(P, cp); + + const int p = P; + + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_z(p+1); + apf::NewArray shape_l(p+1); + + apf::DynamicArray nodes(non); + nodes.setSize(non); + + + int o = 0; + // vertices + nodes[o][0] = cp[0]; nodes[o][1] = cp[0]; nodes[o][2] = cp[0]; + o++; + nodes[o][0] = cp[p]; nodes[o][1] = cp[0]; nodes[o][2] = cp[0]; + o++; + nodes[o][0] = cp[0]; nodes[o][1] = cp[p]; nodes[o][2] = cp[0]; + o++; + nodes[o][0] = cp[0]; nodes[o][1] = cp[0]; nodes[o][2] = cp[p]; + o++; + + // edges + for (int i = 1; i < p; i++) // (0,1) + { + nodes[o][0] = cp[i]; nodes[o][1] = cp[0]; nodes[o][2] = cp[0]; + o++; + } + + for (int i = 1; i < p; i++) // (1,2) + { + nodes[o][0] = cp[p-i]; nodes[o][1] = cp[i]; nodes[o][2] = cp[0]; + o++; + } + + for (int i = 1; i < p; i++) // (2,0) + { + nodes[o][0] = cp[0]; nodes[o][1] = cp[p-i]; nodes[o][2] = cp[0]; + o++; + } + + for (int i = 1; i < p; i++) // (0,3) + { + nodes[o][0] = cp[0]; nodes[o][1] = cp[0]; nodes[o][2] = cp[i]; + o++; + } + + for (int i = 1; i < p; i++) // (1,3) + { + nodes[o][0] = cp[p-i]; nodes[o][1] = cp[0]; nodes[o][2] = cp[i]; + o++; + } + + for (int i = 1; i < p; i++) // (2,3) + { + nodes[o][0] = cp[0]; nodes[o][1] = cp[p-i]; nodes[o][2] = cp[i]; + o++; + } + + + // faces + // (0,1,2) + for (int j = 1; j < p; j++) + for (int i = 1; i + j < p; i++) { + double w = cp[i] + cp[j] + cp[p-i-j]; + nodes[o][0] = cp[i]/w; nodes[o][1] = cp[j]/w; nodes[o][2] = cp[0]; + o++; + } + + // (0,1,3) + for (int j = 1; j < p; j++) + for (int i = 1; i + j < p; i++) { + double w = cp[i] + cp[j] + cp[p-i-j]; + nodes[o][0] = cp[i]/w; nodes[o][1] = cp[0]/w; nodes[o][2] = cp[j]/w; + o++; + } + + // (1,2,3) + for (int j = 1; j < p; j++) + for (int i = 1; i + j < p; i++) { + double w = cp[i] + cp[j] + cp[p-i-j]; + nodes[o][0] = cp[p-i-j]/w; nodes[o][1] = cp[i]/w; nodes[o][2] = cp[j]/w; + o++; + } + + // (0,2,3) + for (int j = 1; j < p; j++) + for (int i = 1; i + j < p; i++) { + double w = cp[i] + cp[j] + cp[p-i-j]; + nodes[o][0] = cp[0]; nodes[o][1] = cp[i]/w; nodes[o][2] = cp[j]/w; + o++; + } + + // Region + for (int k = 1; k < p; k++) { + for (int j = 1; j + k < p; j++) { + for (int i = 1; i + j + k < p; i++) { + double w = cp[i] + cp[j] + cp[k] + cp[p-i-j-k]; + nodes[o][0] = cp[i]/w; nodes[o][1] = cp[j]/w; nodes[o][2] = cp[k]/w; + o++; + } + } + } + + // Populate T + mth::Matrix T(non,non); + for (int m = 0; m < non; m++) + { + o = 0; + double x = nodes[m][0]; double y = nodes[m][1]; double z = nodes[m][2]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, z, &shape_z[0]); + getChebyshevT(p, 1. - x - y - z, &shape_l[0]); + + for (int k = 0; k <= p; k++) + for (int j = 0; j + k <= p; j++) + for (int i = 0; i + j + k <= p; i++) + T(o++, m) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k]; + } + mth::decomposeQR(T, Q, R); +} + +static void getTi( + int P, + int type, + mth::Matrix& Q, + mth::Matrix& R) +{ + + bool cond = (type == apf::Mesh::TRIANGLE || type == apf::Mesh::TET); + PCU_ALWAYS_ASSERT_VERBOSE(cond, + "type should be either apf::Mesh::TRIANGLE or apf::Mesh::TET!"); + + static apf::NewArray transformQ[apf::Mesh::TYPES][MAX_ORDER+1]; + static apf::NewArray transformR[apf::Mesh::TYPES][MAX_ORDER+1]; + int n = type == apf::Mesh::TRIANGLE ? countTriNodes(P) : countTetNodes(P); + + // get the transform matrices if the are not already computed + if (!transformQ[type][P].allocated()) { + mth::Matrix LQ(n,n); + mth::Matrix LR(n,n); + type == apf::Mesh::TRIANGLE ? + computeTriangleTi(P, LQ, LR) : computeTetTi(P, LQ, LR); + + transformQ[type][P].allocate(n*n); + transformR[type][P].allocate(n*n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + transformQ[type][P][i*n+j] = LQ(i,j); + transformR[type][P][i*n+j] = LR(i,j); + } + } + } + + // set Q and R using transformQ and transformR + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + Q(i,j) = transformQ[type][P][i*n+j]; + R(i,j) = transformR[type][P][i*n+j]; + } + } +} + +// internal nodes only +static apf::Vector3 getH1NodeXi(int type, int P, int node) +{ + if (type == apf::Mesh::VERTEX) + return apf::Vector3(0., 0., 0.); + + apf::NewArray cp; + getClosedPoints(P, cp); + + if (type == apf::Mesh::EDGE) { + PCU_ALWAYS_ASSERT(node >= 0 && node < countInternalEdgeNodes(P)); + int c = 0; + for (int i = 1; i < P; i++) + if (node == c) + return apf::Vector3(2*cp[i]-1, 0., 0.); + else + c++; + } + + if (type == apf::Mesh::TRIANGLE) { + PCU_ALWAYS_ASSERT(node >= 0 && node < countInternalTriNodes(P)); + int c = 0; + for (int j = 1; j < P; j++) + for (int i = 1; i + j < P; i++) + if (node == c) { + double w = cp[i] + cp[j] + cp[P-i-j]; + return apf::Vector3(cp[i]/w, cp[j]/w, 0.); + } + else + c++; + } + + if (type == apf::Mesh::TET) { + PCU_ALWAYS_ASSERT(node >= 0 && node < countInternalTetNodes(P)); + int c = 0; + for (int k = 1; k < P; k++) + for (int j = 1; j + k < P; j++) + for (int i = 1; i + j + k < P; i++) + if (c == node) { + double w = cp[i] + cp[j] + cp[k] + cp[P-i-j-k]; + return apf::Vector3(cp[i]/w, cp[j]/w, cp[k]/w); + } + else + c++; + } + + PCU_ALWAYS_ASSERT_VERBOSE(0, "Unsupported type!"); + return apf::Vector3(0., 0., 0.); +} + +template +class H1Shape: public FieldShape { + public: + H1Shape() + { + std::stringstream ss; + ss << "H1Shape_" << P; + name = ss.str(); + registerSelf(name.c_str()); + } + const char* getName() const { return name.c_str(); } + bool isVectorShape() {return false;} + class Vertex : public apf::EntityShape + { + public: + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& /*xi*/, apf::NewArray& shapes) const + { + shapes.allocate(1); + shapes[0] = 1.; + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for H1Shape for Verts. Aborting()!"); + } + int countNodes() const {return 1;} + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getVectorValues not implemented \ + for H1Shape. Try getValues. Aborting()!"); + } + }; + class Edge : public apf::EntityShape + { + public: + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int p = P; + apf::NewArray shape_x(p+1); + int dof = countNodes(); + + double x = (xi[0]+1.)/2.; // go from [-1,1] to [0,1] + + poly1dBasisBarycentric(p, x, &shape_x[0]); + shapes.allocate(dof); + shapes[0] = shape_x[0]; + shapes[1] = shape_x[p]; + for (int i = 1; i < p; i++) + shapes[i+1] = shape_x[i]; + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for H1Shape for Edges. Aborting()!"); + } + int countNodes() const {return countEdgeNodes(P);} + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getVectorValues not implemented \ + for H1Shape. Try getValues. Aborting()!"); + } + }; + class Triangle : public apf::EntityShape + { + public: + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int p = P; + + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_l(p+1); + + int dof = countNodes(); + mth::Matrix u(dof, 1); + + double x = xi[0]; double y = xi[1]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, 1. - x - y, &shape_l[0]); + + int n = 0; + for (int j = 0; j <= p; j++) + for (int i = 0; i + j <= p; i++) + u(n++, 0) = shape_x[i]*shape_y[j]*shape_l[p-i-j]; + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TRIANGLE, Q, R); + + mth::Matrix S(dof, 1); + for(int i = 0; i < 1; i++) // S = Ti * u + { + mth::Vector B (dof); + mth::Vector X (dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b in QR x = b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + shapes[i] = S(i,0); + } + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for H1Shape for Tris. Aborting()!"); + } + int countNodes() const {return countTriNodes(P);} + void alignSharedNodes(apf::Mesh* m, + apf::MeshEntity* elem, apf::MeshEntity* shared, int order[]) + { + int which, rotate; + bool flip; + getAlignment(m, elem, shared, which, flip, rotate); + if (!flip) + for (int i = 0; i < P-1; i++) + order[i] = i; + else + for (int i = 0; i < P-1; i++) + order[i] = P-2-i; + } + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getVectorValues not implemented \ + for H1Shape. Try getValues. Aborting()!"); + } + }; + class Tetrahedron : public apf::EntityShape + { + public: + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int p = P; + + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_z(p+1); + apf::NewArray shape_l(p+1); + + int dof = countNodes(); + mth::Matrix u(dof, 1); + + double x = xi[0]; double y = xi[1]; double z = xi[2]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, z, &shape_z[0]); + getChebyshevT(p, 1. - x - y - z, &shape_l[0]); + + int n = 0; + for (int k = 0; k <= p; k++) + for (int j = 0; j + k <= p; j++) + for (int i = 0; i + j + k <= p; i++) + u(n++, 0) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k]; + + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TET, Q, R); + + mth::Matrix S(dof, 1); + for(int i = 0; i < 1; i++) // S = Ti * u + { + mth::Vector B (dof); + mth::Vector X (dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + shapes[i] = S(i,0); + } + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for H1Shape for Tets. Aborting()!"); + } + int countNodes() const {return countTetNodes(P);} + void alignSharedNodes(apf::Mesh* m, + apf::MeshEntity* elem, apf::MeshEntity* shared, int order[]) + { + int stype = m->getType(shared); + int which, rotate; + bool flip; + getAlignment(m, elem, shared, which, flip, rotate); + if (stype == apf::Mesh::EDGE) { + if (!flip) + for (int i = 0; i < P-1; i++) + order[i] = i; + else + for (int i = 0; i < P-1; i++) + order[i] = P-2-i; + return; + } + PCU_ALWAYS_ASSERT_VERBOSE(stype == apf::Mesh::TRIANGLE, + "shared type must be triangle!"); + int idx0, idx1; + if (!flip) { + idx0 = (3-rotate) % 3; + idx1 = (4-rotate) % 3; + } + else { + idx0 = (2+rotate) % 3; + idx1 = (1+rotate) % 3; + } + int idx = 0; + for (int i = 0; i <= P-3; i++) + for (int j = 0; j <= P-3-i; j++) { + int ijk[3] = {i, j, P-3-i-j}; + order[idx] = ijk[idx0]*(P-2)-ijk[idx0]*(ijk[idx0]-1)/2+ijk[idx1]; + idx++; + } + } + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& /*xi*/, apf::NewArray& /*shapes*/) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getVectorValues not implemented for \ + H1Shape. Try getValues. Aborting()!"); + } + }; + + EntityShape* getEntityShape(int type) + { + static Vertex vert; + static Edge edge; + static Triangle tri; + static Tetrahedron tet; + static apf::EntityShape* shapes[apf::Mesh::TYPES] = + {&vert, + &edge, + &tri, + NULL, + &tet, + NULL, + NULL, + NULL}; + return shapes[type]; + } + bool hasNodesIn(int dimension) + { + return P > dimension; + /* switch (dimension) { */ + /* case 0: */ + /* return true; */ + /* case 1: */ + /* return P>1; */ + /* case 2: */ + /* return P>2; */ + /* case 3; */ + /* return P>3 */ + /* default: */ + /* return false; */ + /* } */ + } + int countNodesOn(int type) + { + switch (type) { + case apf::Mesh::VERTEX: + return 1; + case apf::Mesh::EDGE: + if (P>1) return countInternalEdgeNodes(P); else return 0; + case apf::Mesh::TRIANGLE: + if (P>2) return countInternalTriNodes(P); else return 0; + case apf::Mesh::TET: + if (P>3) return countInternalTetNodes(P); else return 0; + default: + return 0; + } + } + int getOrder() {return P;} + void getNodeXi(int type, int node, Vector3& xi) + { + xi = getH1NodeXi(type, P, node); + } + private: + std::string name; +}; + +apf::FieldShape* getH1Shape(int order) +{ + PCU_ALWAYS_ASSERT_VERBOSE(order > 0, + "order is expected to be bigger than or equal to 1!"); + PCU_ALWAYS_ASSERT_VERBOSE(order <= MAX_ORDER, + "order is expected to be less than or equal to 10!"); + // Note: to have higher order H1 fields all you need to do is to + // instantiate the class up to that order in the following table + // and change the above assert so that the code does not fail. + static H1Shape<1> h1_1; + static H1Shape<2> h1_2; + static H1Shape<3> h1_3; + static H1Shape<4> h1_4; + static H1Shape<5> h1_5; + static H1Shape<6> h1_6; + static H1Shape<7> h1_7; + static H1Shape<8> h1_8; + static H1Shape<9> h1_9; + static H1Shape<10> h1_10; + static FieldShape* const h1Shapes[11] = {NULL, + &h1_1, + &h1_2, + &h1_3, + &h1_4, + &h1_5, + &h1_6, + &h1_7, + &h1_8, + &h1_9, + &h1_10}; + return h1Shapes[order]; +} + +} diff --git a/apf/apfHierarchic.cc b/apf/apfHierarchic.cc index 830a68aac..b7ff960f4 100644 --- a/apf/apfHierarchic.cc +++ b/apf/apfHierarchic.cc @@ -32,6 +32,8 @@ class HVertex : public EntityShape { } void getLocalGradients( Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} + void getVectorValues( + Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} int countNodes() const { return 1; } }; @@ -51,6 +53,8 @@ class HEdge2 : public EntityShape { dN[1] = Vector3(0.5, 0.0, 0.0); dN[2] = Vector3(-0.5 * c0 * xi[0], 0.0, 0.0); } + void getVectorValues( + Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} int countNodes() const { return 3; } }; @@ -72,6 +76,8 @@ class HEdge3 : public EntityShape { dN[2] = Vector3(-0.5 * c0 * xi[0], 0.0, 0.0); dN[3] = Vector3(0.25 * c0 * (1.0 - 3.0*xi[0]*xi[0]), 0.0, 0.0); } + void getVectorValues( + Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} int countNodes() const { return 4; } }; @@ -105,6 +111,8 @@ class HTriangle2 : public EntityShape { dN[4] = Vector3(c0 * xi[1], c0 * xi[0], 0.0); dN[5] = Vector3(-c0 * xi[1], c0 * (1.0 - xi[0] - 2.0*xi[1]), 0.0); } + void getVectorValues( + Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} int countNodes() const { return 6; } }; @@ -197,6 +205,8 @@ class HTriangle3 : public EntityShape { /* face */ dN[9] = dl0*l1*l2 + dl1*l0*l2 + dl2*l0*l1; } + void getVectorValues( + Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} void alignSharedNodes(Mesh*, MeshEntity*, MeshEntity*, int order[]) { /* unlike Lagrange shape functions, hierarchic 'modes' do not @@ -247,6 +257,8 @@ class HTetrahedron2 : public EntityShape { dN[8] = Vector3( xi[2], 0.0, xi[0] ) * c0; dN[9] = Vector3( 0.0, xi[2], xi[1] ) * c0; } + void getVectorValues( + Mesh*, MeshEntity*, Vector3 const&, NewArray&) const {} int countNodes() const {return 10;} }; diff --git a/apf/apfIPShape.cc b/apf/apfIPShape.cc index 3d6bce1b3..e55679bfb 100644 --- a/apf/apfIPShape.cc +++ b/apf/apfIPShape.cc @@ -162,6 +162,12 @@ class VoronoiShape : public IPBase { fail("gradients not defined for Voronoi shapes"); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, + NewArray&) const + { + fail("getVectorValues not defined for Voronoi shapes"); + } int countNodes() const { return points.size(); @@ -227,6 +233,12 @@ class ConstantIPFit : public IPBase grads.allocate(1); grads[0] = Vector3(0,0,0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, + NewArray&) const + { + fail("getVectorValues not defined for ConstantIPFit shapes"); + } int countNodes() const {return 1;} }; class Tetrahedron : public EntityShape @@ -244,6 +256,12 @@ class ConstantIPFit : public IPBase grads.allocate(1); grads[0] = Vector3(0,0,0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, + NewArray&) const + { + fail("getVectorValues not defined for ConstantIPFit shapes"); + } int countNodes() const {return 1;} }; EntityShape* getEntityShape(int type) @@ -308,6 +326,12 @@ class LinearIPFit : public IPBase { fail("grads not implemented yet"); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, + NewArray&) const + { + fail("getVectorValues not defined for LinearIPFit shapes"); + } int countNodes() const {return 3;} }; class Tetrahedron : public EntityShape @@ -338,6 +362,12 @@ class LinearIPFit : public IPBase { fail("grads not implemented yet"); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, + NewArray&) const + { + fail("getVectorValues not defined for LinearIPFit shapes"); + } int countNodes() const {return 4;} }; EntityShape* getEntityShape(int type) diff --git a/apf/apfL2Shapes.cc b/apf/apfL2Shapes.cc new file mode 100644 index 000000000..9f240b7d6 --- /dev/null +++ b/apf/apfL2Shapes.cc @@ -0,0 +1,567 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "apfShape.h" +#include "apfMesh.h" +#include "apfFieldOf.h" +#include "apfElement.h" +#include "apfVectorElement.h" +#include "apfPolyBasis1D.h" +#include +#include +#include +#include +#include + + +namespace apf { + +static unsigned const MAX_ND_ORDER = 10; + +// For L2Shapes there are only internal nodes +static inline int countTriNodes(int P) +{ + return (P+1)*(P+2)/2; +} +// For L2Shapes there are only internal nodes +static inline int countTetNodes(int P) +{ + return (P+1)*(P+2)*(P+3)/6; +} + +static void computeTriangleTi( + int P, /*order*/ + mth::Matrix& Q, /*Q in QR factorization of Ti*/ + mth::Matrix& R) /*R in QR factorization of Ti*/ +{ + int non = countTriNodes(P); + + apf::NewArray op; + getOpenPoints(P, op); + + + const int p = P; + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_l(p+1); + + apf::DynamicArray nodes (non); + + int o = 0; + for (int j = 0; j <= p; j++) { + for (int i = 0; i + j <= p; i++) + { + double w = op[i] + op[j] + op[p-i-j]; + nodes[o][0] = op[i]/w; nodes[o][1] = op[j]/w; nodes[o][2] = 0.; + o++; + } + } + + // Populate T + mth::Matrix T(non,non); + for (int m = 0; m < non; m++) + { + o = 0; + + double x = nodes[m][0]; double y = nodes[m][1]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, 1. - x - y, &shape_l[0]); + + for (int j = 0; j <= p; j++) + for (int i = 0; i + j <= p; i++) + T(o++, m) = shape_x[i]*shape_y[j]*shape_l[p-i-j]; + } + mth::decomposeQR(T, Q, R); +} + +static void computeTetTi( + int P, /*order*/ + mth::Matrix& Q, /*Q in QR factorization of Ti*/ + mth::Matrix& R) /*R in QR factorization of Ti*/ +{ + int non = countTetNodes(P); + + apf::NewArray op; + getOpenPoints(P, op); + + const int p = P; + + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_z(p+1); + apf::NewArray shape_l(p+1); + + apf::DynamicArray nodes (non); + nodes.setSize(non); + + int o = 0; + // Region loops to get nodes and dof2tk for regions + for (int k = 0; k <= p; k++) { + for (int j = 0; j + k <= p; j++) { + for (int i = 0; i + j + k <= p; i++) { + double w = op[i] + op[j] + op[k] + op[p-i-j-k]; + nodes[o][0] = op[i]/w; nodes[o][1] = op[j]/w; nodes[o][2] = op[k]/w; + o++; + } + } + } + + // Populate T + mth::Matrix T(non,non); + for (int m = 0; m < non; m++) + { + o = 0; + double x = nodes[m][0]; double y = nodes[m][1]; double z = nodes[m][2]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, z, &shape_z[0]); + getChebyshevT(p, 1. - x - y - z, &shape_l[0]); + + for (int k = 0; k <= p; k++) + for (int j = 0; j + k <= p; j++) + for (int i = 0; i + j + k <= p; i++) + T(o++, m) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k]; + } + mth::decomposeQR(T, Q, R); +} + +static void getTi( + int P, + int type, + mth::Matrix& Q, + mth::Matrix& R) +{ + + bool cond = (type == apf::Mesh::TRIANGLE || type == apf::Mesh::TET); + PCU_ALWAYS_ASSERT_VERBOSE(cond, + "type should be either apf::Mesh::TRIANGLE or apf::Mesh::TET!"); + + static apf::NewArray transformQ[apf::Mesh::TYPES][MAX_ND_ORDER+1]; + static apf::NewArray transformR[apf::Mesh::TYPES][MAX_ND_ORDER+1]; + int n = type == apf::Mesh::TRIANGLE ? countTriNodes(P) : countTetNodes(P); + + // get the transform matrices if the are not already computed + if (!transformQ[type][P].allocated()) { + mth::Matrix LQ(n,n); + mth::Matrix LR(n,n); + type == apf::Mesh::TRIANGLE ? + computeTriangleTi(P, LQ, LR) : computeTetTi(P, LQ, LR); + + transformQ[type][P].allocate(n*n); + transformR[type][P].allocate(n*n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + transformQ[type][P][i*n+j] = LQ(i,j); + transformR[type][P][i*n+j] = LR(i,j); + } + } + } + + // set Q and R using transformQ and transformR + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + Q(i,j) = transformQ[type][P][i*n+j]; + R(i,j) = transformR[type][P][i*n+j]; + } + } +} + +template +class L2ShapeTri: public FieldShape { + public: + L2ShapeTri() + { + std::stringstream ss; + ss << "L2ShapeTri" << P; + name = ss.str(); + registerSelf(name.c_str()); + } + const char* getName() const { return name.c_str(); } + bool isVectorShape() {return false;} + class Triangle : public apf::EntityShape + { + public: + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int p = P; + + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_l(p+1); + + int dof = countNodes(); + mth::Matrix u(dof, 1); + + double x = xi[0]; double y = xi[1]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, 1. - x - y, &shape_l[0]); + + int n = 0; + for (int j = 0; j <= p; j++) + for (int i = 0; i + j <= p; i++) + u(n++, 0) = shape_x[i]*shape_y[j]*shape_l[p-i-j]; + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TRIANGLE, Q, R); + + mth::Matrix S(dof, 1); + for(int i = 0; i < 1; i++) // S = Ti * u + { + mth::Vector B (dof); + mth::Vector X (dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b in QR x = b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + shapes[i] = S(i,0); + } + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for L2ShapeTri. Aborting()!"); + } + int countNodes() const {return countTriNodes(P);} + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getVectorValues not implemented \ + for L2ShapeTri. Try getValues. Aborting()!"); + } + }; + EntityShape* getEntityShape(int type) + { + PCU_ALWAYS_ASSERT_VERBOSE(type == Mesh::TRIANGLE, + "L2ShapeTri only has entity shapes for TRIANGLEs"); + static Triangle tri; + return &tri; + } + // For the following to member functions we only need to + // consider the interior nodes, i.e., + // Faces: no need to count the nodes associated with bounding edges + // Tets: no need to count the nodes associated with bounding edges/faces + bool hasNodesIn(int dimension) + { + if (dimension == Mesh::typeDimension[Mesh::TRIANGLE]) + return true; + return false; + } + int countNodesOn(int type) + { + if (type == apf::Mesh::TRIANGLE) return countTriNodes(P); + return 0; + } + int getOrder() {return P;} + void getNodeXi(int type, int node, Vector3& xi) + { + PCU_ALWAYS_ASSERT_VERBOSE(type == Mesh::TRIANGLE, + "getNodeXi for L2ShapeTri can be called only for TRIANGLEs"); + apf::NewArray op; + getOpenPoints(P, op); + int c = 0; + for (int j = 0; j <= P; j++) { + for (int i = 0; i + j <= P; i++) { + if (node == c) { + double w = op[i] + op[j] + op[P-i-j]; + xi = Vector3( op[i]/w, op[j]/w, 0. ); + return; + } + else + c++; + } + } + } + private: + std::string name; +}; + +template +class L2ShapeTet: public FieldShape { + public: + L2ShapeTet() + { + std::stringstream ss; + ss << "L2ShapeTet_" << P; + name = ss.str(); + registerSelf(name.c_str()); + } + const char* getName() const { return name.c_str(); } + bool isVectorShape() {return false;} + class Tetrahedron : public apf::EntityShape + { + public: + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int p = P; + + apf::NewArray shape_x(p+1); + apf::NewArray shape_y(p+1); + apf::NewArray shape_z(p+1); + apf::NewArray shape_l(p+1); + + int dof = countNodes(); + mth::Matrix u(dof, 1); + + double x = xi[0]; double y = xi[1]; double z = xi[2]; + + getChebyshevT(p, x, &shape_x[0]); + getChebyshevT(p, y, &shape_y[0]); + getChebyshevT(p, z, &shape_z[0]); + getChebyshevT(p, 1. - x - y - z, &shape_l[0]); + + int n = 0; + for (int k = 0; k <= p; k++) + for (int j = 0; j + k <= p; j++) + for (int i = 0; i + j + k <= p; i++) + u(n++, 0) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k]; + + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TET, Q, R); + + mth::Matrix S(dof, 1); + for(int i = 0; i < 1; i++) // S = Ti * u + { + mth::Vector B (dof); + mth::Vector X (dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + shapes[i] = S(i,0); + } + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for L2ShapeTet. Aborting()!"); + } + int countNodes() const {return countTetNodes(P);} + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& /*xi*/, apf::NewArray& /*shapes*/) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getVectorValues not implemented for \ + L2ShapeTet. Try getValues. Aborting()!"); + } + }; + EntityShape* getEntityShape(int type) + { + PCU_ALWAYS_ASSERT_VERBOSE(type == Mesh::TET, + "L2ShapeTet only has entity shapes for TETs"); + static Tetrahedron tet; + return &tet; + } + // For the following to member functions we only need to + // consider the interior nodes, i.e., + // Faces: no need to count the nodes associated with bounding edges + // Tets: no need to count the nodes associated with bounding edges/faces + bool hasNodesIn(int dimension) + { + if (dimension == Mesh::typeDimension[Mesh::TET]) + return true; + return false; + } + int countNodesOn(int type) + { + if (type == apf::Mesh::TET) return countTetNodes(P); + return 0; + } + int getOrder() {return P;} + void getNodeXi(int type, int node, Vector3& xi) + { + PCU_ALWAYS_ASSERT_VERBOSE(type == Mesh::TET, + "getNodeXi for L2ShapeTet can be called only for TETs"); + apf::NewArray op; + getOpenPoints(P, op); + int c = 0; + for (int k = 0; k <= P; k++) { + for (int j = 0; j + k <= P; j++) { + for (int i = 0; i + j + k <= P; i++) { + if( node == c) { + double w = op[i] + op[j] + op[k] + op[P-i-j-k]; + xi = Vector3( op[i]/w, op[j]/w, op[k]/w ); + return; + } + else + c++; + } + } + } + } + private: + std::string name; +}; + + +static apf::FieldShape* getL2ShapeTri(int order) +{ + PCU_ALWAYS_ASSERT_VERBOSE(order >= 0, + "order is expected to be bigger than or equal to 0!"); + PCU_ALWAYS_ASSERT_VERBOSE(order <= 10, + "order is expected to be less than or equal to 10!"); + static L2ShapeTri<0> l2_0; + static L2ShapeTri<1> l2_1; + static L2ShapeTri<2> l2_2; + static L2ShapeTri<3> l2_3; + static L2ShapeTri<4> l2_4; + static L2ShapeTri<5> l2_5; + static L2ShapeTri<6> l2_6; + static L2ShapeTri<7> l2_7; + static L2ShapeTri<8> l2_8; + static L2ShapeTri<9> l2_9; + static L2ShapeTri<10> l2_10; + static FieldShape* const l2Shapes[11] = {&l2_0, + &l2_1, + &l2_2, + &l2_3, + &l2_4, + &l2_5, + &l2_6, + &l2_7, + &l2_8, + &l2_9, + &l2_10}; + return l2Shapes[order]; +} + +static apf::FieldShape* getL2ShapeTet(int order) +{ + PCU_ALWAYS_ASSERT_VERBOSE(order >= 0, + "order is expected to be bigger than or equal to 0!"); + PCU_ALWAYS_ASSERT_VERBOSE(order <= 10, + "order is expected to be less than or equal to 10!"); + static L2ShapeTet<0> l2_0; + static L2ShapeTet<1> l2_1; + static L2ShapeTet<2> l2_2; + static L2ShapeTet<3> l2_3; + static L2ShapeTet<4> l2_4; + static L2ShapeTet<5> l2_5; + static L2ShapeTet<6> l2_6; + static L2ShapeTet<7> l2_7; + static L2ShapeTet<8> l2_8; + static L2ShapeTet<9> l2_9; + static L2ShapeTet<10> l2_10; + static FieldShape* const l2Shapes[11] = {&l2_0, + &l2_1, + &l2_2, + &l2_3, + &l2_4, + &l2_5, + &l2_6, + &l2_7, + &l2_8, + &l2_9, + &l2_10}; + return l2Shapes[order]; +} + + +apf::FieldShape* getL2Shape(int order, int type) +{ + if (type == Mesh::TRIANGLE) + return getL2ShapeTri(order); + else if (type == Mesh::TET) + return getL2ShapeTet(order); + else + PCU_ALWAYS_ASSERT_VERBOSE(0, + "L2Shapes are only implemented for tris and tets"); +} + +void projectL2Field(Field* to, Field* from) +{ + // checks on the from field + // checks on the to field + apf::FieldShape* tShape = getShape(to); + std::string tName = tShape->getName(); + int tOrder = tShape->getOrder(); + PCU_ALWAYS_ASSERT_VERBOSE((tName == std::string("Linear")) && (tOrder == 1), + "The to field needs to be 1st order Lagrange!"); + + Mesh* m = getMesh(from); + // auxiliary count fields + Field* count = createField(m, "counter", SCALAR, getLagrange(1)); + double xis[4][3] = {{0., 0., 0.}, + {1., 0., 0.}, + {0., 1., 0.}, + {0., 0., 1.}}; + // zero out the fields + zeroField(to); + zeroField(count); + + int nc = countComponents(to); + NewArray atXi(nc); + NewArray currentVal(nc); + NewArray sum(nc); + + MeshEntity* e; + MeshIterator* it = m->begin(m->getDimension()); + while( (e = m->iterate(it)) ) { + MeshElement* me = createMeshElement(m, e); + Element* el = createElement(from, me); + MeshEntity* dvs[4]; + m->getDownward(e, 0, dvs); + for (int i=0; i<4; i++) { + getComponents(el, Vector3(xis[i]), &(atXi[0])); + getComponents(to, dvs[i], 0, &(currentVal[0])); + for (int j = 0; j < nc; j++) { + currentVal[j] += atXi[j]; + } + double currentCount = getScalar(count, dvs[i], 0); + currentCount += 1.; + setComponents(to, dvs[i], 0, &(currentVal[0])); + setScalar(count, dvs[i], 0, currentCount); + } + destroyElement(el); + destroyMeshElement(me); + } + m->end(it); + + // take care of entities on part boundary + accumulate(to); + accumulate(count); + + it = m->begin(0); + while( (e = m->iterate(it)) ) { + getComponents(to, e, 0, &(sum[0])); + int cnt = getScalar(count, e, 0); + for (int i = 0; i < nc; i++) { + sum[i] /= cnt; + } + setComponents(to, e, 0, &(sum[0])); + } + m->end(it); + + // take care of entities on part boundary + synchronize(to); + + m->removeField(count); + destroyField(count); +} + +} diff --git a/apf/apfMatrix.cc b/apf/apfMatrix.cc index 75f668bc8..92d15f402 100644 --- a/apf/apfMatrix.cc +++ b/apf/apfMatrix.cc @@ -141,6 +141,10 @@ template Matrix<1,1> getMinor(Matrix<2,2> const& A, std::size_t i, std::size_t j template Matrix<2,2> getMinor(Matrix<3,3> const& A, std::size_t i, std::size_t j); template Matrix<3,3> getMinor(Matrix<4,4> const& A, std::size_t i, std::size_t j); +template double getCofactor(Matrix<2,2> const& A, std::size_t i, std::size_t); +template double getCofactor(Matrix<3,3> const& A, std::size_t i, std::size_t); +template double getCofactor(Matrix<4,4> const& A, std::size_t i, std::size_t); + template double getDeterminant(Matrix<2,2> const& A); template double getDeterminant(Matrix<3,3> const& A); template double getDeterminant(Matrix<4,4> const& A); diff --git a/apf/apfMatrixElement.cc b/apf/apfMatrixElement.cc index 6c6065d3c..f609f912e 100644 --- a/apf/apfMatrixElement.cc +++ b/apf/apfMatrixElement.cc @@ -5,10 +5,21 @@ * BSD license as described in the LICENSE file in the top-level directory. */ +#include "apfMatrixField.h" #include "apfMatrixElement.h" #include "apfVectorElement.h" namespace apf { + +MatrixElement::MatrixElement(MatrixField* f, MeshElement* e): + ElementOf(f,e) +{ +} + +MatrixElement::~MatrixElement() +{ +} + // laid out in array as F_i*3+j+9*d void MatrixElement::grad(Vector3 const& xi, Vector<27>& g) { diff --git a/apf/apfMatrixField.cc b/apf/apfMatrixField.cc index aa4bd4e8f..600acfa07 100644 --- a/apf/apfMatrixField.cc +++ b/apf/apfMatrixField.cc @@ -10,15 +10,6 @@ namespace apf { -MatrixElement::MatrixElement(MatrixField* f, MeshElement* e): - ElementOf(f,e) -{ -} - -MatrixElement::~MatrixElement() -{ -} - Element* MatrixField::getElement(VectorElement* e) { return new MatrixElement(this,e); diff --git a/apf/apfMatrixField.h b/apf/apfMatrixField.h index 61b3b1bac..3ba9ec677 100644 --- a/apf/apfMatrixField.h +++ b/apf/apfMatrixField.h @@ -19,6 +19,7 @@ class MatrixField : public FieldOf virtual ~MatrixField() {} virtual Element* getElement(VectorElement* e); virtual int getValueType() const {return MATRIX;} + virtual int getShapeType() const {return SCALAR;} virtual int countComponents() const; }; diff --git a/apf/apfMesh.cc b/apf/apfMesh.cc index 6502443b2..34f17f2a8 100644 --- a/apf/apfMesh.cc +++ b/apf/apfMesh.cc @@ -238,12 +238,16 @@ bool Mesh::isParamPointInsideModel(ModelEntity* g, gmi_ent* e = (gmi_ent*)g; gmi_set* adjRegions = gmi_adjacent(getModel(), e, 3); // for 2D models return true - if (adjRegions->n == 0 || adjRegions->n == 2) + if (adjRegions->n == 0 || adjRegions->n == 2) { + gmi_free_set(adjRegions); return true; + } // for faces with more than 1 adj model region return true for now // TODO: update for future - if (adjRegions->n == 2) + if (adjRegions->n == 2) { + gmi_free_set(adjRegions); return true; + } PCU_ALWAYS_ASSERT(adjRegions->n <= 1); gmi_ent* r = (gmi_ent*)adjRegions->e[0]; gmi_eval(getModel(), (gmi_ent*)g, ¶m[0], &x[0]); @@ -748,6 +752,16 @@ void unfreezeFields(Mesh* m) { m->hasFrozenFields = false; } +void freezeFields(Mesh* m) { + Field* f; + for (int i=0; icountFields(); i++) { + f = m->getField(i); + if (!isFrozen(f)) + freeze(f); + } + m->hasFrozenFields = true; +} + Copy getOtherCopy(Mesh* m, MeshEntity* s) { Copies remotes; diff --git a/apf/apfMesh.h b/apf/apfMesh.h index 052f6584b..ac60009f5 100644 --- a/apf/apfMesh.h +++ b/apf/apfMesh.h @@ -573,6 +573,10 @@ void changeMeshShape(Mesh* m, FieldShape* newShape, bool project = true); \details see apf::unfreezeField */ void unfreezeFields(Mesh* m); +/** \brief freeze all associated fields + \details see apf::freezeField */ +void freezeFields(Mesh* m); + /** \brief count the number of mesh entities classified on a model entity */ int countEntitiesOn(Mesh* m, ModelEntity* me, int dim); diff --git a/apf/apfMesh2.h b/apf/apfMesh2.h index b0cb56512..7b5795ea2 100644 --- a/apf/apfMesh2.h +++ b/apf/apfMesh2.h @@ -36,6 +36,8 @@ class Mesh2 : public Mesh virtual void setRemotes(MeshEntity* e, Copies& remotes) = 0; /** \brief Add just one remote copy to an entity */ virtual void addRemote(MeshEntity* e, int p, MeshEntity* r) = 0; +/** \brief Remove remote copies */ + virtual void clearRemotes(MeshEntity* e) = 0; // seol /** \brief Add just one ghost copy to an entity */ diff --git a/apf/apfMixedVectorElement.cc b/apf/apfMixedVectorElement.cc new file mode 100644 index 000000000..3d975e5ea --- /dev/null +++ b/apf/apfMixedVectorElement.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include + +#include "apfVectorElement.h" +#include "apfMixedVectorElement.h" +#include "apfMixedVectorField.h" + +namespace apf { + +MixedVectorElement::MixedVectorElement(MixedVectorField* f, MeshEntity* e): + ElementOf(f,e) +{ +} + +MixedVectorElement::MixedVectorElement(MixedVectorField* f, VectorElement* p): + ElementOf(f,p) +{ +} + +void MixedVectorElement::curl(Vector3 const& xi, Vector3& c) +{ + PCU_ALWAYS_ASSERT_VERBOSE( field->getShape()->isVectorShape(), + "Not applicable for non-vector shape functions!"); + NewArray curlShapeValues; + curlShapeValues.allocate(nen); + getCurlShapeValues(this, xi, curlShapeValues); + for (int ci = 0; ci < 3; ci++) + c[ci] = 0.; + for (int ni = 0; ni < nen; ni++) + for (int ci = 0; ci < 3; ci++) + c[ci] += nodeData[ni] * curlShapeValues[ni][ci]; +} + +}//namespace apf diff --git a/apf/apfMixedVectorElement.h b/apf/apfMixedVectorElement.h new file mode 100644 index 000000000..a6fba7f24 --- /dev/null +++ b/apf/apfMixedVectorElement.h @@ -0,0 +1,34 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef APFMIXEDVECTORELEMENT_H +#define APFMIXEDVECTORELEMENT_H + +#include "apfElementOf.h" +#include "apfMatrix.h" + +namespace apf { + +class MixedVectorField; + +/* Fields with vector shapes are a bit peculiar, in that + * the shapes functions are vectors but the dof holders are + * scalars. Hence the need for this Mixed class. An example of + * such fields are Nedelec fields. + */ +class MixedVectorElement : public ElementOf +{ + public: + MixedVectorElement(MixedVectorField* f, MeshEntity* e); + MixedVectorElement(MixedVectorField* f, VectorElement* p); + virtual ~MixedVectorElement() {} + void curl(Vector3 const& xi, Vector3& c); +}; + +}//namespace apf + +#endif diff --git a/apf/apfMixedVectorField.cc b/apf/apfMixedVectorField.cc new file mode 100644 index 000000000..f9786c828 --- /dev/null +++ b/apf/apfMixedVectorField.cc @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "apfMixedVectorField.h" +#include "apfMixedVectorElement.h" +#include "apfVectorElement.h" + +namespace apf { + +Element* MixedVectorField::getElement(VectorElement* e) +{ + return new MixedVectorElement(this,e); +} + +int MixedVectorField::countComponents() const +{ + return 1; +} + +}//namespace apf diff --git a/apf/apfMixedVectorField.h b/apf/apfMixedVectorField.h new file mode 100644 index 000000000..aa4d5495a --- /dev/null +++ b/apf/apfMixedVectorField.h @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef APFMIXEDVECTORFIELD_H +#define APFMIXEDVECTORFIELD_H + +#include "apfFieldOf.h" +#include "apf.h" + +namespace apf { + +/* This is used for fields with vector shape functions (e.g. Nedelec) + * They are special in the sense that the dofs (or nodal values) + * are scalar but the shape functions are vectors! + */ +class MixedVectorField : public FieldOf +{ + public: + virtual ~MixedVectorField() {} + virtual Element* getElement(VectorElement* e); + virtual int getValueType() const {return SCALAR;} + virtual int getShapeType() const {return VECTOR;} + virtual int countComponents() const; +}; + +}//namespace apf + +#endif diff --git a/apf/apfNedelec.cc b/apf/apfNedelec.cc new file mode 100644 index 000000000..deb9b7d3b --- /dev/null +++ b/apf/apfNedelec.cc @@ -0,0 +1,1247 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "apfShape.h" +#include "apfMesh.h" +#include "apfFieldOf.h" +#include "apfElement.h" +#include "apfVectorElement.h" +#include "apfPolyBasis1D.h" +#include +#include +#include +#include +#include + + +namespace apf { + +// MAX_ND_ORDER is used for static tables. +// The following implementations are for general orders but template classes +// are only instantiated for up to order 10 + +static const int MAX_ND_ORDER = 10; + +static void alignFaceNodes( + int init_order[], + int final_order[], + int nodes, int r, bool f) +{ + if (r == 0 && !f) // CASE 1 + { + int ind = 0; + int offset = 0; + for (int i = 0; i < nodes; i+=2) + { + final_order[i] = init_order[offset]; + final_order[i+1] = init_order[offset]; + offset += 2; + } + ind += nodes; + offset = 1; + for (int i = 0 ; i < nodes; i+=2) + { + final_order[i+ind] = init_order[offset]; + final_order[i+1+ind] = init_order[offset]; + offset += 2; + } + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 0 : 1; + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 1 : 0; + } + else if (r == 1 && !f) // CASE 2 + { + int ind = 0; + int offset = 0; + for (int i = 0; i < nodes; i+=2) + { + final_order[i] = -init_order[offset]-1; + final_order[i+1] = -init_order[offset]-1; + offset += 2; + } + ind += nodes; + offset = 1; + for (int i = 0 ; i < nodes; i+=2) + { + final_order[i+ind] = init_order[offset]; + final_order[i+1+ind] = init_order[offset]; + offset += 2; + } + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = 1; + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 0 : 1; + } + else if (r == 2 && !f) // CASE 3 + { + int ind = 0; + int offset = 0; + for (int i = 0; i < nodes; i+=2) + { + final_order[i] = init_order[offset]; + final_order[i+1] = init_order[offset]; + offset += 2; + } + ind += nodes; + offset = 1; + for (int i = 0 ; i < nodes; i+=2) + { + final_order[i+ind] = -init_order[offset]-1; + final_order[i+1+ind] = -init_order[offset]-1; + offset += 2; + } + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 1 : 0; + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = 1; + } + else if (r == 0 && f) // CASE 4 + { + int ind = 0; + int offset = 0; + for (int i = 0; i < nodes; i+=2) + { + final_order[i] = init_order[offset]; + final_order[i+1] = init_order[offset]; + offset += 2; + } + ind += nodes; + offset = 1; + for (int i = 0 ; i < nodes; i+=2) + { + final_order[i+ind] = -init_order[offset]-1; + final_order[i+1+ind] = -init_order[offset]-1; + offset += 2; + } + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 0 : 1; + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = 1; + } + else if (r == 1 && f) // CASE 5 + { + int ind = 0; + int offset = 0; + for (int i = 0; i < nodes; i+=2) + { + final_order[i] = -init_order[offset]-1; + final_order[i+1] = -init_order[offset]-1; + offset += 2; + } + ind += nodes; + offset = 1; + for (int i = 0 ; i < nodes; i+=2) + { + final_order[i+ind] = init_order[offset]; + final_order[i+1+ind] = init_order[offset]; + offset += 2; + } + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = 1; + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 1 : 0; + } + else if (r == 2 && f) // CASE 6 + { + int ind = 0; + int offset = 0; + for (int i = 0; i < nodes; i+=2) + { + final_order[i] = init_order[offset]; + final_order[i+1] = init_order[offset]; + offset += 2; + } + ind += nodes; + offset = 1; + for (int i = 0 ; i < nodes; i+=2) + { + final_order[i+ind] = init_order[offset]; + final_order[i+1+ind] = init_order[offset]; + offset += 2; + } + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 1 : 0; + ind += nodes; + for (int i = 0; i < nodes; i++) + final_order[i+ind] = (i%2) ? 0 : 1; + } +} + +// This is all nodes, including the nodes associated with bounding edges +static inline int countTriNodes(int P) +{ + // each node on an edge has 1 dof + // each node on a face has 2 dofs + // each term in the following can be understood as follows + // e*dofs*nodes + // e := # of entities of that dimension + // dofs := # of dofs associated with entities of that dimension + // nodes := # of nodes associated with entities of that dimension + return 3*1*P + 1*2*P*(P-1)/2; +} +// This is all nodes, including the nodes associated with bounding edges and faces +static inline int countTetNodes(int P) +{ + // each node on an edge has 1 dof + // each node on a face has 2 dofs + // each node on a tet has 3 dofs + // each term in the following can be understood as follows + // e*dofs*nodes + // e := # of entities of that dimension + // dofs := # of dofs associated with entities of that dimension + // nodes := # of nodes associated with entities of that dimension + if (P<2) return 6*1*P + 4*2*P*(P-1)/2; + return 6*1*P + 4*2*P*(P-1)/2 + 1*3*P*(P-1)*(P-2)/6; +} + +static void computeTriangleTi( + int P, /*order*/ + mth::Matrix& Q, /*Q in QR factorization of Ti*/ + mth::Matrix& R) /*R in QR factorization of Ti*/ +{ + const double tk[8] = {1.,0., -1.,1., 0.,-1., 0.,1.}; + const double c = 1./3.; + int non = countTriNodes(P); + + apf::NewArray eop; + apf::NewArray iop; + getOpenPoints(P - 1, eop); + if (P > 1) + getOpenPoints(P - 2, iop); + + + const int p = P, pm1 = P - 1, pm2 = P - 2; + apf::NewArray shape_x(p); + apf::NewArray shape_y(p); + apf::NewArray shape_l(p); + + apf::DynamicArray nodes (non); + apf::DynamicArray dof2tk (non); + + int o = 0; + // Edge loops to get nodes and dof2tk for edges + for (int i = 0; i < P; i++) // (0,1) + { + nodes[o][0] = eop[i]; nodes[o][1] = 0.; nodes[o][2] = 0.; + dof2tk[o++] = 0; + } + for (int i = 0; i < P; i++) // (1,2) + { + nodes[o][0] = eop[pm1-i]; nodes[o][1] = eop[i]; nodes[o][2] = 0.; + dof2tk[o++] = 1; + } + for (int i = 0; i < P; i++) // (2,0) + { + nodes[o][0] = 0.; nodes[o][1] = eop[pm1-i]; nodes[o][2] = 0.; + dof2tk[o++] = 2; + } + + // Face loops to get nodes and dof2tk for faces + for (int j = 0; j <= pm2; j++) { + for (int i = 0; i + j <= pm2; i++) + { + double w = iop[i] + iop[j] + iop[pm2-i-j]; + nodes[o][0] = iop[i]/w; nodes[o][1] = iop[j]/w; nodes[o][2] = 0.; + dof2tk[o++] = 0; + nodes[o][0] = iop[i]/w; nodes[o][1] = iop[j]/w; nodes[o][2] = 0.; + dof2tk[o++] = 3; + } + } + + // Populate T + mth::Matrix T(non,non); + for (int m = 0; m < non; m++) + { + const double *tm = tk + 2*dof2tk[m]; + o = 0; + + double x = nodes[m][0]; double y = nodes[m][1]; + + getChebyshevT(pm1, x, &shape_x[0]); + getChebyshevT(pm1, y, &shape_y[0]); + getChebyshevT(pm1, 1. - x - y, &shape_l[0]); + + for (int j = 0; j <= pm1; j++) + for (int i = 0; i + j <= pm1; i++) + { + double s = shape_x[i]*shape_y[j]*shape_l[pm1-i-j]; + T(o++, m) = s * tm[0]; + T(o++, m) = s * tm[1]; + } + for (int j = 0; j <= pm1; j++) + { + T(o++, m) = + shape_x[pm1-j]*shape_y[j]*((y - c)*tm[0] - (x - c)*tm[1]); + } + } + mth::decomposeQR(T, Q, R); +} + +static void computeTetTi( + int P, /*order*/ + mth::Matrix& Q, /*Q in QR factorization of Ti*/ + mth::Matrix& R) /*R in QR factorization of Ti*/ +{ + int non = countTetNodes(P); + const double c = 1./4.; + const double tk[21] = { /* edge directions in a tet */ + 1. , 0., 0., + -1. , 1., 0., + 0. , -1., 0., + 0. , 0., 1., + -1. , 0., 1., + 0., -1., 1., + // this last one is the negative of the third one + // and it is used for face and tet tangents only + 0., 1., 0.}; + + + apf::NewArray eop; + apf::NewArray fop; + apf::NewArray iop; + getOpenPoints(P - 1, eop); + if (P > 1) + getOpenPoints(P - 2, fop); + if (P > 2) + getOpenPoints(P - 3, iop); + + const int p = P, pm1 = P - 1, pm2 = P - 2, pm3 = P - 3; + + apf::NewArray shape_x(p); + apf::NewArray shape_y(p); + apf::NewArray shape_z(p); + apf::NewArray shape_l(p); + + apf::DynamicArray nodes (non); + apf::DynamicArray dof2tk (non); + nodes.setSize(non); + dof2tk.setSize(non); + + int o = 0; + // Edge loops to get nodes and dof2tk for edges + for (int i = 0; i < p; i++) // (0,1) + { + nodes[o][0] = eop[i]; nodes[o][1] = 0.; nodes[o][2] = 0.; + dof2tk[o++] = 0; + } + for (int i = 0; i < p; i++) // (1,2) + { + nodes[o][0] = eop[pm1-i]; nodes[o][1] = eop[i]; nodes[o][2] = 0.; + dof2tk[o++] = 1; + } + for (int i = 0; i < p; i++) // (2,0) + { + nodes[o][0] = 0.; nodes[o][1] = eop[pm1-i]; nodes[o][2] = 0.; + dof2tk[o++] = 2; + } + for (int i = 0; i < p; i++) // (0,3) + { + nodes[o][0] = 0.; nodes[o][1] = 0.; nodes[o][2] = eop[i]; + dof2tk[o++] = 3; + } + for (int i = 0; i < p; i++) // (1,3) + { + nodes[o][0] = eop[pm1-i]; nodes[o][1] = 0.; nodes[o][2] = eop[i]; + dof2tk[o++] = 4; + } + for (int i = 0; i < p; i++) // (2,3) + { + nodes[o][0] = 0.; nodes[o][1] = eop[pm1-i]; nodes[o][2] = eop[i]; + dof2tk[o++] = 5; + } + + // Face loops to get nodes and dof2tk for faces + // (0,1,2) + for (int j = 0; j <= pm2; j++) { + for (int i = 0; i + j <= pm2; i++) + { + double w = fop[i] + fop[j] + fop[pm2-i-j]; + nodes[o][0] = fop[i]/w; nodes[o][1] = fop[j]/w; nodes[o][2] = 0.; + dof2tk[o++] = 0; + nodes[o][0] = fop[i]/w; nodes[o][1] = fop[j]/w; nodes[o][2] = 0.; + dof2tk[o++] = 6; + } + } + // (0,1,3) + for (int j = 0; j <= pm2; j++) { + for (int i = 0; i + j <= pm2; i++) + { + double w = fop[i] + fop[j] + fop[pm2-i-j]; + nodes[o][0] = fop[i]/w; nodes[o][1] = 0.; nodes[o][2] = fop[j]/w; + dof2tk[o++] = 0; + nodes[o][0] = fop[i]/w; nodes[o][1] = 0.; nodes[o][2] = fop[j]/w; + dof2tk[o++] = 3; + } + } + // (1,2,3) + for (int j = 0; j <= pm2; j++) { + for (int i = 0; i + j <= pm2; i++) + { + double w = fop[i] + fop[j] + fop[pm2-i-j]; + nodes[o][0] = fop[pm2-i-j]/w; nodes[o][1] = fop[i]/w; nodes[o][2] = fop[j]/w; + dof2tk[o++] = 1; + nodes[o][0] = fop[pm2-i-j]/w; nodes[o][1] = fop[i]/w; nodes[o][2] = fop[j]/w; + dof2tk[o++] = 4; + } + } + // (0,2,3) + for (int j = 0; j <= pm2; j++) { + for (int i = 0; i + j <= pm2; i++) + { + double w = fop[i] + fop[j] + fop[pm2-i-j]; + nodes[o][0] = 0.; nodes[o][1] = fop[i]/w; nodes[o][2] = fop[j]/w; + dof2tk[o++] = 6; + nodes[o][0] = 0.; nodes[o][1] = fop[i]/w; nodes[o][2] = fop[j]/w; + dof2tk[o++] = 3; + } + } + + // Region loops to get nodes and dof2tk for regions + for (int k = 0; k <= pm3; k++) { + for (int j = 0; j + k <= pm3; j++) { + for (int i = 0; i + j + k <= pm3; i++) { + double w = iop[i] + iop[j] + iop[k] + iop[pm3-i-j-k]; + nodes[o][0] = iop[i]/w; nodes[o][1] = iop[j]/w; nodes[o][2] = iop[k]/w; + dof2tk[o++] = 0; + nodes[o][0] = iop[i]/w; nodes[o][1] = iop[j]/w; nodes[o][2] = iop[k]/w; + dof2tk[o++] = 6; + nodes[o][0] = iop[i]/w; nodes[o][1] = iop[j]/w; nodes[o][2] = iop[k]/w; + dof2tk[o++] = 3; + } + } + } + + // Populate T + mth::Matrix T(non,non); + for (int m = 0; m < non; m++) + { + const double *tm = tk + 3*dof2tk[m]; + o = 0; + + double x = nodes[m][0]; double y = nodes[m][1]; double z = nodes[m][2]; + + getChebyshevT(pm1, x, &shape_x[0]); + getChebyshevT(pm1, y, &shape_y[0]); + getChebyshevT(pm1, z, &shape_z[0]); + getChebyshevT(pm1, 1. - x - y - z, &shape_l[0]); + + for (int k = 0; k <= pm1; k++) { + for (int j = 0; j + k <= pm1; j++) { + for (int i = 0; i + j + k <= pm1; i++) + { + double s = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[pm1-i-j-k]; + T(o++, m) = s * tm[0]; + T(o++, m) = s * tm[1]; + T(o++, m) = s * tm[2]; + } + } + } + for (int k = 0; k <= pm1; k++) { + for (int j = 0; j + k <= pm1; j++) { + { + double s = shape_x[pm1-j-k]*shape_y[j]*shape_z[k]; + T(o++, m) = s*((y - c)*tm[0] - (x - c)*tm[1]); + T(o++, m) = s*((z - c)*tm[0] - (x - c)*tm[2]); + } + } + } + for (int k = 0; k <= pm1; k++) + { + T(o++, m) = + shape_y[pm1-k]*shape_z[k]*((z - c)*tm[1] - (y - c)*tm[2]); + } + } + + mth::decomposeQR(T, Q, R); +} + +static void getTi( + int P, + int type, + mth::Matrix& Q, + mth::Matrix& R) +{ + + bool cond = (type == apf::Mesh::TRIANGLE || type == apf::Mesh::TET); + PCU_ALWAYS_ASSERT_VERBOSE(cond, + "type should be either apf::Mesh::TRIANGLE or apf::Mesh::TET!"); + + static apf::NewArray transformQ[apf::Mesh::TYPES][MAX_ND_ORDER+1]; + static apf::NewArray transformR[apf::Mesh::TYPES][MAX_ND_ORDER+1]; + int n = type == apf::Mesh::TRIANGLE ? countTriNodes(P) : countTetNodes(P); + + // get the transform matrices if the are not already computed + if (!transformQ[type][P].allocated()) { + mth::Matrix LQ(n,n); + mth::Matrix LR(n,n); + type == apf::Mesh::TRIANGLE ? + computeTriangleTi(P, LQ, LR) : computeTetTi(P, LQ, LR); + + transformQ[type][P].allocate(n*n); + transformR[type][P].allocate(n*n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + transformQ[type][P][i*n+j] = LQ(i,j); + transformR[type][P][i*n+j] = LR(i,j); + } + } + } + + // set Q and R using transformQ and transformR + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + Q(i,j) = transformQ[type][P][i*n+j]; + R(i,j) = transformR[type][P][i*n+j]; + } + } +} + +template +class Nedelec: public FieldShape { + public: + Nedelec() + { + std::stringstream ss; + ss << "Nedelec_" << P; + name = ss.str(); + registerSelf(name.c_str()); + } + const char* getName() const { return name.c_str(); } + bool isVectorShape() {return true;} + class Vertex : public apf::EntityShape + { + public: + void getValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray& values) const + { + (void)values; + } + void getLocalGradients(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: Nedelec shapes are not \ + implemented for vertices. Aborting()!"); + } + int countNodes() const {return 0;} + void alignSharedNodes(apf::Mesh*, + apf::MeshEntity*, apf::MeshEntity*, int order[]) + { + (void)order; + } + }; + class Edge : public apf::EntityShape + { + private: + const int dim; // ref elem dim + const double c; // center of edge + public: + Edge() : dim(1), c(0.0) {}; + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getValues not implemented for \ + Nedelec Edges. Aborting()!"); + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for Nedelec Edges. Aborting()!"); + } + int countNodes() const {return P;} + void alignSharedNodes(apf::Mesh*, + apf::MeshEntity*, apf::MeshEntity*, int order[]) + { + (void)order; + } + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + // : to be completed + } + void getLocalVectorCurls(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + // : to be completed + } + }; + class Triangle : public apf::EntityShape + { + private: + const int dim; // reference element dim + const double c; // center of tri + public: + Triangle() : dim(2), c(1./3) {}; + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getValues not implemented \ + for Nedelec Triangle. Try getVectorValues. Aborting()!"); + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for Nedelec Triangle. Aborting()!"); + } + int countNodes() const {return countTriNodes(P);} + void alignSharedNodes(apf::Mesh* m, + apf::MeshEntity* elem, apf::MeshEntity* shared, int order[]) + { + int which, rotate; + bool flip; + getAlignment(m,elem,shared,which,flip,rotate); + if(!flip) + for(int i = 0; i < P; ++i) + order[i] = i; + else + for(int i = 0; i < P; ++i) + order[i] = -(P-1-i)-1; + } + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int pm1 = P - 1; + const int p = P; + + apf::NewArray shape_x(p); + apf::NewArray shape_y(p); + apf::NewArray shape_l(p); + + int dof = countNodes(); + mth::Matrix u(dof, dim); + + double x = xi[0]; double y = xi[1]; + + getChebyshevT(pm1, x, &shape_x[0]); + getChebyshevT(pm1, y, &shape_y[0]); + getChebyshevT(pm1, 1. - x - y, &shape_l[0]); + + int n = 0; + for (int j = 0; j <= pm1; j++) + for (int i = 0; i + j <= pm1; i++) + { + double s = shape_x[i]*shape_y[j]*shape_l[pm1-i-j]; + u(n,0) = s; u(n,1) = 0.; n++; + u(n,0) = 0.; u(n,1) = s; n++; + } + for (int j = 0; j <= pm1; j++) + { + double s = shape_x[pm1-j]*shape_y[j]; + u(n,0) = s*(y - c); u(n,1) = -s*(x - c); n++; + } + + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TRIANGLE, Q, R); + + mth::Matrix S(dof, dim); + for(int i = 0; i < dim; i++) // S = Ti * u + { + mth::Vector B (dof); + mth::Vector X (dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b in QR x = b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + shapes[i] = apf::Vector3( S(i,0), S(i,1), 0.0 ); + } + } + void getLocalVectorCurls(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray&) const + { + const int pm1 = P - 1; + const int p = P; + + apf::NewArray shape_x(p); + apf::NewArray shape_y(p); + apf::NewArray shape_l(p); + apf::NewArray dshape_x(p); + apf::NewArray dshape_y(p); + apf::NewArray dshape_l(p); + + int dof = countNodes(); + mth::Vector curlu(dof); + + double x = xi[0]; double y = xi[1]; + + getChebyshevT(pm1, x, &shape_x[0], &dshape_x[0]); + getChebyshevT(pm1, y, &shape_y[0], &dshape_y[0]); + getChebyshevT(pm1, 1. - x - y, &shape_l[0], &dshape_l[0]); + + int n = 0; + for (int j = 0; j <= pm1; j++) + for (int i = 0; i + j <= pm1; i++) + { + int l = pm1-i-j; + const double dx = (dshape_x[i]*shape_l[l] - + shape_x[i]*dshape_l[l]) * shape_y[j]; + const double dy = (dshape_y[j]*shape_l[l] - + shape_y[j]*dshape_l[l]) * shape_x[i]; + + curlu(n++) = -dy; + curlu(n++) = dx; + } + for (int j = 0; j <= pm1; j++) + { + int i = pm1 - j; + // curl of shape_x(i)*shape_y(j) * (ip.y - c, -(ip.x - c), 0): + curlu(n++) = -((dshape_x[i]*(x - c) + shape_x[i]) * shape_y[j] + + (dshape_y[j]*(y - c) + shape_y[j]) * shape_x[i]); + } + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TRIANGLE, Q, R); + + mth::Vector X(dof); + mth::solveFromQR(Q, R, curlu, X); + } + }; + class Tetrahedron : public apf::EntityShape + { + private: + const int dim; + const double c; + public: + Tetrahedron() : dim(3), c(1./4) {}; + int getOrder() {return P;} + void getValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getValues not implemented for \ + Nedelec Tetrahedron. Try getVectorValues. Aborting()!"); + } + void getLocalGradients(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const&, apf::NewArray&) const + { + PCU_ALWAYS_ASSERT_VERBOSE(0, "error: getLocalGradients not \ + implemented for Nedelec Tetrahedron. Aborting()!"); + } + int countNodes() const {return countTetNodes(P);} + void alignSharedNodes(apf::Mesh* m, + apf::MeshEntity* elem, apf::MeshEntity* shared, int order[]) + { + int which, rotate; + bool flip; + getAlignment(m,elem,shared,which,flip,rotate); + if(m->getType(shared) == apf::Mesh::EDGE) + { + if(!flip) + for(int i = 0; i < P; ++i) + order[i] = i; + else + for(int i = 0; i < P; ++i) + order[i] = -(P-1-i)-1; + return; + } + //must be a triangle + int non = P*(P-1); // nodes on the face only + int unique_non = non/2; // bc 2 dofs per node on the face + if ( P > 1 ) // face nodes appear for k > 1 + { + int size = (-1 + sqrt(1+8*unique_non) )/2; + + mth::Matrix Nodes(size,size); // populate nodes matrix + Nodes.zero(); + int num = 0; + for ( int r = size-1; r >= 0; r--) + for ( int c = size - r -1; c < size; c++) + Nodes(r,c) = (num++); + + // CASES + if (rotate == 1 && !flip) // CASE 2 + { + for (int c = size-1; c >= 0; c--) { // vertical swaps + int left = size-c-1; int right = size-1; + for(int range = left; range <= left + (right-left)/2; range++) { + std::swap( Nodes(range,c), Nodes(left+right-range,c) ); + } + } + mth::Matrix temp(size, size); // transpose + mth::transpose(Nodes,temp); + for (int r = 0; r < size; r++) + for(int c = 0; c < size; c++) + Nodes(r,c) = temp(r,c); + } + if (rotate == 2 && !flip) // CASE 3 + { + for (int r = size-1; r >= 0; r--) { // horizontal swaps + int left = size-r-1; int right = size-1; + for(int range = left; range <= left + (right-left)/2; range++) { + std::swap( Nodes(r,range), Nodes(r,left+right-range) ); + } + } + mth::Matrix temp(size, size); // transpose + mth::transpose(Nodes,temp); + for (int r = 0; r < size; r++) + for(int c = 0; c < size; c++) + Nodes(r,c) = temp(r,c); + } + else if (!rotate && flip) // CASE 4 + { + mth::Matrix temp(size, size); // transpose + mth::transpose(Nodes,temp); + for (int r = 0; r < size; r++) + for(int c = 0; c < size; c++) + Nodes(r,c) = temp(r,c); + } + else if (rotate == 1 && flip) // CASE 5 + { + for (int r = size-1; r >= 0; r--) { // horizontal swaps + int left = size-r-1; int right = size-1; + for(int range = left; range <= left + (right-left)/2; range++) { + std::swap( Nodes(r,range), Nodes(r,left+right-range) ); + } + } + } + else if (rotate == 2 && flip) // CASE 6 + { + for (int c = size-1; c >= 0; c--) { // vertical swaps + int left = size-c-1; int right = size-1; + for(int range = left; range <= left + (right-left)/2; range++) { + std::swap( Nodes(range,c), Nodes(left+right-range,c) ); + } + } + } + + // get the init ordered list with all face nodes + int* init_order = new int[non]; + int* final_order = new int[4*non]; + int i = 0; + for ( int r = size-1; r >= 0; r--) + for (int c = size-r-1 ; c < size; c++) { + init_order[i++] = Nodes(r,c)*2; + init_order[i++] = Nodes(r,c)*2 + 1; + } + alignFaceNodes(init_order, final_order, non, rotate, flip); + + for (int i = 0; i < 4*non; i++) + order[i] = final_order[i]; + delete [] init_order; + delete [] final_order; + } + } + void getVectorValues(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& shapes) const + { + const int pm1 = P - 1; + const int p = P; + + apf::NewArray shape_x(p); + apf::NewArray shape_y(p); + apf::NewArray shape_z(p); + apf::NewArray shape_l(p); + + int dof = countNodes(); + mth::Matrix u(dof, dim); + + double x = xi[0]; double y = xi[1]; double z = xi[2]; + + getChebyshevT(pm1, x, &shape_x[0]); + getChebyshevT(pm1, y, &shape_y[0]); + getChebyshevT(pm1, z, &shape_z[0]); + getChebyshevT(pm1, 1. - x - y - z, &shape_l[0]); + + int n = 0; + for (int k = 0; k <= pm1; k++) + for (int j = 0; j + k <= pm1; j++) + for (int i = 0; i + j + k <= pm1; i++) + { + double s = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[pm1-i-j-k]; + u(n,0) = s; u(n,1) = 0.; u(n,2) = 0.; n++; + u(n,0) = 0.; u(n,1) = s; u(n,2) = 0.; n++; + u(n,0) = 0.; u(n,1) = 0.; u(n,2) = s; n++; + } + for (int k = 0; k <= pm1; k++) + for (int j = 0; j + k <= pm1; j++) + { + double s = shape_x[pm1-j-k]*shape_y[j]*shape_z[k]; + u(n,0) = s*(y - c); u(n,1) = -s*(x - c); u(n,2) = 0.; n++; + u(n,0) = s*(z - c); u(n,1) = 0.; u(n,2) = -s*(x - c); n++; + } + for (int k = 0; k <= pm1; k++) + { + double s = shape_y[pm1-k]*shape_z[k]; + u(n,0) = 0.; u(n,1) = s*(z - c); u(n,2) = -s*(y - c); n++; + } + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TET, Q, R); + + mth::Matrix S(dof, dim); + for(int i = 0; i < dim; i++) // S = Ti * u + { + mth::Vector B (dof); + mth::Vector X (dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + shapes[i] = apf::Vector3( S(i,0), S(i,1), S(i,2) ); + } + } + void getLocalVectorCurls(apf::Mesh* /*m*/, apf::MeshEntity* /*e*/, + apf::Vector3 const& xi, apf::NewArray& curl_shapes) const + { + const int pm1 = P - 1; + const int p = P; + + apf::NewArray shape_x(p); + apf::NewArray shape_y(p); + apf::NewArray shape_z(p); + apf::NewArray shape_l(p); + apf::NewArray dshape_x(p); + apf::NewArray dshape_y(p); + apf::NewArray dshape_z(p); + apf::NewArray dshape_l(p); + + int dof = countNodes(); + mth::Matrix u(dof, dim); + + double x = xi[0]; double y = xi[1]; double z = xi[2]; + + getChebyshevT(pm1, x, &shape_x[0], &dshape_x[0]); + getChebyshevT(pm1, y, &shape_y[0], &dshape_y[0]); + getChebyshevT(pm1, z, &shape_z[0], &dshape_z[0]); + getChebyshevT(pm1, 1. - x - y - z, &shape_l[0], &dshape_l[0]); + + int n = 0; + for (int k = 0; k <= pm1; k++) + for (int j = 0; j + k <= pm1; j++) + for (int i = 0; i + j + k <= pm1; i++) + { + int l = pm1-i-j-k; + const double dx = (dshape_x[i]*shape_l[l] - + shape_x[i]*dshape_l[l])*shape_y[j]*shape_z[k]; + const double dy = (dshape_y[j]*shape_l[l] - + shape_y[j]*dshape_l[l])*shape_x[i]*shape_z[k]; + const double dz = (dshape_z[k]*shape_l[l] - + shape_z[k]*dshape_l[l])*shape_x[i]*shape_y[j]; + + u(n,0) = 0.; u(n,1) = dz; u(n,2) = -dy; n++; + u(n,0) = -dz; u(n,1) = 0.; u(n,2) = dx; n++; + u(n,0) = dy; u(n,1) = -dx; u(n,2) = 0.; n++; + } + for (int k = 0; k <= pm1; k++) + for (int j = 0; j + k <= pm1; j++) + { + int i = pm1 - j - k; + // s = shape_x(i)*shape_y(j)*shape_z(k); + // curl of s*(ip.y - c, -(ip.x - c), 0): + u(n,0) = shape_x[i]*(x - c)*shape_y[j]*dshape_z[k]; + u(n,1) = shape_x[i]*shape_y[j]*(y - c)*dshape_z[k]; + u(n,2) = -((dshape_x[i]*(x - c) + shape_x[i])*shape_y[j]*shape_z[k] + + (dshape_y[j]*(y - c) + shape_y[j])*shape_x[i]*shape_z[k]); + n++; + // curl of s*(ip.z - c, 0, -(ip.x - c)): + u(n,0) = -shape_x[i]*(x - c)*dshape_y[j]*shape_z[k]; + u(n,1) = (shape_x[i]*shape_y[j]*(dshape_z[k]*(z - c) + shape_z[k]) + + (dshape_x[i]*(x - c) + shape_x[i])*shape_y[j]*shape_z[k]); + u(n,2) = -shape_x[i]*dshape_y[j]*shape_z[k]*(z - c); + n++; + } + for (int k = 0; k <= pm1; k++) + { + int j = pm1 - k; + // curl of shape_y(j)*shape_z(k)*(0, ip.z - c, -(ip.y - c)): + u(n,0) = -((dshape_y[j]*(y - c) + shape_y[j])*shape_z[k] + + shape_y[j]*(dshape_z[k]*(z - c) + shape_z[k])); + u(n,1) = 0.; + u(n,2) = 0.; n++; + } + + + mth::Matrix Q(dof, dof); + mth::Matrix R(dof, dof); + getTi(P, apf::Mesh::TET, Q, R); + + mth::Matrix S(dof, dim); + for(int i = 0; i < dim; i++) // S = Ti * u + { + mth::Vector B(dof); + mth::Vector X(dof); + for (int j = 0; j < dof; j++) B[j] = u(j,i); // populate b + mth::solveFromQR(Q, R, B, X); + for (int j = 0; j < dof; j++) S(j,i) = X[j]; // populate S with x + } + + curl_shapes.allocate(dof); + for (int i = 0; i < dof; i++) // populate y + { + curl_shapes[i] = apf::Vector3( S(i,0), S(i,1), S(i,2) ); + } + } + }; + EntityShape* getEntityShape(int type) + { + static Edge edge; + static Triangle triangle; + static Tetrahedron tet; + static EntityShape* shapes[apf::Mesh::TYPES] = + {NULL, //vertex + &edge, //edge + &triangle, //triangle + NULL, //quad + &tet, //tetrahedron + NULL, //hex + NULL, //prism + NULL //pyramid + }; + return shapes[type]; + } + // For the following to member functions we only need to + // consider the interior nodes, i.e., + // Faces: no need to count the nodes associated with bounding edges + // Tets: no need to count the nodes associated with bounding edges/faces + bool hasNodesIn(int dimension) + { + if (dimension == 1) return true; + if (dimension == 2) return P > 1; + if (dimension == 3) return P > 2; + // if not returned by now dimension should be 0, so return false + return false; + } + int countNodesOn(int type) + { + if (type == apf::Mesh::EDGE) return P; + if (type == apf::Mesh::TRIANGLE) return 2*P*(P-1)/2; + if (type == apf::Mesh::TET && P>2) return 3*P*(P-1)*(P-2)/6; + // for any other case (type and P combination) return 0; + return 0; + } + int getOrder() {return P;} + void getNodeXi(int type, int node, Vector3& xi) + { + apf::NewArray op; + if(type == Mesh::EDGE) + { + if (P > 0) + getOpenPoints(P-1, op); + xi = Vector3( -1 + 2*op[node], 0., 0. ); // map from [0,1] to [-1,1] + return; + } + else if (type == Mesh::TRIANGLE) + { + if (P > 1) + getOpenPoints(P-2, op); + int pm2 = P - 2; int c = 0; + + for (int j = 0; j <= pm2; j++) { + for (int i = 0; i + j <= pm2; i++) { + if (node/2 == c) { // since 2 dofs per node on the face + double w = op[i] + op[j] + op[pm2-i-j]; + xi = Vector3( op[i]/w, op[j]/w, 0. ); + return; + } + else + c++; + } + } + } + else if (type == Mesh::TET) + { + if (P>2) + getOpenPoints(P-3, op); + int pm3 = P - 3; int c = 0; + + for (int k = 0; k <= pm3; k++) { + for (int j = 0; j + k <= pm3; j++) { + for (int i = 0; i + j + k <= pm3; i++) { + if( node/3 == c) { // since 3 dofs per node on interior tet + double w = op[i] + op[j] + op[k] + op[pm3-i-j-k]; + xi = Vector3( op[i]/w, op[j]/w, op[k]/w ); + return; + } + else + c++; + } + } + } + } + else + xi = Vector3(0,0,0); + } + void getNodeTangent(int type, int node, Vector3& t) + { + if(type == Mesh::EDGE) + { + // Edges are parametrized from -1 to 1. + // Having the 2 here enables us to avoid multiplying dofs by 2 for + // when computing them for edges + t = Vector3( 2., 0., 0.); + return; + } + else if(type == Mesh::TRIANGLE) + { + PCU_ALWAYS_ASSERT_VERBOSE(P >= 2, + "face nodes appear only for order bigger than or equal to 2!"); + (node % 2 == 0) ? t = Vector3(1., 0., 0.) : t = Vector3(0., 1., 0.); + return; + } + else if(type == Mesh::TET) + { + PCU_ALWAYS_ASSERT_VERBOSE(P >= 3, + "volume nodes appear only for order bigger than or equal to 3!"); + if (node % 3 == 0) t = Vector3(1., 0., 0.); + else if (node % 3 == 1) t = Vector3(0., 1., 0.); + else t = Vector3(0., 0., 1.); + return; + } + else + t = Vector3(0, 0, 0); + } + private: + std::string name; +}; + +apf::FieldShape* getNedelec(int order) +{ + PCU_ALWAYS_ASSERT_VERBOSE(order >= 1, + "order is expected to be bigger than or equal to 1!"); + PCU_ALWAYS_ASSERT_VERBOSE(order <= MAX_ND_ORDER, + "order is expected to be less than or equal to 10!"); + static Nedelec<1> ND1; + static Nedelec<2> ND2; + static Nedelec<3> ND3; + static Nedelec<4> ND4; + static Nedelec<5> ND5; + static Nedelec<6> ND6; + static Nedelec<7> ND7; + static Nedelec<8> ND8; + static Nedelec<9> ND9; + static Nedelec<10> ND10; + static FieldShape* const nedelecShapes[MAX_ND_ORDER] = + {&ND1, &ND2, &ND3, &ND4, &ND5, &ND6, &ND7, &ND8, &ND9, &ND10}; + return nedelecShapes[order-1]; +} + +void projectNedelecField(Field* to, Field* from) +{ + Mesh* m = getMesh(from); + apf::FieldShape* fs = getShape(to); + std::string name(fs->getName()); + + apf::MeshEntity* e; + apf::MeshIterator* it; + + // Integration Point Shapes (IPShapes) has to be treated differently + if (name.find(std::string("IP")) != std::string::npos) + { + int ip_order = fs->getOrder(); + it = m->begin(m->getDimension()); + while ( (e = m->iterate(it)) ) { + apf::MeshElement* me = apf::createMeshElement(m, e); + apf::Element* el = apf::createElement(from, me); + int np = apf::countIntPoints(me, ip_order); + for (int i = 0; i < np; i++) { + apf::Vector3 xi; + apf::getIntPoint(me, ip_order, i, xi); + apf::Vector3 val; + apf::getVector(el, xi, val); + apf::setVector(to, e, i, val); + } + apf::destroyElement(el); + apf::destroyMeshElement(me); + } + m->end(it); + } + // Other fields such as Lagrange and H1 can be treated here + else + { + // auxiliary count fields + Field* count = createField(m, "counter", SCALAR, fs); + + // zero out the fields + zeroField(to); + zeroField(count); + + it = m->begin(m->getDimension()); + while( (e = m->iterate(it)) ) { + MeshElement* me = createMeshElement(m, e); + Element* el = createElement(from, me); + for (int d = 0; d <= m->getDimension(); d++) { + if (!fs->hasNodesIn(d)) continue; + MeshEntity* down[12]; + int nd = m->getDownward(e, d, down); + for (int i=0; igetType(down[i]); + int non = fs->countNodesOn(type); + for (int j = 0; j < non; j++) { + Vector3 nodeXiChild; + fs->getNodeXi(type, j, nodeXiChild); + Vector3 nodeXiParent = boundaryToElementXi( + m, down[i], e, nodeXiChild); + Vector3 atXi; + getVector(el, nodeXiParent, atXi); + Vector3 currentVal; + getVector(to, down[i], j, currentVal); + double currentCount = getScalar(count, down[i], j); + currentVal += atXi; + currentCount += 1.; + setVector(to, down[i], j, currentVal); + setScalar(count, down[i], j, currentCount); + } + } + } + destroyElement(el); + destroyMeshElement(me); + } + m->end(it); + + // take care of entities on part boundary + accumulate(to); + accumulate(count); + + for (int d = 0; d <= m->getDimension(); d++) { + if (!fs->hasNodesIn(d)) continue; + it = m->begin(d); + while( (e = m->iterate(it)) ) { + int type = m->getType(e); + int non = fs->countNodesOn(type); + for (int j = 0; j < non; j++) { + Vector3 sum; + getVector(to, e, j, sum); + setVector(to, e, j, sum/getScalar(count, e, j)); + } + } + m->end(it); + } + // take care of entities on part boundary + synchronize(to); + + m->removeField(count); + destroyField(count); + } +} + +} diff --git a/apf/apfPackedField.h b/apf/apfPackedField.h index 5236b5f64..f293c9b50 100644 --- a/apf/apfPackedField.h +++ b/apf/apfPackedField.h @@ -20,6 +20,7 @@ class PackedField : public Field virtual ~PackedField() {} virtual Element* getElement(VectorElement* e); virtual int getValueType() const {return PACKED;} + virtual int getShapeType() const {return SCALAR;} virtual int countComponents() const {return components;} virtual void project(Field* from); virtual void axpy(double a, Field* x); diff --git a/apf/apfPolyBasis1D.cc b/apf/apfPolyBasis1D.cc new file mode 100644 index 000000000..9af87c6e9 --- /dev/null +++ b/apf/apfPolyBasis1D.cc @@ -0,0 +1,276 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "apfPolyBasis1D.h" + +#include +#include +#include +#include +#include + +namespace apf { + +static void getPoints( + int order, + apf::NewArray& points, + int type) +{ + int np = order + 1; + points.allocated() ? points.resize(np) : points.allocate(np); + switch (type) + { + case GAUSS_LEGENDRE: + { + getGaussLegendrePoints(np, &points[0]); + break; + } + case GAUSS_LOBATTO: + { + getGaussLobattoPoints(np, &points[0]); + break; + } + default: + PCU_ALWAYS_ASSERT_VERBOSE(false, + "type should be either GAUSS_LEGENDRE or GAUSS_LOBATTO!"); + } +} + +void getGaussLegendrePoints(int np, double* pts) +{ + switch (np) + { + case 1: + pts[0] = 0.5; + return; + case 2: + pts[0] = 0.21132486540518711775; + pts[1] = 0.78867513459481288225; + return; + case 3: + pts[0] = 0.11270166537925831148; + pts[1] = 0.5; + pts[2] = 0.88729833462074168852; + return; + } + + const int n = np; + const int m = (n+1)/2; + + for (int i = 1; i <= m; i++) + { + double z = cos(M_PI * (i - 0.25) / (n + 0.5)); + double pp, p1, dz, xi = 0.; + bool done = false; + while (1) + { + double p2 = 1; + p1 = z; + for (int j = 2; j <= n; j++) + { + double p3 = p2; + p2 = p1; + p1 = ((2 * j - 1) * z * p2 - (j - 1) * p3) / j; + } + // p1 is Legendre polynomial + pp = n * (z*p1-p2) / (z*z - 1); + if (done) { break; } + dz = p1/pp; + if (fabs(dz) < 1e-16) + { + done = true; + // map the new point (z-dz) to (0,1): + xi = ((1 - z) + dz)/2; // (1 - (z - dz))/2 has bad round-off + // continue the computation: get pp at the new point, then exit + } + // update: z = z - dz + z -= dz; + } + pts[i-1] = xi; + pts[n-i] = 1 - xi; + } +} + +void getGaussLobattoPoints(int np, double* pts) +{ + PCU_ALWAYS_ASSERT_VERBOSE(np >= 2, "np is expected to be greater or equal to 2!"); + // end points are 0 and 1 + pts[0] = 0.; + pts[np-1] = 1.; + + // interior points are symmetric + for (int i = 1; i <= (np-1)/2; i++) { + double x_i = std::sin(M_PI * ((double)(i)/(np-1) - 0.5)); + double z_i = 0.; + double p_l; + bool done = false; + for (int iter = 0; true; iter++) { + double p_lm1 = 1.; + p_l = x_i; + for (int l = 1; l < (np-1); l++) { + double p_lp1 = ((2*l + 1)*x_i*p_l - l*p_lm1)/(l + 1); + p_lm1 = p_l; + p_l = p_lp1; + } + if (done) break; + + double dx = (x_i*p_l - p_lm1) / (np*p_l); + if (std::abs(dx) < 1e-16) + { + done = true; + z_i = ((1.0 + x_i) - dx)/2; + } + PCU_ALWAYS_ASSERT_VERBOSE(iter < 8, + "something went wrong in getGaussLobattoPoints!"); + x_i -= dx; + } + + pts[i] = z_i; + pts[np-i-1] = 1-z_i; + } +} + + +void getOpenPoints( + int order, + apf::NewArray& op, + int type) +{ + getPoints(order, op, type); +} + +void getClosedPoints( + int order, + apf::NewArray& cp, + int type) +{ + getPoints(order, cp, type); +} + +void getChebyshevT(int order, double xi, double* u) +{ + // recursive definition, z in [-1,1] + // T_0(z) = 1, T_1(z) = z + // T_{n+1}(z) = 2*z*T_n(z) - T_{n-1}(z) + double z; + u[0] = 1.; + if (order == 0) { return; } + u[1] = z = 2.*xi - 1.; + for (int n = 1; n < order; n++) + { + u[n+1] = 2*z*u[n] - u[n-1]; + } +} + +void getChebyshevT(int order, double xi, double* u, double* d) +{ + // recursive definition, z in [-1,1] + // T_0(z) = 1, T_1(z) = z + // T_{n+1}(z) = 2*z*T_n(z) - T_{n-1}(z) + // T'_n(z) = n*U_{n-1}(z) + // U_0(z) = 1 U_1(z) = 2*z + // U_{n+1}(z) = 2*z*U_n(z) - U_{n-1}(z) + // U_n(z) = z*U_{n-1}(z) + T_n(z) = z*T'_n(z)/n + T_n(z) + // T'_{n+1}(z) = (n + 1)*(z*T'_n(z)/n + T_n(z)) + double z; + u[0] = 1.; + d[0] = 0.; + if (order == 0) { return; } + u[1] = z = 2.*xi - 1.; + d[1] = 2.; + for (int n = 1; n < order; n++) + { + u[n+1] = 2*z*u[n] - u[n-1]; + d[n+1] = (n + 1)*(z*d[n]/n + 2*u[n]); + } +} + +void getChebyshevT(int order, double xi, double* u, double* d, double* dd) +{ + // recursive definition, z in [-1,1] + // T_0(z) = 1, T_1(z) = z + // T_{n+1}(z) = 2*z*T_n(z) - T_{n-1}(z) + // T'_n(z) = n*U_{n-1}(z) + // U_0(z) = 1 U_1(z) = 2*z + // U_{n+1}(z) = 2*z*U_n(z) - U_{n-1}(z) + // U_n(z) = z*U_{n-1}(z) + T_n(z) = z*T'_n(z)/n + T_n(z) + // T'_{n+1}(z) = (n + 1)*(z*T'_n(z)/n + T_n(z)) + // T''_{n+1}(z) = (n + 1)*(2*(n + 1)*T'_n(z) + z*T''_n(z)) / n + double z; + u[0] = 1.; + d[0] = 0.; + dd[0]= 0.; + if (order == 0) { return; } + u[1] = z = 2.*xi - 1.; + d[1] = 2.; + dd[1] = 0; + for (int n = 1; n < order; n++) + { + u[n+1] = 2*z*u[n] - u[n-1]; + d[n+1] = (n + 1)*(z*d[n]/n + 2*u[n]); + dd[n+1] = (n + 1)*(2.*(n + 1)*d[n] + z*dd[n])/n; + } +} + +void poly1dBasisBarycentric(int order, double xi, double* u) +{ + // order 0 is trivial + if (order == 0) { + u[0] = 1.; + return; + } + + apf::NewArray nodes; + getClosedPoints(order, nodes); + // anything other than 0 + apf::NewArray x(order+1); + apf::NewArray w(order+1); + + for (int i = 0; i < order+1; i++) { + x[i] = nodes[i]; + w[i] = 1.0; + } + + for (int i = 0; i < order+1; i++) { + for (int j = 0; j < i; j++) { + double xij = x[i] - x[j]; + w[i] *= xij; + w[j] *= -xij; + } + } + + for (int i = 0; i < order+1; i++) { + w[i] = 1./w[i]; + } + + int i, k, p = order; + double l, lk; + lk = 1.; + + for (k = 0; k < p; k++) { + if (xi >= (x[k] + x[k+1]) / 2.) + lk *= xi - x[k]; + else { + for (i = k+1; i <= p; i++) { + lk *= xi - x[i]; + } + break; + } + } + l = lk * (xi - x[k]); + + for (i = 0; i < k; i++) { + u[i] = l * w[i] / (xi - x[i]); + } + + u[k] = lk * w[k]; + + for(i++; i <= p; i++) + u[i] = l * w[i] / (xi - x[i]); +} + +} diff --git a/apf/apfPolyBasis1D.h b/apf/apfPolyBasis1D.h new file mode 100644 index 000000000..2ddec82ec --- /dev/null +++ b/apf/apfPolyBasis1D.h @@ -0,0 +1,54 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, incensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directroy. + */ + +#ifndef APFPOLYBASES1D_H +#define APFPOLYBASES1D_H + +#include "apfNew.h" + +namespace apf { + +/** \brief Gauss point types */ +enum Gauss{ + GAUSS_LEGENDRE, + GAUSS_LOBATTO, + GAUSS +}; + + +/** \brief Gauss Legendre points */ +void getGaussLegendrePoints(int np, double* pts); + +/** \brief Gauss Lobatto points */ +void getGaussLobattoPoints(int np, double* pts); + +/** \brief get open points for a given order and Gauss point type */ +void getOpenPoints( + int order, + NewArray& op, + int type = GAUSS_LEGENDRE); + +/** \brief get closed points for a given order and Gauss point type */ +void getClosedPoints( + int order, + NewArray& cp, + int type = GAUSS_LOBATTO); + +/** \brief Chebyshev polynomials of the first kind */ +void getChebyshevT(int order, double xi, double* u); + +/** \brief Chebyshev polynomials of the first kind and 1st derivative */ +void getChebyshevT(int order, double xi, double* u, double* d); + +/** \brief Chebyshev polynomials of the first kind and 1st and 2nd derivative */ +void getChebyshevT(int order, double xi, double* u, double* d, double* dd); + +void poly1dBasisBarycentric(int order, double xi, double* u); + +} + +#endif diff --git a/apf/apfScalarField.h b/apf/apfScalarField.h index cdfffb90d..866ad4094 100644 --- a/apf/apfScalarField.h +++ b/apf/apfScalarField.h @@ -19,6 +19,7 @@ class ScalarField : public FieldOf virtual ~ScalarField() {} virtual Element* getElement(VectorElement* e); virtual int getValueType() const {return SCALAR;} + virtual int getShapeType() const {return SCALAR;} virtual int countComponents() const; }; diff --git a/apf/apfShape.cc b/apf/apfShape.cc index 7add80ff7..5806b07fe 100644 --- a/apf/apfShape.cc +++ b/apf/apfShape.cc @@ -34,11 +34,27 @@ FieldShape::~FieldShape() { } +void EntityShape::getLocalVectorCurls(Mesh* , MeshEntity* , + Vector3 const&, NewArray&) const +{ + fail("unimplemented getLocalVectorCurls called"); +} + void FieldShape::getNodeXi(int, int, Vector3&) { fail("unimplemented getNodeXi called"); } +void FieldShape::getNodeTangent(int, int, Vector3&) +{ + fail("unimplemented getNodeTangent called"); +} + +bool FieldShape::isVectorShape() +{ + return false; +} + void FieldShape::registerSelf(const char* name_) { std::string name = name_; @@ -62,6 +78,10 @@ FieldShape* getShapeByName(const char* name) getIPShape(2,1); getVoronoiShape(2,1); getIPFitShape(2,1); + getNedelec(1); + getL2Shape(0, apf::Mesh::TRIANGLE); + getL2Shape(0, apf::Mesh::TET); + getH1Shape(1); std::string s(name); if (registry.count(s)) return registry[s]; @@ -86,6 +106,11 @@ class Linear : public FieldShape Vector3 const&, NewArray&) const { } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 1;} }; class Edge : public EntityShape @@ -105,6 +130,11 @@ class Linear : public FieldShape grads[0] = Vector3(-0.5,0,0); grads[1] = Vector3( 0.5,0,0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 2;} }; class Triangle : public EntityShape @@ -126,6 +156,11 @@ class Linear : public FieldShape grads[1] = Vector3( 1, 0,0); grads[2] = Vector3( 0, 1,0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 3;} }; class Quad : public EntityShape @@ -158,6 +193,11 @@ class Linear : public FieldShape grads[2] = Vector3( l1y, l1x,0)/4; grads[3] = Vector3(-l1y, l0x,0)/4; } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 4;} }; class Tetrahedron : public EntityShape @@ -181,6 +221,11 @@ class Linear : public FieldShape grads[2] = Vector3( 0, 1, 0); grads[3] = Vector3( 0, 0, 1); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 4;} }; class Prism : public EntityShape @@ -228,6 +273,11 @@ class Linear : public FieldShape grads[4] = tg[1] * up + eg[1] * nt[1]; grads[5] = tg[2] * up + eg[1] * nt[2]; } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 6;} }; class Pyramid : public EntityShape @@ -265,6 +315,11 @@ class Linear : public FieldShape grads[3] = Vector3(-l1y * l0z, l0x * l0z, -l0x * l1y) / 8; grads[4] = Vector3(0,0,0.5); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 5;} }; class Hexahedron : public EntityShape @@ -309,6 +364,11 @@ class Linear : public FieldShape grads[6] = Vector3( l1y * l1z, l1x * l1z, l1x * l1y) / 8; grads[7] = Vector3(-l1y * l1z, l0x * l1z, l0x * l1y) / 8; } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 8;} }; EntityShape* getEntityShape(int type) @@ -376,6 +436,11 @@ class QuadraticBase : public FieldShape grads[1] = Vector3((2*xi[0]+1)/2.0,0,0); grads[2] = Vector3(-2*xi[0],0,0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 3;} }; class Triangle : public EntityShape @@ -406,6 +471,11 @@ class QuadraticBase : public FieldShape grads[4] = Vector3(4*xi[1],4*xi[0],0); grads[5] = Vector3(-4*xi[1],4*(xi2-xi[1]),0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 6;} }; class Tetrahedron : public EntityShape @@ -445,6 +515,11 @@ class QuadraticBase : public FieldShape grads[8] = Vector3(4*xi[2],0,4*xi[0]); grads[9] = Vector3(0,4*xi[2],4*xi[1]); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 10;} }; EntityShape* getEntityShape(int type) @@ -544,6 +619,11 @@ class LagrangeQuadratic : public QuadraticBase -2.0*e*(1-(n*n)), -2.0*n*(1-(e*e)), 0.0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 9;} }; EntityShape* getEntityShape(int type) @@ -616,6 +696,11 @@ class SerendipityQuadratic : public QuadraticBase grads[6] = Vector3(-xi[0] - xi[0]*xi[1], 0.5 - xi[0]*xi[0]/2.0, 0.0); grads[7] = Vector3(xi[1]*xi[1]/2.0 - 0.5, xi[0]*xi[1] - xi[1], 0.0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 8;} }; EntityShape* getEntityShape(int type) @@ -661,6 +746,11 @@ class LagrangeCubic : public FieldShape Vector3 const&, NewArray&) const { } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 1;} }; class Edge : public EntityShape @@ -686,6 +776,11 @@ class LagrangeCubic : public FieldShape dN[2] = Vector3(27./16.*(3.*xi*xi-2./3.*xi-1.), 0, 0); dN[3] = Vector3(27./16.*(-3.*xi*xi-2./3.*xi+1.), 0, 0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 4;} }; class Triangle : public EntityShape @@ -730,6 +825,11 @@ class LagrangeCubic : public FieldShape dN[8] = (gl3*(3.*l1*l1-l1) + gl1*(6.*l3*l1-l3))*9./2.; dN[9] = (gl1*l2*l3+gl2*l1*l3+gl3*l1*l2)*27.; } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 10;} void alignSharedNodes(Mesh* m, MeshEntity* elem, MeshEntity* edge, int order[]) { @@ -811,6 +911,11 @@ class LagrangeCubic : public FieldShape dN[18] = (gl1*l2*l3 + gl2*l1*l3 + gl3*l1*l2)*27.; dN[19] = (gl0*l2*l3 + gl2*l0*l3 + gl3*l0*l2)*27.; } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for nodal shapes"); + } int countNodes() const {return 20;} void alignSharedNodes(Mesh* m, MeshEntity* elem, MeshEntity* edge, int order[]) { @@ -933,6 +1038,11 @@ class Constant : public FieldShape grads.allocate(1); grads[0] = Vector3( 0, 0, 0); } + void getVectorValues(Mesh*, MeshEntity*, + Vector3 const&, NewArray&) const + { + fail("getVectorValues not defined for constant shapes"); + } int countNodes() const {return 1;} int getDimension() const {return D;} }; @@ -959,6 +1069,20 @@ class Constant : public FieldShape return 0; } int getOrder() {return 0;} + void getNodeXi(int type, int node, Vector3& xi) + { + PCU_ALWAYS_ASSERT(node == 0); + if (type == Mesh::VERTEX) + xi = Vector3(0., 0., 0.); + else if (type == Mesh::EDGE) + xi = Vector3(0., 0., 0.); + else if (type == Mesh::TRIANGLE) + xi = Vector3(1./3., 1./3., 0); + else if (type == Mesh::TET) + xi = Vector3(1./4., 1./4., 1./4.); + else + PCU_ALWAYS_ASSERT_VERBOSE(0, "non implemented for non simplex types!"); + } private: std::string name; }; @@ -979,4 +1103,113 @@ int countElementNodes(FieldShape* s, int type) return s->getEntityShape(type)->countNodes(); } +void getElementNodeXis(FieldShape* s, int type, + apf::NewArray& xis) +{ + PCU_ALWAYS_ASSERT_VERBOSE(isSimplex(type), + "Only implemented for simplex types!"); + + static apf::Vector3 const vert_vert_xi[1] = { + apf::Vector3(0,0,0) + }; + static apf::Vector3 const edge_vert_xi[2] = { + apf::Vector3(-1,0,0), + apf::Vector3(1,0,0), + }; + static apf::Vector3 const tri_vert_xi[3] = { + apf::Vector3(0,0,0), + apf::Vector3(1,0,0), + apf::Vector3(0,1,0), + }; + static apf::Vector3 const tet_vert_xi[4] = { + apf::Vector3(0,0,0), + apf::Vector3(1,0,0), + apf::Vector3(0,1,0), + apf::Vector3(0,0,1), + }; + + static apf::Vector3 const* const elem_vert_xi[apf::Mesh::TYPES] = { + vert_vert_xi, + edge_vert_xi, + tri_vert_xi, + 0, /* quad */ + tet_vert_xi, + 0, /* hex */ + 0, /* prism */ + 0 /* pyramid */ + }; + + if (!xis.allocated()) + xis.allocate(countElementNodes(s, type)); + else + xis.resize(countElementNodes(s, type)); + + int td = apf::Mesh::typeDimension[type]; + apf::Vector3 childXi, parentXi; + int evi = 0; + + int row = 0; + for(int d = 0; d <= td; ++d){ + int nDown = apf::Mesh::adjacentCount[type][d]; + int bt = apf::Mesh::simplexTypes[d]; + apf::EntityShape* shape = apf::getLagrange(1)->getEntityShape(bt); + int non = s->countNodesOn(bt); + for(int j = 0; j < nDown; ++j){ + for(int x = 0; x < non; ++x){ + s->getNodeXi(bt, x, childXi); + apf::NewArray shape_vals; + + shape->getValues(0, 0, childXi, shape_vals); + parentXi.zero(); + evi = j; + for (int i = 0; i < apf::Mesh::adjacentCount[bt][0]; ++i) { + if(bt == apf::Mesh::EDGE && type == apf::Mesh::TRIANGLE) + evi = apf::tri_edge_verts[j][i]; + else if(bt == apf::Mesh::EDGE && type == apf::Mesh::TET) + evi = apf::tet_edge_verts[j][i]; + else if(bt == apf::Mesh::TRIANGLE && type == apf::Mesh::TET) + evi = apf::tet_tri_verts[j][i]; + else if(bt == type) + evi = i; + parentXi += elem_vert_xi[type][evi] * shape_vals[i]; + } + xis[row] = parentXi; + ++row; + } + } + } + PCU_ALWAYS_ASSERT(row == s->getEntityShape(type)->countNodes()); +} + +void getElementNodeXis(FieldShape* s, Mesh* m, MeshEntity* e, + apf::NewArray& xis) +{ + int type = m->getType(e); + if (!xis.allocated()) + xis.allocate(countElementNodes(s, type)); + else + xis.resize(countElementNodes(s, type)); + + int td = apf::Mesh::typeDimension[type]; + apf::Vector3 childXi, parentXi; + + int row = 0; + for(int d = 0; d <= td; ++d){ + MeshEntity* down[12]; + int nDown = m->getDownward(e, d, down); + int bt = apf::Mesh::simplexTypes[d]; + int non = s->countNodesOn(bt); + if (!non) continue; + for(int j = 0; j < nDown; ++j){ + for(int x = 0; x < non; ++x){ + s->getNodeXi(bt, x, childXi); + xis[row] = (bt != type) ? + boundaryToElementXi(m, down[j], e, childXi) : childXi; + ++row; + } + } + } + PCU_ALWAYS_ASSERT(row == s->getEntityShape(type)->countNodes()); +} + }//namespace apf diff --git a/apf/apfShape.h b/apf/apfShape.h index 60308454d..3439f5d24 100644 --- a/apf/apfShape.h +++ b/apf/apfShape.h @@ -42,6 +42,24 @@ class EntityShape MeshEntity* e, Vector3 const& xi, NewArray& grads) const = 0; +/** \brief evaluate element vector shape functions + \details this is used only for Nedelec + \param xi the parent element coordinates + \param values each entry is the vector shape function value for one node */ + virtual void getVectorValues( + Mesh* m, + MeshEntity* e, + Vector3 const& xi, + NewArray& values) const = 0; +/** \brief evaluate element vector curl shape functions + \details this is used only for Nedelec + \param xi the parent element coordinates + \param values each entry is the vector shape function value for one node */ + virtual void getLocalVectorCurls( + Mesh* m, + MeshEntity* e, + Vector3 const& xi, + NewArray& values) const; /** \brief return the number of nodes affecting this element \details in a linear mesh, there are two nodes affecting and edge, three nodes affecting a triangle, @@ -94,6 +112,14 @@ class FieldShape \param node index from element node ordering \param xi parent element coordinates */ virtual void getNodeXi(int type, int node, Vector3& xi); +/** \brief Get the parent element tangent vector of an element node + \param type element type, select from apf::Mesh::Type + \param node index from element node ordering + \param t parent element tangent + \details this is only applicable for vector shapes */ + virtual void getNodeTangent(int type, int node, Vector3& t); +/** \brief Returns true if the shape functions are vectors */ + virtual bool isVectorShape(); /** \brief Get a unique string for this shape function scheme */ virtual const char* getName() const = 0; void registerSelf(const char* name); @@ -136,15 +162,52 @@ FieldShape* getIPFitShape(int dimension, int order); */ FieldShape* getHierarchic(int order); +/** \brief Get the Nedelec shape function of a given order + \details TODO: complete later + */ +FieldShape* getNedelec(int order); + +/** \brief Get the L2 shapes of a given order and entity type + \details + */ +FieldShape* getL2Shape(int order, int type); + +/** \brief Get the H1 shapes of a given order + \details These are hierarchic shapes that are compatible with MFEM's impl. + */ +FieldShape* getH1Shape(int order); + /** \brief Project a hierarchic field */ void projectHierarchicField(Field* to, Field* from); +/** \brief Project a Nedelec field */ +void projectNedelecField(Field* to, Field* from); + +/** \brief Project a L2 field */ +void projectL2Field(Field* to, Field* from); + FieldShape* getShapeByName(const char* name); /** \brief count the number of nodes affecting an element type \param type select from apf::Mesh::Type */ int countElementNodes(FieldShape* s, int type); +/** \brief gets the xi coordinates for all the nodes + \details order follows canonical notation. See tables + apf::Mesh::tri_edge_verts, apf::Mesh::tet_edge_verts, and + apf::Mesh::tet_tri_verts + \param type select from apf::Mesh::Type */ +void getElementNodeXis(FieldShape* s, int type, + apf::NewArray& xis); + +/** \brief gets the xi coordinates for all the nodes + \details order follows downward adjacency and global + directions for the bounding entities. xi coordinates + will be with respect to the entity e + \param type select from apf::Mesh::Type */ +void getElementNodeXis(FieldShape* s, Mesh* m, MeshEntity* e, + apf::NewArray& xis); + /** \brief Reparameterize from boundary entity to element \details This function converts a point in the local parametric space of a boundary mesh entity into the diff --git a/apf/apfVectorField.h b/apf/apfVectorField.h index c2f705a51..36a3dc076 100644 --- a/apf/apfVectorField.h +++ b/apf/apfVectorField.h @@ -19,6 +19,7 @@ class VectorField : public FieldOf virtual ~VectorField() {} virtual Element* getElement(VectorElement* e); virtual int getValueType() const {return VECTOR;} + virtual int getShapeType() const {return SCALAR;} virtual int countComponents() const; }; diff --git a/apf/apfVerify.cc b/apf/apfVerify.cc index b4fa5da3d..448b1ee41 100644 --- a/apf/apfVerify.cc +++ b/apf/apfVerify.cc @@ -281,19 +281,19 @@ static void receiveAllCopies(Mesh* m) static void verifyRemoteCopies(Mesh* m) { - PCU_Comm_Begin(); for (int d = 0; d <= m->getDimension(); ++d) { + PCU_Comm_Begin(); MeshIterator* it = m->begin(d); MeshEntity* e; while ((e = m->iterate(it))) if (m->isShared(e) && !m->isGhost(e)) sendAllCopies(m, e); m->end(it); + PCU_Comm_Send(); + while (PCU_Comm_Receive()) + receiveAllCopies(m); } - PCU_Comm_Send(); - while (PCU_Comm_Receive()) - receiveAllCopies(m); } // ghost verification @@ -521,19 +521,18 @@ static void receiveAlignment(Mesh* m) static void verifyAlignment(Mesh* m) { - PCU_Comm_Begin(); - for (int d = 1; d <= m->getDimension(); ++d) - { + for (int d = 1; d <= m->getDimension(); ++d) { + PCU_Comm_Begin(); MeshIterator* it = m->begin(d); MeshEntity* e; while ((e = m->iterate(it))) if (m->isShared(e)) sendAlignment(m, e); m->end(it); + PCU_Comm_Send(); + while (PCU_Comm_Receive()) + receiveAlignment(m); } - PCU_Comm_Send(); - while (PCU_Comm_Receive()) - receiveAlignment(m); } void packFieldInfo(Field* f, int to) @@ -773,31 +772,29 @@ static void verifyTags(Mesh* m) // verify tag data - PCU_Comm_Begin(); - for (int d = 0; d <= m->getDimension(); ++d) + for (int d = 0; d <= m->getDimension(); ++d) { + PCU_Comm_Begin(); MeshIterator* it = m->begin(d); MeshEntity* e; - while ((e = m->iterate(it))) + while ((e = m->iterate(it))) { if (m->getOwner(e)!=PCU_Comm_Self()) continue; - if (m->isShared(e)) - { + if (m->isShared(e)) { Copies r; m->getRemotes(e, r); sendTagData(m, e, tags, r); } - if (m->isGhosted(e)) - { + if (m->isGhosted(e)) { Copies g; m->getGhosts(e, g); sendTagData(m, e, tags, g, true); } } // while m->end(it); + PCU_Comm_Send(); + receiveTagData(m, tags); } // for - PCU_Comm_Send(); - receiveTagData(m, tags); } void verify(Mesh* m, bool abort_on_error) diff --git a/apf/apfVtkPieceWiseFields.cc b/apf/apfVtkPieceWiseFields.cc new file mode 100644 index 000000000..35016f775 --- /dev/null +++ b/apf/apfVtkPieceWiseFields.cc @@ -0,0 +1,232 @@ +/* + * Copyright 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include +#include +#include +#include "apfMesh.h" +#include "apfNumbering.h" +#include "apfNumberingClass.h" +#include "apfShape.h" +#include "apfFieldData.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// === includes for safe_mkdir === +#include +#include /*required for mode_t for mkdir on some systems*/ +#include /*using POSIX mkdir call for SMB "foo/" path*/ +#include /* for checking the error from mkdir */ +// =============================== + +namespace apf { + +static void safe_mkdir(const char* path) +{ + mode_t const mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + int err; + errno = 0; + err = mkdir(path, mode); + if (err != 0 && errno != EEXIST) + { + reel_fail("MDS: could not create directory \"%s\"\n", path); + } +} + +/* Paraview/VTK has trouble with sub-normal double precision floating point + * ASCII values. + * + * http://www.paraview.org/Bug/view.php?id=15925 + * + * This function exists to cast "double" to "float" + * before writing it to file, the others are to maintain the + * templated design of writeCornerCoords and others */ + + +static std::string getPieceFileName(int id) +{ + std::stringstream ss; + ss << id << ".vtk"; + return ss.str(); +} + +static std::string getFileNameAndPathVtu(const char* prefix, + std::string fileName, + int id) +{ + std::stringstream ss; + ss << prefix << '/' << prefix << '_' << id << '_' << fileName; + return ss.str(); +} + +static void writeNedelecVtkFile(const char* prefix, Mesh* m, + std::vector writeFields) +{ + double t0 = PCU_Time(); + + // get the number of points on this part + MeshEntity* e; + MeshIterator* it = m->begin(m->getDimension()); + int point_count = 0; + int cell_count = 0; + bool isSimplexMesh = true; + while( (e = m->iterate(it)) ) { + int type = m->getType(e); + if (!apf::isSimplex(type)) { + isSimplexMesh = false; + break; + } + point_count += Mesh::adjacentCount[type][0]; + cell_count++; + } + m->end(it); + + PCU_ALWAYS_ASSERT_VERBOSE(isSimplexMesh, + "writeNedelecVtk only implemented for all simplex meshes"); + + static Vector3 xis[4] = { + apf::Vector3(0., 0., 0.), + apf::Vector3(1., 0., 0.), + apf::Vector3(0., 1., 0.), + apf::Vector3(0., 0., 1.) + }; + + std::string fileName = getPieceFileName(PCU_Comm_Self()); + std::string fileNameAndPath = getFileNameAndPathVtu(prefix, fileName, PCU_Comm_Self()); + std::stringstream buf; + buf << + "# vtk DataFile Version 3.0\n" + "Made by SCOREC/core\n" + "ASCII\n" + "DATASET UNSTRUCTURED_GRID\n"; + buf << "POINTS " << point_count << " double\n"; + + it = m->begin(m->getDimension()); + while( (e = m->iterate(it)) ) { + int type = m->getType(e); + int n = Mesh::adjacentCount[type][0]; + MeshElement* me = createMeshElement(m, e); + for(int i=0; iend(it); + + buf << "CELLS " << cell_count << ' ' << point_count + cell_count << '\n'; + + it = m->begin(m->getDimension()); + int N = 0; + while( (e = m->iterate(it)) ) { + int type = m->getType(e); + int n = Mesh::adjacentCount[type][0]; + buf << n; + for(int i=0; iend(it); + + buf << "CELL_TYPES " << cell_count << '\n'; + it = m->begin(m->getDimension()); + while( (e = m->iterate(it)) ) { + int type = m->getType(e); + int vtk_type = -1; + switch (type) { + case Mesh::TRIANGLE: vtk_type = 5; break; + case Mesh::TET: vtk_type = 10; break; + default: + PCU_ALWAYS_ASSERT_VERBOSE(0, + "only TRIANGLE and TET supported"); + break; + } + buf << vtk_type << '\n'; + } + m->end(it); + + buf << "CELL_DATA " << cell_count << '\n'; + buf << "SCALARS part_id int\n"; + buf << "LOOKUP_TABLE default\n"; + it = m->begin(m->getDimension()); + while( (e = m->iterate(it)) ) { + buf << PCU_Comm_Self() << '\n'; + } + m->end(it); + + buf << "POINT_DATA " << point_count << '\n' << std::flush; + + for(std::size_t i = 0; i < writeFields.size(); i++) { + Field* f = m->findField(writeFields[i].c_str()); + const char* fName = f->getName(); + buf << "VECTORS " << fName << " double\n"; + + it = m->begin(m->getDimension()); + while( (e = m->iterate(it)) ) { + int type = m->getType(e); + int n = Mesh::adjacentCount[type][0]; + MeshElement* me = createMeshElement(m, e); + Element* el = createElement(f, me); + for(int j=0; jend(it); + } + double t1 = PCU_Time(); + if (!PCU_Comm_Self()) + { + lion_oprint(1,"writeVtuFile into buffers: %f seconds\n", t1 - t0); + } + { //block forces std::ofstream destructor call + std::ofstream file(fileNameAndPath.c_str()); + PCU_ALWAYS_ASSERT(file.is_open()); + file << buf.rdbuf(); + } + double t2 = PCU_Time(); + if (!PCU_Comm_Self()) + { + lion_oprint(1,"writeNedelecVtkFile buffers to disk: %f seconds\n", t2 - t1); + } +} + +std::vector populateNedelecFields(Mesh* m) +{ + std::vector writeFields; + for (int i=0; i < m->countFields(); ++i) + { + Field* f = m->getField(i); + std::string fsName = f->getShape()->getName(); + if (fsName == std::string("Nedelec")) + writeFields.push_back(f->getName()); + } + return writeFields; +} + +void writeNedelecVtkFiles(const char* prefix, Mesh* m) +{ + // bool isWritingBinary = true; + std::vector writeFields = populateNedelecFields(m); + if (writeFields.size() == 0) return; + safe_mkdir(prefix); + writeNedelecVtkFile(prefix, m, writeFields); +} + +} diff --git a/apf/pkg_tribits.cmake b/apf/pkg_tribits.cmake index 9e2d3d048..e558ab4dc 100644 --- a/apf/pkg_tribits.cmake +++ b/apf/pkg_tribits.cmake @@ -28,9 +28,15 @@ set(APF_SOURCES apfShape.cc apfIPShape.cc apfHierarchic.cc + apfPolyBasis1D.cc + apfNedelec.cc + apfL2Shapes.cc + apfH1Shapes.cc apfVector.cc apfVectorElement.cc apfVectorField.cc + apfMixedVectorElement.cc + apfMixedVectorField.cc apfPackedField.cc apfNumbering.cc apfMixedNumbering.cc diff --git a/apf_sim/apfSIM.cc b/apf_sim/apfSIM.cc index 5a8489959..8132edfe7 100644 --- a/apf_sim/apfSIM.cc +++ b/apf_sim/apfSIM.cc @@ -542,6 +542,19 @@ class TagSIM comm = AttachDataCommu_new(unitSize/sizeof(int),0,count); PM_setMigrId(mesh,id); } + TagSIM(pParMesh m, + pMeshDataId simTag, + const char* n, + std::size_t unitSize, + int c): + count(c), + mesh(m), + id(simTag), + name(n) + { + comm = AttachDataCommu_new(unitSize/sizeof(int),0,count); + PM_setMigrId(mesh,id); + } virtual ~TagSIM() { MD_removeMeshCallback(id,CBdelete); @@ -650,6 +663,14 @@ class IntTagSIM : public TagSIM return 1; } + IntTagSIM(pParMesh m, pMeshDataId simTag, const char* name, int c): + TagSIM(m,simTag,name,sizeof(int),c) + { + MD_setMeshCallback(id,CBdelete,deleteIntCB,this); + MD_setMeshCallback(id,CBmigrateOut,pm_sendIntArray,comm); + MD_setMeshCallback(id,CBmigrateIn,pm_recvIntArray,comm); + } + IntTagSIM(pParMesh m, const char* name, int c): TagSIM(m,name,sizeof(int),c) { @@ -750,6 +771,15 @@ MeshTag* MeshSIM::createIntTag(const char* name, int size) return reinterpret_cast(tag); } +MeshTag* MeshSIM::createIntTag(const char* name, pMeshDataId simTag, int size) +{ + TagSIM* tag = new IntTagSIM(mesh,simTag,name,size); + tags.push_back(tag); + return reinterpret_cast(tag); +} + + + MeshTag* MeshSIM::createLongTag(const char* name, int size) { TagSIM* tag = new LongTagSIM(mesh,name,size); @@ -1042,7 +1072,8 @@ static bool findMatches(Mesh* m) while ((e = m->iterate(it))) { pEntity ent = reinterpret_cast(e); -#if SIMMODSUITE_MAJOR_VERSION <= 14 && SIMMODSUITE_MINOR_VERSION < 190921 +#if (SIMMODSUITE_MAJOR_VERSION <= 14 && SIMMODSUITE_MINOR_VERSION < 190921) || \ + (SIMMODSUITE_MAJOR_VERSION >= 16 && SIMMODSUITE_MINOR_VERSION >= 210623) pPList l = EN_getMatchingEnts(ent, NULL); #else pPList l = EN_getMatchingEnts(ent, NULL, 0); diff --git a/apf_sim/apfSIM.h b/apf_sim/apfSIM.h index 9d806ab74..3d87bd4eb 100644 --- a/apf_sim/apfSIM.h +++ b/apf_sim/apfSIM.h @@ -60,6 +60,7 @@ class MeshSIM : public Mesh2 void getResidence(MeshEntity* e, Parts& residence); MeshTag* createDoubleTag(const char* name, int size); MeshTag* createIntTag(const char* name, int size); + MeshTag* createIntTag(const char* name, pMeshDataId simTag, int size); MeshTag* createLongTag(const char* name, int size); MeshTag* findTag(const char* name); void destroyTag(MeshTag* tag); @@ -95,6 +96,7 @@ class MeshSIM : public Mesh2 // Mesh2 interface ============================== void setRemotes(MeshEntity*, Copies&) {} void addRemote(MeshEntity*, int, MeshEntity*) {} + void clearRemotes(MeshEntity*) {} void setResidence(MeshEntity*, Parts&) {} void setParam(MeshEntity*, Vector3 const &) {} void increment(MeshIterator*) {} diff --git a/cdash/nightly.cmake b/cdash/nightly.cmake index 8bd6d01c8..596d3f5d5 100644 --- a/cdash/nightly.cmake +++ b/cdash/nightly.cmake @@ -1,10 +1,11 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 2.17) SET(CTEST_DO_SUBMIT ON) SET(CTEST_TEST_TYPE Nightly) +set(CTEST_BUILD_CONFIGURATION RelWithDebInfo) set(CTEST_NIGHTLY_START_TIME "17:00:00 EST") -set(CTEST_SITE "pachisi.scorec.rpi.edu" ) +set(CTEST_SITE "cranium.scorec.rpi.edu" ) set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=SCOREC") @@ -13,7 +14,6 @@ set(CTEST_BUILD_NAME "linux-gcc-${CTEST_BUILD_CONFIGURATION}") set(CTEST_DASHBOARD_ROOT "/lore/cwsmith/nightlyBuilds/" ) set(CTEST_CMAKE_GENERATOR "Unix Makefiles") -set(CTEST_BUILD_CONFIGURATION RelWithDebInfo) set(CTEST_BUILD_FLAGS -j4) set(CTEST_PROJECT_NAME "SCOREC") @@ -305,6 +305,7 @@ SET(CONFIGURE_OPTIONS "-DENABLE_ZOLTAN:BOOL=ON" "-DPCU_COMPRESS:BOOL=ON" "-DIS_TESTING:BOOL=True" + "-DSCOREC_CXX_WARNINGS:BOOL=ON" "-DMESHES:STRING=${MESHES}/meshes" ) @@ -312,7 +313,7 @@ SET(CONFIGURE_OPTIONS-sim "${CONFIGURE_OPTIONS}" "-DENABLE_SIMMETRIX:BOOL=ON" "-DSIM_PARASOLID:BOOL=ON" - "-DSIM_MPI:STRING=mpich3.3" + "-DSIM_MPI:STRING=mpich3.3.2" ) setup_repo() diff --git a/cdash/nightly.sh b/cdash/nightly.sh index d0438e05b..cf4c37b9b 100755 --- a/cdash/nightly.sh +++ b/cdash/nightly.sh @@ -7,13 +7,15 @@ export PATH=/usr/share/lmod/lmod/libexec:$PATH #setup spack modules unset MODULEPATH -module use /opt/scorec/spack/lmod/linux-rhel7-x86_64/Core/ -module load gcc/7.3.0-bt47fwr -module load cmake/3.12.1-wfk2b7e -module load mpich -module load zoltan/3.83-int32-gx3prjr -module load simmetrix-simmodsuite/14.0-190928-snlypqg +module unuse /opt/scorec/spack/lmod/linux-rhel7-x86_64/Core +module use /opt/scorec/modules +module use /opt/scorec/spack/v0154_2/lmod/linux-rhel7-x86_64/Core +module load gcc/10.1.0 +module load mpich/3.3.2 +module load simmetrix-simmodsuite/17.0-220516 +module load zoltan/3.83-int32 +module load cmake/3.20.0 #cdash output root d=/lore/cwsmith/nightlyBuilds/ @@ -23,10 +25,10 @@ cd $d touch $d/startedCoreNightly #run nightly.cmake script -ctest -V --script $d/repos/core/cdash/nightly.cmake +ctest -V --script $d/nightly.cmake touch $d/doneCoreNightly #create doxygen docs cd build/master make doc -cp -r doc/html/* /net/web/scorec/scorec-web/htdocs/pumi/doxygen/. +cp -r doc/html /net/web/scorec/scorec-web/htdocs/pumi/doxygen diff --git a/cmake/FindMPI4PY.cmake b/cmake/FindMPI4PY.cmake index fcd16d689..f30da590d 100644 --- a/cmake/FindMPI4PY.cmake +++ b/cmake/FindMPI4PY.cmake @@ -9,7 +9,7 @@ if(NOT MPI4PY_INCLUDE_DIR) execute_process(COMMAND - "${Python2_EXECUTABLE}" "-c" "import mpi4py; print(mpi4py.get_include())" + "${Python_EXECUTABLE}" "-c" "import mpi4py; print(mpi4py.get_include())" OUTPUT_VARIABLE MPI4PY_INCLUDE_DIR RESULT_VARIABLE MPI4PY_COMMAND_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -33,7 +33,7 @@ endif(NOT MPI4PY_INCLUDE_DIR) if(MPI4PY_FOUND) execute_process(COMMAND - "${Python2_EXECUTABLE}" "-c" "from mpi4py import MPI; print(MPI.get_vendor())" + "${Python_EXECUTABLE}" "-c" "from mpi4py import MPI; print(MPI.get_vendor())" OUTPUT_VARIABLE MPI4PY_VENDOR OUTPUT_STRIP_TRAILING_WHITESPACE) endif() diff --git a/cmake/FindParmetis.cmake b/cmake/FindParmetis.cmake index b00a11d41..e9a544418 100644 --- a/cmake/FindParmetis.cmake +++ b/cmake/FindParmetis.cmake @@ -28,7 +28,13 @@ if(NOT EXISTS "${METIS_LIBRARY}") message(FATAL_ERROR "metis library not found") endif() -set(PARMETIS_LIBRARIES ${PARMETIS_LIBRARY} ${METIS_LIBRARY}) +find_library(GK_LIBRARY GKlib PATHS "${PARMETIS_PREFIX}/lib") +if(EXISTS "${GK_LIBRARY}") + set(PARMETIS_LIBRARIES ${PARMETIS_LIBRARY} ${METIS_LIBRARY} ${GK_LIBRARY}) +else() + set(PARMETIS_LIBRARIES ${PARMETIS_LIBRARY} ${METIS_LIBRARY}) +endif() + set(PARMETIS_INCLUDE_DIRS ${PARMETIS_INCLUDE_DIR} ${METIS_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) diff --git a/cmake/FindSimModSuite.cmake b/cmake/FindSimModSuite.cmake index 190f86fb5..ccbb3d7a6 100644 --- a/cmake/FindSimModSuite.cmake +++ b/cmake/FindSimModSuite.cmake @@ -65,26 +65,26 @@ string(REGEX REPLACE "${SIMMODSUITE_INCLUDE_DIR}") string(REGEX MATCH - "[0-9]+.[0-9]+-[0-9]+" + "[0-9]+[.][0-9]+-[0-9]+" SIM_VERSION "${SIMMODSUITE_INCLUDE_DIR}") #VERSION_LESS and VERSION_GREATER need '.' delimited version strings. string(REGEX REPLACE - "([0-9]+.[0-9]+)-([0-9]+)" + "([0-9]+[.][0-9]+)-([0-9]+)" "\\1.\\2" SIM_DOT_VERSION "${SIM_VERSION}") string(REGEX REPLACE - "([0-9]+).([0-9]+)-([0-9]+)" + "([0-9]+)[.]([0-9]+)-([0-9]+)" "\\1" SIMMODSUITE_MAJOR_VERSION "${SIM_VERSION}") string(REGEX REPLACE - "([0-9]+).([0-9]+)-([0-9]+)" + "([0-9]+)[.]([0-9]+)-([0-9]+)" "\\3" SIMMODSUITE_MINOR_VERSION "${SIM_VERSION}") -set(MIN_VALID_SIM_VERSION 12.0.190225) -set(MAX_VALID_SIM_VERSION 15.0.200110) +set(MIN_VALID_SIM_VERSION 15.0.191017) +set(MAX_VALID_SIM_VERSION 18.0.220930) if( ${SKIP_SIMMETRIX_VERSION_CHECK} ) message(STATUS "Skipping Simmetrix SimModSuite version check." " This may result in undefined behavior") @@ -113,7 +113,7 @@ message(STATUS "SIM_ARCHOS ${SIM_ARCHOS}") option(SIM_PARASOLID "Use Parasolid through Simmetrix" OFF) if (SIM_PARASOLID) set(MIN_SIM_PARASOLID_VERSION 290) - set(MAX_SIM_PARASOLID_VERSION 310) + set(MAX_SIM_PARASOLID_VERSION 350) foreach(version RANGE ${MAX_SIM_PARASOLID_VERSION} ${MIN_SIM_PARASOLID_VERSION} -10) diff --git a/cmake/bob.cmake b/cmake/bob.cmake index 74c5400fd..52c312666 100644 --- a/cmake/bob.cmake +++ b/cmake/bob.cmake @@ -1,3 +1,4 @@ +include(GNUInstallDirs) function(bob_always_full_rpath) # CMake RPATH "always full" configuration, see: # https://cmake.org/Wiki/CMake_RPATH_handling#Always_full_RPATH @@ -6,12 +7,17 @@ function(bob_always_full_rpath) # when building, don't use the install RPATH already # (but later on when installing) set(CMAKE_BUILD_WITH_INSTALL_RPATH False PARENT_SCOPE) - # the RPATH to be used when installing, but only if it's not a system directory - list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES - "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) - if("${isSystemDir}" STREQUAL "-1") - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" PARENT_SCOPE) + + if(APPLE) + set(base @loader_path) + else() + set(base $ORIGIN) endif() + file(RELATIVE_PATH relDir + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} + ) + set(CMAKE_INSTALL_RPATH ${base} ${base}/${relDir} PARENT_SCOPE) # add the automatically determined parts of the RPATH # which point to directories outside the build tree to the install RPATH set(CMAKE_INSTALL_RPATH_USE_LINK_PATH True PARENT_SCOPE) @@ -44,7 +50,7 @@ endmacro(bob_set_shared_libs) function(bob_begin_cxx_flags) option(${PROJECT_NAME}_CXX_OPTIMIZE "Compile C++ with optimization" ON) option(${PROJECT_NAME}_CXX_SYMBOLS "Compile C++ with debug symbols" ON) - option(${PROJECT_NAME}_CXX_WARNINGS "Compile C++ with warnings" ON) + option(${PROJECT_NAME}_CXX_WARNINGS "Compile C++ with warnings" OFF) set(FLAGS "") if(${PROJECT_NAME}_CXX_OPTIMIZE) if (NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")) diff --git a/crv/crv.h b/crv/crv.h index 60e8c2c1a..3a0e7f9e8 100644 --- a/crv/crv.h +++ b/crv/crv.h @@ -37,6 +37,9 @@ int getBlendingOrder(const int type); /** \brief count invalid elements of the mesh */ int countNumberInvalidElements(apf::Mesh2* m); +/** \brief converts Interpolating nodes to Control points for a Bezier mesh*/ +void interpolatingToBezier(apf::Mesh2* m); + /** \brief Base Mesh curving object \details P is the order, S is the space dimension, different from the mesh dimension, used to distinguish between planar 2D @@ -119,6 +122,11 @@ ma::Input* configureShapeCorrection( note that this function will delete the Input object */ void adapt(ma::Input* in); +/** \brief crv adapt with custom configuration + \details see maInput.h for details. + note that this function will delete the Input object */ +void adapt(const ma::Input* in); + /** \brief crv stats to get statistic information about the mesh \details statistic considered are (1)final/desired edge-lengths (2) linear quality (3) curved quality (minJ/maxJ) diff --git a/crv/crvAdapt.cc b/crv/crvAdapt.cc index 3b6baec15..12d58abb1 100644 --- a/crv/crvAdapt.cc +++ b/crv/crvAdapt.cc @@ -154,7 +154,7 @@ ma::Input* configureShapeCorrection( ma::Mesh* m, ma::SizeField* f, ma::SolutionTransfer* s) { - ma::Input* in = ma::configureIdentity(m,f,s); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m,f,s)); in->shouldFixShape = true; in->shouldSnap = in->mesh->canSnap(); in->shouldTransferParametric = in->mesh->canSnap(); @@ -168,14 +168,12 @@ static int fixInvalidElements(crv::Adapt* a) + crv::fixInvalidEdges(a); int originalCount = count; int prev_count; - int i = 0; do { if ( ! count) break; prev_count = count; count = crv::fixLargeBoundaryAngles(a) + crv::fixInvalidEdges(a); - ++i; } while(count < prev_count); crv::fixLargeBoundaryAngles(a); @@ -235,9 +233,20 @@ void adapt(ma::Input* in) apf::printStats(a->mesh); crv::clearTags(a); delete a; + // cleanup input object and associated sizefield and solutiontransfer objects + if (in->ownsSizeField) + delete in->sizeField; + if (in->ownsSolutionTransfer) + delete in->solutionTransfer; delete in; } + +void adapt(const ma::Input* in) +{ + crv::adapt(ma::makeAdvanced(in)); +} + /** \brief Measures entity related quantities for a given mesh \details quantities include normalized edge length, linear quality and curved quality. The values can be computed in both metric (if diff --git a/crv/crvBezier.cc b/crv/crvBezier.cc index 6007d669a..99c84f8ac 100644 --- a/crv/crvBezier.cc +++ b/crv/crvBezier.cc @@ -90,6 +90,11 @@ class Bezier : public apf::FieldShape apf::Vector3 const&, apf::NewArray&) const { } + void getVectorValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + fail("getVectorValues is not implemented for Bezier shapes"); + } int countNodes() const {return 1;} void alignSharedNodes(apf::Mesh*, apf::MeshEntity*, apf::MeshEntity*, int order[]) @@ -112,6 +117,11 @@ class Bezier : public apf::FieldShape grads.allocate(P+1); bezierGrads[apf::Mesh::EDGE](P,xi,grads); } + void getVectorValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + fail("getVectorValues is not implemented for Bezier shapes"); + } int countNodes() const {return P+1;} void alignSharedNodes(apf::Mesh*, apf::MeshEntity*, apf::MeshEntity*, int order[]) @@ -146,6 +156,11 @@ class Bezier : public apf::FieldShape BlendedTriangleGetLocalGradients(m,e,xi,grads); } + void getVectorValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + fail("getVectorValues is not implemented for Bezier shapes"); + } int countNodes() const {return getNumControlPoints(apf::Mesh::TRIANGLE,P);} void alignSharedNodes(apf::Mesh* m, apf::MeshEntity* elem, apf::MeshEntity* shared, int order[]) @@ -179,6 +194,11 @@ class Bezier : public apf::FieldShape BlendedTetGetLocalGradients(m,e,xi,grads); } } + void getVectorValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + fail("getVectorValues is not implemented for Bezier shapes"); + } int countNodes() const { if(!useBlending(apf::Mesh::TET)) return (P+1)*(P+2)*(P+3)/6; @@ -372,6 +392,11 @@ class GregorySurface4 : public apf::FieldShape BlendedTriangleGetLocalGradients(m,e,xi,grads); } + void getVectorValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + fail("getVectorValues is not implemented for Gregory shapes"); + } int countNodes() const { return 18; @@ -513,6 +538,11 @@ class GregorySurface4 : public apf::FieldShape BlendedTetGetLocalGradients(m,e,xi,grads); } } + void getVectorValues(apf::Mesh*, apf::MeshEntity*, + apf::Vector3 const&, apf::NewArray&) const + { + fail("getVectorValues is not implemented for Gregory shapes"); + } int countNodes() const { if(!useBlending(apf::Mesh::TET)) return 47; diff --git a/crv/crvCurveMesh.cc b/crv/crvCurveMesh.cc index 5cc39bbfb..2180856ee 100644 --- a/crv/crvCurveMesh.cc +++ b/crv/crvCurveMesh.cc @@ -44,6 +44,53 @@ void convertInterpolationPoints(apf::Mesh2* m, apf::MeshEntity* e, apf::destroyElement(elem); } +void interpolatingToBezier(apf::Mesh2* m) +{ + apf::FieldShape * fs = m->getShape(); + int order = fs->getOrder(); + + int md = m->getDimension(); + int blendingOrder = getBlendingOrder(apf::Mesh::simplexTypes[md]); + // go downward, and convert interpolating to control points + int startDim = md - (blendingOrder > 0); + + for(int d = startDim; d >= 1; --d){ + if(!fs->hasNodesIn(d)) continue; + int n = fs->getEntityShape(apf::Mesh::simplexTypes[d])->countNodes(); + int ne = fs->countNodesOn(apf::Mesh::simplexTypes[d]); + apf::NewArray c; + getBezierTransformationCoefficients(order, + apf::Mesh::simplexTypes[d],c); + apf::MeshEntity* e; + apf::MeshIterator* it = m->begin(d); + while ((e = m->iterate(it))){ + if(m->isOwned(e)) + convertInterpolationPoints(m,e,n,ne,c); + } + m->end(it); + } + // if we have a full representation, we need to place internal nodes on + // triangles and tetrahedra + for(int d = 2; d <= md; ++d){ + if(!fs->hasNodesIn(d) || + getBlendingOrder(apf::Mesh::simplexTypes[d])) continue; + int n = fs->getEntityShape(apf::Mesh::simplexTypes[d])->countNodes(); + int ne = fs->countNodesOn(apf::Mesh::simplexTypes[d]); + apf::NewArray c; + getInternalBezierTransformationCoefficients(m,order,1, + apf::Mesh::simplexTypes[d],c); + apf::MeshEntity* e; + apf::MeshIterator* it = m->begin(d); + while ((e = m->iterate(it))){ + if(!isBoundaryEntity(m,e) && m->isOwned(e)) + convertInterpolationPoints(m,e,n-ne,ne,c); + } + m->end(it); + } + apf::synchronize(m->getCoordinateField()); + +} + void snapToInterpolate(apf::Mesh2* m, apf::MeshEntity* e, bool isNew) { PCU_ALWAYS_ASSERT(m->canSnap()); @@ -125,49 +172,7 @@ bool InterpolatingCurver::run() void BezierCurver::convertInterpolatingToBezier() { - apf::FieldShape * fs = m_mesh->getShape(); - int order = fs->getOrder(); - - int md = m_mesh->getDimension(); - int blendingOrder = getBlendingOrder(apf::Mesh::simplexTypes[md]); - // go downward, and convert interpolating to control points - int startDim = md - (blendingOrder > 0); - - for(int d = startDim; d >= 1; --d){ - if(!fs->hasNodesIn(d)) continue; - int n = fs->getEntityShape(apf::Mesh::simplexTypes[d])->countNodes(); - int ne = fs->countNodesOn(apf::Mesh::simplexTypes[d]); - apf::NewArray c; - getBezierTransformationCoefficients(order, - apf::Mesh::simplexTypes[d],c); - apf::MeshEntity* e; - apf::MeshIterator* it = m_mesh->begin(d); - while ((e = m_mesh->iterate(it))){ - if(m_mesh->isOwned(e)) - convertInterpolationPoints(m_mesh,e,n,ne,c); - } - m_mesh->end(it); - } - // if we have a full representation, we need to place internal nodes on - // triangles and tetrahedra - for(int d = 2; d <= md; ++d){ - if(!fs->hasNodesIn(d) || - getBlendingOrder(apf::Mesh::simplexTypes[d])) continue; - int n = fs->getEntityShape(apf::Mesh::simplexTypes[d])->countNodes(); - int ne = fs->countNodesOn(apf::Mesh::simplexTypes[d]); - apf::NewArray c; - getInternalBezierTransformationCoefficients(m_mesh,order,1, - apf::Mesh::simplexTypes[d],c); - apf::MeshEntity* e; - apf::MeshIterator* it = m_mesh->begin(d); - while ((e = m_mesh->iterate(it))){ - if(!isBoundaryEntity(m_mesh,e) && m_mesh->isOwned(e)) - convertInterpolationPoints(m_mesh,e,n-ne,ne,c); - } - m_mesh->end(it); - } - - synchronize(); + interpolatingToBezier(m_mesh); } bool BezierCurver::run() diff --git a/crv/crvShapeHandler.cc b/crv/crvShapeHandler.cc index 9ec487ec0..8040d8760 100644 --- a/crv/crvShapeHandler.cc +++ b/crv/crvShapeHandler.cc @@ -500,7 +500,6 @@ class BezierHandler : public ma::ShapeHandler int P = fs->getOrder(); int n = fs->getEntityShape(apf::Mesh::EDGE)->countNodes(); - int numNewElements = 0; int numNewTriangles = 0; int numMiddleEdges = 0; // upper bound @@ -531,8 +530,6 @@ class BezierHandler : public ma::ShapeHandler if (newType == apf::Mesh::TRIANGLE) numNewTriangles++; - if (newType == apf::Mesh::TET) - numNewElements++; // zero new entities bool snap = isBoundaryEntity(mesh,newEntities[i]) && shouldSnap; if (newType != apf::Mesh::EDGE) diff --git a/crv/crvVtk.cc b/crv/crvVtk.cc index 489e1f3ae..dd72bafac 100644 --- a/crv/crvVtk.cc +++ b/crv/crvVtk.cc @@ -949,12 +949,11 @@ static void makeDirectories(const char* prefix, int type, int n) void writeCurvedWireFrame(apf::Mesh* m, int n, const char* prefix) { - apf::Mesh2* wireMesh = apf::makeEmptyMdsMesh(0, 1, false); + apf::Mesh2* wireMesh = apf::makeEmptyMdsMesh(NULL, 1, false); apf::Field* f = m->getCoordinateField(); apf::MeshEntity* ent; apf::MeshIterator* it; it = m->begin(1); - int count = 0; while( (ent = m->iterate(it)) ) { if(!m->isOwned(ent)) continue; @@ -989,18 +988,14 @@ void writeCurvedWireFrame(apf::Mesh* m, int n, const char* prefix) v[1] = vs[i+1]; apf::buildElement(wireMesh, 0, apf::Mesh::EDGE, v); } - count++; } - // wireMesh is not a valid mesh, so neither of the following will work! - /* apf::deriveMdsModel(wireMesh); */ - /* wireMesh->acceptChanges(); */ - /* wireMesh->verify(); */ + m->end(it); apf::printStats(wireMesh); std::stringstream ss; ss << prefix << "_wire"; apf::writeVtkFiles(ss.str().c_str(),wireMesh); - /* wireMesh->destroyNative(); */ - /* apf::destroyMesh(wireMesh); */ + wireMesh->destroyNative(); + apf::destroyMesh(wireMesh); } diff --git a/gmi/gmi_base.c b/gmi/gmi_base.c index cfd6d4b21..ad44e7be3 100644 --- a/gmi/gmi_base.c +++ b/gmi/gmi_base.c @@ -12,6 +12,8 @@ #include "agm.h" #include #include +#include +#include struct gmi_model_ops gmi_base_ops = { .begin = gmi_base_begin, @@ -38,11 +40,9 @@ struct gmi_ent* gmi_from_agm(struct agm_ent e) struct agm_ent agm_from_gmi(struct gmi_ent* e) { - char* p; - int uid; + intptr_t uid; struct agm_ent a; - p = (char*)e; - uid = p - ((char*)0); + uid = (intptr_t)e; if (uid == 0) { a.type = 0; a.id = -1; diff --git a/ma/CMakeLists.txt b/ma/CMakeLists.txt index 68e72d2ea..59c0abb21 100644 --- a/ma/CMakeLists.txt +++ b/ma/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES maLayerCollapse.cc maMatch.cc maSolutionTransfer.cc + maSolutionTransferHelper.cc maSnap.cc maEdgeSwap.cc maShape.cc diff --git a/ma/ma.cc b/ma/ma.cc index b5c2d3ca6..64d692e25 100644 --- a/ma/ma.cc +++ b/ma/ma.cc @@ -45,12 +45,22 @@ void adapt(Input* in) postBalance(a); Mesh* m = a->mesh; delete a; + // cleanup input object and associated sizefield and solutiontransfer objects + if (in->ownsSizeField) + delete in->sizeField; + if (in->ownsSolutionTransfer) + delete in->solutionTransfer; delete in; double t1 = PCU_Time(); print("mesh adapted in %f seconds",t1-t0); apf::printStats(m); } +void adapt(const Input* in) +{ + adapt(makeAdvanced(in)); +} + void adaptVerbose(Input* in, bool verbose) { print("version 2.0 - dev !"); @@ -109,12 +119,22 @@ void adaptVerbose(Input* in, bool verbose) postBalance(a); Mesh* m = a->mesh; delete a; + // cleanup input object and associated sizefield and solutiontransfer objects + if (in->ownsSizeField) + delete in->sizeField; + if (in->ownsSolutionTransfer) + delete in->solutionTransfer; delete in; double t1 = PCU_Time(); print("mesh adapted in %f seconds",t1-t0); apf::printStats(m); } +void adaptVerbose(const Input* in, bool verbose) +{ + adaptVerbose(makeAdvanced(in), verbose); +} + void adapt(Mesh* m, AnisotropicFunction* f, SolutionTransfer* s) { diff --git a/ma/ma.h b/ma/ma.h index e142acd4d..261948e01 100644 --- a/ma/ma.h +++ b/ma/ma.h @@ -45,14 +45,22 @@ namespace ma { void adapt(Mesh* m, IsotropicFunction* f, SolutionTransfer* s=0); /** \brief adapt based on an anisotropic function */ void adapt(Mesh* m, AnisotropicFunction* f, SolutionTransfer* s=0); -/** \brief adapt with custom configuration +/** \brief adapt with custom mutable configuration Input \details see maInput.h for details. note that this function will delete the Input object. */ void adapt(Input* in); -/** \brief adapt verbose for debugging +/** \brief adapt with un-mutable configuration Input + \details see maInput.h for details. + note that this function will delete the Input object. */ +void adapt(const Input* in); +/** \brief adapt verbose for debugging with mutable configuration Input \details see maInput.h for details. The mesh will be written (vtk-format) at each operation stage */ void adaptVerbose(Input* in, bool verbosef = false); +/** \brief adapt verbose for debugging with unmutable configuration Input + \details see maInput.h for details. The mesh will be + written (vtk-format) at each operation stage */ +void adaptVerbose(const Input* in, bool verbosef = false); /** \brief run uniform refinement, plus snapping and shape correction */ void runUniformRefinement(Mesh* m, int n=1, SolutionTransfer* s=0); /** \brief run uniform refinement with matched entity support diff --git a/ma/maAdapt.cc b/ma/maAdapt.cc index 65c011a66..606e095d5 100644 --- a/ma/maAdapt.cc +++ b/ma/maAdapt.cc @@ -353,6 +353,7 @@ Cavity::Cavity() adapter = 0; solutionTransfer = 0; shape = 0; + shouldTransferSizeField = false; } void Cavity::init(Adapt* a) @@ -368,6 +369,8 @@ void Cavity::init(Adapt* a) shouldTransfer = true; if (shape->hasNodesOn(d)) shouldFit = true; + if (adapter->sizeField->hasNodesOn(d)) + shouldTransferSizeField = true; } } @@ -409,6 +412,12 @@ void Cavity::transfer(EntityArray& oldElements) newEntities.retrieve(a); solutionTransfer->onCavity(oldElements,a); } + if (shouldTransferSizeField) + { + EntityArray a; + newEntities.retrieve(a); + adapter->sizeField->onCavity(oldElements,a); + } } void Cavity::fit(EntityArray& oldElements) diff --git a/ma/maAdapt.h b/ma/maAdapt.h index c41675422..81a943e0a 100644 --- a/ma/maAdapt.h +++ b/ma/maAdapt.h @@ -137,6 +137,7 @@ class Cavity void fit(EntityArray& oldElements); bool shouldTransfer; bool shouldFit; + bool shouldTransferSizeField; private: Adapt* adapter; SolutionTransfer* solutionTransfer; diff --git a/ma/maBalance.cc b/ma/maBalance.cc index ebc845f28..edeefa96c 100644 --- a/ma/maBalance.cc +++ b/ma/maBalance.cc @@ -4,6 +4,8 @@ #include #include +#define MAX_ZOLTAN_GRAPH_RANKS 16*1024 + namespace ma { static double clamp(double x, double max, double min) @@ -121,17 +123,60 @@ void printEntityImbalance(Mesh* m) print("element imbalance %.0f%% of average",p); } +double estimateWeightedImbalance(Adapt* a) +{ + Tag* w = getElementWeights(a); + double imb[4]; + Parma_GetWeightedEntImbalance(a->mesh, w, &imb); + removeTagFromDimension(a->mesh, w, a->mesh->getDimension()); + a->mesh->destroyTag(w); + return imb[a->mesh->getDimension()]; +} + void preBalance(Adapt* a) { if (PCU_Comm_Peers()==1) return; Input* in = a->input; - if (in->shouldRunPreZoltan) + // First take care of user overrides. That is, if any of the three options + // is true, apply that balancer and return. + if (in->shouldRunPreZoltan) { runZoltan(a); - if (in->shouldRunPreZoltanRib) + return; + } + if (in->shouldRunPreZoltanRib) { runZoltan(a,apf::RIB); - if (in->shouldRunPreParma) + return; + } + if (in->shouldRunPreParma) { runParma(a); + return; + } + + // Then, take care of the case where all the options are set to false. + // That is, if the default values have not changed by the user. In + // this case, we apply the best possible balancer, if weighted imbalance + // is bigger than in->maximumImbalance + if ((!in->shouldRunPreZoltan) && + (!in->shouldRunPreZoltanRib) && + (!in->shouldRunPreParma) && + (estimateWeightedImbalance(a) > in->maximumImbalance)) { +#ifdef PUMI_HAS_ZOLTAN + // The parmetis multi-level graph partitioner memory usage grows + // significantly with process count beyond 16K processes + if (PCU_Comm_Peers() < MAX_ZOLTAN_GRAPH_RANKS) { + runZoltan(a); + return; + } + else { + runZoltan(a, apf::RIB); + return; + } +#else + runParma(a); + return; +#endif + } } void midBalance(Adapt* a) @@ -139,10 +184,39 @@ void midBalance(Adapt* a) if (PCU_Comm_Peers()==1) return; Input* in = a->input; - if (in->shouldRunMidZoltan) + // First take care of user overrides. That is, if any of the three options + // is true, apply that balancer and return. + if (in->shouldRunMidZoltan) { runZoltan(a); - if (in->shouldRunMidParma) + return; + } + if (in->shouldRunMidParma) { + runParma(a); + return; + } + // Then, take care of the case where all the options are set to false. + // That is, if the default values have not changed by the user. In + // this case, we apply the best possible balancer, if weighted imbalance + // is bigger than in->maximumImbalance + if ((!in->shouldRunMidZoltan) && + (!in->shouldRunMidParma) && + (estimateWeightedImbalance(a) > in->maximumImbalance)) { +#ifdef PUMI_HAS_ZOLTAN + // The parmetis multi-level graph partitioner memory usage grows + // significantly with process count beyond 16K processes + if (PCU_Comm_Peers() < MAX_ZOLTAN_GRAPH_RANKS) { + runZoltan(a); + return; + } + else { + runZoltan(a, apf::RIB); + return; + } +#else runParma(a); + return; +#endif + } } void postBalance(Adapt* a) @@ -150,13 +224,50 @@ void postBalance(Adapt* a) if (PCU_Comm_Peers()==1) return; Input* in = a->input; - if (in->shouldRunPostZoltan) + // First take care of user overrides. That is, if any of the three options + // is true, apply that balancer and return. + if (in->shouldRunPostZoltan) { runZoltan(a); - if (in->shouldRunPostZoltanRib) + printEntityImbalance(a->mesh); + return; + } + if (in->shouldRunPostZoltanRib) { runZoltan(a,apf::RIB); - if (in->shouldRunPostParma) + printEntityImbalance(a->mesh); + return; + } + if (in->shouldRunPostParma) { runParma(a); - printEntityImbalance(a->mesh); + printEntityImbalance(a->mesh); + return; + } + // Then, take care of the case where all the options are set to false. + // That is, if the default values have not changed by the user. In + // this case, we apply the best possible balancer, if weighted imbalance + // is bigger than in->maximumImbalance + if ((!in->shouldRunPostZoltan) && + (!in->shouldRunPostZoltanRib) && + (!in->shouldRunPostParma) && + (estimateWeightedImbalance(a) > in->maximumImbalance)) { +#ifdef PUMI_HAS_ZOLTAN + // The parmetis multi-level graph partitioner memory usage grows + // significantly with process count beyond 16K processes + if (PCU_Comm_Peers() < MAX_ZOLTAN_GRAPH_RANKS) { + runZoltan(a); + printEntityImbalance(a->mesh); + return; + } + else { + runZoltan(a, apf::RIB); + printEntityImbalance(a->mesh); + return; + } +#else + runParma(a); + printEntityImbalance(a->mesh); + return; +#endif + } } } diff --git a/ma/maDBG.cc b/ma/maDBG.cc index 233bb4a10..ccbd48218 100644 --- a/ma/maDBG.cc +++ b/ma/maDBG.cc @@ -10,6 +10,7 @@ #include "maDBG.h" #include "maShape.h" #include "maAdapt.h" +#include "maRefine.h" #include #include #include @@ -26,6 +27,7 @@ #include #include +static double PI = 3.14159265359; namespace ma_dbg { @@ -256,5 +258,177 @@ void createCavityMesh(ma::Adapt* a, createCavityMesh(a, tetsArray, prefix); } +static apf::Vector3 getPointOnEllipsoid( + apf::Vector3 center, + apf::Vector3 abc, + apf::Matrix3x3 rotation, + double scaleFactor, + double u, + double v) +{ + apf::Vector3 result; + result[0] = abc[0] * cos(u) * cos(v); + result[1] = abc[1] * cos(u) * sin(v); + result[2] = abc[2] * sin(u); + + result = result * scaleFactor; + + result = rotation * result + center; + return result; +} + +static void makeEllipsoid( + apf::Mesh2* msf, + apf::Mesh2* mesh, + apf::Field* sizes, + apf::Field* frames, + apf::MeshEntity* ent, + int node, + double scaleFactor, + int sampleSize[2]) +{ + // first get the coordinate at node location + apf::Vector3 xi; + apf::Vector3 center; + apf::FieldShape* fs = apf::getShape(sizes); + fs->getNodeXi(mesh->getType(ent), node, xi); + apf::MeshElement* me = apf::createMeshElement(mesh, ent); + apf::mapLocalToGlobal(me, xi, center); + apf::destroyMeshElement(me); + + + // second get the sizes and frames at node + apf::Vector3 abc; + apf::getVector(sizes, ent, node, abc); + + apf::Matrix3x3 rotation; + apf::getMatrix(frames, ent, node, rotation); + + + double U0 = 0.0; + double U1 = 2 * PI; + double V0 = -PI/2.; + double V1 = PI/2.; + int n = sampleSize[0]; + int m = sampleSize[1]; + double dU = (U1 - U0) / (n-1); + double dV = (V1 - V0) / (m-1); + + // make the array of vertex coordinates in the physical space + std::vector ps; + for (int j = 0; j < m; j++) { + for (int i = 0; i < n; i++) { + double u = U0 + i * dU; + double v = V0 + j * dV; + apf::Vector3 pt = getPointOnEllipsoid(center, abc, rotation, scaleFactor, u, v); + ps.push_back(pt); + } + } + // make the vertexes and set the coordinates using the array + std::vector vs; + for (size_t i = 0; i < ps.size(); i++) { + apf::MeshEntity* newVert = msf->createVert(0); + msf->setPoint(newVert, 0, ps[i]); + vs.push_back(newVert); + } + + PCU_ALWAYS_ASSERT(vs.size() == ps.size()); + + apf::MeshEntity* v[3]; + // make the lower/upper t elems + for (int i = 0; i < n-1; i++) { + for (int j = 0; j < m-1; j++) { + // upper triangle + v[0] = vs[(i + 0) + n * (j + 0)]; + v[1] = vs[(i + 0) + n * (j + 1)]; + v[2] = vs[(i + 1) + n * (j + 0)]; + apf::buildElement(msf, 0, apf::Mesh::TRIANGLE, v); + // upper triangle + v[0] = vs[(i + 0) + n * (j + 1)]; + v[1] = vs[(i + 1) + n * (j + 1)]; + v[2] = vs[(i + 1) + n * (j + 0)]; + apf::buildElement(msf, 0, apf::Mesh::TRIANGLE, v); + } + } +} + +void visualizeSizeField( + apf::Mesh2* m, + apf::Field* sizes, + apf::Field* frames, + int sampleSize[2], + double userScale, + const char* outputPrefix) +{ + // create the size-field visualization mesh + apf::Mesh2* msf = apf::makeEmptyMdsMesh(gmi_load(".null"), 2, false); + + apf::FieldShape* fs = apf::getShape(sizes); + int dim = m->getDimension(); + + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!fs->hasNodesIn(d)) continue; + it = m->begin(d); + while ( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = fs->countNodesOn(type); + for (int n = 0; n < non; n++) + makeEllipsoid(msf, m, sizes, frames, ent, n, userScale , sampleSize); + } + m->end(it); + } + + apf::writeVtkFiles(outputPrefix, msf); + + msf->destroyNative(); + apf::destroyMesh(msf); +} + +struct SplitByTag : public ma::Predicate +{ + SplitByTag(ma::Adapt* a_, int type_, int tag_) : + a(a_), type(type_), tag(tag_) {} + bool operator() (ma::Entity* e) { + ma::Mesh* m = a->mesh; + int mtype = m->getModelType(m->toModel(e)); + int mtag = m->getModelTag(m->toModel(e)); + if (mtype != type) return false; + if (mtag != tag ) return false; + return true; + } + ma::Adapt* a; + int type; + int tag; +}; + +void uniformAdaptByModelTag( + apf::Mesh2* m, + int mtype, + int mtag, + int level) +{ + ma::Input* in = ma::makeAdvanced(ma::configureUniformRefine(m, 0)); + ma::validateInput(in); + ma::Adapt* a = new ma::Adapt(in); + for(int i = 0; i < level; i++) { + SplitByTag p(a, mtype, mtag); + ma::markEntities(a, 1, p, ma::SPLIT, ma::NEED_NOT_SPLIT, + ma::DONT_SPLIT | ma::NEED_NOT_SPLIT); + PCU_ALWAYS_ASSERT(ma::checkFlagConsistency(a,1,ma::SPLIT)); + ma::Refine* r = a->refine; + ma::resetCollection(r); + ma::collectForTransfer(r); + ma::collectForMatching(r); + ma::addAllMarkedEdges(r); + ma::splitElements(r); + ma::processNewElements(r); + ma::destroySplitElements(r); + ma::forgetNewEntities(r); + } + delete(a); +} } diff --git a/ma/maDBG.h b/ma/maDBG.h index 8e1c7bc64..027a245fd 100644 --- a/ma/maDBG.h +++ b/ma/maDBG.h @@ -60,5 +60,17 @@ void createCavityMesh(ma::Adapt* a, ma::EntitySet& tets, const char* prefix); +void visualizeSizeField( + apf::Mesh2* m, + apf::Field* sizes, + apf::Field* frames, + int smapleSize[2], + double userScale, + const char* OutputPrefix); +void uniformAdaptByModelTag( + apf::Mesh2* m, + int mtype, + int mtag, + int level); } #endif diff --git a/ma/maFaceSplit.cc b/ma/maFaceSplit.cc index 0b3ac04fe..73f6da425 100644 --- a/ma/maFaceSplit.cc +++ b/ma/maFaceSplit.cc @@ -123,16 +123,22 @@ void FaceSplit::cancel() void FaceSplit::transfer() { Mesh* m = adapter->mesh; + int td; + td = adapter->shape->getTransferDimension(); + for (int d = td; d <= m->getDimension(); ++d) + for (size_t i = 0; i < toSplit[d].getSize(); ++i) + adapter->shape->onRefine(toSplit[d][i], newEntities[d][i]); + SolutionTransfer* st = adapter->solutionTransfer; - int td = st->getTransferDimension(); + td = st->getTransferDimension(); for (int d = td; d <= m->getDimension(); ++d) for (size_t i = 0; i < toSplit[d].getSize(); ++i) st->onRefine(toSplit[d][i], newEntities[d][i]); - td = adapter->shape->getTransferDimension(); + td = adapter->sizeField->getTransferDimension(); for (int d = td; d <= m->getDimension(); ++d) for (size_t i = 0; i < toSplit[d].getSize(); ++i) - adapter->shape->onRefine(toSplit[d][i], newEntities[d][i]); + adapter->sizeField->onRefine(toSplit[d][i], newEntities[d][i]); } void FaceSplit::destroyOldElements() diff --git a/ma/maInput.cc b/ma/maInput.cc index e384a8b26..89d7401d8 100644 --- a/ma/maInput.cc +++ b/ma/maInput.cc @@ -8,22 +8,16 @@ *******************************************************************************/ #include "maInput.h" +#include "maAdapt.h" #include #include #include +#include #include #include namespace ma { -Input::~Input() -{ - if (ownsSizeField) - delete sizeField; - if (ownsSolutionTransfer) - delete solutionTransfer; -} - void setDefaultValues(Input* in) { in->ownsSizeField = true; @@ -73,11 +67,25 @@ void setDefaultValues(Input* in) void rejectInput(const char* str) { + if (PCU_Comm_Self() != 0) + return; lion_eprint(1,"MeshAdapt input error:\n"); lion_eprint(1,"%s\n",str); abort(); } +// if more than 1 option is true return true +static bool moreThanOneOptionIsTrue(bool op1, bool op2, bool op3) +{ + int cnt = 0; + if (op1) cnt++; + if (op2) cnt++; + if (op3) cnt++; + if (cnt > 1) + return true; + return false; +} + void validateInput(Input* in) { if ( ! in->sizeField) @@ -120,6 +128,42 @@ void validateInput(Input* in) rejectInput("maximum imbalance less than 1.0"); if (in->maximumEdgeRatio < 1.0) rejectInput("maximum tet edge ratio less than one"); + if (moreThanOneOptionIsTrue( + in->shouldRunPreZoltan, + in->shouldRunPreZoltanRib, + in->shouldRunPreParma)) + rejectInput("only one of Zoltan, ZoltanRib, and Parma PreBalance options can be set to true!"); + if (moreThanOneOptionIsTrue( + in->shouldRunPostZoltan, + in->shouldRunPostZoltanRib, + in->shouldRunPostParma)) + rejectInput("only one of Zoltan, ZoltanRib, and Parma PostBalance options can be set to true!"); + if (in->shouldRunMidZoltan && in->shouldRunMidParma) + rejectInput("only one of Zoltan and Parma MidBalance options can be set to true!"); +#ifndef PUMI_HAS_ZOLTAN + if (in->shouldRunPreZoltan || + in->shouldRunPreZoltanRib || + in->shouldRunMidZoltan) + rejectInput("core is not compiled with Zoltan. Use a different balancer or compile core with ENABLE_ZOLTAN=ON!"); +#endif +} + +static void updateMaxIterBasedOnSize(Mesh* m, Input* in) +{ + // number of iterations + double maxMetricLength = getMaximumEdgeLength(m, in->sizeField); + int iter = std::ceil(std::log2(maxMetricLength)); + if (iter >= 10) { + print("ma::configure: Based on requested sizefield, MeshAdapt requires at least %d iterations,\n" + " which is equal to or larger than the maximum of 10 allowed.\n" + " Setting the number of iteration to 10!", iter); + in->maximumIterations = 10; + } + else { + print("ma::configure: Based on requested sizefield, MeshAdapt requires at least %d iterations.\n" + " Setting the number of iteration to %d!", iter, iter+1); + in->maximumIterations = iter+1; + } } void setSolutionTransfer(Input* in, SolutionTransfer* s) @@ -136,7 +180,7 @@ void setSolutionTransfer(Input* in, SolutionTransfer* s) } } -Input* configure( +static Input* configure( Mesh* m, SolutionTransfer* s) { @@ -147,7 +191,7 @@ Input* configure( return in; } -Input* configure( +const Input* configure( Mesh* m, AnisotropicFunction* f, SolutionTransfer* s, @@ -160,30 +204,33 @@ Input* configure( solution transfer */ Input* in = configure(m,s); in->sizeField = makeSizeField(m, f, logInterpolation); + updateMaxIterBasedOnSize(m, in); return in; } -Input* configure( +const Input* configure( Mesh* m, IsotropicFunction* f, SolutionTransfer* s) { Input* in = configure(m,s); in->sizeField = makeSizeField(m, f); + updateMaxIterBasedOnSize(m, in); return in; } -Input* configure( +const Input* configure( Mesh* m, apf::Field* f, SolutionTransfer* s) { Input* in = configure(m,s); in->sizeField = makeSizeField(m, f); + updateMaxIterBasedOnSize(m, in); return in; } -Input* configure( +const Input* configure( Mesh* m, apf::Field* sizes, apf::Field* frames, @@ -192,10 +239,11 @@ Input* configure( { Input* in = configure(m,s); in->sizeField = makeSizeField(m, sizes, frames, logInterpolation); + updateMaxIterBasedOnSize(m, in); return in; } -Input* configureUniformRefine(Mesh* m, int n, SolutionTransfer* s) +const Input* configureUniformRefine(Mesh* m, int n, SolutionTransfer* s) { Input* in = configure(m,s); in->sizeField = new UniformRefiner(m); @@ -205,15 +253,15 @@ Input* configureUniformRefine(Mesh* m, int n, SolutionTransfer* s) return in; } -Input* configureMatching(Mesh* m, int n, SolutionTransfer* s) +const Input* configureMatching(Mesh* m, int n, SolutionTransfer* s) { - Input* in = configureUniformRefine(m,n,s); + Input* in = makeAdvanced(configureUniformRefine(m,n,s)); in->shouldHandleMatching = true; in->shouldFixShape = false; return in; } -Input* configureIdentity(Mesh* m, SizeField* f, SolutionTransfer* s) +const Input* configureIdentity(Mesh* m, SizeField* f, SolutionTransfer* s) { Input* in = configure(m,s); if (f) @@ -232,4 +280,11 @@ Input* configureIdentity(Mesh* m, SizeField* f, SolutionTransfer* s) return in; } +Input* makeAdvanced(const Input* in) +{ + Input* in2 = new Input(*in); + delete in; + return in2; +} + } diff --git a/ma/maInput.h b/ma/maInput.h index b154f16e3..b6829c0c6 100644 --- a/ma/maInput.h +++ b/ma/maInput.h @@ -34,14 +34,20 @@ typedef ShapeHandler* (*ShapeHandlerFunction)(Adapt* a); class Input { public: - ~Input(); + ~Input() {} + Input() {} // default empty c-tor + Input(const Input& in) = default; // copy c-tor Mesh* mesh; SizeField* sizeField; bool ownsSizeField; SolutionTransfer* solutionTransfer; bool ownsSolutionTransfer; ShapeHandlerFunction shapeHandler; -/** \brief number of refine/coarsen iterations to run (default 3) */ +/** \brief number of refine/coarsen iterations to run (default 3) + \details this value will be set to the minimum required iterations + inside the call to ma::configure in cases where there is a size information. + Users can override this by setting in->maximumIterations after the + call to ma::configure and before the call to ma::adapt routine.*/ int maximumIterations; /** \brief whether to perform the collapse step */ bool shouldCoarsen; @@ -49,10 +55,10 @@ class Input \details requires modeler support, see gmi_can_eval */ bool shouldSnap; /** \brief whether to transfer parametric coordinates - \details requires modeler support, see gmi_reparam */ + \details requires modeler support, see gmi_reparam */ bool shouldTransferParametric; /** \brief whether to transfer to the parametric coords of the closest point - \details requires modeler support, see gmi_closest_point */ + \details requires modeler support, see gmi_closest_point */ bool shouldTransferToClosestPoint; /** \brief whether to update matched entity info (limited support) */ bool shouldHandleMatching; @@ -63,35 +69,51 @@ class Input /** \brief whether to print the worst shape quality */ bool shouldPrintQuality; /** \brief minimum desired mean ratio cubed for simplex elements - \details a different measure is used for curved elements */ + \details a different measure is used for curved elements */ double goodQuality; /** \brief whether to check the quality of split elems in DoubleSplitsCollapse - (default false) */ + (default false) */ double shouldCheckQualityForDoubleSplits; /** \brief minimum valid mean ratio cubed for simplex elements (default 1e-10) - \details used to define inside-out tetrahedra. - a different measure is used for curved elements */ + \details used to define inside-out tetrahedra. + a different measure is used for curved elements */ double validQuality; /** \brief imbalance target for all load balancing tools (default 1.10) */ double maximumImbalance; -/** \brief whether to run zoltan predictive load balancing (default false) */ +/** \brief whether to run zoltan predictive load balancing (default false) + \details if this and all the other PreBalance options are false, pre-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunPreZoltan; -/** \brief whether to run zoltan predictive load balancing using RIB (default false) */ +/** \brief whether to run zoltan predictive load balancing using RIB (default false) + \details if this and all the other PreBalance options are false, pre-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunPreZoltanRib; -/** \brief whether to run parma predictive load balancing (default false) */ +/** \brief whether to run parma predictive load balancing (default false) + \details if this and all the other PreBalance options are false, pre-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunPreParma; -/** \brief whether to run zoltan during adaptation (default false) */ +/** \brief whether to run zoltan during adaptation (default false) + \details if this and all the other MidBalance options are false, mid-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunMidZoltan; -/** \brief whether to run parma during adaptation (default false)*/ +/** \brief whether to run parma during adaptation (default false) + \details if this and all the other MidBalance options are false, mid-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunMidParma; -/** \brief whether to run zoltan after adapting (default false) */ +/** \brief whether to run zoltan after adapting (default false) + \details if this and all the other PostBalance options are false, post-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunPostZoltan; -/** \brief whether to run zoltan RIB after adapting (default false) */ +/** \brief whether to run zoltan RIB after adapting (default false) + \details if this and all the other PostBalance options are false, post-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunPostZoltanRib; -/** \brief whether to run parma after adapting (default false) */ +/** \brief whether to run parma after adapting (default false) + \details if this and all the other PostBalance options are false, post-balancing + occurs only if the imbalance is greater than in->maximumImbalance */ bool shouldRunPostParma; /** \brief the ratio between longest and shortest edges that differentiates a - "short edge" element from a "large angle" element. */ + "short edge" element from a "large angle" element. */ double maximumEdgeRatio; /** \brief whether to tetrahedronize the boundary layer (default false) */ bool shouldTurnLayerToTets; @@ -115,7 +137,7 @@ class Input \param s if non-zero, use that to transfer all fields. otherwise, transfer any associated fields with default algorithms \param logInterpolation if true uses logarithmic interpolation */ -Input* configure( +const Input* configure( Mesh* m, AnisotropicFunction* f, SolutionTransfer* s=0, @@ -123,7 +145,7 @@ Input* configure( /** \brief generate a configuration based on an isotropic function \param s if non-zero, use that to transfer all fields. otherwise, transfer any associated fields with default algorithms */ -Input* configure( +const Input* configure( Mesh* m, IsotropicFunction* f, SolutionTransfer* s=0); @@ -135,7 +157,7 @@ Input* configure( \param s if non-zero, use that to transfer all fields. otherwise, transfer any associated fields with default algorithms \param logInterpolation if true uses logarithmic interpolation */ -Input* configure( +const Input* configure( Mesh* m, apf::Field* sizes, apf::Field* frames, @@ -145,21 +167,24 @@ Input* configure( \param size a scalar field of desired element size \param s if non-zero, use that to transfer all fields. otherwise, transfer any associated fields with default algorithms */ -Input* configure( +const Input* configure( Mesh* m, apf::Field* size, SolutionTransfer* s=0); /** \brief generate a uniform refinement configuration */ -Input* configureUniformRefine(Mesh* m, int n=1, SolutionTransfer* s=0); +const Input* configureUniformRefine(Mesh* m, int n=1, SolutionTransfer* s=0); /** \brief generate a matched uniform refinement configuration */ -Input* configureMatching(Mesh* m, int n=1, SolutionTransfer* s=0); +const Input* configureMatching(Mesh* m, int n=1, SolutionTransfer* s=0); /** \brief generate a no-op configuration */ -Input* configureIdentity(Mesh* m, SizeField* f=0, SolutionTransfer* s=0); +const Input* configureIdentity(Mesh* m, SizeField* f=0, SolutionTransfer* s=0); /** \brief used internally, but users can call this if they want */ void validateInput(Input* in); +/** \brief creates a new non-constant Input for advanced users */ +Input* makeAdvanced(const Input* in); + } #endif diff --git a/ma/maLayerCoarsen.cc b/ma/maLayerCoarsen.cc index b1a8acad0..12feced3d 100644 --- a/ma/maLayerCoarsen.cc +++ b/ma/maLayerCoarsen.cc @@ -190,7 +190,7 @@ static void migrateForLayerCollapse(Adapt* a, int d, int round) } void localizeLayerStacks(Mesh* m) { - Input* in = configureIdentity(m); + Input* in = makeAdvanced(configureIdentity(m)); Adapt* a = new Adapt(in); //mark layer bases findLayerBase(a); @@ -206,6 +206,11 @@ void localizeLayerStacks(Mesh* m) { for (int d = 1; d < m->getDimension(); ++d) migrateForLayerCollapse(a,d,round); delete a; + // cleanup input object and associated sizefield and solutiontransfer objects + if (in->ownsSizeField) + delete in->sizeField; + if (in->ownsSolutionTransfer) + delete in->solutionTransfer; delete in; } diff --git a/ma/maMap.cc b/ma/maMap.cc index 7aa4bd80f..b247e6863 100644 --- a/ma/maMap.cc +++ b/ma/maMap.cc @@ -9,6 +9,8 @@ *******************************************************************************/ #include "maMap.h" +#include +#include #include namespace ma { @@ -100,4 +102,57 @@ double getInsideness(apf::Mesh* m, Entity* e, Vector const& xi) return 0; } +Vector curvedElemInvMap( + apf::Mesh* m, + Entity* e, + const Vector& p, + const double tol, + const int maxIter) +{ + int iter = 0; + // put the initial guess at the center of the elements + Vector xi0; + int type = m->getType(e); + if (type == apf::Mesh::VERTEX) + xi0 = Vector(0., 0., 0.); + else if (type == apf::Mesh::EDGE) + xi0 = Vector(0., 0., 0.); + else if (type == apf::Mesh::TRIANGLE) + xi0 = Vector(1./3., 1./3., 1./3.); + else if (type == apf::Mesh::TET) + xi0 = Vector(1./4., 1./4., 1./4.); + else + PCU_ALWAYS_ASSERT_VERBOSE(0, "unsupported type!"); + + + apf::MeshElement* me = apf::createMeshElement(m, e); + + Matrix Jinv; + Vector x; + Vector xi_new = xi0; + Vector xi_old = xi0; + double err = 1.e16; + + // initial iteration + apf::getJacobianInv(me, xi_old, Jinv); + Jinv = apf::transpose(Jinv); + apf::mapLocalToGlobal(me, xi_old, x); + xi_new = xi_old - Jinv*(x-p); + err = (xi_new - xi_old)*(xi_new - xi_old); + + while (err > tol) { + iter++; + if (iter > maxIter) break; + xi_old = xi_new; + apf::getJacobianInv(me, xi_old, Jinv); + Jinv = apf::transpose(Jinv); + apf::mapLocalToGlobal(me, xi_old, x); + xi_new = xi_old - Jinv*(x-p); + err = (xi_new - xi_old)*(xi_new - xi_old); + } + + apf::destroyMeshElement(me); + return xi_new; +} + } diff --git a/ma/maMap.h b/ma/maMap.h index c26f6551c..7e9047c21 100644 --- a/ma/maMap.h +++ b/ma/maMap.h @@ -20,6 +20,13 @@ Affine getMap(apf::Mesh* m, Entity* e); double getInsideness(apf::Mesh* m, Entity* e, Vector const& xi); +Vector curvedElemInvMap( + apf::Mesh* m, + Entity* e, + const Vector& p, + const double tol = 1.e-16, + const int maxIter = 10); + } #endif diff --git a/ma/maMesh.cc b/ma/maMesh.cc index f5e5ffc7e..7d9b5876f 100644 --- a/ma/maMesh.cc +++ b/ma/maMesh.cc @@ -123,6 +123,7 @@ int findTetRotation(Mesh* m, Entity* tet, Entity** v) that match the original set of vertices */ void unrotateTetXi(Vector& xi, int rotation) { + PCU_ALWAYS_ASSERT(rotation >= 0 && rotation < 12); double a[4]; a[0] = 1-xi[0]-xi[1]-xi[2]; a[1] = xi[0]; a[2] = xi[1]; a[3] = xi[2]; int const* originalIndexOf = tet_rotation[rotation]; @@ -132,6 +133,23 @@ void unrotateTetXi(Vector& xi, int rotation) xi[0] = b[1]; xi[1] = b[2]; xi[2] = b[3]; } +/* given a set of element-local coordinates computed + based on the original set of vertices, this function + takes the rotation code and gives the coordinates + that match the coordinates computed based on a rotated + set of vertices */ +void rotateTetXi(Vector& xi, int rotation) +{ + PCU_ALWAYS_ASSERT(rotation >= 0 && rotation < 12); + double a[4]; + a[0] = 1-xi[0]-xi[1]-xi[2]; a[1] = xi[0]; a[2] = xi[1]; a[3] = xi[2]; + int const* inverseIndexOf = tet_inv_rotation[rotation]; + double b[4]; + for (int i = 0; i < 4; i++) + b[ inverseIndexOf[i] ] = a[i]; + xi[0] = b[1]; xi[1] = b[2]; xi[2] = b[3]; +} + void rotateOct(Entity** iv, int n, Entity** ov) { for (int i=0; i < 6; ++i) diff --git a/ma/maMesh.h b/ma/maMesh.h index 724a9a92b..f5409d2b8 100644 --- a/ma/maMesh.h +++ b/ma/maMesh.h @@ -60,6 +60,7 @@ void rotateEntity(apf::Mesh* m, Entity* e, int n, Entity** v); int findTetRotation(Mesh* m, Entity* tet, Entity** v); void unrotateTetXi(Vector& xi, int rotation); +void rotateTetXi(Vector& xi, int rotation); void rotateOct(Entity** iv, int n, Entity** ov); diff --git a/ma/maRefine.cc b/ma/maRefine.cc index 4325186fb..df7f5c0ed 100644 --- a/ma/maRefine.cc +++ b/ma/maRefine.cc @@ -344,15 +344,26 @@ void transferElements(Refine* r) { Adapt* a = r->adapt; Mesh* m = a->mesh; - SolutionTransfer* st = a->solutionTransfer; - int td = st->getTransferDimension(); - for (int d = td; d <= m->getDimension(); ++d) - for (size_t i=0; i < r->toSplit[d].getSize(); ++i) - st->onRefine(r->toSplit[d][i],r->newEntities[d][i]); - td = a->shape->getTransferDimension(); - for (int d = td; d <= m->getDimension(); ++d) - for (size_t i=0; i < r->toSplit[d].getSize(); ++i) - a->shape->onRefine(r->toSplit[d][i],r->newEntities[d][i]); + { // first take care of the coordinates + int td = a->shape->getTransferDimension(); + for (int d = td; d <= m->getDimension(); ++d) + for (size_t i=0; i < r->toSplit[d].getSize(); ++i) + a->shape->onRefine(r->toSplit[d][i],r->newEntities[d][i]); + } + { // then take care of the fields + SolutionTransfer* st = a->solutionTransfer; + int td = st->getTransferDimension(); + for (int d = td; d <= m->getDimension(); ++d) + for (size_t i=0; i < r->toSplit[d].getSize(); ++i) + st->onRefine(r->toSplit[d][i],r->newEntities[d][i]); + } + { + SizeField* sf = a->sizeField; + int td = sf->getTransferDimension(); + for (int d = td; d <= m->getDimension(); ++d) + for (size_t i=0; i < r->toSplit[d].getSize(); ++i) + sf->onRefine(r->toSplit[d][i],r->newEntities[d][i]); + } } void forgetNewEntities(Refine* r) diff --git a/ma/maShape.cc b/ma/maShape.cc index 458c9f4eb..c3479a07c 100644 --- a/ma/maShape.cc +++ b/ma/maShape.cc @@ -141,12 +141,10 @@ void unMarkBadQuality(Adapt* a) Mesh* m = a->mesh; Iterator* it; Entity* e; - int count = 0; it = m->begin(m->getDimension()); while ((e = m->iterate(it))) { if (getFlag(a, e, ma::BAD_QUALITY)) clearFlag(a, e, ma::BAD_QUALITY); - count++; } m->end(it); } @@ -652,6 +650,7 @@ class LargeAngleTriFixer : public Operator apf::MeshElement* me = apf::createMeshElement(mesh, tri); Vector center(1./3.,1./3.,1./3.); sf->getTransform(me,center,Q); + apf::destroyMeshElement(me); // pick the edge opposite to the largest angle (in metric) for swap Entity* edges[3]; diff --git a/ma/maSize.cc b/ma/maSize.cc index c0fab50c7..1dea70eab 100644 --- a/ma/maSize.cc +++ b/ma/maSize.cc @@ -9,6 +9,7 @@ *******************************************************************************/ #include #include "maSize.h" +#include "maSolutionTransferHelper.h" #include "apfMatrix.h" #include #include @@ -20,6 +21,32 @@ SizeField::~SizeField() { } +void SizeField::onRefine(Entity*, EntityArray&) +{ + PCU_ALWAYS_ASSERT_VERBOSE(0, + "unimplemented onRefine was called for a size-field!"); +} + +void SizeField::onCavity(EntityArray&, EntityArray&) +{ + PCU_ALWAYS_ASSERT_VERBOSE(0, + "unimplemented onCavity was called for a size-field!"); +} + +int SizeField::getTransferDimension() +{ + // By default there should be no size_field transfer. + // This is used in a loop to get the lowest dimension + // entities that require transfer. So something bigger + // than mesh dimension will ignore that loop + return 4; +} + +bool SizeField::hasNodesOn(int) +{ + return false; +} + IdentitySizeField::IdentitySizeField(Mesh* m): mesh(m) { @@ -96,14 +123,14 @@ static void orthogonalizeR(Matrix& R) static void orthogonalEigenDecompForSymmetricMatrix(Matrix const& A, Vector& v, Matrix& R) { - /* here we assume A to be real symmetric 3x3 matrix, + /* here we assume A to be real symmetric 3x3 matrix, * we should be able to get 3 orthogonal eigen vectors * we also normalize the eigen vectors */ double eigenValues[3]; Vector eigenVectors[3]; - + apf::eigen(A, eigenVectors, eigenValues); - + Matrix RT(eigenVectors); // eigen vectors are stored in the rows of RT RT[0] = RT[0].normalize(); @@ -132,8 +159,8 @@ static double parentMeasure[apf::Mesh::TYPES] = class SizeFieldIntegrator : public apf::Integrator { public: - SizeFieldIntegrator(SizeField* sF): - Integrator(2), + SizeFieldIntegrator(SizeField* sF, int order): + Integrator(order), measurement(0), sizeField(sF), meshElement(0), @@ -181,7 +208,8 @@ struct MetricSizeField : public SizeField { double measure(Entity* e) { - SizeFieldIntegrator sFI(this); + SizeFieldIntegrator sFI(this, + std::max(mesh->getShape()->getOrder(), order)+1); apf::MeshElement* me = apf::createMeshElement(mesh, e); sFI.process(me); apf::destroyMeshElement(me); @@ -201,6 +229,7 @@ struct MetricSizeField : public SizeField return measure(e) / parentMeasure[mesh->getType(e)]; } Mesh* mesh; + int order; // this is the underlying sizefield order (default 1) }; AnisotropicFunction::~AnisotropicFunction() @@ -337,6 +366,8 @@ struct AnisoSizeField : public MetricSizeField { AnisoSizeField() { + mesh = 0; + order = 1; } AnisoSizeField(Mesh* m, AnisotropicFunction* f): bothEval(f), @@ -344,6 +375,7 @@ struct AnisoSizeField : public MetricSizeField frameEval(&bothEval) { mesh = m; + order = 1; hField = apf::createUserField(m, "ma_sizes", apf::VECTOR, apf::getLagrange(1), &sizesEval); rField = apf::createUserField(m, "ma_frame", apf::MATRIX, @@ -357,6 +389,7 @@ struct AnisoSizeField : public MetricSizeField void init(Mesh* m, apf::Field* sizes, apf::Field* frames) { mesh = m; + order = apf::getShape(sizes)->getOrder(); hField = sizes; rField = frames; } @@ -424,11 +457,14 @@ struct LogAnisoSizeField : public MetricSizeField { LogAnisoSizeField() { + mesh = 0; + order = 1; } LogAnisoSizeField(Mesh* m, AnisotropicFunction* f): logMEval(f) { mesh = m; + order = 1; logMField = apf::createUserField(m, "ma_logM", apf::MATRIX, apf::getLagrange(1), &logMEval); } @@ -439,20 +475,34 @@ struct LogAnisoSizeField : public MetricSizeField void init(Mesh* m, apf::Field* sizes, apf::Field* frames) { mesh = m; - logMField = apf::createField(m, "ma_logM", apf::MATRIX, apf::getLagrange(1)); - Entity* v; - Iterator* it = m->begin(0); - while ( (v = m->iterate(it)) ) { - Vector h; - Matrix f; - apf::getVector(sizes, v, 0, h); - apf::getMatrix(frames, v, 0, f); - Vector s(log(1/h[0]/h[0]), log(1/h[1]/h[1]), log(1/h[2]/h[2])); - Matrix S(s[0], 0 , 0, - 0 , s[1], 0, - 0 , 0 , s[2]); - apf::setMatrix(logMField, v, 0, f * S * transpose(f)); + order = apf::getShape(sizes)->getOrder(); + logMField = apf::createField(m, "ma_logM", apf::MATRIX, + apf::getShape(sizes)); + int dim = m->getDimension(); + Entity* ent; + Iterator* it; + for (int d = 0; d <= dim; d++) { + if (!apf::getShape(logMField)->countNodesOn(apf::Mesh::simplexTypes[d])) + continue; + it = m->begin(d); + while( (ent = m->iterate(it)) ){ + int type = m->getType(ent); + int non = apf::getShape(logMField)->countNodesOn(type); + for (int i = 0; i < non; i++) { + Vector h; + Matrix f; + apf::getVector(sizes, ent, i, h); + apf::getMatrix(frames, ent, i, f); + Vector s(log(1/h[0]/h[0]), log(1/h[1]/h[1]), log(1/h[2]/h[2])); + Matrix S(s[0], 0 , 0, + 0 , s[1], 0, + 0 , 0 , s[2]); + apf::setMatrix(logMField, ent, i, f * S * transpose(f)); + } + } + m->end(it); } + fieldVal.allocate(apf::countComponents(logMField)); } void getTransform( apf::MeshElement* me, @@ -497,6 +547,34 @@ struct LogAnisoSizeField : public MetricSizeField 0,value,0, 0,0,value)); } + void onRefine( + Entity* parent, + EntityArray& newEntities) + { + transfer(logMField, &(fieldVal[0]), 1, &parent, newEntities); + } + void onCavity( + EntityArray& oldElements, + EntityArray& newEntities) + { + transfer(logMField, &(fieldVal[0]), + oldElements.getSize(), &(oldElements[0]), newEntities); + } + int getTransferDimension() + { + int transferDimension = 4; + for (int d = 1; d <=3; d++) + if (hasNodesOn(d)) { + transferDimension = d; + break; + } + return transferDimension; + } + bool hasNodesOn(int dimension) + { + return apf::getShape(logMField)->hasNodesIn(dimension); + } + apf::NewArray fieldVal; apf::Field* logMField; LogMEval logMEval; }; diff --git a/ma/maSize.h b/ma/maSize.h index f3daec934..b48a03cfa 100644 --- a/ma/maSize.h +++ b/ma/maSize.h @@ -36,6 +36,14 @@ class SizeField Vector const& xi, Matrix& t) = 0; virtual double getWeight(Entity* e) = 0; + virtual void onRefine( + Entity* parent, + EntityArray& newEntities); + virtual void onCavity( + EntityArray& oldElements, + EntityArray& newEntities); + virtual int getTransferDimension(); + virtual bool hasNodesOn(int dimension); }; struct IdentitySizeField : public SizeField @@ -54,6 +62,7 @@ struct IdentitySizeField : public SizeField Matrix& t); double getWeight(Entity*); Mesh* mesh; + }; struct UniformRefiner : public IdentitySizeField diff --git a/ma/maSnap.cc b/ma/maSnap.cc index 1e92b4231..195dca005 100644 --- a/ma/maSnap.cc +++ b/ma/maSnap.cc @@ -66,7 +66,7 @@ static size_t isSurfUnderlyingFaceDegenerate( param[periodicAxes] = candidatePeriodicParam; param[degenAxes] = candidateDegenParam; Vector p(param[0], param[1], 0.0); - m->getFirstDerivative(g, param, uTan, vTan); + m->getFirstDerivative(g, p, uTan, vTan); double uTanSize = uTan.getLength(); double vTanSize = vTan.getLength(); #ifdef HAVE_CAPSTONE @@ -220,7 +220,12 @@ static Vector getInvSphere(const Vector& x) double phi = atan2(x[1], x[0]); if (phi < 0) phi = phi + 2 * M_PI; - double the = acos(x[2]); + // the following 3 lines are to avoid errors + // caused by floating point operations + double x2 = x[2]; + if (x2 > 1.0) x2 = 1.0; + if (x2 < -1.0) x2 = -1.0; + double the = acos(x2); Vector res(phi, the, 0.0); return res; } diff --git a/ma/maSolutionTransfer.cc b/ma/maSolutionTransfer.cc index 89b564e82..bf8c0d6c7 100644 --- a/ma/maSolutionTransfer.cc +++ b/ma/maSolutionTransfer.cc @@ -8,8 +8,7 @@ *******************************************************************************/ #include "maSolutionTransfer.h" -#include "maAffine.h" -#include "maMap.h" +#include "maSolutionTransferHelper.h" #include #include #include @@ -39,18 +38,6 @@ void SolutionTransfer::onCavity( { } -static int getMinimumDimension(apf::FieldShape* s) -{ - int transferDimension = 4; - for (int d=1; d <= 3; ++d) - if (s->hasNodesIn(d)) - { - transferDimension = d; - break; - } - return transferDimension; -} - int SolutionTransfer::getTransferDimension() { int transferDimension = 4; @@ -112,89 +99,18 @@ class CavityTransfer : public FieldTransfer { minDim = getMinimumDimension(getShape(f)); } - void transferToNodeIn( - apf::Element* elem, - apf::Node const& node, - Vector const& elemXi) - { - apf::getComponents(elem,elemXi,&(value[0])); - apf::setComponents(field,node.entity,node.node,&(value[0])); - } - int getBestElement( - int n, - apf::Element** elems, - Affine* elemInvMaps, - Vector const& point, - Vector& bestXi) - { - double bestValue = -DBL_MAX; - int bestI = 0; - for (int i = 0; i < n; ++i) - { - Vector xi = elemInvMaps[i] * point; - double value = getInsideness(mesh,apf::getMeshEntity(elems[i]),xi); - if (value > bestValue) - { - bestValue = value; - bestI = i; - bestXi = xi; - } - } - return bestI; - } - void transferToNode( - int n, - apf::Element** elems, - Affine* elemInvMaps, - apf::Node const& node) - { - Vector xi; - shape->getNodeXi(mesh->getType(node.entity),node.node,xi); - Affine childMap = getMap(mesh,node.entity); - Vector point = childMap * xi; - Vector elemXi; - int i = getBestElement(n,elems,elemInvMaps,point,elemXi); - transferToNodeIn(elems[i],node,elemXi); - } - void transfer( - int n, - Entity** cavity, - EntityArray& newEntities) - { - if (getDimension(mesh, cavity[0]) < minDim) - return; - apf::NewArray elems(n); - for (int i = 0; i < n; ++i) - elems[i] = apf::createElement(field,cavity[i]); - apf::NewArray elemInvMaps(n); - for (int i = 0; i < n; ++i) - elemInvMaps[i] = invert(getMap(mesh,cavity[i])); - for (size_t i = 0; i < newEntities.getSize(); ++i) - { - int type = mesh->getType(newEntities[i]); - if (type == apf::Mesh::VERTEX) - continue; //vertices will have been handled specially beforehand - int nnodes = shape->countNodesOn(type); - for (int j = 0; j < nnodes; ++j) - { - apf::Node node(newEntities[i],j); - transferToNode(n,&(elems[0]),&(elemInvMaps[0]),node); - } - } - for (int i = 0; i < n; ++i) - apf::destroyElement(elems[i]); - } virtual void onRefine( Entity* parent, EntityArray& newEntities) { - transfer(1,&parent,newEntities); + transfer(field, &(value[0]), 1,&parent,newEntities); } virtual void onCavity( EntityArray& oldElements, EntityArray& newEntities) { - transfer(oldElements.getSize(),&(oldElements[0]),newEntities); + transfer(field, &(value[0]), + oldElements.getSize(),&(oldElements[0]),newEntities); } private: int minDim; diff --git a/ma/maSolutionTransferHelper.cc b/ma/maSolutionTransferHelper.cc new file mode 100644 index 000000000..d799d9b1b --- /dev/null +++ b/ma/maSolutionTransferHelper.cc @@ -0,0 +1,123 @@ + +/******************************************************************************* + +opyright 2013 Scientific Computation Research Center, + Rensselaer Polytechnic Institute. All rights reserved. + + The LICENSE file included with this distribution describes the terms + of the SCOREC Non-Commercial License this program is distributed under. + +*******************************************************************************/ +#include "maSolutionTransferHelper.h" +#include "maAffine.h" +#include "maMap.h" +#include +#include +#include + +namespace ma { + +int getMinimumDimension(apf::FieldShape* s) +{ + int transferDimension = 4; + for (int d=1; d <= 3; ++d) + if (s->hasNodesIn(d)) + { + transferDimension = d; + break; + } + return transferDimension; +} + +int getBestElement( + apf::Mesh* mesh, + int n, + apf::Element** elems, + Affine* elemInvMaps, + Vector const& point, + Vector& bestXi) +{ + double bestValue = -DBL_MAX; + int bestI = 0; + for (int i = 0; i < n; ++i) + { + Vector xi; + if (mesh->getShape()->getOrder() == 1) + xi = elemInvMaps[i] * point; + else + xi = curvedElemInvMap(mesh, apf::getMeshEntity(elems[i]), point); + double value = getInsideness(mesh,apf::getMeshEntity(elems[i]),xi); + if (value > bestValue) + { + bestValue = value; + bestI = i; + bestXi = xi; + } + } + return bestI; +} + +void transferToNode( + apf::Field* field, + double *value, + int n, + apf::Element** elems, + Affine* elemInvMaps, + apf::Node const& node) +{ + apf::Mesh* mesh = apf::getMesh(field); + // first get the physical coordinate of the node + Vector xi; + Vector point; + apf::FieldShape* shape = apf::getShape(field); + shape->getNodeXi(mesh->getType(node.entity),node.node,xi); + if (mesh->getShape()->getOrder() == 1) { // if linear mesh use the affine + Affine childMap = getMap(mesh,node.entity); + point = childMap * xi; + } + else { // else inquire the physical coordinated of local coordinate xi + apf::MeshElement* me = apf::createMeshElement(mesh,node.entity); + apf::mapLocalToGlobal(me, xi, point); + apf::destroyMeshElement(me); + } + Vector elemXi; + int i = getBestElement(mesh,n,elems,elemInvMaps,point,elemXi); + apf::getComponents(elems[i],elemXi,value); + apf::setComponents(field,node.entity,node.node,value); +} + +void transfer( + apf::Field* field, + double *value, + int n, // size of the cavity + Entity** cavity, + EntityArray& newEntities) +{ + apf::Mesh* mesh = apf::getMesh(field); + apf::FieldShape* shape = apf::getShape(field); + int minDim = getMinimumDimension(shape); + if (getDimension(mesh, cavity[0]) < minDim) + return; + apf::NewArray elems(n); + for (int i = 0; i < n; ++i) + elems[i] = apf::createElement(field,cavity[i]); + apf::NewArray elemInvMaps(n); + for (int i = 0; i < n; ++i) + elemInvMaps[i] = invert(getMap(mesh,cavity[i])); + for (size_t i = 0; i < newEntities.getSize(); ++i) + { + int type = mesh->getType(newEntities[i]); + if (type == apf::Mesh::VERTEX) + continue; //vertices will have been handled specially beforehand + int nnodes = shape->countNodesOn(type); + for (int j = 0; j < nnodes; ++j) + { + apf::Node node(newEntities[i],j); + transferToNode(field, value, + n,&(elems[0]),&(elemInvMaps[0]),node); + } + } + for (int i = 0; i < n; ++i) + apf::destroyElement(elems[i]); +} +} diff --git a/ma/maSolutionTransferHelper.h b/ma/maSolutionTransferHelper.h new file mode 100644 index 000000000..b1834a799 --- /dev/null +++ b/ma/maSolutionTransferHelper.h @@ -0,0 +1,31 @@ + +/******************************************************************************* + +Copyright 2013 Scientific Computation Research Center, + Rensselaer Polytechnic Institute. All rights reserved. + + The LICENSE file included with this distribution describes the terms + of the SCOREC Non-Commercial License this program is distributed under. + +*******************************************************************************/ + +#ifndef MA_SOLUTIONTRANSFERHELPER_H +#define MA_SOLUTIONTRANSFERHELPER_H + +#include +#include "maMesh.h" + +#include "maAffine.h" +#include "maMap.h" + +namespace ma { +int getMinimumDimension(apf::FieldShape* s); +void transfer( + apf::Field* field, + double *value, + int n, // size of the cavity + Entity** cavity, + EntityArray& newEntities); +} + +#endif diff --git a/ma/maStats.cc b/ma/maStats.cc index eb27b7add..479dcce21 100644 --- a/ma/maStats.cc +++ b/ma/maStats.cc @@ -51,7 +51,7 @@ void getLinearQualitiesInPhysicalSpace(ma::Mesh* m, ma::Iterator* it; it = m->begin(m->getDimension()); while( (e = m->iterate(it)) ) { - double lq; + double lq = 0; if (m->getType(e) == apf::Mesh::TRIANGLE) { ma::Vector p[3]; ma::getVertPoints(m, e, p); diff --git a/ma/maTables.cc b/ma/maTables.cc index 7d80ad034..8a5f7eb7d 100644 --- a/ma/maTables.cc +++ b/ma/maTables.cc @@ -65,6 +65,24 @@ int const tet_rotation[12][4] = ,{3,1,0,2}//11 }; +/* un-rotates a rotated tet to get the original. This can be + thought of as the inverse of tet_rotation. */ + +int const tet_inv_rotation[12][4] = +{{0,1,2,3} //{0,1,2,3}//0 +,{0,3,1,2} //{0,2,3,1}//1 +,{0,2,3,1} //{0,3,1,2}//2 +,{1,0,3,2} //{1,0,3,2}//3 +,{3,0,2,1} //{1,3,2,0}//4 +,{2,0,1,3} //{1,2,0,3}//5 +,{1,2,0,3} //{2,0,1,3}//6 +,{3,1,0,2} //{2,1,3,0}//7 +,{2,3,0,1} //{2,3,0,1}//8 +,{1,3,2,0} //{3,0,2,1}//9 +,{3,2,1,0} //{3,2,1,0}//10 +,{2,1,3,0} //{3,1,0,2}//11 +}; + /* these are the canonical edge split configurations that form the first layer of filtering for tet refinement diff --git a/ma/maTables.h b/ma/maTables.h index 64666ad5d..40dd38a1c 100644 --- a/ma/maTables.h +++ b/ma/maTables.h @@ -30,6 +30,9 @@ extern CodeMatch const* code_match[apf::Mesh::TYPES]; /* this table defines the mapping from new to old vertex indices for one of the 12 rotations. */ extern int const tet_rotation[12][4]; +/* this table defines the inverse mapping from new to old + vertex indices for one of the 12 rotations. */ +extern int const tet_inv_rotation[12][4]; extern int const prism_rotation[6][6]; extern int const pyramid_rotation[4][5]; diff --git a/ma/pkg_tribits.cmake b/ma/pkg_tribits.cmake index f7fe9f20e..d1db5c276 100644 --- a/ma/pkg_tribits.cmake +++ b/ma/pkg_tribits.cmake @@ -22,6 +22,7 @@ set(SOURCES maLayerCollapse.cc maMatch.cc maSolutionTransfer.cc + maSolutionTransferHelper.cc maSnap.cc maEdgeSwap.cc maShape.cc diff --git a/mds/apfBox.cc b/mds/apfBox.cc index 0e8ecf88d..b6dec78c0 100644 --- a/mds/apfBox.cc +++ b/mds/apfBox.cc @@ -335,4 +335,13 @@ Mesh2* makeMdsBox( return bb.m; } +gmi_model* makeMdsBoxModel( + int nex, int ney, int nez, + double wx, double wy, double wz, bool is) +{ + BoxBuilder bb(nex, ney, nez, wx, wy, wz, is); + return bb.buildModel(); +} + + } diff --git a/mds/apfBox.h b/mds/apfBox.h index 5e61ae317..035a29b78 100644 --- a/mds/apfBox.h +++ b/mds/apfBox.h @@ -73,6 +73,11 @@ struct BoxBuilder Mesh2* makeMdsBox( int nx, int ny, int nz, double wx, double wy, double wz, bool is); +/** \brief see makeMdsBox - only creates geometric model */ +gmi_model* makeMdsBoxModel( + int nx, int ny, int nz, double wx, double wy, double wz, bool is); + + } #endif diff --git a/mds/apfMDS.cc b/mds/apfMDS.cc index 121027d43..4c082f4c7 100644 --- a/mds/apfMDS.cc +++ b/mds/apfMDS.cc @@ -600,7 +600,7 @@ class MeshMDS : public Mesh2 apf::destroyField(coordinateField); coordinateField = 0; gmi_model* model = static_cast(mesh->user_model); - if (ownsModel) + if (ownsModel && model) gmi_destroy(model); mds_apf_destroy(mesh); mesh = 0; @@ -632,6 +632,11 @@ class MeshMDS : public Mesh2 } //seol + void clearRemotes(MeshEntity* e) + { + mds_set_copies(&mesh->remotes, &mesh->mds, fromEnt(e), 0); + } + void addGhost(MeshEntity* e, int p, MeshEntity* r) { mds_copy c; @@ -1054,10 +1059,10 @@ void deriveMdlFromManifold(Mesh2* mesh, bool* isModelVert, PCU_ALWAYS_ASSERT_VERBOSE(!mesh->findTag("_vert_id"), "MeshTag name \"_vert_id\" is used internally in this method\n"); - apf::MeshTag* vIDTag = mesh->createIntTag("_vert_id", 1); + apf::MeshTag* vIDTag = mesh->createLongTag("_vert_id", 1); for (apf::GlobalToVert::iterator vit = globalToVert.begin(); - vit != globalToVert.end(); vit++) { - mesh->setIntTag(vit->second, vIDTag, &(vit->first)); + vit != globalToVert.end(); vit++) { + mesh->setLongTag(vit->second, vIDTag, &(vit->first)); } // Reserve tags used for model faces @@ -1170,10 +1175,10 @@ void derive2DMdlFromManifold(Mesh2* mesh, bool* isModelVert, PCU_ALWAYS_ASSERT_VERBOSE(!mesh->findTag("_vert_id"), "MeshTag name \"_vert_id\" is used internally in this method\n"); - apf::MeshTag* vIDTag = mesh->createIntTag("_vert_id", 1); + apf::MeshTag* vIDTag = mesh->createLongTag("_vert_id", 1); for (apf::GlobalToVert::iterator vit = globalToVert.begin(); vit != globalToVert.end(); vit++) { - mesh->setIntTag(vit->second, vIDTag, &(vit->first)); + mesh->setLongTag(vit->second, vIDTag, &(vit->first)); } // Reserve tags used for model edges diff --git a/mds/apfMDS.h b/mds/apfMDS.h index 284f85b06..afb130fa2 100644 --- a/mds/apfMDS.h +++ b/mds/apfMDS.h @@ -49,7 +49,7 @@ class Migration; class Field; /** \brief a map from global ids to vertex objects */ -typedef std::map GlobalToVert; +typedef std::map GlobalToVert; /** \brief create an empty MDS part \param model the geometric model interface @@ -203,8 +203,12 @@ Mesh2* loadMdsFromCGNS(gmi_model* g, const char* filename, CGNSBCMap& cgnsBCMap) // names of mesh data to read from file: (VERTEX, VelocityX; CellCentre, Pressure) Mesh2* loadMdsFromCGNS(gmi_model* g, const char* filename, CGNSBCMap& cgnsBCMap, const std::vector>& meshData); +int gmshMajorVersion(const char* filename); + Mesh2* loadMdsFromGmsh(gmi_model* g, const char* filename); +Mesh2* loadMdsDmgFromGmsh(const char* fnameDmg, const char* filename); + Mesh2* loadMdsFromUgrid(gmi_model* g, const char* filename); void printUgridPtnStats(gmi_model* g, const char* ugridfile, const char* ptnfile, diff --git a/mds/mdsCGNS.cc b/mds/mdsCGNS.cc index 955e95031..acdef4aae 100644 --- a/mds/mdsCGNS.cc +++ b/mds/mdsCGNS.cc @@ -1161,7 +1161,8 @@ apf::Mesh2 *DoIt(gmi_model *g, const std::string &fname, apf::CGNSBCMap &cgnsBCM if (std::get<1>(ret) > 0) { const std::vector vertexIDs = std::get<0>(ret); - localElements.emplace_back(apf::assemble(mesh, vertexIDs.data(), std::get<1>(ret), type, globalToVert)); // corresponding finalize below + std::vector vertexIDs_l(vertexIDs.begin(), vertexIDs.end()); + localElements.emplace_back(apf::assemble(mesh, vertexIDs_l.data(), std::get<1>(ret), type, globalToVert)); // corresponding finalize below const auto nverts = sizes[0]; const auto ordinates = ReadCGNSCoords(cgid, base, zone, ncoords, nverts, vertexIDs, globalToVert); diff --git a/mds/mdsGmsh.cc b/mds/mdsGmsh.cc index a2584a23f..4d0ce7875 100644 --- a/mds/mdsGmsh.cc +++ b/mds/mdsGmsh.cc @@ -52,24 +52,16 @@ struct Reader { char* line; char* word; size_t linecap; + int major_version; + int minor_version; bool isQuadratic; std::map nodeMap; std::map entMap[4]; + //the 0th vector is not used as mesh vertices don't have a 'physical entity' + //association in the legacy 2.* gmsh format + std::vector physicalType[4]; }; -void initReader(Reader* r, apf::Mesh2* m, const char* filename) -{ - r->mesh = m; - r->file = fopen(filename, "r"); - if (!r->file) { - lion_eprint(1,"couldn't open Gmsh file \"%s\"\n",filename); - abort(); - } - r->line = static_cast(malloc(1)); - r->line[0] = '\0'; - r->linecap = 1; - r->isQuadratic = false; -} void freeReader(Reader* r) { @@ -94,6 +86,16 @@ long getLong(Reader* r) return x; } +double getDouble(Reader* r) +{ + double x; + int nchars; + int ret = sscanf(r->word, "%lf%n", &x, &nchars); + PCU_ALWAYS_ASSERT(ret == 1); + r->word += nchars; + return x; +} + bool startsWith(char const* prefix, char const* s) { int ls = strlen(s); @@ -115,18 +117,114 @@ void checkMarker(Reader* r, char const* marker) PCU_ALWAYS_ASSERT(startsWith(marker, r->line)); } -void readNode(Reader* r) +void initReader(Reader* r, apf::Mesh2* m, const char* filename) +{ + r->mesh = m; + r->file = fopen(filename, "r"); + if (!r->file) { + lion_eprint(1,"couldn't open Gmsh file \"%s\"\n",filename); + abort(); + } + r->line = static_cast(malloc(1)); + r->line[0] = '\0'; + r->linecap = 1; + r->isQuadratic = false; + seekMarker(r, "$MeshFormat"); + int fileType, dataSize; + int ret = sscanf(r->line, "%d.%d %d %d\n", + &r->major_version, &r->minor_version, &fileType, &dataSize); + PCU_ALWAYS_ASSERT(ret==4); +} + +void readNode(Reader* r, int bm=-1) { - long id; Node n; apf::Vector3& p = n.point; - sscanf(r->line, "%ld %lf %lf %lf", &id, &p[0], &p[1], &p[2]); - r->nodeMap[id] = n; + if(r->major_version == 4) { + sscanf(r->line, "%lf %lf %lf", &p[0], &p[1], &p[2]); + r->nodeMap[bm] = n; + } else if(r->major_version == 2) { + long id; + sscanf(r->line, "%ld %lf %lf %lf", &id, &p[0], &p[1], &p[2]); + r->nodeMap[id] = n; + } getLine(r); } -void readNodes(Reader* r) +void readEntities(Reader* r,const char* fnameDmg) +{ + seekMarker(r, "$Entities"); + long nlde,ilde,iud,tag,isign,nMV,nME,nMF,nMR; + double x,y,z; + FILE* f = fopen(fnameDmg, "w"); + sscanf(r->line, "%ld %ld %ld %ld", &nMV, &nME, &nMF, &nMR); + fprintf(f, "%ld %ld %ld %ld \n", nMR, nMF, nME, nMV); // just reverse order + fprintf(f, "%f %f %f \n ", 0.0, 0.0, 0.0); // Probaby model bounding box? + fprintf(f, "%f %f %f \n", 0.0, 0.0, 0.0); // + + getLine(r); // because readNode gets the next line we need this outside for Nodes_Block + for (long i = 0; i < nMV; ++i){ + sscanf(r->line, "%ld %lf %lf %lf %ld ", &tag, &x, &y, &z, &iud); + fprintf(f, "%ld %lf %lf %lf \n",tag,x,y,z); + getLine(r); + } + for (long i = 0; i < nME; ++i){ + tag = getLong(r); + fprintf(f, "%ld", tag); + for (int i=0; i< 6; ++i) x=getDouble(r); // read past min maxes + iud = getLong(r); + for(long j =0; j < iud; ++j) isign=getLong(r); // read past iud user tags + nlde=getLong(r); // 2 in straight edged models but... + for(long j =0; j < nlde; ++j) { + ilde=getLong(r); + fprintf(f, " %ld", std::abs(ilde)); // modVerts started from 1 + } + fprintf(f, "\n"); + getLine(r); + } + for (long i = 0; i < nMF; ++i){ + tag = getLong(r); + fprintf(f, "%ld %d\n", tag, 1); + for (int i=0; i< 6; ++i) x=getDouble(r); // read past min maxes + iud = getLong(r); + for(long j =0; j < iud; ++j) isign=getLong(r); // read past iud user tags + nlde=getLong(r); + fprintf(f, " %ld \n", nlde); + for(long j =0; j < nlde; ++j) { + ilde=getLong(r); + if(ilde > 0 ) + isign=1; + else + isign=0; + fprintf(f, " %ld %ld \n", std::abs(ilde),isign); + } + getLine(r); + } + for (long i = 0; i < nMR; ++i){ + tag = getLong(r); + fprintf(f, "%ld %d \n", tag, 1); + for (int i=0; i< 6; ++i) x=getDouble(r); // read past min maxes + iud = getLong(r); + for(long j =0; j < iud; ++j) getLong(r); // read past iud user tags + nlde=getLong(r); + fprintf(f, "%ld \n", nlde); + for(long j =0; j < nlde; ++j) { + ilde=getLong(r); + if(ilde > 0 ) + isign=1; + else + isign=0; + fprintf(f, "%ld %ld \n", std::abs(ilde),isign); + } + getLine(r); + } + checkMarker(r, "$EndEntities"); + fclose(f); +} + +void readNodesV2(Reader* r) { + PCU_ALWAYS_ASSERT(r->major_version == 2); seekMarker(r, "$Nodes"); long n = getLong(r); getLine(r); @@ -135,6 +233,28 @@ void readNodes(Reader* r) checkMarker(r, "$EndNodes"); } +void readNodesV4(Reader* r) +{ + PCU_ALWAYS_ASSERT(r->major_version == 4); + seekMarker(r, "$Nodes"); + long Num_EntityBlocks,Num_Nodes,Nodes_Block,edim,etag,junk1,junk2,junk3; + sscanf(r->line, "%ld %ld %ld %ld", &Num_EntityBlocks, &Num_Nodes, &junk1, &junk2); + getLine(r); // because readNode gets the next line we need this outside for Nodes_Block + for (long i = 0; i < Num_EntityBlocks; ++i){ + sscanf(r->line, "%ld %ld %ld %ld", &edim, &etag, &junk3, &Nodes_Block); + long* blockMap = new long[Nodes_Block]; + for (long j = 0; j < Nodes_Block; ++j){ + getLine(r); + sscanf(r->line, "%ld", &blockMap[j]); + } + getLine(r); + for (long j = 0; j < Nodes_Block; ++j) + readNode(r,blockMap[j]); // has a genLine at end + delete [] blockMap; + } + checkMarker(r, "$EndNodes"); +} + apf::MeshEntity* lookupVert(Reader* r, long nodeId, apf::ModelEntity* g) { PCU_ALWAYS_ASSERT(r->nodeMap.count(nodeId)); @@ -146,22 +266,38 @@ apf::MeshEntity* lookupVert(Reader* r, long nodeId, apf::ModelEntity* g) return n.entity; } -void readElement(Reader* r) +void readElement(Reader* r, long gmshType=-1, long gtag=-1) { long id = getLong(r); - long gmshType = getLong(r); + if(r->major_version == 2) { + gmshType = getLong(r); + } if (isQuadratic(gmshType)) r->isQuadratic = true; int apfType = apfFromGmsh(gmshType); PCU_ALWAYS_ASSERT(0 <= apfType); int nverts = apf::Mesh::adjacentCount[apfType][0]; int dim = apf::Mesh::typeDimension[apfType]; - long ntags = getLong(r); - PCU_ALWAYS_ASSERT(ntags >= 2); - getLong(r); /* discard physical type */ - long gtag = getLong(r); - for (long i = 2; i < ntags; ++i) - getLong(r); /* discard all other element tags */ + if(r->major_version == 2) { + long ntags = getLong(r); + /* The Gmsh 4.9 documentation on the legacy 2.* format states: + * "By default, the first tag is the tag of the physical entity to which the + * element belongs; the second is the tag of the elementary model entity to + * which the element belongs; the third is the number of mesh partitions to + * which the element belongs, followed by the partition ids (negative + * partition ids indicate ghost cells). A zero tag is equivalent to no tag. + * Gmsh and most codes using the MSH 2 format require at least the first two + * tags (physical and elementary tags)." + * A physical entity is a user defined grouping of elementary model entities. + * An elementary model entity is a geometric model entity. */ + PCU_ALWAYS_ASSERT(ntags >= 2); + const int physType = static_cast(getLong(r)); + PCU_ALWAYS_ASSERT(dim>=0 && dim<4); + r->physicalType[dim].push_back(physType); + gtag = getLong(r); + for (long i = 2; i < ntags; ++i) + getLong(r); /* discard all other element tags */ + } apf::ModelEntity* g = r->mesh->findModelEntity(dim, gtag); apf::Downward verts; for (int i = 0; i < nverts; ++i) { @@ -178,8 +314,9 @@ void readElement(Reader* r) getLine(r); } -void readElements(Reader* r) +void readElementsV2(Reader* r) { + PCU_ALWAYS_ASSERT(r->major_version == 2); seekMarker(r, "$Elements"); long n = getLong(r); getLine(r); @@ -188,6 +325,37 @@ void readElements(Reader* r) checkMarker(r, "$EndElements"); } +void readElementsV4(Reader* r) +{ + PCU_ALWAYS_ASSERT(r->major_version == 4); + seekMarker(r, "$Elements"); + long Num_EntityBlocks,Num_Elements,Elements_Block,Edim,gtag,gmshType,junk1,junk2; + sscanf(r->line, "%ld %ld %ld %ld", &Num_EntityBlocks, &Num_Elements, &junk1, &junk2); + getLine(r); + for (long i = 0; i < Num_EntityBlocks; ++i){ + sscanf(r->line, "%ld %ld %ld %ld", &Edim, >ag, &gmshType, &Elements_Block); + getLine(r); + for (long j = 0; j < Elements_Block; ++j) { + readElement(r,gmshType,gtag); + } + } + checkMarker(r, "$EndElements"); +} + +void setElmPhysicalType(Reader* r, apf::Mesh2* m) { + apf::MeshEntity* e; + apf::MeshTag* tag = m->createIntTag("gmsh_physical_entity", 1); + for(int dim=1; dim<=m->getDimension(); dim++) { //vertices don't have a physical entity ? + if( ! r->physicalType[dim].size() ) continue; + int* tagPtr = r->physicalType[dim].data(); + int i = 0; + apf::MeshIterator* it = m->begin(dim); + while ((e = m->iterate(it))) + m->setIntTag(e, tag, &tagPtr[i++]); + m->end(it); + } +} + static const double gmshTet10EdgeIndices[6] = {0, 1, 2, 3, 5, 4}; static int getQuadGmshIdx(const int apfIdx, const int apfType) { @@ -252,17 +420,44 @@ void readGmsh(apf::Mesh2* m, const char* filename) { Reader r; initReader(&r, m, filename); - readNodes(&r); - readElements(&r); - freeReader(&r); - m->acceptChanges(); + if(r.major_version == 4) { + readNodesV4(&r); + readElementsV4(&r); + m->acceptChanges(); + } else if(r.major_version == 2) { + readNodesV2(&r); + readElementsV2(&r); + m->acceptChanges(); + setElmPhysicalType(&r,m); + } if (r.isQuadratic) readQuadratic(&r, m, filename); + freeReader(&r); } +} // closes original namespace +namespace apf { + +int gmshMajorVersion(const char* filename) { + Reader r; + Mesh2* m=NULL; + initReader(&r, m, filename); + int version = r.major_version; + freeReader(&r); + return version; +} + +void gmshFindDmg(const char* fnameDmg, const char* filename) +{ + Reader r; + + Mesh2* m=NULL; + initReader(&r, m, filename); + PCU_ALWAYS_ASSERT(r.major_version==4); + readEntities(&r, fnameDmg); + freeReader(&r); } -namespace apf { Mesh2* loadMdsFromGmsh(gmi_model* g, const char* filename) { @@ -271,4 +466,12 @@ Mesh2* loadMdsFromGmsh(gmi_model* g, const char* filename) return m; } +Mesh2* loadMdsDmgFromGmsh(const char*fnameDmg, const char* filename) +{ + gmshFindDmg(fnameDmg, filename); // new function that scans $Entities and writes a dmg + Mesh2* m = makeEmptyMdsMesh(gmi_load(fnameDmg), 0, false); + readGmsh(m, filename); + return m; +} + } diff --git a/mds/mdsUgrid.cc b/mds/mdsUgrid.cc index 84bf57902..ceb8a001b 100644 --- a/mds/mdsUgrid.cc +++ b/mds/mdsUgrid.cc @@ -308,14 +308,20 @@ namespace { apf::Vector3 vtx_coord; m->getPoint(vtx, 0, vtx_coord); - bool same_dim = std::all_of(upward_dim.begin(), upward_dim.end(), - [upward_dim](const int i) { - return upward_dim[0] == i; - }); - bool same_id = std::all_of(upward_id.begin(), upward_id.end(), - [upward_id](const int i) { - return upward_id[0] == i; - }); + bool same_dim = true; + for(size_t i=0; i edge_indx; - std::vector::iterator iter = upward_dim.begin(); - while ((iter = std::find_if(iter, upward_dim.end(), - [](const int i){ return i == 1; })) - != upward_dim.end()) - { - edge_indx.push_back(std::distance(upward_dim.begin(), iter)); - iter++; + for(size_t i = 0; i < upward_dim.size(); i++) { + if(upward_dim[i] == 1) { + edge_indx.push_back(i); + } } std::vector edge_id; for (size_t i = 0; i < edge_indx.size(); i++) { edge_id.push_back(upward_id[edge_indx[i]]); } - bool same_edge_id = std::all_of(edge_id.begin(), edge_id.end(), - [edge_id](const int i) { - return edge_id[0] == i; - }); + bool same_edge_id = true; + for(size_t i=0; id; ++d) + for (d = 1; d < m->d; ++d){ + PCU_Comm_Begin(); for (e = mds_begin(m, d); e != MDS_NONE; e = mds_next(m, e)) { c = mds_get_copies(net, e); if (!c) @@ -278,10 +278,11 @@ static int align_copies(struct mds_net* net, struct mds* m) if (owns_copies(e, c)) downs_to_copies(m, e, c); } - PCU_Comm_Send(); - while (PCU_Comm_Receive()) - if (recv_down_copies(net, m)) - did_change = 1; + PCU_Comm_Send(); + while (PCU_Comm_Receive()) + if (recv_down_copies(net, m)) + did_change = 1; + } return PCU_Or(did_change); } diff --git a/mds/mds_smb.c b/mds/mds_smb.c index 5a6898957..f0590d679 100644 --- a/mds/mds_smb.c +++ b/mds/mds_smb.c @@ -22,7 +22,7 @@ #include /*using POSIX mkdir call for SMB "foo/" path*/ #include /* for checking the error from mkdir */ -enum { SMB_VERSION = 5 }; +enum { SMB_VERSION = 6 }; enum { SMB_VERT, diff --git a/mth/mth.h b/mth/mth.h index bda16d1eb..93e70a492 100644 --- a/mth/mth.h +++ b/mth/mth.h @@ -38,7 +38,7 @@ Vector reject(Vector const& a, Vector const& b); /** \brief transpose of a static matrix */ template -Matrix transpose(Matrix const& a); +void transpose(Matrix const& a, Matrix& b); /** \brief determinant of a 2 by 2 matrix */ template diff --git a/mth/mth_def.h b/mth/mth_def.h index b05c31945..11624f3cc 100644 --- a/mth/mth_def.h +++ b/mth/mth_def.h @@ -57,12 +57,12 @@ template void transpose(Matrix const& a, Matrix& b) { - unsigned m = a.rows(); + unsigned m = a.cols(); unsigned n = a.rows(); b.resize(m, n); for (unsigned i=0; i < m; ++i) - for (unsigned j=0; j < n; ++j) - b(j,i) = a(i,j); + for (unsigned j=0; j < n; ++j) + b(i,j) = a(j,i); } template diff --git a/parma/diffMC/.clang_complete b/parma/diffMC/.clang_complete index 93a06dfc0..ae7332293 100644 --- a/parma/diffMC/.clang_complete +++ b/parma/diffMC/.clang_complete @@ -1,4 +1,4 @@ --D_HAVE_ZOLTAN_ +-D_PUMI_HAS_ZOLTAN_ -D_HAVE_PARMETIS_ -DFMDB_PARALLEL -DFMDB diff --git a/parma/diffMC/maximalIndependentSet/misLuby.cc b/parma/diffMC/maximalIndependentSet/misLuby.cc index 91162d23a..b2b2fbf5e 100644 --- a/parma/diffMC/maximalIndependentSet/misLuby.cc +++ b/parma/diffMC/maximalIndependentSet/misLuby.cc @@ -402,7 +402,6 @@ int mis(partInfo& part, bool randNumsPredefined,bool isNeighbors) { vector rmtNodesToRemove; int isInMis = 0; - int loopCount = 0; int tag = 0; int numNodesAdded; do { @@ -461,7 +460,6 @@ int mis(partInfo& part, bool randNumsPredefined,bool isNeighbors) { rmtNodesToRemove.clear(); numNodesAdded = PCU_Add_Int(numNodesAdded); - loopCount++; } while (numNodesAdded > 0); return isInMis; diff --git a/parma/parma.h b/parma/parma.h index c6a3bf284..065477fa7 100644 --- a/parma/parma.h +++ b/parma/parma.h @@ -16,6 +16,12 @@ /** * @brief get entity imbalance + * @remark The imbalance of a given entity order (i.e., vtx, edge, face, rgn) + * is defined as the maximum count of that entity order on a part, across all parts, + * divided by the average entity count across all parts. + * For example if there are four parts and the parts have 5, 7, 12, and 8 + * vertices, respectively, then the vertex imbalance is 50%; + * 12 / ((5+7+8+12)/4) = 1.5. * @param mesh (InOut) partitioned mesh * @param entImb (InOut) entity imbalance [vtx, edge, face, rgn] */ @@ -23,9 +29,12 @@ void Parma_GetEntImbalance(apf::Mesh* mesh, double (*entImb)[4]); /** * @brief see Parma_GetEntImbalance(...) - * @remark On a part, if an entity order (vtx, edge, face, rgn) does not have + * @remark The weighted imbalance definition replaces the entity count with the + * sum of entity weights. If the weight for all the entities of a given order + * is one, then the two definitions are equivalent. + * On a part, if an entity order (vtx, edge, face, rgn) does not have * weights set on all its entities then a weight of one will be assigned to each - * of the entities (of the given order) + * of the entities (of the given order). * @param mesh (InOut) partitioned mesh * @param weight (In) entity weights used for computing imbalance * @param entImb (InOut) entity imbalance [vtx, edge, face, rgn] diff --git a/pcu/CMakeLists.txt b/pcu/CMakeLists.txt index cb4431963..91649e43f 100644 --- a/pcu/CMakeLists.txt +++ b/pcu/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCES pcu_coll.c pcu_io.c pcu_buffer.c + pcu_mem.c pcu_mpi.c pcu_msg.c pcu_order.c diff --git a/pcu/PCU.h b/pcu/PCU.h index 67b2ecccd..f9a53362e 100644 --- a/pcu/PCU.h +++ b/pcu/PCU.h @@ -77,6 +77,8 @@ void PCU_Min_Ints(int* p, size_t n); int PCU_Min_Int(int x); void PCU_Max_Ints(int* p, size_t n); int PCU_Max_Int(int x); +void PCU_Max_Longs(long* p, size_t n); +long PCU_Max_Long(long x); int PCU_Or(int c); int PCU_And(int c); @@ -126,6 +128,9 @@ void PCU_Protect(void); /*MPI_Wtime() equivalent*/ double PCU_Time(void); +/*Memory usage*/ +double PCU_GetMem(void); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/pcu/pcu.c b/pcu/pcu.c index e600b6fdf..b6c30f901 100644 --- a/pcu/pcu.c +++ b/pcu/pcu.c @@ -40,6 +40,8 @@ #include /*required for mode_t for mkdir on some systems*/ #include /*using POSIX mkdir call for SMB "foo/" path*/ #include /* for checking the error from mkdir */ +#include /*INT_MAX*/ +#include /*abort*/ enum state { uninit, init }; static enum state global_state = uninit; @@ -138,6 +140,10 @@ int PCU_Comm_Pack(int to_rank, const void* data, size_t size) reel_fail("Comm_Pack called before Comm_Init"); if ((to_rank < 0)||(to_rank >= pcu_mpi_size())) reel_fail("Invalid rank in Comm_Pack"); + if ( size > (size_t)INT_MAX ) { + fprintf(stderr, "ERROR Attempting to pack a PCU message whose size exceeds INT_MAX... exiting\n"); + abort(); + } memcpy(pcu_msg_pack(get_msg(),to_rank,size),data,size); return PCU_SUCCESS; } @@ -469,6 +475,22 @@ int PCU_Max_Int(int x) PCU_Max_Ints(a, 1); return a[0]; } +/** \brief Performs an Allreduce maximum of long arrays. + */ +void PCU_Max_Longs(long* p, size_t n) +{ + if (global_state == uninit) + reel_fail("Max_Longs called before Comm_Init"); + pcu_allreduce(&(get_msg()->coll),pcu_max_longs,p,n*sizeof(long)); +} + +long PCU_Max_Long(long x) +{ + long a[1]; + a[0] = x; + PCU_Max_Longs(a, 1); + return a[0]; +} /** \brief Performs a parallel logical OR reduction */ diff --git a/pcu/pcu_coll.c b/pcu/pcu_coll.c index 836a69346..7bdf8a221 100644 --- a/pcu/pcu_coll.c +++ b/pcu/pcu_coll.c @@ -88,6 +88,15 @@ void pcu_max_ints(void* local, void* incoming, size_t size) a[i] = MAX(a[i],b[i]); } +void pcu_max_longs(void* local, void* incoming, size_t size) +{ + long* a = local; + long* b= incoming; + size_t n = size/sizeof(long); + for (size_t i=0; i < n; ++i) + a[i] = MAX(a[i],b[i]); +} + void pcu_min_sizets(void* local, void* incoming, size_t size) { size_t* a = local; diff --git a/pcu/pcu_coll.h b/pcu/pcu_coll.h index 83ca77daa..11f204ee8 100644 --- a/pcu/pcu_coll.h +++ b/pcu/pcu_coll.h @@ -33,6 +33,7 @@ void pcu_min_doubles(void* local, void* incoming, size_t size); void pcu_add_ints(void* local, void* incoming, size_t size); void pcu_min_ints(void* local, void* incoming, size_t size); void pcu_max_ints(void* local, void* incoming, size_t size); +void pcu_max_longs(void* local, void* incoming, size_t size); void pcu_add_longs(void* local, void* incoming, size_t size); void pcu_add_sizets(void* local, void* incoming, size_t size); void pcu_min_sizets(void* local, void* incoming, size_t size); diff --git a/pcu/pcu_io.c b/pcu/pcu_io.c index 01462ac4d..a706f6155 100644 --- a/pcu/pcu_io.c +++ b/pcu/pcu_io.c @@ -244,31 +244,35 @@ static const uint16_t pcu_endian_value = 1; #define PCU_BIG_ENDIAN 0 #define PCU_ENCODED_ENDIAN PCU_BIG_ENDIAN //consistent with network byte order -static void pcu_swap_16(uint16_t* p) -{ - uint8_t* p2 = (uint8_t*)p; - uint8_t temp = p2[0]; - p2[0] = p2[1]; - p2[1] = temp; -} - static void pcu_swap_32(uint32_t* p) { - uint16_t* p2 = (uint16_t*)p; - uint16_t temp = p2[0]; - p2[0] = p2[1]; - p2[1] = temp; - pcu_swap_16(p2); - pcu_swap_16(p2+1); + uint32_t a = *p; +#if defined(__GNUC__) + a = __builtin_bswap32(a); +#elif defined(_MSC_VER) + a = _byteswap_ulong(a); +#else + a = ((a & 0x000000FF) << 24) | ((a & 0x0000FF00) << 8) | + ((a & 0x00FF0000) >> 8) | ((a & 0xFF000000) >> 24); +#endif + *p = a; } -static void pcu_swap_64(uint32_t* p) +static void pcu_swap_64(uint64_t* p) { - uint32_t temp = p[0]; - p[0] = p[1]; - p[1] = temp; - pcu_swap_32(p); - pcu_swap_32(p+1); + uint64_t a = *p; +#if defined(__GNUC__) && !defined(__CUDA_ARCH__) + a = __builtin_bswap64(a); +#elif defined(_MSC_VER) && !defined(__CUDA_ARCH__) + a = _byteswap_uint64(a); +#else + a = ((a & 0x00000000000000FFULL) << 56) | + ((a & 0x000000000000FF00ULL) << 40) | + ((a & 0x0000000000FF0000ULL) << 24) | ((a & 0x00000000FF000000ULL) << 8) | + ((a & 0x000000FF00000000ULL) >> 8) | ((a & 0x0000FF0000000000ULL) >> 24) | + ((a & 0x00FF000000000000ULL) >> 40) | ((a & 0xFF00000000000000ULL) >> 56); +#endif + *p = a; } void pcu_swap_unsigneds(unsigned* p, size_t n) @@ -282,7 +286,7 @@ void pcu_swap_doubles(double* p, size_t n) { PCU_ALWAYS_ASSERT(sizeof(double)==8); for (size_t i=0; i < n; ++i) - pcu_swap_64((uint32_t*)(p++)); + pcu_swap_64((uint64_t*)(p++)); } void pcu_write_unsigneds(pcu_file* f, unsigned* p, size_t n) diff --git a/pcu/pcu_mem.c b/pcu/pcu_mem.c new file mode 100644 index 000000000..8ac2345c1 --- /dev/null +++ b/pcu/pcu_mem.c @@ -0,0 +1,58 @@ +/****************************************************************************** + + (c) 2023 Scientific Computation Research Center, + Rensselaer Polytechnic Institute. All rights reserved. + + This work is open source software, licensed under the terms of the + BSD license as described in the LICENSE file in the top-level directory. + +*******************************************************************************/ +#include + +#if defined(__APPLE__) + +#include +#include + +#elif defined(__bgq__) + +//the BG/Q headers have warning-triggering +//code in them. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#include + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#else + +#include //warning - this is GNU-specific + +#endif + +double PCU_GetMem() { + const double M = 1024*1024; +#if defined(__APPLE__) + bool resident = true; + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); + size_t size = (resident ? t_info.resident_size : t_info.virtual_size); + return (double)size/M; +#elif defined(__bgq__) + size_t heap; + Kernel_GetMemorySize(KERNEL_MEMSIZE_HEAP, &heap); + return (double)heap/M; +#elif defined(__GNUC__) && defined(PUMI_HAS_MALLINFO2) + struct mallinfo2 meminfo_now = mallinfo2(); + return ((double)meminfo_now.arena)/M; +#elif defined(__GNUC__) + struct mallinfo meminfo_now = mallinfo(); + return ((double)meminfo_now.arena)/M; +#endif +} diff --git a/phasta/CMakeLists.txt b/phasta/CMakeLists.txt index 9ee43eb26..0a785e268 100644 --- a/phasta/CMakeLists.txt +++ b/phasta/CMakeLists.txt @@ -115,6 +115,7 @@ target_link_libraries(ph parma pcu apf_zoltan + pumi ) if(ENABLE_SIMMETRIX) target_link_libraries(ph PUBLIC diff --git a/phasta/phAdapt.cc b/phasta/phAdapt.cc index 805cfdbe9..9ae8f783a 100644 --- a/phasta/phAdapt.cc +++ b/phasta/phAdapt.cc @@ -57,7 +57,7 @@ struct AdaptCallback : public Parma_GroupCode AdaptCallback(apf::Mesh2* m, apf::Field* szfld, ph::Input* inp) : mesh(m), field(szfld), in(inp) { } void run(int) { - ma::Input* ma_in = ma::configure(mesh, field); + ma::Input* ma_in = ma::makeAdvanced(ma::configure(mesh, field)); if( in ) { //chef defaults ma_in->shouldRunPreZoltan = true; @@ -156,7 +156,7 @@ static void runFromGivenSize(Input& in, apf::Mesh2* m) void tetrahedronize(Input& in, apf::Mesh2* m) { - ma::Input* ma_in = ma::configureIdentity(m); + ma::Input* ma_in = ma::makeAdvanced(ma::configureIdentity(m)); ph::setupBalance("preAdaptBalanceMethod", in.preAdaptBalanceMethod, ma_in->shouldRunPreParma, ma_in->shouldRunPreZoltan, ma_in->shouldRunPreZoltanRib); @@ -212,7 +212,7 @@ namespace chef { void adaptLevelSet(ph::Input& in, apf::Mesh2* m) { - ma::Input* ma_in = ma::configureMatching(m, in.recursiveUR); + ma::Input* ma_in = ma::makeAdvanced(ma::configureMatching(m, in.recursiveUR)); ph::setupBalance("preAdaptBalanceMethod", in.preAdaptBalanceMethod, ma_in->shouldRunPreParma, ma_in->shouldRunPreZoltan, ma_in->shouldRunPreZoltanRib); @@ -244,7 +244,7 @@ namespace chef { void uniformRefinement(ph::Input& in, apf::Mesh2* m) { - ma::Input* ma_in = ma::configureMatching(m, in.recursiveUR); + ma::Input* ma_in = ma::makeAdvanced(ma::configureMatching(m, in.recursiveUR)); ph::setupBalance("preAdaptBalanceMethod", in.preAdaptBalanceMethod, ma_in->shouldRunPreParma, ma_in->shouldRunPreZoltan, ma_in->shouldRunPreZoltanRib); diff --git a/phasta/phBlock.cc b/phasta/phBlock.cc index 441847e7a..573532e6d 100644 --- a/phasta/phBlock.cc +++ b/phasta/phBlock.cc @@ -245,9 +245,9 @@ std::string getElementType {NULL ,"tetrahedron " ,"hexahedron " - ,"wedge " + ,"wedge triface" // this will push this into the interior element as well but have to propagate ,"wedge quadface " - ,"pyramid " + ,"pyramid quadface" ,"pyramid triface "}; return typeTable[elementType]; } diff --git a/phasta/phCook.cc b/phasta/phCook.cc index 35abd2714..264fb8c5d 100644 --- a/phasta/phCook.cc +++ b/phasta/phCook.cc @@ -31,6 +31,18 @@ #include #include +static void print_stats(const char* name, double value) +{ + double min, max, avg; + min = PCU_Min_Double(value); + max = PCU_Max_Double(value); + avg = PCU_Add_Double(value); + avg /= PCU_Comm_Peers(); + double imb = max / avg; + if (!PCU_Comm_Self()) + printf("%s: min %f max %f avg %f imb %f\n", name, min, max, avg, imb); +} + #define SIZET(a) static_cast(a) namespace { @@ -78,8 +90,10 @@ void originalMain(apf::Mesh2*& m, ph::Input& in, m = loadMesh(g, in); else apf::printStats(m); - m->verify(); - if (in.solutionMigration && !in.useAttachedFields) +// Need to set a flag to enable avoiding this when short on time m->verify(); + if (in.useAttachedFields) + lion_eprint(1,"because useAttachedFields set restart not read\n"); + else if (in.solutionMigration && !in.useAttachedFields) ph::readAndAttachFields(in, m); else ph::attachZeroSolution(in, m); @@ -134,6 +148,7 @@ namespace chef { namespace ph { void checkBalance(apf::Mesh2* m, ph::Input& in) { /* check if balancing was requested */ + Parma_PrintPtnStats(m, "postSplit", false); if (in.prePhastaBalanceMethod != "none" && PCU_Comm_Peers() > 1) ph::balance(in,m); } @@ -148,9 +163,18 @@ namespace ph { in.isReorder ) { apf::MeshTag* order = NULL; + + print_stats("malloc used before Bfs", PCU_GetMem()); + if (in.isReorder && PCU_Comm_Peers() > 1) order = Parma_BfsReorder(m); + + print_stats("malloc used before reorder", PCU_GetMem()); + apf::reorderMdsMesh(m,order); + + print_stats("malloc used after reorder", PCU_GetMem()); + } } @@ -250,6 +274,8 @@ namespace chef { ph::Output out; out.openfile_write = openfile_write; bake(g,m,in,out); + if ((in.writeVTK) == 1) apf::writeVtkFiles("rendered",m); + } void cook(gmi_model*& g, apf::Mesh2*& m, ph::Input& ctrl) { diff --git a/phasta/phGeomBC.cc b/phasta/phGeomBC.cc index 9ab9ecbc4..e996b7818 100644 --- a/phasta/phGeomBC.cc +++ b/phasta/phGeomBC.cc @@ -283,6 +283,15 @@ static void writeGrowthCurves(Output& o, FILE* f) } } +static void writeSpanwiseAvgArrays(Output& o, FILE* f) +{ + if (o.arrays.nfather > 0) { + writeInt(f, "number of father-nodes", o.arrays.nfather); + writeInts(f, "number of son-nodes for each father", o.arrays.nsonsArr,o.arrays.nfather); + writeInts(f, "keyword ifath", o.arrays.ifather,o.mesh->count(0)); + } +} + void writeGeomBC(Output& o, std::string path, int timestep) { double t0 = PCU_Time(); @@ -341,11 +350,30 @@ void writeGeomBC(Output& o, std::string path, int timestep) ph_write_doubles(f, "m2g parametric coordinate", o.arrays.m2gParCoord, params[0] * params[1], 2, params); } - params[0] = m->count(0); params[1] = 3; ph_write_doubles(f, "co-ordinates", o.arrays.coordinates, params[0] * params[1], 2, params); +// start effort to write coords to a flat ascii file for each part + int npts=params[0]; + char coordfilename[64]; + //bzero((void*)coordfilename,64); + if (o.txtCoord == 1) { + int rank = PCU_Comm_Self() + 1; + sprintf(coordfilename, "coords.%d",rank); + FILE* fc = fopen(coordfilename, "w"); + fprintf ( fc, "%d \n", npts ); + double x,y,z; + + for (int j = 0; j < npts; j++) { + x=o.arrays.coordinates[j]; + y=o.arrays.coordinates[j+npts]; + z=o.arrays.coordinates[j+2*npts]; + fprintf ( fc, "%.15E,%.15E,%.15E,\n", x,y,z); + } + fclose(fc); + } + writeInt(f, "number of processors", PCU_Comm_Peers()); writeInt(f, "size of ilwork array", o.nlwork); if (o.nlwork) @@ -363,6 +391,7 @@ void writeGeomBC(Output& o, std::string path, int timestep) writeElementGraph(o, f); writeEdges(o, f); writeGrowthCurves(o, f); + writeSpanwiseAvgArrays(o, f); PHASTAIO_CLOSETIME(fclose(f);) double t1 = PCU_Time(); if (!PCU_Comm_Self()) diff --git a/phasta/phIO.c b/phasta/phIO.c index 4352dadbb..2806dd783 100644 --- a/phasta/phIO.c +++ b/phasta/phIO.c @@ -7,6 +7,7 @@ #include #include #include +#include #define PH_LINE 1024 #define MAGIC 362436 @@ -81,8 +82,8 @@ static int find_header(FILE* f, const char* name, char* found, char header[PH_LI tmp[PH_LINE-1] = '\0'; parse_header(tmp, &hname, &bytes, 0, NULL); if (!strncmp(name, hname, strlen(name))) { - strncpy(found, hname, strlen(hname)); - found[strlen(hname)] = '\0'; + assert(strlen(hname) < PH_LINE); + strcpy(found, hname); return 1; } fseek(f, bytes, SEEK_CUR); diff --git a/phasta/phInput.cc b/phasta/phInput.cc index 96b0f523c..ffc9989c2 100644 --- a/phasta/phInput.cc +++ b/phasta/phInput.cc @@ -62,6 +62,7 @@ static void setDefaults(Input& in) in.parmaVerbosity = 1; //fairly quiet in.writeGeomBCFiles = 0; // write additional geombc file for vis in streaming in.writeRestartFiles = 0; // write additional restart file for vis in streaming + in.writeVTK = 0; in.ramdisk = 0; in.meshqCrtn = 0.027; in.elementImbalance = 1.03; @@ -89,6 +90,9 @@ static void setDefaults(Input& in) in.simSizeLowerBound = 0.0; in.simSizeUpperBound = 1.0e16; in.simMaxAdaptMeshElements = 1.0e16; + in.spanAvg = 0; // prepare and write spanwise average arrays + in.nfathers = 0; // number of father nodes (# of pts in xy (2D) plane for z averaging) + in.nsons = 0; // number of sons for each father (constant nz for each father ONLY for ijk grids) } Input::Input() @@ -148,6 +152,7 @@ static void formMaps(Input& in, StringMap& stringMap, IntMap& intMap, DblMap& db intMap["axisymmetry"] = &in.axisymmetry; intMap["parmaLoops"] = &in.parmaLoops; intMap["parmaVerbosity"] = &in.parmaVerbosity; + intMap["writeVTK"] = &in.writeVTK; intMap["writeGeomBCFiles"] = &in.writeGeomBCFiles; intMap["writeRestartFiles"] = &in.writeRestartFiles; intMap["ramdisk"] = &in.ramdisk; @@ -176,6 +181,9 @@ static void formMaps(Input& in, StringMap& stringMap, IntMap& intMap, DblMap& db dblMap["simSizeLowerBound"] = &in.simSizeLowerBound; dblMap["simSizeUpperBound"] = &in.simSizeUpperBound; dblMap["simMaxAdaptMeshElements"] = &in.simMaxAdaptMeshElements; + intMap["spanAverage"] = &in.spanAvg; + intMap["nfathers"] = &in.nfathers; + intMap["nsons"] = &in.nsons; } template diff --git a/phasta/phInput.h b/phasta/phInput.h index b7f8367e6..a6bf88c90 100644 --- a/phasta/phInput.h +++ b/phasta/phInput.h @@ -23,6 +23,7 @@ class Input public: Input(); void load(const char* filename); + int txtCoord; //HACK added to get through compile int timeStepNumber; /** \brief this corresponds to the number of degrees of freedom in the solution field of the output restart file. @@ -142,6 +143,7 @@ class Input int formEdges; int parmaLoops; int parmaVerbosity; + int writeVTK; /** \brief write the geombc file during in-memory data transfer between phasta and chef. */ int writeGeomBCFiles; @@ -201,6 +203,10 @@ class Input double simSizeUpperBound; /** \brief number of allowed mesh elements of adapted mesh */ double simMaxAdaptMeshElements; + /* Stuff for spanwise averaging */ + int spanAvg; + int nfathers; + int nsons; }; int countNaturalBCs(Input& in); diff --git a/phasta/phInterfaceCutter.cc b/phasta/phInterfaceCutter.cc index 24a59bba9..90fe0d2dc 100644 --- a/phasta/phInterfaceCutter.cc +++ b/phasta/phInterfaceCutter.cc @@ -246,7 +246,6 @@ int migrateInterface(apf::Mesh2*& m, ph::BCs& bcs) { apf::Migration* plan = new apf::Migration(m); apf::Parts residence; - int nDG = 0; while ((f = m->iterate(it))) { apf::ModelEntity* me = m->toModel(f); if (m->getModelType(me) != faceDim) @@ -256,7 +255,6 @@ int migrateInterface(apf::Mesh2*& m, ph::BCs& bcs) { if (!ph::isInterface(m->getModel(), gf, fbcs)) continue; - ++nDG; apf::DgCopies dgCopies; m->getDgCopies(f, dgCopies); diff --git a/phasta/phOutput.cc b/phasta/phOutput.cc index e27e95bc2..f5dd4985d 100644 --- a/phasta/phOutput.cc +++ b/phasta/phOutput.cc @@ -941,6 +941,55 @@ static void getEdges(Output& o, apf::Numbering* vn, apf::Numbering* rn, BCs& bcs } } +static void getSpanwiseAverageArrays(Input& in, Output& o) { + if (in.spanAvg == 1) { + apf::Mesh* m = o.mesh; +// not used gmi_model* gm = m->getModel(); + int nnodes = m->count(0); // number of nodes of whole mesh or part?? + /* this will come from the adapt.inp file and is constant for all geombc + it is the total number of father nodes, nx*ny, and each geombc loads this */ + o.txtCoord=in.txtCoord; // controls whether we write coords.part + int nfather = in.nfathers; + o.arrays.nfather = nfather; + /* this will come from the adapt.inp file and is constant for all geombc + this is the number of son nodes for each father, nz, and each geombc loads this + loading from adapt.inp only works when all fathers have same nz */ + int nsons = in.nsons; + o.arrays.nsons = nsons; + o.arrays.nsonsArr = new int[nfather]; //initialize nsonsArr + for (int i=0; ibegin(0); + o.arrays.ifather = new int[nnodes]; //initialize ifath + apf::MeshTag* t = m->findTag("fathers2D"); + if (t==NULL) { + if (!PCU_Comm_Self()) + lion_oprint(1,"Did not find tag fathers2D\n"); + } else if (t != NULL) { + if (!PCU_Comm_Self()) + lion_oprint(1,"Found tag fathers2D\n"); + } + int tagNum; + int count = 0; + while ((v = m->iterate(it))) { // loop over mesh vertices + m->getIntTag(v,t,&tagNum); + o.arrays.ifather[count] = tagNum; + if(nsons==0) ++o.arrays.nsonsArr[tagNum]; // increment the nsons counter + //std::cout<<"Tag number "<end(it); + PCU_ALWAYS_ASSERT(count == nnodes); + } else { + o.arrays.nfather = 0; + o.arrays.nsons = 0; + } +} + Output::~Output() { delete [] arrays.coordinates; @@ -1048,6 +1097,7 @@ void generateOutput(Input& in, BCs& bcs, apf::Mesh* mesh, Output& o) getGCEssentialBCs(o, n); getInitialConditions(bcs, o); getElementGraph(o, rn, bcs); + getSpanwiseAverageArrays(in, o); apf::destroyNumbering(n); apf::destroyNumbering(rn); if (in.initBubbles) diff --git a/phasta/phOutput.h b/phasta/phOutput.h index abc573df7..04bb6a13f 100644 --- a/phasta/phOutput.h +++ b/phasta/phOutput.h @@ -126,6 +126,16 @@ idx: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int* rigidBodyMTs; /* an integer to indicate rigid body tag of a vertex */ int* rigidBodyTag; +/* an integer to indicate how many father nodes there are in the mesh*/ + int nfather; +/* an integer to indicate how many sons there are for each father */ +/* NOTE: currently this assumes each father has the same number of sons */ +/* uniform z spacing and constant domain thickness */ + int nsons; +/* an array of integers that holds the number of the father node */ + int* ifather; +/* an array of integers of size nfather that has nsons in each entry */ + int* nsonsArr; }; @@ -134,6 +144,7 @@ struct Output ~Output(); Input* in; apf::Mesh* mesh; + int txtCoord; int nOverlapNodes; int nOwnedNodes; int nBoundaryElements; diff --git a/phasta/phPartition.cc b/phasta/phPartition.cc index bb0c0a207..8546c4188 100644 --- a/phasta/phPartition.cc +++ b/phasta/phPartition.cc @@ -9,6 +9,8 @@ #include #include + + #ifdef HAVE_SIMMETRIX #include #include "SimPartitionedMesh.h" @@ -18,11 +20,38 @@ namespace ph { +void setWeight(apf::Mesh* m, apf::MeshTag* tag, int dim) { + double w = 1.0; + apf::MeshEntity* e; + apf::MeshIterator* it = m->begin(dim); + int nverts =1; + apf::Downward verts; + while ((e = m->iterate(it))){ + int dimEnt=getDimension(m,e); + if(dimEnt==3) { + w=1.0; + nverts = m->getDownward(e, 0, verts); + if(nverts==8) w=6.0; + if(nverts==6) w=3.0; + } + m->setDoubleTag(e, tag, &w); + } + m->end(it); +} + +apf::MeshTag* setWeights(apf::Mesh* m) { + apf::MeshTag* tag = m->createDoubleTag("parma_weight", 1); + setWeight(m, tag, 0); + setWeight(m, tag, m->getDimension()); + return tag; +} + + apf::Migration* getSplitPlan(Input& in, apf::Mesh2* m) { PCU_ALWAYS_ASSERT(in.splitFactor >= 1); apf::Migration* plan; - if (in.splitFactor != 1) { + if (in.splitFactor != 1 ) { //0 ) { // 1) { // 0 will force reparttition even when split factor is 1 apf::Splitter* splitter; if (in.partitionMethod == "rib") { //prefer SCOREC RIB over Zoltan RIB splitter = Parma_MakeRibSplitter(m); @@ -37,7 +66,9 @@ apf::Migration* getSplitPlan(Input& in, apf::Mesh2* m) else splitter = apf::makeZoltanGlobalSplitter(m, method, apf::REPARTITION); } - apf::MeshTag* weights = Parma_WeighByMemory(m); +// apf::MeshTag* weights = Parma_WeighByMemory(m); + apf::MeshTag* weights = setWeights(m); +// Parma_PrintPtnStats(m, "preSplit",false); plan = splitter->split(weights, 1.01, in.splitFactor); apf::removeTagFromDimension(m, weights, m->getDimension()); m->destroyTag(weights); @@ -66,22 +97,6 @@ bool isMixed(apf::Mesh2* m) { return PCU_Max_Int(mixed); } -void setWeight(apf::Mesh* m, apf::MeshTag* tag, int dim) { - double w = 1.0; - apf::MeshEntity* e; - apf::MeshIterator* it = m->begin(dim); - while ((e = m->iterate(it))) - m->setDoubleTag(e, tag, &w); - m->end(it); -} - -apf::MeshTag* setWeights(apf::Mesh* m) { - apf::MeshTag* tag = m->createDoubleTag("parma_weight", 1); - setWeight(m, tag, 0); - setWeight(m, tag, m->getDimension()); - return tag; -} - void clearTags(apf::Mesh* m, apf::MeshTag* t) { apf::removeTagFromDimension(m, t, 0); apf::removeTagFromDimension(m, t, m->getDimension()); @@ -101,7 +116,8 @@ void neighborReduction(apf::Mesh2* m, apf::MeshTag* weights, int verbose, bool f void parmaMixed(Input& in, apf::Mesh2* m) { bool fineStats=false; // set to true for per part stats Parma_PrintPtnStats(m, "preRefine", fineStats); //FIXME - apf::MeshTag* weights = Parma_WeighByMemory(m); +// apf::MeshTag* weights = Parma_WeighByMemory(m); + apf::MeshTag* weights = setWeights(m); const double step = 0.2; const int verbose = 0; apf::Balancer* balancer = Parma_MakeElmBalancer(m, step, verbose); @@ -139,9 +155,9 @@ void parmaTet(Input& in, apf::Mesh2* m, bool runGap) { } void parmaBalance(Input& in, apf::Mesh2* m, bool runGap) { - if( isMixed(m) ) - parmaMixed(in,m); - else +// if( isMixed(m) ) +// parmaMixed(in,m); +// else parmaTet(in,m,runGap); } diff --git a/phasta/phRestart.cc b/phasta/phRestart.cc index 6e0940b25..2135f2616 100644 --- a/phasta/phRestart.cc +++ b/phasta/phRestart.cc @@ -286,7 +286,9 @@ static bool isNodalField(const char* fieldname, int nnodes, apf::Mesh* m) "wssbar", "pressure projection vectors", "projection vectors", - "vorticity" + "vorticity", + "BCs", + "cdelsq" }; static char const* const known_cell_fields[] = { "VOF solution", @@ -294,7 +296,8 @@ static bool isNodalField(const char* fieldname, int nnodes, apf::Mesh* m) "meshCFL", "VMS_error", "err_tri_f", - "material_type" + "material_type", + "lesnut" }; static char const* const known_rand_fields[] = { "rbParams" @@ -541,6 +544,10 @@ void detachAndWriteSolution(Input& in, Output& out, apf::Mesh* m, std::string pa detachAndWriteField(in, m, f, "dc_lag"); if (m->findField("pressure projection vectors")) detachAndWriteField(in, m, f, "pressure projection vectors"); + if (m->findField("BCs")) + detachAndWriteField(in, m, f, "BCs"); + if (m->findField("cdelsq")) + detachAndWriteField(in, m, f, "cdelsq"); if (in.displacementMigration) detachAndWriteField(in, m, f, "displacement"); if (in.dwalMigration) diff --git a/phasta/ph_convert.cc b/phasta/ph_convert.cc index 65df57f3b..52e22a6af 100644 --- a/phasta/ph_convert.cc +++ b/phasta/ph_convert.cc @@ -55,7 +55,7 @@ static void fixPyramids(apf::Mesh2* m) return; /* no pyramids exist in 2D */ if (apf::countEntitiesOfType(m, apf::Mesh::HEX)) return; /* meshadapt can't even look at hexes */ - ma::Input* in = ma::configureIdentity(m); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m)); in->shouldCleanupLayer = true; ma::adapt(in); } diff --git a/pumi-meshes b/pumi-meshes index 6d1601a29..b8973d0bd 160000 --- a/pumi-meshes +++ b/pumi-meshes @@ -1 +1 @@ -Subproject commit 6d1601a29152e2043b16b2522c1a346b877920a0 +Subproject commit b8973d0bd907d73218a611f8b6f7efbe580acd09 diff --git a/pumi/pumi.h b/pumi/pumi.h index af252137e..529d528ba 100644 --- a/pumi/pumi.h +++ b/pumi/pumi.h @@ -86,7 +86,10 @@ class pumi public: pumi(); ~pumi(); - static pumi* instance(); + static pumi* instance() { + static pumi _instance; + return &_instance; + }; pMesh mesh; pGeom model; @@ -97,8 +100,6 @@ class pumi pMeshTag ghost_tag; std::vector ghost_vec[4]; std::vector ghosted_vec[4]; -private: - static pumi* _instance; }; //************************************ diff --git a/pumi/pumi_geom.cc b/pumi/pumi_geom.cc index 6e5bc5273..6fe0edb5b 100644 --- a/pumi/pumi_geom.cc +++ b/pumi/pumi_geom.cc @@ -24,10 +24,7 @@ gModel::gModel(gmi_model* model) : TagHolder() g = model; } -gModel::~gModel() -{ - delete g; -} +gModel::~gModel() {} gEntity* gModel::getGeomEnt(int d, gmi_ent* ge) { @@ -90,13 +87,18 @@ void pumi_geom_delete(pGeom g) { pTag id_tag=pumi_geom_findTag(g, "ID"); - for (int i=0; i<4; ++i) + for (int i=0; i<4; ++i) { + std::vector vge(g->size(i)); for (pGeomIter gent_it = g->begin(i); gent_it!=g->end(i);++gent_it) { if (id_tag) pumi_gent_deleteTag(*gent_it, id_tag); - delete *gent_it; + vge.push_back(*gent_it); } - pumi_geom_deleteTag(g, id_tag); + for(size_t j=0; j fields; std::vector frozen_fields; diff --git a/pumi/pumi_mesh.cc b/pumi/pumi_mesh.cc index 422315647..b48ed473f 100644 --- a/pumi/pumi_mesh.cc +++ b/pumi/pumi_mesh.cc @@ -162,17 +162,9 @@ pumi::~pumi() delete [] num_own_ent; delete [] num_global_ent; } - delete _instance; - _instance = NULL; } -pumi* pumi::_instance=NULL; -pumi* pumi::instance() -{ - if (_instance==NULL) - _instance = new pumi(); - return _instance; -} + apf::Migration* getPlan(apf::Mesh* m, int num_target_part) { diff --git a/pumi/pumi_sys.cc b/pumi/pumi_sys.cc index 6b620a151..b41be4ff3 100644 --- a/pumi/pumi_sys.cc +++ b/pumi/pumi_sys.cc @@ -62,50 +62,9 @@ double pumi_getTime() return double(ruse_now.ru_utime.tv_sec) + double(ruse_now.ru_utime.tv_usec)/1000000.0; } -#if defined(__APPLE__) - -#include -#include - -#elif defined(__bgq__) - -//the BG/Q headers have warning-triggering -//code in them. -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - -#include - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#else - -#include //warning - this is GNU-specific - -#endif - double pumi_getMem() { - const double M = 1024*1024; -#if defined(__APPLE__) - bool resident = true; - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); - size_t size = (resident ? t_info.resident_size : t_info.virtual_size); - return (double)size/M; -#elif defined(__bgq__) - size_t heap; - Kernel_GetMemorySize(KERNEL_MEMSIZE_HEAP, &heap); - return (double)heap/M; -#else - struct mallinfo meminfo_now = mallinfo(); - return ((double)meminfo_now.arena)/M; -#endif + return PCU_GetMem(); } void pumi_printTimeMem(const char* msg, double time, double memory) diff --git a/python_wrappers/CMakeLists.txt b/python_wrappers/CMakeLists.txt index 0c80759a6..1a16cd3e0 100644 --- a/python_wrappers/CMakeLists.txt +++ b/python_wrappers/CMakeLists.txt @@ -4,7 +4,7 @@ # TODO: MPI4PY mpich build has to be the same as the one scorec is # built with. That is if OPENMPI is being used for building scorec, # an MPI4PY build with OPENMPI must be used. -find_package(Python2 REQUIRED COMPONENTS Interpreter Development) +find_package(Python REQUIRED COMPONENTS Interpreter Development) find_package(MPI4PY REQUIRED) message(STATUS "MPI4PY_VENDOR is ${MPI4PY_VENDOR}") include_directories(${MPI4PY_INCLUDE_DIR}) @@ -44,10 +44,11 @@ set_property(TARGET pyCore ${CMAKE_SOURCE_DIR}/ma ${CMAKE_SOURCE_DIR}/can ${CMAKE_SOURCE_DIR}/spr + ${CMAKE_SOURCE_DIR}/crv ) set(pyCoreDepLibs - Python2::Python + Python::Python core) if(ENABLE_SIMMETRIX) diff --git a/python_wrappers/apf.i b/python_wrappers/apf.i index e32cc2242..68cc8372e 100644 --- a/python_wrappers/apf.i +++ b/python_wrappers/apf.i @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef HAVE_SIMMETRIX #include @@ -61,6 +62,7 @@ void PCU_ALWAYS_ASSERT_VERBOSE(int cond, const char* msg); #ifdef HAVE_SIMMETRIX void start_sim(const char* logfile = 0); void stop_sim(); + bool is_sim_started(); #endif /* GMI RELATED WRAPPERS */ @@ -71,6 +73,8 @@ void gmi_register_null(void); void gmi_register_sim(void); void gmi_sim_start(void); void gmi_sim_stop(void); + void gmi_sim_stop(void); + gmi_model* gmi_sim_load(const char* nativefile, const char* smdfile); #endif @@ -159,7 +163,103 @@ void lion_set_verbosity(int lvl); } return sum/count; } + apf::Field* getCurrentIsoSize(const char* name) + { + apf::Field* currentSize = apf::createField( + self, name, apf::SCALAR, apf::getLagrange(1)); + + apf::Field* cnt = apf::createField( + self, "current_size_cnt", apf::SCALAR, apf::getLagrange(1)); + + apf::zeroField(currentSize); + apf::zeroField(cnt); + + + apf::MeshEntity* e; + apf::MeshIterator* it = self->begin(0); + while ( (e = self->iterate(it)) ) { + double local_sum = 0.; + double local_cnt = 0.; + for (int i = 0; i < self->countUpward(e); i++) { + local_sum += apf::measure(self, self->getUpward(e, i)); + local_cnt += 1.0; + } + apf::setScalar(currentSize, e, 0, local_sum); + apf::setScalar(cnt, e, 0, local_cnt); + + } + self->end(it); + + apf::accumulate(currentSize); + apf::accumulate(cnt); + + it = self->begin(0); + while ( (e = self->iterate(it)) ) { + if (!self->isOwned(e)) continue; + double sum = apf::getScalar(currentSize, e, 0); + double count = apf::getScalar(cnt, e, 0); + apf::setScalar(currentSize, e, 0, sum/count); + } + self->end(it); + + apf::synchronize(currentSize); + self->removeField(cnt); + apf::destroyField(cnt); + return currentSize; + } + double getMinOfScalarField(apf::Field* field) + { + PCU_ALWAYS_ASSERT(apf::getValueType(field) == apf::SCALAR); + double local_min = 1.0e32; + apf::MeshEntity* e; + apf::MeshIterator* it = self->begin(0); + while ( (e = self->iterate(it)) ) { + if (!self->isOwned(e)) + continue; + double val = apf::getScalar(field, e, 0); + if (val < local_min) + local_min = val; + } + self->end(it); + PCU_Min_Doubles(&local_min, 1); + return local_min; + } + double getMaxOfScalarField(apf::Field* field) + { + PCU_ALWAYS_ASSERT(apf::getValueType(field) == apf::SCALAR); + double local_max = -1.0e32; + apf::MeshEntity* e; + apf::MeshIterator* it = self->begin(0); + while ( (e = self->iterate(it)) ) { + if (!self->isOwned(e)) + continue; + double val = apf::getScalar(field, e, 0); + if (val > local_max) + local_max = val; + } + self->end(it); + PCU_Max_Doubles(&local_max, 1); + return local_max; + } + bool isBoundingModelRegion(int rtag, int dim, int tag) + { + if (dim != 2) return false; + gmi_model* gmodel = self->getModel(); + gmi_ent* gregion = gmi_find(gmodel, 3, rtag); + gmi_set* adj = gmi_adjacent(gmodel, gregion, dim); + + for(int i = 0; i < adj->n; i++) { + int adj_g_tag = gmi_tag(gmodel, adj->e[i]); + if (adj_g_tag == tag) { + gmi_free_set(adj); + return true; + } + } + gmi_free_set(adj); + return false; + } } + #define __attribute__(x) %ignore apf::fail; %include @@ -171,6 +271,7 @@ void lion_set_verbosity(int lvl); namespace apf { apf::Mesh2* makeEmptyMdsMesh(gmi_model* model, int dim, bool isMatched); apf::Mesh2* loadMdsMesh(const char* modelfile, const char* meshfile); + apf::Mesh2* loadMdsMesh(gmi_model* model, const char* meshfile); void writeASCIIVtkFiles(const char* prefix, apf::Mesh2* m); /* void writeVtkFiles(const char* prefix, apf::Mesh* m, int cellDim = -1); */ /* void writeVtkFiles(const char* prefix, apf::Mesh* m, */ @@ -210,3 +311,20 @@ namespace ma { void adapt(Input* in); void adaptVerbose(Input* in, bool verbosef = false); } + +/* CRV RELATED WRAPPERS */ +%rename(crvadapt) crv::adapt; +namespace crv { + void adapt(ma::Input* in); + class BezierCurver : public MeshCurver + { + public: + BezierCurver(apf::Mesh2* m, int P, int B) : MeshCurver(m,P) + { + setBlendingOrder(apf::Mesh::TYPES,B); + }; + virtual bool run(); + }; + void writeCurvedVtuFiles(apf::Mesh* m, int type, int n, const char* prefix); + void writeCurvedWireFrame(apf::Mesh* m, int n, const char* prefix); +} diff --git a/python_wrappers/sim_helper.cc b/python_wrappers/sim_helper.cc index 384c8de72..e228db922 100644 --- a/python_wrappers/sim_helper.cc +++ b/python_wrappers/sim_helper.cc @@ -7,6 +7,7 @@ void start_sim(const char* logfile) Sim_readLicenseFile(0); if (logfile) Sim_logOn(logfile); + is_started = true; } void stop_sim() @@ -16,3 +17,7 @@ void stop_sim() MS_exit(); } +bool is_sim_started() +{ + return is_started; +} diff --git a/python_wrappers/sim_helper.h b/python_wrappers/sim_helper.h index b881b5d12..1030042d5 100644 --- a/python_wrappers/sim_helper.h +++ b/python_wrappers/sim_helper.h @@ -6,7 +6,10 @@ #include #include +static bool is_started = false; + void start_sim(const char* logfile = 0); void stop_sim(); +bool is_sim_started(); #endif diff --git a/ree/CMakeLists.txt b/ree/CMakeLists.txt new file mode 100644 index 000000000..e7b935540 --- /dev/null +++ b/ree/CMakeLists.txt @@ -0,0 +1,32 @@ +if(DEFINED TRIBITS_PACKAGE) + include(pkg_tribits.cmake) + return() +endif() + +# Package sources +set(SOURCES + reeResidualFunctionals.cc + reeFluxCorrection.cc + reeCorrectedFlux.cc + reeEstimateError.cc + reeSizeField.cc) + +# Package headers +set(HEADERS + ree.h) + +# Add the ree library +add_library(ree ${SOURCES}) + +# Include directories +target_include_directories(ree INTERFACE + $ + $ + ) + +# Link this package to these libraries +target_link_libraries(ree PUBLIC apf pcu) + +scorec_export_library(ree) + +bob_end_subdir() diff --git a/ree/cmake/Dependencies.cmake b/ree/cmake/Dependencies.cmake new file mode 100644 index 000000000..9d9456a31 --- /dev/null +++ b/ree/cmake/Dependencies.cmake @@ -0,0 +1,3 @@ +TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( + LIB_REQUIRED_PACKAGES SCORECapf + ) diff --git a/ree/pkg_tribits.cmake b/ree/pkg_tribits.cmake new file mode 100644 index 000000000..7819acdfa --- /dev/null +++ b/ree/pkg_tribits.cmake @@ -0,0 +1,23 @@ +tribits_package(SCORECree) + +# THIS IS WHERE TRIBITS GETS HEADERS +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +#Sources & Headers +set(SOURCES + reeResidualFunctionals.cc + reeFluxCorrection.cc + reeCorrectedFlux.cc + reeEstimateError.cc + reeSizeField.cc) + +set(HEADERS + ree.h) + +#Library +tribits_add_library( + ree + HEADERS ${HEADERS} + SOURCES ${SOURCES}) + +tribits_package_postprocess() diff --git a/ree/ree.h b/ree/ree.h new file mode 100644 index 000000000..5e337484c --- /dev/null +++ b/ree/ree.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef REE_H +#define REE_H + + +/** \file ree.h + * \brief The Residual Error Estimator (REE) interface + */ + +#include "apf.h" +#include +#include +#include +#include "apfShape.h" +#include "apfField.h" +#include +#include +#include + +/** \namespace ree + * \brief All Residual Error Estimator functions + */ +namespace ree { + +/** @brief Computes a nodal size field from the element error field + * obtained after running the residual error estimator. + * @param ef (In) nedelec electric field + * @param error_field (In) per-element residual error field + * @param n a parameter to prescribe allowable error + * @param alpha floor on the size field; alpha < h_new/h_old < beta + * @param beta ceiling on the size field; alpha < h_new/h_old < beta + * @returns a scalar mesh size field at mesh vertices + */ +apf::Field* getTargetEMSizeField( + apf::Field* ef, + apf::Field* error_field, + int n, + double alpha = 0.25, + double beta = 2.0); + +/** @brief Computes equilibrated residuals using the fem nedelec electric field. + * @param f (In) nedelec electric field + */ +apf::Field* equilibrateResiduals(apf::Field* f); + +/** @brief Uses the fem nedelec electric field and the equilibrated residuals to + * compute the 'correction' to the flux vectors on each face. + * @param ef (In) nedelec electric field + * @param g (In) equilibrated residuals field + */ +apf::Field* computeFluxCorrection(apf::Field* ef, apf::Field* g); + +/** @brief Uses the fem nedelec electric field and the 'correction' to the flux + * vectors to compute the 'corrected' flux vectors on each face. + * @param ef (In) nedelec electric field + * @param theta (In) correction to the flux vectors + */ +apf::Field* computeCorrectedFlux(apf::Field* ef, apf::Field* theta); + +/** @brief Solves additional local BVPs on a one order higher nedelec field on + * each element to estimate the dsicretization error. + * @param ef (In) nedelec electric field + * @param correctedFlux (In) flux field which provides Neumann BCs for each + * local BVP. + * @returns a per-element scalar residual error field. + */ +apf::Field* computeErrorField(apf::Field* ef, apf::Field* correctedFlux); + +/** @brief run the residual error estimator. + * @param f the fem nedelec electric field + * scales the output size field. + * @returns a per-element scalar residual error field. + */ +apf::Field* estimateError(apf::Field* f); + +// helper functions +void assembleCurlCurlElementMatrix(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Matrix& elmat); + +void assembleVectorMassElementMatrix(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Matrix& elmat); + +void assembleElementMatrix(apf::Mesh* mesh, apf::MeshEntity*e, + apf::Field* f, mth::Matrix& elmat); + +void assembleDomainLFElementVector(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Vector& elvect); + +apf::Vector3 computeFaceOutwardNormal(apf::Mesh* m, + apf::MeshEntity* t, apf::MeshEntity* f, apf::Vector3 const& p); + +bool isOnDomainBoundary(apf::Mesh* m, apf::MeshEntity* e); + +} + +#endif diff --git a/ree/reeCorrectedFlux.cc b/ree/reeCorrectedFlux.cc new file mode 100644 index 000000000..c9abfe895 --- /dev/null +++ b/ree/reeCorrectedFlux.cc @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ +#include +#include "ree.h" + +namespace ree { + +enum {VISITED}; + +/* overall information useful during computation of corrected flux */ +struct CorrectFlux +{ + apf::Mesh* mesh; + /* mesh dimension, so far handling 3 only */ + int dim; + /* polynomial order of nedelec space */ + int order; + /* polynomial order of p+1 nedelec space */ + int orderp1; + /* input nedelec field containing scalar dofs for electric field */ + apf::Field* ef; + /* input theta field for flux correction + * (3 scalar dofs on each face, 1 per edge) */ + apf::Field* theta; + /* tags each face once it has been visited */ + apf::MeshTag* tag; + /* output per-element field containing correctedFlux vectors, + * which are computed using theta and electric fields */ + apf::Field* correctedFlux; +}; + +static void setupCorrectFlux( + CorrectFlux* cf, + apf::Field* f, + apf::Field* theta, + apf::Field* correctedFlux) +{ + cf->mesh = apf::getMesh(f); + cf->dim = cf->mesh->getDimension(); + PCU_ALWAYS_ASSERT(cf->dim == 3); + cf->order = f->getShape()->getOrder(); + cf->orderp1 = cf->order+1; + cf->ef = f; + cf->theta = theta; + cf->tag = cf->mesh->createIntTag("isVisited", 1); + + cf->correctedFlux = correctedFlux; + int nc = apf::countComponents(cf->correctedFlux); + double* zeros = new double[nc]; + for (int i = 0; i < nc; i++) + zeros[i] = 0.; + apf::MeshEntity* tet; + apf::MeshIterator* it = cf->mesh->begin(3); + while ((tet = cf->mesh->iterate(it))) { + apf::MeshElement* me = apf::createMeshElement(cf->mesh, tet); + int orderp1 = cf->order+1; + int np = apf::countIntPoints(me, 2*orderp1-1); + + for (int i = 0; i < np; i++) { + apf::setComponents(cf->correctedFlux, tet, i, zeros); + } + apf::destroyMeshElement(me); + } + cf->mesh->end(it); + delete [] zeros; +} + +typedef std::vector EntityVector; +struct FaceCavity +{ + apf::Mesh* mesh; + apf::MeshEntity* entity; + CorrectFlux* correctflux; + EntityVector tets; +}; + +static void setupFaceCavity(FaceCavity* fc, CorrectFlux* cf) +{ + fc->mesh = cf->mesh; + fc->correctflux = cf; + fc->entity = 0; +} + +static void startFaceCavity(FaceCavity* fc, apf::MeshEntity* f) +{ + fc->entity = f; + fc->tets.clear(); +} + +static void addEntityToCavity(FaceCavity* fc, apf::MeshEntity* e) +{ + PCU_ALWAYS_ASSERT(fc->mesh->getType(e) == apf::Mesh::TET); + fc->tets.push_back(e); +} + +static void addEntitiesToCavity( + FaceCavity* fc, apf::DynamicArray& es) +{ + for (std::size_t i=0; i < es.getSize(); ++i) + addEntityToCavity(fc, es[i]); +} + +static bool getInitialFaceCavity(FaceCavity* fc, apf::CavityOp* o) +{ + if (! o->requestLocality(&fc->entity, 1)) + return false; + + apf::DynamicArray adjacent; + fc->mesh->getAdjacent(fc->entity, 3, adjacent); + addEntitiesToCavity(fc, adjacent); + return true; +} + +static bool buildFaceCavity(FaceCavity* fc, apf::CavityOp* o) +{ + if (!getInitialFaceCavity(fc, o)) return false; + return true; +} + + +static void computeCorrectedFlux(FaceCavity* fc) +{ + apf::MeshEntity* face = fc->entity; + + // 1. get upward tets of the face + apf::Up up; + fc->mesh->getUp(face, up); + if (isOnDomainBoundary(fc->mesh, face)) + PCU_ALWAYS_ASSERT(up.n == 1); + else + PCU_ALWAYS_ASSERT(up.n == 2); + + apf::MeshEntity* firstTet = up.e[0]; + apf::MeshEntity* secondTet = nullptr; + if (up.n == 2) + secondTet = up.e[1]; + + // 2. get positions of the face in downward faces of upward tets + apf::Downward tet1_faces, tet2_faces; + int nf = fc->mesh->getDownward(firstTet, 2, tet1_faces); + if (up.n == 2) + fc->mesh->getDownward(secondTet, 2, tet2_faces); + int tet1_pos = -1; + int tet2_pos = -1; + tet1_pos = apf::findIn(tet1_faces, nf, face); + if (up.n == 2) + tet2_pos = apf::findIn(tet2_faces, nf, face); + + // 3. get downward edges of the face + apf::Downward edges; + int nedges = fc->mesh->getDownward(face, 1, edges); + + // 4. get theta coeffs on the face + double components[3]; + apf::getComponents(fc->correctflux->theta, face, 0, components); + mth::Vector theta_coeffs(3); + theta_coeffs(0) = components[0]; + theta_coeffs(1) = components[1]; + theta_coeffs(2) = components[2]; + + + // 5. Evaluate and save corrected flux vector in an auxiliary field + int ftype = fc->mesh->getType(face); + PCU_ALWAYS_ASSERT(ftype == apf::Mesh::TRIANGLE); + int nfdofs = apf::countElementNodes(fc->correctflux->ef->getShape(), ftype); + apf::NewArray vectorshapes(nfdofs); + + apf::MeshElement* fme = apf::createMeshElement(fc->mesh, face); + apf::Element* fel = apf::createElement(fc->correctflux->ef, fme); + int int_order = 2*fc->correctflux->orderp1-1; + int np = apf::countIntPoints(fme, int_order); + int nc = apf::countComponents(fc->correctflux->correctedFlux); + + apf::Vector3 p, tet1xi, tet2xi, curl1, curl2, curl, + fnormal1, fnormal2, tk, tk1, tk2, vshape; + for (int n = 0; n < np; n++) { + + apf::getIntPoint(fme, int_order, n, p); + + // evaluate theta vector using theta coeffs + apf::Vector3 theta_vector, theta_vector1, theta_vector2; + theta_vector.zero(); theta_vector1.zero(); theta_vector2.zero(); + apf::NewArray triVectorShapes (nfdofs); + apf::getVectorShapeValues(fel, p, triVectorShapes); + for (int i = 0; i < nedges; i++) { + apf::Vector3 v = triVectorShapes[i]; + v = v * theta_coeffs[i]; + theta_vector += v; + } + + // orient face outward normals wrt tets and theta vectors + fnormal1 = computeFaceOutwardNormal(fc->mesh, firstTet, face, p); + fnormal2 = apf::Vector3(0.,0.,0.); + theta_vector1 = theta_vector; + if (up.n == 2) { + fnormal2 = computeFaceOutwardNormal(fc->mesh, secondTet, face, p); + theta_vector2 = theta_vector * -1.; + } + + curl.zero(); + // compute curl1 + tet1xi = apf::boundaryToElementXi(fc->mesh, face, firstTet, p); + apf::MeshElement* me1 = apf::createMeshElement(fc->mesh, firstTet); + apf::Element* el1 = apf::createElement(fc->correctflux->ef, me1); + apf::getCurl(el1, tet1xi, curl1); + apf::Vector3 temp1 = apf::cross(fnormal1, curl1); + curl += temp1; + apf::destroyElement(el1); + apf::destroyMeshElement(me1); + + // compute curl2 + if (up.n == 2) { + tet2xi = apf::boundaryToElementXi(fc->mesh, face, secondTet, p); + apf::MeshElement* me2 = apf::createMeshElement(fc->mesh, secondTet); + apf::Element* el2 = apf::createElement(fc->correctflux->ef, me2); + apf::getCurl(el2, tet2xi, curl2); + apf::Vector3 temp2 = apf::cross(fnormal2, curl2); + curl += (temp2 * -1.); + curl = curl * 1./2.; + apf::destroyElement(el2); + apf::destroyMeshElement(me2); + } + + tk = curl; + tk1 = tk; + apf::Vector3 theta_plus_tk1 = theta_vector1 + tk1; + + // get and set components in the auxiliary field + double* comp1 = new double[nc]; + apf::getComponents(fc->correctflux->correctedFlux, firstTet, n, comp1); + int id1 = tet1_pos * 3; + comp1[id1] = theta_plus_tk1[0]; + comp1[id1+1] = theta_plus_tk1[1]; + comp1[id1+2] = theta_plus_tk1[2]; + apf::setComponents(fc->correctflux->correctedFlux, firstTet, n, comp1); + delete [] comp1; + + if (up.n == 2) { + tk2 = tk * -1.; + apf::Vector3 theta_plus_tk2 = theta_vector2 + tk2; + double* comp2 = new double[nc]; + apf::getComponents(fc->correctflux->correctedFlux, secondTet, n, comp2); + int id2 = tet2_pos * 3; + comp2[id2] = theta_plus_tk2[0]; + comp2[id2+1] = theta_plus_tk2[1]; + comp2[id2+2] = theta_plus_tk2[2]; + apf::setComponents(fc->correctflux->correctedFlux, secondTet, n, comp2); + delete [] comp2; + } + } + apf::destroyElement(fel); + apf::destroyMeshElement(fme); +} + +class FaceCavityOp : public apf::CavityOp +{ +public: + FaceCavityOp(CorrectFlux* cf): + apf::CavityOp(cf->mesh) + { + setupFaceCavity(&face_cavity, cf); + } + virtual Outcome setEntity(apf::MeshEntity* e) + { + if (face_cavity.mesh->hasTag(e, face_cavity.correctflux->tag)) + return SKIP; + startFaceCavity(&face_cavity, e); + if ( ! buildFaceCavity(&face_cavity, this)) { + return REQUEST; + } + return OK; + } + virtual void apply() + { + computeCorrectedFlux(&face_cavity); + int n = VISITED; + face_cavity.mesh->setIntTag( + face_cavity.entity, face_cavity.correctflux->tag, &n); + } + FaceCavity face_cavity; +}; + +apf::Field* computeCorrectedFlux(apf::Field* ef, apf::Field* theta) +{ + int dim = apf::getMesh(ef)->getDimension(); + PCU_ALWAYS_ASSERT(dim==3); + int order = ef->getShape()->getOrder() + 1; // local BVPs require p+1 + int int_order = 2*order-1; + int nc = 4*3; // 1 flux vector per face + apf::Field* correctedFlux = createPackedField( + apf::getMesh(ef), "correctedFlux", nc, apf::getIPShape(dim, int_order)); + + CorrectFlux correctflux; + setupCorrectFlux(&correctflux, ef, theta, correctedFlux); + FaceCavityOp op (&correctflux); + op.applyToDimension(2); + return correctedFlux; + +} +} diff --git a/ree/reeEstimateError.cc b/ree/reeEstimateError.cc new file mode 100644 index 000000000..c3a4ab060 --- /dev/null +++ b/ree/reeEstimateError.cc @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include +#include "ree.h" + +namespace ree { + +static void computeResidualBLF(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, apf::Field* fp1, mth::Vector& blf) +{ + apf::FieldShape* fp1s = fp1->getShape(); + int type = mesh->getType(e); + PCU_ALWAYS_ASSERT(type == apf::Mesh::TET); + int nd = apf::countElementNodes(fp1s, type); + int dim = apf::getDimension(mesh, e); + double w; + + apf::NewArray curlshape(nd); + apf::NewArray vectorshape(nd); + mth::Matrix phys_curlshape(nd, dim); + mth::Matrix vectorShape(nd, dim); + mth::Vector curlcurl_vec (nd); + mth::Vector mass_vec (nd); + blf.resize(nd); + + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* fp1el = apf::createElement(fp1, me); + apf::Element* fel = apf::createElement(f, me); + int int_order = 2 * fp1s->getOrder(); + int np = apf::countIntPoints(me, int_order); + + // 1. Compute Curl Curl Integration + curlcurl_vec.zero(); + apf::Vector3 p, curl; + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, int_order, i, p); + double weight = apf::getIntWeight(me, int_order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + w = weight; + + apf::getCurl(fel, p, curl); + + // get curlshape values + fp1el->getShape()->getLocalVectorCurls(mesh, e, p, curlshape); + phys_curlshape.zero(); + for (int i = 0; i < nd; i++) + for (int j = 0; j < dim; j++) + for (int k = 0; k < dim; k++) + phys_curlshape(i,j) += curlshape[i][k] * J[k][j]; + + // multiply + mth::Vector V (nd); + V.zero(); + mth::Vector c (dim); + c(0) = curl[0]; c(1) = curl[1]; c(2) = curl[2]; + mth::multiply(phys_curlshape, c, V); + V *= w; + + curlcurl_vec += V; + } + + // 2. Compute Vector Mass Integration + int_order = 2 * fp1s->getOrder(); + np = apf::countIntPoints(me, int_order); + + mass_vec.zero(); + apf::Vector3 vvalue; + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, int_order, i, p); + double weight = apf::getIntWeight(me, int_order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + double jdet = apf::getJacobianDeterminant(J, dim); + + w = weight * jdet; + + apf::getVector(fel, p, vvalue); + + apf::getVectorShapeValues(fp1el, p, vectorshape); + vectorShape.zero(); + for (int i = 0; i < nd; i++) + for (int j = 0; j < dim; j++) + vectorShape(i,j) = vectorshape[i][j]; + + + mth::Vector V (nd); + V.zero(); + mth::Vector v (dim); + v(0) = vvalue[0]; v(1) = vvalue[1]; v(2) = vvalue[2]; + mth::multiply(vectorShape, v, V); + V *= w; + + mass_vec += V; + } + + // 3. get result + blf.zero(); + blf += curlcurl_vec; + blf += mass_vec; + + apf::destroyElement(fel); + apf::destroyElement(fp1el); + apf::destroyMeshElement(me); +} + +static void computeLambdaVector( + apf::Mesh* mesh, + apf::MeshEntity* e, + apf::Field* f, + apf::Field* fp1, + apf::Field* flux_field, + mth::Vector& lambda) +{ + apf::FieldShape* fp1s = fp1->getShape(); + int order = fp1s->getOrder(); + int etype = mesh->getType(e); + PCU_ALWAYS_ASSERT(etype == apf::Mesh::TET); + int nedofs = apf::countElementNodes(fp1s, etype); + lambda.resize(nedofs); + + int nc = apf::countComponents(flux_field); + + // get the downward faces of the element + apf::Downward faces; + int nf = mesh->getDownward(e, 2, faces); + + lambda.zero(); + // assemble lambda vector LOOP OVER DOWNWARD FACES + for (int ii = 0; ii < nf; ii++) { + apf::MeshEntity* face = faces[ii]; + + int ftype = mesh->getType(face); + PCU_ALWAYS_ASSERT(ftype == apf::Mesh::TRIANGLE); + int nfdofs = apf::countElementNodes(f->getShape(), ftype); + apf::NewArray vectorshape(nfdofs); + + apf::MeshElement* fme = apf::createMeshElement(mesh, face); + int np = apf::countIntPoints(fme, 2*order-1); + + // 4. Compute integral on the face + apf::Vector3 p; + for (int n = 0; n < np; n++) { + + apf::getIntPoint(fme, 2*order-1, n, p); + double weight = apf::getIntWeight(fme, 2*order-1, n); + apf::Matrix3x3 fJ; + apf::getJacobian(fme, p, fJ); + double jdet = apf::getJacobianDeterminant( + fJ, apf::getDimension(mesh, face)); + + // obtain corrected flux vector + double* comp = new double[nc]; + apf::getComponents(flux_field, e, n, comp); + apf::Vector3 theta_plus_tk; + int index = ii*3; + theta_plus_tk[0] = comp[index]; + theta_plus_tk[1] = comp[index+1]; + theta_plus_tk[2] = comp[index+2]; + delete [] comp; + + // compute p+1 order 3D vector shapes + apf::NewArray tetVectorShapes (nedofs); + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* el = apf::createElement(fp1, me); + apf::Vector3 tetxi = apf::boundaryToElementXi(mesh, face, e, p); + apf::getVectorShapeValues(el, tetxi, tetVectorShapes); + + apf::destroyElement(el); + apf::destroyMeshElement(me); + + // compute integral + double w = weight * jdet; + theta_plus_tk = theta_plus_tk * w; + + // matrix vector multiplication + for (int i = 0; i < nedofs; i++) + lambda(i) += theta_plus_tk * tetVectorShapes[i]; + + } // end integral loop + apf::destroyMeshElement(fme); + } // end face loop +} + +static void getEssentialElementNDDofs(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, apf::NewArray& ess_dofs, apf::NewArray& uness_dofs) +{ + apf::FieldShape* fs = f->getShape(); + int etype = mesh->getType(e); + PCU_ALWAYS_ASSERT(etype == apf::Mesh::TET); + int nedofs = apf::countElementNodes(fs, etype); + + apf::NewArray marker_list (nedofs); + for (int i = 0; i < nedofs; i++) + marker_list[i] = 0; + + // populate marker list by iterating over downward edges and faces + apf::Downward edges; + int nedges = mesh->getDownward(e, 1, edges); + PCU_ALWAYS_ASSERT(nedges == 6); + for (int i = 0; i < nedges; i++) { + int nodesOnEdge = fs->countNodesOn(mesh->getType(edges[i])); + if ( isOnDomainBoundary(mesh, edges[i]) ) { + for (int n = 0; n < nodesOnEdge; n++) { + marker_list[(i*nodesOnEdge)+n] = -1; + } + } + } + int nodesOnEdges = fs->countNodesOn(apf::Mesh::EDGE) * nedges; + + apf::Downward faces; + int nfaces = mesh->getDownward(e, 2, faces); + PCU_ALWAYS_ASSERT(nfaces == 4); + for (int i = 0; i < nfaces; i++) { + int nodesOnFace = fs->countNodesOn(mesh->getType(faces[i])); + if ( isOnDomainBoundary(mesh, faces[i]) ) { + for (int n = 0; n < nodesOnFace; n++) { + marker_list[(nodesOnEdges + i*nodesOnFace)+n] = -1; + } + } + } + + int num_marked = 0; + for (int i = 0; i < nedofs; i++) { + if (marker_list[i]) + num_marked++; + } + + // use marker list to get ess_dofs list + ess_dofs.resize(num_marked); + uness_dofs.resize(nedofs-num_marked); + int ess_dof_counter = 0; + int uness_dof_counter = 0; + for (int i = 0; i < nedofs; i++) { + if(marker_list[i]) + ess_dofs[ess_dof_counter++] = i; + else + uness_dofs[uness_dof_counter++] = i; + } +} + +/** + * Inputs: Matrix A, Vector X, Vector B, essential dofs, other dofs. + * Output: reduced matrix A, reduced rhs B. + */ +static void eliminateDBCs( + mth::Matrix const &A, + mth::Vector const &X, + mth::Vector const &B, + apf::NewArray const &ess_dofs, + apf::NewArray const &uness_dofs, + mth::Matrix &Anew, + mth::Vector &Bnew) +{ + int num_ess_dofs = ess_dofs.size(); + int num_uness_dofs = uness_dofs.size(); + + // 1. Remove rows of A corresponding to + // ess_dofs by copying into Anew + Anew.resize(num_uness_dofs, num_uness_dofs); + for(int rr = 0; rr < num_uness_dofs; rr++) { + int i = uness_dofs[rr]; + for(int cc = 0; cc < num_uness_dofs; cc++) { + int j = uness_dofs[cc]; + Anew(rr,cc) = A(i,j); + } + } + + // 2. Assemble new B + Bnew.resize(num_uness_dofs); + for(int i = 0; i < num_uness_dofs; i++) { + Bnew(i) = B(uness_dofs[i]); + } + + if (num_ess_dofs > 0) { + // 3. Subtract from Bnew: (Bnew -= Ae*Xe) + mth::Matrix Ae(num_uness_dofs, num_ess_dofs); + for(int rr = 0; rr < num_uness_dofs; rr++) { + int i = uness_dofs[rr]; + for(int cc = 0; cc < num_ess_dofs; cc++) { + int j = ess_dofs[cc]; + Ae(rr,cc) = A(i,j); + } + } + + mth::Vector Xe(num_ess_dofs); + for(int i = 0; i < num_ess_dofs; i++) { + Xe(i) = X(ess_dofs[i]); + } + + mth::Vector temp; + mth::multiply(Ae, Xe, temp); + Bnew -= temp; + } +} + +static double computeL2Error(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Vector const error_dofs) +{ + double error = 0.0; + + apf::FieldShape* fs = f->getShape(); + int type = mesh->getType(e); + PCU_ALWAYS_ASSERT(type == apf::Mesh::TET); + int nd = apf::countElementNodes(fs, type); + int dim = apf::getDimension(mesh, e); + double w; + + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* el = apf::createElement(f, me); + int order = 2 * fs->getOrder(); + int np = apf::countIntPoints(me, order); + + apf::NewArray vectorshape(nd); + + apf::Vector3 p; + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, order, i, p); + double weight = apf::getIntWeight(me, order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + double jdet = apf::getJacobianDeterminant(J, dim); + w = weight * jdet; + + apf::getVectorShapeValues(el, p, vectorshape); + mth::Matrix vectorShape (nd, dim); + for (int j = 0; j < nd; j++) + for (int k = 0; k < dim; k++) + vectorShape(j,k) = vectorshape[j][k]; + + mth::Matrix vectorShapeT (dim, nd); + mth::transpose(vectorShape, vectorShapeT); + + mth::Vector err_func; + mth::multiply(vectorShapeT, error_dofs, err_func); + + error += w * (err_func * err_func); + } + apf::destroyElement(el); + apf::destroyMeshElement(me); + + if (error < 0.0) + error = -error; + + return sqrt(error); +} + +apf::Field* computeErrorField(apf::Field* ef, apf::Field* correctedFlux) +{ + // 1. Create per-element SCALAR error field + apf::Field* error_field = apf::createIPField( + apf::getMesh(ef), "residual_error_field", apf::SCALAR, 1); + + // 2. Create p+1 order tet ND field + int order = ef->getShape()->getOrder(); + int orderp1 = order+1; + apf::Field* efp1 = apf::createField(apf::getMesh(ef), + "orderp1_nedelec_field", apf::SCALAR, apf::getNedelec(orderp1)); + apf::zeroField(efp1); + + // 2. iterate over all elements of the mesh + apf::MeshEntity* el; + apf::MeshIterator* it = apf::getMesh(ef)->begin(3); + while ((el = apf::getMesh(ef)->iterate(it))) { + + // 2(a). Assemble LHS element matrix + mth::Matrix A; + assembleElementMatrix( apf::getMesh(ef), el, efp1, A); + + // 2(b). Compute Bilinear Form Vector + mth::Vector blf; + computeResidualBLF(apf::getMesh(efp1), el, ef, efp1, blf); + + // 2(c). Compute Linear Form Vector + mth::Vector lf; + assembleDomainLFElementVector(apf::getMesh(efp1), el, efp1, lf); + + // 2(d). Compute Lambda Vector + mth::Vector lambda; + computeLambdaVector( + apf::getMesh(ef), el, ef, efp1, correctedFlux, lambda); + + // 2(e). Assemble RHS element vector = blf - lf - lambda + mth::Vector B(blf.size()); + B.zero(); + B += blf; B -= lf; B -= lambda; + + // 2(f). Get List of Essential Dofs + apf::NewArray ess_dofs, uness_dofs; + getEssentialElementNDDofs( + apf::getMesh(efp1), el, efp1, ess_dofs, uness_dofs); + + // 2(g). eliminate Dirichlet (Essential) Boundary Conditions + mth::Vector X, Bnew; + mth::Matrix Anew; + X.resize(B.size()); + X.zero(); // initialize X with exact DBC (e = 0.0) + eliminateDBCs(A, X, B, ess_dofs, uness_dofs, Anew, Bnew); + + // 2(h). Solve the reduced system + mth::Matrix Q, R; + mth::decomposeQR(Anew, Q, R); + mth::Vector Xnew; + mth::solveFromQR(Q, R, Bnew, Xnew); + + // 2(i). Recover the solution + mth::Vector error_dofs(B.size()); + for(unsigned int i = 0; i < ess_dofs.size(); i++) { + int index = ess_dofs[i]; + error_dofs(index) = X(index); + } + for(unsigned int i = 0; i < uness_dofs.size(); i++) { + int index = uness_dofs[i]; + error_dofs(index) = Xnew(i); + } + + // 2(j). Compute L2 Norm Error + double l2_error = computeL2Error(apf::getMesh(ef), el, efp1, error_dofs); + apf::setScalar(error_field, el, 0, l2_error); + } + apf::getMesh(ef)->end(it); + apf::destroyField(efp1); + + return error_field; +} + +apf::Field* estimateError(apf::Field* f) +{ + double t0 = PCU_Time(); + apf::Field* g = ree::equilibrateResiduals(f); + lion_eprint(1,"1/4: residuals equilibrated \n"); + apf::Field* theta = ree::computeFluxCorrection(f, g); + lion_eprint(1,"2/4: flux corrections computed \n"); + apf::destroyField(g); + PCU_Barrier(); + + apf::Field* correctedFlux = ree::computeCorrectedFlux(f, theta); + lion_eprint(1,"3/4: corrected flux field computed\n"); + apf::destroyField(theta); + + apf::Field* error_field = ree::computeErrorField(f, correctedFlux); + lion_eprint(1,"4/4: error computed \n"); + apf::destroyField(correctedFlux); + + double t1 = PCU_Time(); + if (!PCU_Comm_Self()) + lion_eprint(1,"REE: Error estimated in %f seconds\n",t1-t0); + + return error_field; +} +} diff --git a/ree/reeFluxCorrection.cc b/ree/reeFluxCorrection.cc new file mode 100644 index 000000000..f7e759cdc --- /dev/null +++ b/ree/reeFluxCorrection.cc @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ +#include "ree.h" + +namespace ree { + +struct QRDecomp { + mth::Matrix Q; + mth::Matrix R; +}; + +/* + * This function takes the NedelecField and g parameters field + * and returns the THETA field. It computes the 3 scalar parameters + * per face for the flux correction vectors. + */ +apf::Field* computeFluxCorrection(apf::Field* ef, apf::Field* g) +{ + apf::Mesh* mesh = apf::getMesh(ef); + apf::Field* THETA_Field = createPackedField( + mesh, "theta_field", 3, apf::getConstant(2)); + + apf::MeshEntity* face; + apf::MeshIterator* it = mesh->begin(2); + while ((face = mesh->iterate(it))) { + + // 1. assemble RHS vector + double components[3]; + apf::getComponents(g, face, 0, components); + + mth::Vector rhs(3); + apf::Downward edges; + int ne = mesh->getDownward(face, 1, edges); + for (int i = 0; i < ne; i++) { + int which, rotate; bool flip; + apf::getAlignment(mesh, face, edges[i], which, flip, rotate); + if (flip) + rhs(i) = -1. * components[i]; + else + rhs(i) = components[i]; + } + + // 2. assemble LHS face mass matrix + mth::Matrix M; + assembleVectorMassElementMatrix(mesh, face, ef, M); + + // 3. solve the system + QRDecomp qr; + mth::decomposeQR(M, qr.Q, qr.R); + mth::Vector theta; + mth::solveFromQR(qr.Q, qr.R, rhs, theta); + + // set solution vector on face field + components[0] = theta(0); + components[1] = theta(1); + components[2] = theta(2); + apf::setComponents(THETA_Field, face, 0, components); + } + mesh->end(it); + + return THETA_Field; +} + +} + + diff --git a/ree/reeResidualFunctionals.cc b/ree/reeResidualFunctionals.cc new file mode 100644 index 000000000..f26adea88 --- /dev/null +++ b/ree/reeResidualFunctionals.cc @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ +#include +#include +#include "apfElement.h" +#include "ree.h" + +namespace ree { + +enum {VISITED}; + +/* overall information useful during equilibration */ +struct Equilibration { + apf::Mesh* mesh; + /* mesh dimension, so far handling 3 only */ + int dim; + /* polynomial order of Nedelec space */ + int order; + /* input scalar field containing Nedelec dofs for solution electric field */ + apf::Field* ef; + /* tags each edge once it has been visited during the equilibration process */ + apf::MeshTag* tag; + /* output field containing correction values. + * currently 3 scalar values are stored on each face + * in order corresponding to downward edges of the face */ + apf::Field* g; +}; + +static void setupEquilibration(Equilibration* eq, apf::Field* f, apf::Field* g) +{ + eq->mesh = apf::getMesh(f); + eq->tag = eq->mesh->createIntTag("isVisited", 1); + eq->dim = eq->mesh->getDimension(); + eq->ef = f; + eq->order = f->getShape()->getOrder(); + eq->g = g; + + double zeros[3] = {0., 0., 0.}; + apf::MeshEntity* face; + apf::MeshIterator* it = eq->mesh->begin(2); + while ((face = eq->mesh->iterate(it))) { + apf::setComponents(eq->g, face, 0, zeros); + } + eq->mesh->end(it); +} + +struct QRDecomp { + mth::Matrix Q; + mth::Matrix R; +}; + +typedef std::vector EntityVector; + +struct EdgePatch { + apf::Mesh* mesh; + Equilibration* equilibration; + /* the edge entity around which the patch + is centered. a patch collects entities + (faces & tets) around this edge entity */ + apf::MeshEntity* entity; + bool isOnBdry; + EntityVector tets; + EntityVector faces; + mth::Matrix A; + mth::Matrix At; + mth::Matrix T; // T = A*At + 1 + mth::Vector b; + mth::Vector x; + QRDecomp qr; +}; + +static void setupEdgePatch(EdgePatch* ep, Equilibration* eq) +{ + ep->mesh = eq->mesh; + ep->equilibration = eq; + ep->entity = 0; +} + +static void startEdgePatch(EdgePatch* ep, apf::MeshEntity* e) +{ + ep->tets.clear(); + ep->faces.clear(); + ep->entity = e; + ep->isOnBdry = isOnDomainBoundary(ep->mesh, ep->entity); +} + +static void addEntityToPatch(EdgePatch* ep, apf::MeshEntity* e) +{ + if(ep->mesh->getType(e) == apf::Mesh::TRIANGLE) + ep->faces.push_back(e); + if(ep->mesh->getType(e) == apf::Mesh::TET) + ep->tets.push_back(e); +} + +static void addEntitiesToPatch( + EdgePatch* ep, apf::DynamicArray& es) +{ + for (std::size_t i=0; i < es.getSize(); ++i) + addEntityToPatch(ep, es[i]); +} + +static bool getInitialEdgePatch(EdgePatch* ep, apf::CavityOp* o) +{ + if ( ! o->requestLocality(&ep->entity,1)) + return false; + apf::DynamicArray adjacent; + ep->mesh->getAdjacent(ep->entity, 3, adjacent); + addEntitiesToPatch(ep, adjacent); + + ep->mesh->getAdjacent(ep->entity, 2, adjacent); + addEntitiesToPatch(ep, adjacent); + + return true; +} + +static bool buildEdgePatch(EdgePatch* ep, apf::CavityOp* o) +{ + if (!getInitialEdgePatch(ep, o)) return false; + return true; +} + +/* + * Reference: Leszek Demkowicz, Computing with hp-adaptive + * finite elements, vol2, equation (11.118, 11.119, 11.122). + */ +static void assembleEdgePatchLHS(EdgePatch* ep) +{ + int ne = ep->tets.size(); + int nf = ep->faces.size(); + if( isOnDomainBoundary(ep->mesh, ep->entity) ) { + ep->T.resize(ne+nf, ne+nf); + ep->T.zero(); + for (int i = 0; i < nf; i++) + ep->T(i,i) = 2.; + for (int i = 0; i < ne-1; i++) { + ep->T(i+nf,i) = 1.; ep->T(i+nf,i+1) = -1.; + ep->T(i,i+nf) = 1.; ep->T(i+1,i+nf) = -1.; + } + ep->T(ne+nf-1, ne-1) = 1.; ep->T(ne+nf-1, ne) = 1.; + ep->T(ne-1, ne+nf-1) = 1.; ep->T(ne, ne+nf-1) = 1.; + } + else if( ! isOnDomainBoundary(ep->mesh, ep->entity) ) { + ep->A.resize(ne, nf); + ep->A.zero(); + for (int i = 0; i < ne-1; i++) { + ep->A(i,i) = 1.; ep->A(i,i+1) = -1.; + } + ep->A(ne-1,0) = -1.; ep->A(ne-1,ne-1) = 1.; + // A is singular so do (A*At) + 1.0 + // to pick a particular solution + ep->At.resize(nf,ne); + mth::transpose(ep->A, ep->At); + mth::multiply(ep->A, ep->At, ep->T); + + for (int i = 0; i < ne; i++) + for (int j = 0; j < ne; j++) + ep->T(i,j) += 1.; + } + mth::decomposeQR(ep->T, ep->qr.Q, ep->qr.R); +} + +/* + * Performs Curl Curl integration using curl vector Nedelec shapes + */ +void assembleCurlCurlElementMatrix(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Matrix& elmat) +{ + apf::FieldShape* fs = f->getShape(); + int type = mesh->getType(e); + PCU_ALWAYS_ASSERT(type == apf::Mesh::TET); + int nd = apf::countElementNodes(fs, type); + int dim = apf::getDimension(mesh, e); + int dimc = (dim == 3) ? 3 : 1; + double w; + + apf::NewArray curlshape(nd); + mth::Matrix phys_curlshape(nd, dimc); + elmat.resize(nd,nd); + + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* el = apf::createElement(f, me); + int int_order = 2 * fs->getOrder() - 2; + int np = apf::countIntPoints(me, int_order); + + elmat.zero(); + apf::Vector3 p; + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, int_order, i, p); + double weight = apf::getIntWeight(me, int_order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + double jdet = apf::getJacobianDeterminant(J, dim); + w = weight / jdet; + + if (dim == 3) { + el->getShape()->getLocalVectorCurls(mesh, e, p, curlshape); + phys_curlshape.zero(); + for (int j = 0; j < nd; j++) + for (int k = 0; k < dim; k++) + for (int l = 0; l < dim; l++) + phys_curlshape(j,k) += curlshape[j][l] * J[l][k]; + } + mth::Matrix phys_curlshapeT; + mth::transpose(phys_curlshape, phys_curlshapeT); + + mth::Matrix M (nd, nd); + M.zero(); + mth::multiply(phys_curlshape, phys_curlshapeT, M); + M *= w; + elmat += M; + } + apf::destroyElement(el); + apf::destroyMeshElement(me); +} + +/* + * Performs Vector Vector Mass integration using vector Nedelec shapes + */ +void assembleVectorMassElementMatrix(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Matrix& elmat) +{ + apf::FieldShape* fs = f->getShape(); + int type = mesh->getType(e); + PCU_ALWAYS_ASSERT(type == apf::Mesh::TET || type == apf::Mesh::TRIANGLE); + int nd = apf::countElementNodes(fs, type); + int dim = apf::getDimension(mesh, e); + int sdim = mesh->getDimension(); + double w; + + apf::NewArray vectorshapes(nd); + elmat.resize(nd,nd); + + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* el = apf::createElement(f, me); + int int_order = 2 * fs->getOrder(); + int np = apf::countIntPoints(me, int_order); + + elmat.zero(); + apf::Vector3 p; + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, int_order, i, p); + double weight = apf::getIntWeight(me, int_order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + double jdet = apf::getJacobianDeterminant(J, dim); + w = weight * jdet; + + apf::getVectorShapeValues(el, p, vectorshapes); + mth::Matrix vectorShapes (nd, sdim); + for (int j = 0; j < nd; j++) + for (int k = 0; k < sdim; k++) + vectorShapes(j,k) = vectorshapes[j][k]; + + mth::Matrix vectorShapesT (sdim, nd); + mth::transpose(vectorShapes, vectorShapesT); + + mth::Matrix M (nd,nd); + M.zero(); + mth::multiply(vectorShapes, vectorShapesT, M); + M *= w; + elmat += M; + } + + apf::destroyElement(el); + apf::destroyMeshElement(me); +} + +void assembleElementMatrix(apf::Mesh* mesh, apf::MeshEntity*e, + apf::Field* f, mth::Matrix& elmat) +{ + mth::Matrix curl_elmat, mass_elmat; + assembleCurlCurlElementMatrix(mesh, e, f, curl_elmat); + assembleVectorMassElementMatrix(mesh, e, f, mass_elmat); + + elmat.resize(curl_elmat.rows(), curl_elmat.cols()); + elmat.zero(); + elmat += curl_elmat; + elmat += mass_elmat; +} + +/* + * computes local bilinear form integral restricted to + * an edge of a tet element + */ +static double getLocalEdgeBLF(EdgePatch* ep, apf::MeshEntity* tet) +{ + PCU_ALWAYS_ASSERT(ep->mesh->getType(tet) == apf::Mesh::TET); + // findIn edge in downward edges of tet + apf::Downward e; + int ne = ep->mesh->getDownward(tet, 1, e); + int ei = apf::findIn(e, ne, ep->entity); + // get Element Dofs + apf::MeshElement* me = apf::createMeshElement(ep->mesh, tet); + apf::Element* el = apf::createElement(ep->equilibration->ef, me); + int type = ep->mesh->getType(tet); + int nd = apf::countElementNodes(el->getFieldShape(), type); + apf::NewArray d (nd); + el->getElementNodeData(d); + mth::Vector dofs (nd); + for (int i = 0; i < nd; i++) + dofs(i) = d[i]; + // assemble curl curl element matrix + mth::Matrix curl_elmat; + assembleCurlCurlElementMatrix(ep->mesh, tet, + ep->equilibration->ef, curl_elmat); + // assemble vector mass element matrix + mth::Matrix mass_elmat; + assembleVectorMassElementMatrix(ep->mesh, tet, + ep->equilibration->ef, mass_elmat); + // add element matrices + mth::Matrix elmat(nd, nd); + elmat.zero(); + elmat += curl_elmat; + elmat += mass_elmat; + // multiply element matrix with element dofs + mth::Vector blf_integrals (nd); + mth::multiply(elmat, dofs, blf_integrals); + + apf::destroyElement(el); + apf::destroyMeshElement(me); + + // pick edge index from the resulting vector + // negation of negative ND dofs + int which, rotate; bool flip; + apf::getAlignment(ep->mesh, tet, ep->entity, which, flip, rotate); + if (flip) + blf_integrals(ei) = -1*blf_integrals(ei); + return blf_integrals(ei); +} + +/* + * Capability can be added in the future to allow the user + * to define a custom function */ +void pumiUserFunction(apf::Mesh* mesh, apf::MeshEntity* e, + const apf::Vector3& x, mth::Vector& f) +{ + double freq = 1.; + double kappa = freq * M_PI; + int dim = apf::getDimension(mesh, e); + if (dim == 3) { + f(0) = (1. + kappa * kappa) * sin(kappa * x[1]); + f(1) = (1. + kappa * kappa) * sin(kappa * x[2]); + f(2) = (1. + kappa * kappa) * sin(kappa * x[0]); + /*f(0) = 0.; + f(1) = 0.; + f(2) = 0.;*/ + } + else { + f(0) = (1. + kappa * kappa) * sin(kappa * x[1]); + f(1) = (1. + kappa * kappa) * sin(kappa * x[0]); + f(2) = 0.0; + } +} +void assembleDomainLFElementVector(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f, mth::Vector& elvect) +{ + apf::FieldShape* fs = f->getShape(); + int type = mesh->getType(e); + PCU_ALWAYS_ASSERT(type == apf::Mesh::TET); + int nd = apf::countElementNodes(fs, type); + int dim = apf::getDimension(mesh, e); + double w; + + apf::NewArray vectorshapes(nd); + elvect.resize(nd); + mth::Vector val (dim); + val.zero(); + apf::Vector3 p; + + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* el = apf::createElement(f, me); + int int_order = 2 * fs->getOrder(); + int np = apf::countIntPoints(me, int_order); + + elvect.zero(); + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, int_order, i, p); + double weight = apf::getIntWeight(me, int_order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + double jdet = apf::getJacobianDeterminant(J, dim); + w = weight * jdet; + + apf::getVectorShapeValues(el, p, vectorshapes); + apf::Vector3 global; + apf::mapLocalToGlobal(me, p, global); + pumiUserFunction(mesh, e, global, val); + val *= w; + + mth::Matrix vectorShapes (nd, dim); + for (int j = 0; j < nd; j++) + for (int k = 0; k < dim; k++) + vectorShapes(j,k) = vectorshapes[j][k]; + mth::Vector V (nd); + V.zero(); + mth::multiply(vectorShapes, val, V); + elvect += V; + } + + apf::destroyElement(el); + apf::destroyMeshElement(me); +} + +/* + * computes local linear form integral restricted to + * an edge of a tet element + */ +static double getLocalEdgeLF(EdgePatch* ep, apf::MeshEntity* tet) +{ + PCU_ALWAYS_ASSERT(ep->mesh->getType(tet) == apf::Mesh::TET); + // findIn edge in downward edges of tet + apf::Downward e; + int ne = ep->mesh->getDownward(tet, 1, e); + int ei = apf::findIn(e, ne, ep->entity); + // assemble Domain LF Vector + mth::Vector elvect; + assembleDomainLFElementVector(ep->mesh, tet, + ep->equilibration->ef, elvect); + int which, rotate; bool flip; + apf::getAlignment(ep->mesh, tet, ep->entity, which, flip, rotate); + if (flip) + elvect(ei) = -1*elvect(ei); + return elvect(ei); +} + +/* + * Given a tet and one of its faces, the vertex of the tet + * opposite to the given face is returned. + */ +static apf::MeshEntity* getTetOppVert( + apf::Mesh* m, apf::MeshEntity* t, apf::MeshEntity* f) +{ + apf::Downward fvs; + int fnv = m->getDownward(f, 0, fvs); + apf::Downward tvs; + int tnv = m->getDownward(t, 0, tvs); + PCU_ALWAYS_ASSERT(tnv == 4 && fnv == 3); + for (int i = 0; i < tnv; i++) { + if (apf::findIn(fvs, fnv, tvs[i]) == -1) + return tvs[i]; + } + return 0; +} + +/* + * Given a face and one of its edges, the vertex of the face + * opposite to the given edge is returned. + */ +static apf::MeshEntity* getFaceOppVert( + apf::Mesh* m, apf::MeshEntity* f, apf::MeshEntity* e) +{ + apf::Downward evs; + int env = m->getDownward(e, 0, evs); + apf::Downward fvs; + int fnv = m->getDownward(f, 0, fvs); + PCU_ALWAYS_ASSERT(env == 2 && fnv == 3); + for (int i = 0; i < fnv; i++) { + if (apf::findIn(evs, env, fvs[i]) == -1) + return fvs[i]; + } + return 0; +} + +static apf::Vector3 computeFaceNormal( + apf::Mesh* m, apf::MeshEntity* f, apf::Vector3 const& p) +{ + // Compute face normal using face Jacobian, + // so it can also be used for curved mesh elements + apf::MeshElement* me = apf::createMeshElement(m, f); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + apf::destroyMeshElement(me); + + apf::Vector3 g1 = J[0]; + apf::Vector3 g2 = J[1]; + apf::Vector3 n = apf::cross( g1, g2 ); + return n.normalize(); +} + +apf::Vector3 computeFaceOutwardNormal(apf::Mesh* m, + apf::MeshEntity* t, apf::MeshEntity* f, apf::Vector3 const& p) +{ + apf::Vector3 n = computeFaceNormal(m, f, p); + + // orient the normal outwards from the tet + apf::MeshEntity* oppVert = getTetOppVert(m, t, f); + apf::Vector3 vxi = apf::Vector3(0.,0.,0.); + + apf::Vector3 txi; + m->getPoint(oppVert, 0, txi); + + apf::MeshElement* fme = apf::createMeshElement(m, f); + apf::Vector3 pxi; + apf::mapLocalToGlobal(fme, p, pxi); + apf::destroyMeshElement(fme); + + apf::Vector3 pxiTotxi = txi - pxi; + if (pxiTotxi*n > 0) { + n = n*-1.; + } + return n; +} + +// TODO this needs generalisation for cases with internal model boundaries +// (i.e., interfaces) +bool isOnDomainBoundary(apf::Mesh* m, apf::MeshEntity* e) +{ + return m->getModelType(m->toModel(e)) < m->getDimension(); +} + +/* + * computes local flux integral restricted to + * an edge of a tet element + */ +static double getLocalFluxIntegral(EdgePatch* ep, apf::MeshEntity* tet) +{ + PCU_ALWAYS_ASSERT(ep->mesh->getType(tet) == apf::Mesh::TET); + + double fluxIntegral = 0.0; + // 1. get faces of the tet in the patch + apf::Downward f; + int nf = ep->mesh->getDownward(tet, 2, f); + std::vector patchFaces; + for (int i = 0; i < nf; i++) { + if(std::find(ep->faces.begin(), ep->faces.end(), f[i]) != ep->faces.end()) + patchFaces.push_back(f[i]); + } + PCU_ALWAYS_ASSERT(patchFaces.size() == 2); + + // 2. loop over the patch faces + for (unsigned int i = 0; i < patchFaces.size(); i++) { + double fluxFaceIntegral = 0.0; + // 3. get upward tets of the current face + apf::Up up; + apf::MeshEntity* currentFace = patchFaces[i]; + ep->mesh->getUp(currentFace, up); + if (isOnDomainBoundary(ep->mesh, currentFace)) + PCU_ALWAYS_ASSERT( up.n == 1); + else + PCU_ALWAYS_ASSERT( up.n == 2); + + apf::MeshEntity* firstTet = up.e[0]; + apf::MeshEntity* secondTet = nullptr; + if (up.n == 2) + secondTet = up.e[1]; + + // 4. findIn edge in downward edges of current face + apf::Downward e; + int ne = ep->mesh->getDownward(currentFace, 1, e); + PCU_ALWAYS_ASSERT(ne == 3); + int ei = apf::findIn(e, ne, ep->entity); + + // 5. count integration points for flux face integral + apf::FieldShape* fs = ep->equilibration->ef->getShape(); + int int_order = 2 * fs->getOrder(); + apf::MeshElement* fme = apf::createMeshElement(ep->mesh, currentFace); + apf::Element* fel = apf::createElement(ep->equilibration->ef, fme); + int np = apf::countIntPoints(fme, int_order); + + // loop over integration points + apf::Vector3 p, tet1xi, tet2xi, curl1, curl2, curl, + fn1, fn2, tk, vshape; + for (int n = 0; n < np; n++) { + apf::getIntPoint(fme, int_order, n, p); + double weight = apf::getIntWeight(fme, int_order, n); + apf::Matrix3x3 fJ; + apf::getJacobian(fme, p, fJ); + double jdet = apf::getJacobianDeterminant( + fJ, apf::getDimension(ep->mesh, currentFace)); + + // compute face outward normals wrt tets + if (tet == firstTet) { + fn1 = computeFaceOutwardNormal(ep->mesh, firstTet, currentFace, p); + fn2 = apf::Vector3(0.,0.,0.); + } + else { + fn1 = computeFaceOutwardNormal(ep->mesh, secondTet, currentFace, p); + fn2 = apf::Vector3(0.,0.,0.); + } + if (up.n == 2) { + if (tet == firstTet) + fn2 = computeFaceOutwardNormal(ep->mesh, secondTet, currentFace, p); + else + fn2 = computeFaceOutwardNormal(ep->mesh, firstTet, currentFace, p); + } + + curl.zero(); + // compute curl1 + tet1xi = apf::boundaryToElementXi(ep->mesh, currentFace, firstTet, p); + apf::MeshElement* me1 = apf::createMeshElement(ep->mesh, firstTet); + apf::Element* el1 = apf::createElement(ep->equilibration->ef, me1); + apf::getCurl(el1, tet1xi, curl1); + apf::Vector3 temp1 = apf::cross(fn1, curl1); + curl += temp1; + apf::destroyElement(el1); + apf::destroyMeshElement(me1); + + // compute curl2 + if (up.n == 2) { + tet2xi = apf::boundaryToElementXi(ep->mesh, currentFace, secondTet, p); + apf::MeshElement* me2 = apf::createMeshElement(ep->mesh, secondTet); + apf::Element* el2 = apf::createElement(ep->equilibration->ef, me2); + apf::getCurl(el2, tet2xi, curl2); + apf::Vector3 temp2 = apf::cross(fn2, curl2); + curl += (temp2 * -1.); + curl = curl * 1./2.; + apf::destroyElement(el2); + apf::destroyMeshElement(me2); + } + + // compute tk (inter-element averaged flux) + tk = curl; + + // compute vector shape + int type = apf::Mesh::TRIANGLE; + int nd = apf::countElementNodes(fs, type); + apf::NewArray vectorshapes (nd); + apf::getVectorShapeValues(fel, p, vectorshapes); + vshape = vectorshapes[ei]; + + // compute integral + fluxFaceIntegral += (tk * vshape) * weight * jdet; + } + apf::destroyElement(fel); + apf::destroyMeshElement(fme); + + fluxIntegral += fluxFaceIntegral; + } + return fluxIntegral; +} + +static void assembleEdgePatchRHS(EdgePatch* p) +{ + if (p->isOnBdry) { + p->b.resize(p->tets.size() + p->faces.size()); + p->b.zero(); + } + else { + p->b.resize(p->tets.size()); + p->b.zero(); + } + int ne = p->tets.size(); + int nf = p->faces.size(); + for (int i = 0; i < ne; i++) { + apf::MeshEntity* tet = p->tets[i]; + double blfIntegral = getLocalEdgeBLF(p, tet); + double lfIntegral = getLocalEdgeLF(p, tet); + double fluxIntegral = getLocalFluxIntegral(p, tet); + if(p->isOnBdry) + p->b(nf+i) = blfIntegral - lfIntegral - fluxIntegral; + else + p->b(i) = blfIntegral - lfIntegral - fluxIntegral; + } +} + +static apf::MeshEntity* getTetOppFaceSharingEdge( + apf::Mesh* m, apf::MeshEntity* t, apf::MeshEntity* f, apf::MeshEntity* e) +{ + apf::MeshEntity* fs[4]; + m->getDownward(t, 2, fs); + for (int i = 0; i < 4; i++) { + if (fs[i] == f) continue; + apf::MeshEntity* es[3]; + m->getDownward(fs[i], 1, es); + if (apf::findIn(es, 3, e) > -1) + return fs[i]; + } + return 0; +} + +/* + * Orders tets and faces in an edge cavity in a + * clockwise (or ccw) direction around the edge. + */ +static void getOrderedTetsandFaces(apf::Mesh* mesh, apf::MeshEntity* edge, + EntityVector& tets, EntityVector& faces) +{ + tets.clear(); + faces.clear(); + if( ! isOnDomainBoundary(mesh, edge) ) { + apf::MeshEntity* currentFace = mesh->getUpward(edge, 0); + apf::Up up; + mesh->getUp(currentFace, up); + PCU_ALWAYS_ASSERT(up.n == 2); + apf::MeshEntity* firstTet = up.e[0]; + apf::MeshEntity* nextTet = up.e[1]; + tets.push_back(firstTet); + apf::MeshEntity* firstFace = getTetOppFaceSharingEdge(mesh, firstTet, + currentFace, edge); + faces.push_back(firstFace); + + while (nextTet != firstTet) { + tets.push_back(nextTet); + faces.push_back(currentFace); + currentFace = getTetOppFaceSharingEdge(mesh, nextTet, currentFace, edge); + PCU_ALWAYS_ASSERT(currentFace); + apf::Up up; + mesh->getUp(currentFace, up); + PCU_ALWAYS_ASSERT(up.n == 2); + if (nextTet != up.e[0]) + nextTet = up.e[0]; + else + nextTet = up.e[1]; + } + } + else { + apf::Up up; + mesh->getUp(edge, up); + apf::MeshEntity* firstFace = nullptr; + for (int i = 0; i < up.n; i++) { + if ( isOnDomainBoundary(mesh, up.e[i]) ) { + firstFace = up.e[i]; break; + } + } + PCU_ALWAYS_ASSERT(firstFace); + faces.push_back(firstFace); + mesh->getUp(firstFace, up); + PCU_ALWAYS_ASSERT(up.n == 1); + apf::MeshEntity* firstTet = up.e[0]; + tets.push_back(firstTet); + + apf::MeshEntity* nextFace = getTetOppFaceSharingEdge(mesh, firstTet, + firstFace, edge); + apf::MeshEntity* nextTet = firstTet; + mesh->getUp(nextFace, up); + while( up.n == 2) { + faces.push_back(nextFace); + if (nextTet != up.e[0]) + nextTet = up.e[0]; + else + nextTet = up.e[1]; + tets.push_back(nextTet); + + nextFace = getTetOppFaceSharingEdge(mesh, nextTet, nextFace, edge); + mesh->getUp(nextFace, up); + } + faces.push_back(nextFace); + } +} + +void getClockwiseTetsandFaces(EdgePatch* p) +{ + if (p->isOnBdry) { + if (p->tets.size() > 1) { + apf::MeshEntity* secondFace = p->faces[1]; + apf::MeshEntity* firstTet = p->tets[0]; + apf::MeshEntity* secondTet = p->tets[1]; + + apf::Downward firstTetFaces, secondTetFaces; + int n_firstTetFaces = p->mesh->getDownward(firstTet, 2, firstTetFaces); + int n_secondTetFaces = p->mesh->getDownward(secondTet, 2, secondTetFaces); + int fi1 = apf::findIn(firstTetFaces, n_firstTetFaces, secondFace); + int fi2 = apf::findIn(secondTetFaces, n_secondTetFaces, secondFace); + PCU_ALWAYS_ASSERT(fi1 != -1 && fi2 != -1); + + // first tet opp vertex crd + apf::MeshEntity* firstTetOppVert = getTetOppVert( + p->mesh, firstTet, secondFace); + apf::Vector3 firstTetOppVertCrd; + p->mesh->getPoint(firstTetOppVert, 0, firstTetOppVertCrd); + + // second tet opp vertex crd + apf::MeshEntity* secondTetOppVert = getTetOppVert( + p->mesh, secondTet, secondFace); + apf::Vector3 secondTetOppVertCrd; + p->mesh->getPoint(secondTetOppVert, 0, secondTetOppVertCrd); + + // normal to the face + apf::Downward edge_vertices; + p->mesh->getDownward(p->entity, 0, edge_vertices); + apf::Vector3 p0, p1, p2; + p->mesh->getPoint(edge_vertices[0], 0, p0); + p->mesh->getPoint(edge_vertices[1], 0, p1); + apf::MeshEntity* faceOppVert = getFaceOppVert( + p->mesh, secondFace, p->entity); + p->mesh->getPoint(faceOppVert, 0, p2); + + apf::Vector3 normal = apf::cross(p1-p0, p2-p0); + + // direction vectors from p0 to opp tet verts + apf::Vector3 vFirst = firstTetOppVertCrd - p0; + apf::Vector3 vLast = secondTetOppVertCrd - p0; + + if ((vFirst * normal > 0) && (vLast * normal < 0)) { + // reverse list of tets and faces + std::reverse(p->tets.begin(), p->tets.end()); + std::reverse(p->faces.begin(), p->faces.begin()); + } + } + } + else { + apf::MeshEntity* firstFace = p->faces[0]; + apf::MeshEntity* firstTet = p->tets[0]; + apf::MeshEntity* lastTet = p->tets[p->tets.size()-1]; + + apf::Downward firstTetFaces, lastTetFaces; + int n_firstTetFaces = p->mesh->getDownward(firstTet, 2, firstTetFaces); + int n_lastTetFaces = p->mesh->getDownward(lastTet, 2, lastTetFaces); + int fi1 = apf::findIn(firstTetFaces, n_firstTetFaces, firstFace); + int filast = apf::findIn(lastTetFaces, n_lastTetFaces, firstFace); + PCU_ALWAYS_ASSERT(fi1 != -1 && filast != -1); + + // first tet opp vertex crd + apf::MeshEntity* firstTetOppVert = getTetOppVert( + p->mesh, firstTet, firstFace); + apf::Vector3 firstTetOppVertCrd; + p->mesh->getPoint(firstTetOppVert, 0, firstTetOppVertCrd); + + // last tet opp vertex crd + apf::MeshEntity* lastTetOppVert = getTetOppVert( + p->mesh, lastTet, firstFace); + apf::Vector3 lastTetOppVertCrd; + p->mesh->getPoint(lastTetOppVert, 0, lastTetOppVertCrd); + + // normal to the face + apf::Downward edge_vertices; + p->mesh->getDownward(p->entity, 0, edge_vertices); + apf::Vector3 p0, p1, p2; + p->mesh->getPoint(edge_vertices[0], 0, p0); + p->mesh->getPoint(edge_vertices[1], 0, p1); + apf::MeshEntity* faceOppVert = getFaceOppVert( + p->mesh, firstFace, p->entity); + p->mesh->getPoint(faceOppVert, 0, p2); + + apf::Vector3 normal = apf::cross(p1-p0, p2-p0); + + // direction vectors from p0 to opp tet verts + apf::Vector3 vFirst = firstTetOppVertCrd - p0; + apf::Vector3 vLast = lastTetOppVertCrd - p0; + + if ((vFirst * normal > 0) && (vLast * normal < 0)) { + // reverse list of tets and faces + std::reverse(p->tets.begin(), p->tets.end()); + std::reverse(p->faces.begin(), p->faces.begin()); + } + } +} + +static void runErm(EdgePatch* ep) +{ + getOrderedTetsandFaces(ep->mesh, ep->entity, ep->tets, ep->faces); + //getClockwiseTetsandFaces(ep); + assembleEdgePatchLHS(ep); + assembleEdgePatchRHS(ep); + mth::solveFromQR(ep->qr.Q, ep->qr.R, ep->b, ep->x); + + if (!ep->isOnBdry) { // solve At*mu = g for g + mth::Vector temp(ep->tets.size()); + mth::multiply(ep->At, ep->x, temp); + for (size_t i = 0; i < ep->tets.size(); i++) { + ep->x(i) = temp(i); + } + } + + int nf = ep->faces.size(); + for(int i = 0; i < nf; i++) { + apf::MeshEntity* face = ep->faces[i]; + + apf::Downward e; + int ned = ep->mesh->getDownward(face, 1, e); + int ei = apf::findIn(e, ned, ep->entity); + PCU_ALWAYS_ASSERT(ned == 3 && ei != -1); + + double components[3]; + apf::getComponents(ep->equilibration->g, face, 0, components); + components[ei] = ep->x(i); + apf::setComponents(ep->equilibration->g, face, 0, components); + } +} + + +class EdgePatchOp : public apf::CavityOp +{ +public: + EdgePatchOp(Equilibration* eq): + apf::CavityOp(eq->mesh) + { + setupEdgePatch(&edgePatch, eq); + } + virtual Outcome setEntity(apf::MeshEntity* e) + { + if (edgePatch.mesh->hasTag(e, edgePatch.equilibration->tag)) + return SKIP; + startEdgePatch(&edgePatch, e); + if ( ! buildEdgePatch(&edgePatch, this)) + return REQUEST; + return OK; + } + virtual void apply() + { + runErm(&edgePatch); + int n = VISITED; + edgePatch.mesh->setIntTag( + edgePatch.entity, edgePatch.equilibration->tag, &n); + } + EdgePatch edgePatch; +}; + +apf::Field* equilibrateResiduals(apf::Field* f) +{ + apf::Field* g = createPackedField( + apf::getMesh(f), "g", 3, apf::getConstant(2) ); + Equilibration equilibration; + setupEquilibration(&equilibration, f, g); + EdgePatchOp op(&equilibration); + op.applyToDimension(1); // edges + + apf::MeshEntity* ent; + apf::MeshIterator* it = apf::getMesh(f)->begin(1); + while ((ent = apf::getMesh(f)->iterate(it))) { + apf::getMesh(f)->removeTag(ent, equilibration.tag); + } + apf::getMesh(f)->end(it); + apf::getMesh(f)->destroyTag(equilibration.tag); + + return g; +} + + +} diff --git a/ree/reeSizeField.cc b/ree/reeSizeField.cc new file mode 100644 index 000000000..00765a01a --- /dev/null +++ b/ree/reeSizeField.cc @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2011 Scientific Computation Research Center + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ +#include "apfElement.h" +#include +#include "ree.h" + +namespace ree { + + +struct Sizefield { + apf::Mesh* mesh; + /* the polynomial order of the solution electric Nedelec field */ + int order; + /* the input solution electric field obtained after the FEM solve */ + apf::Field* ef; + /* the per element error field obtained after executing the implicit + residual error estimator */ + apf::Field* errorField; + double size_factor; + /* target error = sum of all element erros / total number of elements */ + double target_error; + double alpha; + double beta; + /* a temporary field storing desired sizes at elements */ + apf::Field* element_size; + /* the resulting size field, recovered from the element_size field + (using a local average recovery method much weaker than SPR) */ + apf::Field* size; +}; + +static void setupSizefield( + Sizefield* sz, + apf::Field* ef, + apf::Field* error_field, + int n, + double alpha, + double beta) +{ + sz->mesh = ef->getMesh(); + sz->order = ef->getShape()->getOrder(); + sz->errorField = error_field; + sz->size_factor = 0; + + apf::MeshEntity* entity; + int d = sz->mesh->getDimension(); + apf::MeshIterator* it = sz->mesh->begin(d); + double total_error = 0.; + while ((entity = sz->mesh->iterate(it))) { + total_error += apf::getScalar(sz->errorField, entity, 0); + } + sz->mesh->end(it); + sz->target_error = total_error / (sz->mesh->count(d)* n); + + sz->alpha = alpha; + sz->beta = beta; + sz->element_size = 0; + sz->size = 0; +} + +static double getCurrentSize(apf::Mesh* m, apf::MeshEntity* e) +{ + /* right now minimum edge length is the formula... */ + apf::Downward edges; + int ne = m->getDownward(e,1,edges); + double h = std::numeric_limits::max(); + for (int i=0; i < ne; ++i) + h = std::min(h, measure(m, edges[i])); + return h; +} + +static double getDesiredSize(Sizefield* sz, apf::MeshEntity* entity) +{ + double element_error = apf::getScalar(sz->errorField, entity, 0); + double h = getCurrentSize(sz->mesh, entity); + + double p = sz->order; + int d = sz->mesh->getDimension(); + sz->size_factor = pow(sz->target_error/element_error, (2. / (2.*p + d))); + + double h_new = h * sz->size_factor; + if (h_new < sz->alpha*h) h_new = sz->alpha*h; + if (h_new > sz->beta*h) h_new = sz->beta*h; + return h_new; +} + +static void getElementSizeField(Sizefield* sz) +{ + apf::Field* eSize = apf::createStepField( + sz->mesh, "esize", apf::SCALAR); + int d = sz->mesh->getDimension(); + apf::MeshEntity* entity; + apf::MeshIterator* elements = sz->mesh->begin(d); + while ((entity = sz->mesh->iterate(elements))) { + double h = getDesiredSize(sz, entity); + apf::setScalar(eSize, entity, 0, h); + } + sz->mesh->end(elements); + sz->element_size = eSize; +} + + +void averageToVertex(apf::Field* ef, apf::Field* vf, apf::MeshEntity* ent) +{ + apf::Mesh* m = apf::getMesh(ef); + apf::Adjacent elements; + m->getAdjacent(ent, m->getDimension(), elements); + double s=0; + for (std::size_t i=0; i < elements.getSize(); ++i) + s += apf::getScalar(ef, elements[i], 0); + s /= elements.getSize(); + apf::setScalar(vf, ent, 0, s); +} + +class AverageOp : public apf::CavityOp +{ +public: + AverageOp(Sizefield* sz): + apf::CavityOp(sz->mesh), + sizefield(sz), + entity(0) + { + } + virtual Outcome setEntity(apf::MeshEntity* e) + { + entity = e; + if (apf::hasEntity(sizefield->size, entity)) + return SKIP; + if ( !requestLocality(&entity,1)) + return REQUEST; + return OK; + } + virtual void apply() + { + averageToVertex(sizefield->element_size, + sizefield->size, entity); + } + Sizefield* sizefield; + apf::MeshEntity* entity; +}; + +void averageSizeField(Sizefield* sz) +{ + sz->size = apf::createLagrangeField(sz->mesh, "size", apf::SCALAR, 1); + AverageOp op(sz); + op.applyToDimension(0); +} + +apf::Field* getTargetEMSizeField( + apf::Field* ef, + apf::Field* error_field, + int n, + double alpha /*= 0.25*/, + double beta /*= 2.0*/) +{ + double t0 = PCU_Time(); + Sizefield sz; + setupSizefield(&sz, ef, error_field, n, alpha, beta); + getElementSizeField(&sz); + averageSizeField(&sz); + apf::destroyField(sz.element_size); + double t1 = PCU_Time(); + if (!PCU_Comm_Self()) + lion_eprint(1,"EM: SizeField computed in %f seconds\n",t1-t0); + return sz.size; +} + +} diff --git a/stk/apfSTK.cc b/stk/apfSTK.cc index d59b84788..d0dc5d853 100644 --- a/stk/apfSTK.cc +++ b/stk/apfSTK.cc @@ -514,7 +514,7 @@ class StkBridge stk::mesh::EntityRank rank = stk::topology::NODE_RANK; if (isQP) rank = stk::topology::ELEMENT_RANK; - bulkData->get_buckets(rank, overlapSelector, buckets); + buckets = bulkData->get_buckets(rank, overlapSelector); GlobalMap* globalIdsToEnts = &globalIdsToVerts; if (isQP) globalIdsToEnts = &globalIdsToElems; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 709492f91..56142ac32 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,7 +32,6 @@ util_exe_func(mktopomodel mktopomodel.cc) util_exe_func(modelInfo modelInfo.cc) # Mesh generation utilities -util_exe_func(box box.cc) util_exe_func(uniform uniform.cc) util_exe_func(classifyThenAdapt classifyThenAdapt.cc) util_exe_func(refine2x refine2x.cc) @@ -51,6 +50,7 @@ util_exe_func(from_neper neper.cc) util_exe_func(from_ugrid ugrid.cc) util_exe_func(nektar_align nektar_align.cc) util_exe_func(icesheet icesheet.cc) +util_exe_func(matchedNodeElmReader matchedNodeElmReader.cc) if(ENABLE_OMEGA_H) util_exe_func(smb2osh smb2osh.cc) util_exe_func(osh2smb osh2smb.cc) @@ -68,6 +68,7 @@ endif() util_exe_func(measureAnisoStats measureAnisoStats.cc) util_exe_func(measureIsoStats measureIsoStats.cc) util_exe_func(visualizeAnisoSizes visualizeAnisoSizes.cc) +test_exe_func(bezierShapeEval bezierShapeEval.cc) if(ENABLE_SIMMETRIX) util_exe_func(runSimxAnisoAdapt runSimxAnisoAdapt.cc) endif() @@ -112,7 +113,7 @@ util_exe_func(reorder reorder.cc) util_exe_func(fixshape fixshape.cc) util_exe_func(fixlayer fixlayer.cc) util_exe_func(fixDisconnected fixDisconnected.cc) -util_exe_func(scale scale.cc) +util_exe_func(scale scale.cc) # Phasta utilities util_exe_func(chef ../phasta/chef.cc) @@ -132,7 +133,11 @@ if(SIMMODSUITE_SimAdvMeshing_FOUND) target_compile_definitions(cut_interface PRIVATE -DHAVE_SIMADVMESHING) endif() +#PUMIPic print partition utility +util_exe_func(print_pumipic_partition print_pumipic_partition.cc) + # Unit tests / functionality regression tests +test_exe_func(swapDoubles swapDoubles.cc) test_exe_func(qr qr.cc) test_exe_func(eigen_test eigen_test.cc) test_exe_func(integrate integrate.cc) @@ -158,6 +163,7 @@ test_exe_func(fusion3 fusion3.cc) test_exe_func(1d 1d.cc) test_exe_func(base64 base64.cc) test_exe_func(test_pumi pumi.cc) +test_exe_func(pumiLoadMesh pumiLoadMesh.cc) test_exe_func(xgc_split xgc_split.cc) test_exe_func(ma_insphere ma_insphere.cc) test_exe_func(ma_test ma_test.cc) @@ -175,6 +181,9 @@ test_exe_func(test_scaling test_scaling.cc) test_exe_func(mixedNumbering mixedNumbering.cc) test_exe_func(test_verify test_verify.cc) test_exe_func(hierarchic hierarchic.cc) +test_exe_func(nedelecShapes nedelecShapes.cc) +test_exe_func(L2Shapes L2Shapes.cc) +test_exe_func(H1Shapes H1Shapes.cc) test_exe_func(poisson poisson.cc) test_exe_func(ph_adapt ph_adapt.cc) test_exe_func(assert_timing assert_timing.cc) @@ -182,6 +191,7 @@ test_exe_func(create_mis create_mis.cc) test_exe_func(fieldReduce fieldReduce.cc) test_exe_func(test_integrator test_integrator.cc) test_exe_func(test_matrix_gradient test_matrix_grad.cc) + if(ENABLE_DSP) test_exe_func(graphdist graphdist.cc) test_exe_func(moving moving.cc) @@ -189,10 +199,14 @@ endif() if(ENABLE_SIMMETRIX) test_exe_func(curvetest curvetest.cc) test_exe_func(curve_to_bezier curve_to_bezier.cc) + test_exe_func(make_all_cavities makeAllCavities.cc) test_exe_func(inClosureOf_test inClosureOf_test.cc) test_exe_func(degenerate_test degenerateSurfs.cc) test_exe_func(simZBalance simZBalance.cc) test_exe_func(crack_test crack_test.cc) + test_exe_func(residualErrorEstimation_test residualErrorEstimation_test.cc) + test_exe_func(highOrderSolutionTransfer highOrderSolutionTransfer.cc) + test_exe_func(highOrderSizeFields highOrderSizeFields.cc) endif() # send all the newly added utility executable targets @@ -200,6 +214,7 @@ endif() # in scorec-config.cmake set(TARGETS ${TARGETS} PARENT_SCOPE) +include(smokeTesting.cmake) # Add the nightly testing script, if needed if(IS_TESTING) include(testing.cmake) diff --git a/test/H1Shapes.cc b/test/H1Shapes.cc new file mode 100644 index 000000000..d69b978f1 --- /dev/null +++ b/test/H1Shapes.cc @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +// User defined vector functions E(x,y,z) of order up to 6 +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p); +void M_exact(const apf::Vector3& x, apf::Vector3& value, int p); + +void testH1( + apf::Mesh2* m, + const apf::Vector3& testXi, + int ndOrder, int exactOrder, + int dataType); /* apf::VECTOR or apf::MATRIX */ + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + + lion_set_verbosity(1); + + if (argc != 3) { + if(0==PCU_Comm_Self()) + std::cerr << "usage: " << argv[0] + << " \n"; + return EXIT_FAILURE; + } + + gmi_register_mesh(); + gmi_register_null(); + + gmi_model* g = gmi_load(argv[1]); + apf::Mesh2* m = apf::loadMdsMesh(g,argv[2]); + m->verify(); + + // test fields interpolating a user-defined vector field + for (int i = 1; i <= 6; i++) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "----TESTING VECTOR FIELD OF ORDER %d----\n", i); + testH1( + m, /* mesh */ + apf::Vector3(1./7., 1./11., 1./3.), /* test point */ + i, /* order of h1 field */ + i, /* order of test field */ + apf::VECTOR); + } + + // test fields interpolating a user-defined matrix field + for (int i = 1; i <= 6; i++) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "----TESTING MATRIX FIELD OF ORDER %d----\n", i); + testH1( + m, /* mesh */ + apf::Vector3(1./7., 1./11., 1./3.), /* test point */ + i, /* order of h1 field */ + i, /* order of test field */ + apf::MATRIX); + } + + apf::destroyMesh(m); + PCU_Comm_Free(); + MPI_Finalize(); +} + +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p) +{ + // Polynomial coefficients for each component of exact vector field + double a[7] = { 1.0, -1.0, 2., -2., -1.0, 1.0, -1.0}; + double b[7] = {-2.0, 1.0, -2., 2., -1.0, -1.0, 1.0}; + double c[7] = { 3.0, 0.0, -1., 100., -1.0, 1.0, 0.0}; + + value[0] = 0.0; + value[1] = 0.0; + value[2] = 0.0; + for (int i = p; i >= 0; i--) { + value[0] += pow(x[0],p)*a[p]; + value[1] += pow(x[1],p)*b[p]; + value[2] += pow(x[2],p)*c[p]; + } +} + +void M_exact(const apf::Vector3& x, apf::Matrix3x3& mat, int p) +{ + // Polynomial coefficients for each component of exact vector field + double a[7] = { 1.0, -1.0, 2., -2., -1.0, 1.0, -1.0}; + double b[7] = {-2.0, 1.0, -2., 2., -1.0, -1.0, 1.0}; + double c[7] = { 3.0, 0.0, -1., 100., -1.0, 1.0, 0.0}; + + apf::Vector3 value; + value[0] = 0.0; + value[1] = 0.0; + value[2] = 0.0; + for (int i = p; i >= 0; i--) { + value[0] += pow(x[0],p)*a[p]; + value[1] += pow(x[1],p)*b[p]; + value[2] += pow(x[2],p)*c[p]; + } + + mat[0] = value; + mat[1] = apf::Vector3(value[2], 1., 0.); + mat[2] = apf::Vector3(0., value[0], 1.); +} + + + +void testH1( + apf::Mesh2* m, + const apf::Vector3& testXi, + int ndOrder, int exactOrder, + int dataType) +{ + PCU_ALWAYS_ASSERT(dataType == apf::VECTOR || dataType == apf::MATRIX); + // Loop over all nodes and set scalar dofs. + int dim = m->getDimension(); + apf::Field* h1Field; + + if (dataType == apf::VECTOR) + h1Field = apf::createField( + m, "h1_test", apf::VECTOR, apf::getH1Shape(ndOrder)); + else + h1Field = apf::createField( + m, "h1_test", apf::MATRIX, apf::getH1Shape(ndOrder)); + + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!h1Field->getShape()->countNodesOn(apf::Mesh::simplexTypes[d])) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "no nodes in dimension %d\n", d); + continue; + } + else + if(0==PCU_Comm_Self()) + lion_oprint(1, "computing dofs for dimension %d\n", d); + it = m->begin(d); + while( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = h1Field->getShape()->countNodesOn(type); + apf::MeshElement* me = apf::createMeshElement(m, ent); + for (int i = 0; i < non; i++) + { + apf::Vector3 xi, p; + h1Field->getShape()->getNodeXi(type, i, xi); + apf::mapLocalToGlobal(me, xi, p); + if (dataType == apf::VECTOR) { + apf::Vector3 value; + E_exact(p, value, exactOrder); + apf::setVector(h1Field, ent, i, value); + } + else { + apf::Matrix3x3 mat; + M_exact(p, mat, exactOrder); + apf::setMatrix(h1Field, ent, i, mat); + } + } + apf::destroyMeshElement(me); + } + m->end(it); + } + + + // Verify that interpolated solution field agrees with exact field. + for (int d = 0; d <= dim; d++) { + + double L2Error = 0.; + it = m->begin(d); + while( (ent = m->iterate(it)) ) { + apf::MeshElement* me = apf::createMeshElement(m, ent); + apf::Vector3 x; + apf::mapLocalToGlobal(me, testXi, x); + apf::Element* el = apf::createElement(h1Field, me); + double err; + if (dataType == apf::VECTOR) { + apf::Vector3 eFieldExact; + E_exact(x, eFieldExact, exactOrder); + apf::Vector3 eFieldValue; + apf::getVector(el, testXi, eFieldValue); + err = ((eFieldValue - eFieldExact) * (eFieldValue - eFieldExact)); + err /= (eFieldExact * eFieldExact); // normalization factor + } + else { + apf::Matrix3x3 mFieldExact; + M_exact(x, mFieldExact, exactOrder); + apf::Matrix3x3 mFieldValue; + apf::getMatrix(el, testXi, mFieldValue); + err = apf::getInnerProduct( + mFieldValue - mFieldExact, + mFieldValue - mFieldExact); + err /= apf::getInnerProduct(mFieldExact, mFieldExact); + } + L2Error += err; + apf::destroyElement(el); + apf::destroyMeshElement(me); + } + m->end(it); + + // check for field interpolation + if(0==PCU_Comm_Self()) { + lion_oprint(1, "L2Error for entities of dimension %d is %e\n", d, L2Error); + } + PCU_ALWAYS_ASSERT_VERBOSE(L2Error < 1.e-12, + "Fields were not interpolated correctly!"); + + } + m->removeField(h1Field); + apf::destroyField(h1Field); +} diff --git a/test/L2Shapes.cc b/test/L2Shapes.cc new file mode 100644 index 000000000..a2db1e071 --- /dev/null +++ b/test/L2Shapes.cc @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; + +// User defined vector functions E(x,y,z) of order up to 6 +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p); + +void testL2( + apf::Mesh2* m, + const apf::Vector3& testXi, + int ndOrder, int exactOrder); + +void testL2writeNative( + apf::Mesh2* m, + int ndOrder, int exactOrder); + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + + lion_set_verbosity(1); + + if (argc != 3) { + if(0==PCU_Comm_Self()) + std::cerr << "usage: " << argv[0] + << " \n"; + return EXIT_FAILURE; + } + + gmi_register_mesh(); + gmi_register_null(); + + gmi_model* g = gmi_load(argv[1]); + apf::Mesh2* m = apf::loadMdsMesh(g,argv[2]); + m->verify(); + + for (int i = 0; i <= 6; i++) { + testL2( + m, /* mesh */ + apf::Vector3(1./7., 1./11., 1./3.), /* test point */ + i, /* order of l2 field */ + i); /* order of test field */ + } + + testL2writeNative(m, 3, 3); + + apf::destroyMesh(m); + PCU_Comm_Free(); + MPI_Finalize(); +} + +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p) +{ + // Polynomial coefficients for each component of exact vector field + double a[7] = { 1.0, -1.0, 2., -2., -1.0, 1.0, -1.0}; + double b[7] = {-2.0, 1.0, -2., 2., -1.0, -1.0, 1.0}; + double c[7] = { 3.0, 0.0, -1., 1., -1.0, 1.0, 0.0}; + + value[0] = 0.0; + value[1] = 0.0; + value[2] = 0.0; + for (int i = p; i >= 0; i--) { + value[0] += pow(x[0],p)*a[p]; + value[1] += pow(x[1],p)*b[p]; + value[2] += pow(x[2],p)*c[p]; + } +} + +void testL2( + apf::Mesh2* m, + const apf::Vector3& testXi, + int ndOrder, int exactOrder) +{ + // Loop over all nodes and set scalar dofs. + int dim = m->getDimension(); + apf::Field* l2Field = apf::createField( + m, "l2_test", apf::VECTOR, apf::getL2Shape(ndOrder, apf::Mesh::simplexTypes[dim])); + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!l2Field->getShape()->countNodesOn(apf::Mesh::simplexTypes[d])) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "no nodes in dimension %d\n", d); + continue; + } + else + if(0==PCU_Comm_Self()) + lion_oprint(1, "computing dofs for dimension %d\n", d); + it = m->begin(d); + while( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = l2Field->getShape()->countNodesOn(type); + apf::MeshElement* me = apf::createMeshElement(m, ent); + for (int i = 0; i < non; i++) + { + apf::Vector3 xi, p, value; + l2Field->getShape()->getNodeXi(type, i, xi); + apf::mapLocalToGlobal(me, xi, p); + E_exact(p, value, exactOrder); + + apf::setVector(l2Field, ent, i, value); + } + apf::destroyMeshElement(me); + } + m->end(it); + } + + + // Verify that interpolated solution field agrees with exact field. + double L2ErrorE = 0.; + it = m->begin(dim); + while( (ent = m->iterate(it)) ) { + apf::MeshElement* me = apf::createMeshElement(m, ent); + apf::Vector3 x; + apf::mapLocalToGlobal(me, testXi, x); + apf::Vector3 eFieldExact; + E_exact(x, eFieldExact, exactOrder); + + // obtain interpolated value + apf::Element* el = apf::createElement(l2Field, me); + apf::Vector3 eFieldValue; + apf::getVector(el, testXi, eFieldValue); + + double err = ((eFieldValue - eFieldExact) * (eFieldValue - eFieldExact)); + err /= (eFieldExact * eFieldExact); // normalization factor + L2ErrorE += err; + apf::destroyMeshElement(me); + apf::destroyElement(el); + } + m->end(it); + + // check for field interpolation + if(0==PCU_Comm_Self()) + lion_oprint(1, "L2ErrorE is %e\n", L2ErrorE); + PCU_ALWAYS_ASSERT_VERBOSE(L2ErrorE < 1.e-16, + "Fields were not interpolated correctly!"); + + m->removeField(l2Field); + apf::destroyField(l2Field); +} + +void testL2writeNative( + apf::Mesh2* m, + int ndOrder, int exactOrder) +{ + // Loop over all nodes and set scalar dofs. + int dim = m->getDimension(); + apf::Field* l2Field = apf::createField( + m, "l2_test", apf::VECTOR, apf::getL2Shape(ndOrder, apf::Mesh::simplexTypes[dim])); + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!l2Field->getShape()->countNodesOn(apf::Mesh::simplexTypes[d])) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "no nodes in dimension %d\n", d); + continue; + } + else + if(0==PCU_Comm_Self()) + lion_oprint(1, "computing dofs for dimension %d\n", d); + it = m->begin(d); + while( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = l2Field->getShape()->countNodesOn(type); + apf::MeshElement* me = apf::createMeshElement(m, ent); + for (int i = 0; i < non; i++) + { + apf::Vector3 xi, p, value; + l2Field->getShape()->getNodeXi(type, i, xi); + apf::mapLocalToGlobal(me, xi, p); + E_exact(p, value, exactOrder); + apf::setVector(l2Field, ent, i, value); + } + apf::destroyMeshElement(me); + } + m->end(it); + } + + // project to nodal field + apf::Field* projectedL2Field = apf::createField(m, "projected_l2_test", apf::VECTOR, + apf::getLagrange(1)); + apf::projectL2Field(projectedL2Field, l2Field); + + // write to native + // 1- write the mesh to native + // 2- read the mesh back in and make sure fields are on the mesh + // 3- clean up the newly loaded mesh + m->writeNative("L2Shape_test_mesh.smb"); + apf::Mesh2* m2 = apf::loadMdsMesh(".null", "./L2Shape_test_mesh.smb"); + int fCount = 0; + for (int i = 0; i < m2->countFields(); i++) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "field %d's name and shape are %s and %s\n", i, + m2->getField(i)->getName(), m2->getField(i)->getShape()->getName()); + fCount++; + } + PCU_ALWAYS_ASSERT_VERBOSE(fCount == 2, "Expecting 2 fields on m2 at this point"); + + apf::destroyMesh(m2); + // clean up the fields + apf::destroyField(l2Field); + apf::destroyField(projectedL2Field); +} diff --git a/test/aniso_ma_test.cc b/test/aniso_ma_test.cc index b93dc500e..4eb9a0278 100644 --- a/test/aniso_ma_test.cc +++ b/test/aniso_ma_test.cc @@ -53,7 +53,7 @@ int main(int argc, char** argv) m->verify(); apf::writeVtkFiles("aniso_before",m); AnIso sf(m); - ma::Input* in = ma::configure(m, &sf, 0, logInterpolation); + ma::Input* in = ma::makeAdvanced(ma::configure(m, &sf, 0, logInterpolation)); in->shouldRunPreZoltan = true; in->shouldRunMidParma = true; in->shouldRunPostParma = true; diff --git a/test/bezierRefine.cc b/test/bezierRefine.cc index 1900202bb..681930071 100644 --- a/test/bezierRefine.cc +++ b/test/bezierRefine.cc @@ -223,7 +223,7 @@ void test2D() } } double v0 = measureMesh(m); - ma::Input* inRefine = ma::configureUniformRefine(m,1); + ma::Input* inRefine = ma::makeAdvanced(ma::configureUniformRefine(m,1)); inRefine->shouldSnap = true; inRefine->shouldTransferParametric = true; if(order > 1) @@ -270,7 +270,7 @@ void test3D() bc.run(); double v0 = measureMesh(m); - ma::Input* inRefine = ma::configureUniformRefine(m,1); + ma::Input* inRefine = ma::makeAdvanced(ma::configureUniformRefine(m,1)); inRefine->shouldSnap = false; inRefine->shouldTransferParametric = false; if(order > 1) @@ -290,7 +290,7 @@ void test3D() bc.run(); double v0 = measureMesh(m); - ma::Input* inRefine = ma::configureUniformRefine(m,1); + ma::Input* inRefine = ma::makeAdvanced(ma::configureUniformRefine(m,1)); inRefine->shouldSnap = false; inRefine->shouldTransferParametric = false; if(order > 1) diff --git a/test/bezierShapeEval.cc b/test/bezierShapeEval.cc new file mode 100644 index 000000000..07abf4785 --- /dev/null +++ b/test/bezierShapeEval.cc @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* This file contains miscellaneous tests relating to bezier shapes and + * blended bezier shapes + */ + +static apf::Mesh2* makeOneTriMesh(int order, apf::MeshEntity* &ent); +static apf::Mesh2* makeOneTetMesh(int order, apf::MeshEntity* &ent); + + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + lion_set_verbosity(1); + if ( argc != 7 ) { + if ( !PCU_Comm_Self() ) { + printf("Usage: %s >\n", argv[0]); + printf(" can only be 2 (for triangles) and 4 (for tets)\n"); + printf(" is the order of bezier\n"); + printf(" can be -1, 0, 1, 2 (-1 means no blending)\n"); + printf(" inquiry point in the parent entity)\n"); + } + MPI_Finalize(); + exit(EXIT_FAILURE); + } + + int type = atoi(argv[1]); + int order = atoi(argv[2]); + int b = atoi(argv[3]); + PCU_ALWAYS_ASSERT_VERBOSE(type == 2 || type == 4, + " can only be 2 or 4!"); + PCU_ALWAYS_ASSERT_VERBOSE(b > -2 && b < 3, + " must be between -1 and 2!"); + + apf::MeshEntity* ent = 0; + apf::Mesh2* m = type == 2 ? makeOneTriMesh(order,ent) : makeOneTetMesh(order,ent); + + double xi0 = atof(argv[4]); + double xi1 = atof(argv[5]); + double xi2 = atof(argv[6]); + + + // set the order + apf::FieldShape* bezierShape = crv::getBezier(order); + int non = bezierShape->getEntityShape(type)->countNodes(); + apf::Vector3 xi(xi0, xi1, xi2); + apf::NewArray vals(non); + + if (b == -1) // regular shape functions + bezierShape->getEntityShape(type)->getValues(m,ent,xi,vals); + else // blended shape functions + { + crv::setBlendingOrder(type, b); + if (type == 2) + crv::BlendedTriangleGetValues(m,ent,xi,vals); + else + crv::BlendedTetGetValues(m,ent,xi,vals); + } + + + printf("shape values are \n"); + for (int i = 0; i < non; i++) { + printf("%d, %E \n", i, vals[i]); + } + + PCU_Comm_Free(); + MPI_Finalize(); +} + +static apf::Mesh2* makeOneTriMesh(int order, apf::MeshEntity* &ent) +{ + apf::Mesh2* mesh = apf::makeEmptyMdsMesh(0, 2, false); + + double vert_coords[3][6] = { + {0.,0.,0., 0., 0., 0.}, + {1.,0.,0., 0., 0., 0.}, + {0.,1.,0., 0., 0., 0.}, + }; + + // each edge is defined by the bounding verts + int edge_info[3][2] = { + {0,1}, + {1,2}, + {2,0} + }; + + apf::MeshEntity* verts[3]; + apf::MeshEntity* edges[3]; + + for (int i = 0; i < 3; i++) { + apf::Vector3 coords(vert_coords[i][0], + vert_coords[i][1], + vert_coords[i][2]); + apf::Vector3 params(vert_coords[i][3], + vert_coords[i][4], + vert_coords[i][5]); + verts[i] = mesh->createVertex(0, coords, params); + } + for (int i = 0; i < 3; i++) { + apf::MeshEntity* down_vs[2] = {verts[edge_info[i][0]], + verts[edge_info[i][1]]}; + edges[i] = mesh->createEntity(apf::Mesh::EDGE, 0, down_vs); + } + + ent = mesh->createEntity(apf::Mesh::TRIANGLE, 0, edges); + + mesh->acceptChanges(); + + apf::changeMeshShape(mesh, crv::getBezier(order),true); + + printf ("Made tri with verts:\n"); + for (int i = 0; i < 3; i++) { + printf("v0: (%e,%e,%e)\n", vert_coords[i][0], vert_coords[i][1], vert_coords[i][2]); + } + + printf ("all nodes of the tri:\n"); + apf::Element* elem = apf::createElement(mesh->getCoordinateField(),ent); + apf::NewArray nodes; + apf::getVectorNodes(elem,nodes); + apf::destroyElement(elem); + for (int i=0; i<(int)nodes.size(); i++) + { + printf("node %d: (%e,%e,%e)\n", i, nodes[i][0], nodes[i][1], nodes[i][2]); + } + return mesh; +} + +static apf::Mesh2* makeOneTetMesh(int order, apf::MeshEntity* &ent) +{ + + apf::Mesh2* mesh = apf::makeEmptyMdsMesh(0, 3, false); + + double vert_coords[4][6] = { + {0.,0.,0., 0., 0., 0.}, + {1.,0.,0., 0., 0., 0.}, + {0.,1.,0., 0., 0., 0.}, + {0.,0.,1., 0., 0., 0.}, + }; + + // each edge is defined by the bounding verts + int const (*edge_info)[2] = apf::tet_edge_verts; + int face_info[4][3] = { + {0,1,2}, + {0,4,3}, + {1,5,4}, + {2,5,3} + }; + + + apf::MeshEntity* verts[4]; + apf::MeshEntity* edges[6]; + apf::MeshEntity* faces[4]; + + for (int i = 0; i < 4; i++) { + apf::Vector3 coords(vert_coords[i][0], + vert_coords[i][1], + vert_coords[i][2]); + apf::Vector3 params(vert_coords[i][3], + vert_coords[i][4], + vert_coords[i][5]); + verts[i] = mesh->createVertex(0, coords, params); + } + for (int i = 0; i < 6; i++) { + apf::MeshEntity* down_vs[2] = {verts[edge_info[i][0]], + verts[edge_info[i][1]]}; + edges[i] = mesh->createEntity(apf::Mesh::EDGE, 0, down_vs); + } + + for (int i = 0; i < 4; i++) { + apf::MeshEntity* down_es[3] = {edges[face_info[i][0]], + edges[face_info[i][1]], + edges[face_info[i][2]]}; + faces[i] = mesh->createEntity(apf::Mesh::TRIANGLE, 0, down_es); + } + + ent = mesh->createEntity(apf::Mesh::TET, 0, faces); + + mesh->acceptChanges(); + + apf::changeMeshShape(mesh, crv::getBezier(order),true); + + printf ("Made tet with verts:\n"); + for (int i = 0; i < 4; i++) { + printf("v0: (%e,%e,%e)\n", vert_coords[i][0], vert_coords[i][1], vert_coords[i][2]); + } + + printf ("all nodes of the tet:\n"); + apf::Element* elem = apf::createElement(mesh->getCoordinateField(),ent); + apf::NewArray nodes; + apf::getVectorNodes(elem,nodes); + apf::destroyElement(elem); + for (int i=0; i<(int)nodes.size(); i++) + { + printf("node %d: (%e,%e,%e)\n", i, nodes[i][0], nodes[i][1], nodes[i][2]); + } + return mesh; +} diff --git a/test/classifyThenAdapt.cc b/test/classifyThenAdapt.cc index 2cc2cccd6..a2d01f3d0 100644 --- a/test/classifyThenAdapt.cc +++ b/test/classifyThenAdapt.cc @@ -139,12 +139,7 @@ int main(int argc, char** argv) printClassCounts(m); - ma::Input* in = ma::configureUniformRefine(m, 1); - if (in->shouldSnap) { - in->shouldSnap = false; - PCU_ALWAYS_ASSERT(in->shouldTransferParametric); - } - in->shouldFixShape = false; + const ma::Input* in = ma::configureUniformRefine(m, 1); ma::adapt(in); printClassCounts(m); diff --git a/test/collapse.cc b/test/collapse.cc index 6615ebf97..da45c1fd4 100644 --- a/test/collapse.cc +++ b/test/collapse.cc @@ -60,6 +60,8 @@ int main(int argc, char** argv) { code.mesh = apf::loadMdsMesh(modelFile, meshFile); apf::Unmodulo outMap(PCU_Comm_Self(), PCU_Comm_Peers()); Parma_ShrinkPartition(code.mesh, partitionFactor, code); + code.mesh->destroyNative(); + apf::destroyMesh(code.mesh); #ifdef HAVE_SIMMETRIX gmi_sim_stop(); Sim_unregisterAllKeys(); diff --git a/test/construct.cc b/test/construct.cc index 962316162..a702b973d 100644 --- a/test/construct.cc +++ b/test/construct.cc @@ -16,7 +16,7 @@ int main(int argc, char** argv) lion_set_verbosity(1); gmi_register_mesh(); gmi_register_null(); - int* conn; + apf::Gid* conn; double* coords; int nelem; int etype; diff --git a/test/constructThenGhost.cc b/test/constructThenGhost.cc index 56cdb85e2..e90e41b34 100644 --- a/test/constructThenGhost.cc +++ b/test/constructThenGhost.cc @@ -18,7 +18,7 @@ int main(int argc, char** argv) lion_set_verbosity(1); gmi_register_mesh(); gmi_register_null(); - int* conn; + apf::Gid* conn; double* coords; int nelem; int etype; diff --git a/test/convert.cc b/test/convert.cc index 18f6fd5c1..904c0797a 100644 --- a/test/convert.cc +++ b/test/convert.cc @@ -2,6 +2,8 @@ #include #include #include +#include "SimParasolidKrnl.h" +#include #include #include #include @@ -11,15 +13,37 @@ #include #include #include +#include #include #include #include #include #include - +#include #include +#include +#include + +using namespace std; + + +apf::Field* convert_my_tag(apf::Mesh* m, apf::MeshTag* t) { + apf::MeshEntity* vtx; + apf::MeshIterator* it = m->begin(0); + apf::Field* f = apf::createFieldOn(m, "fathers2D_field", apf::SCALAR); + int vals[1]; + double vals_d; + while ((vtx = m->iterate(it))) { + m->getIntTag(vtx, t, vals); + vals_d = vals[0]; + apf::setScalar(f, vtx, 0, vals_d); + } + m->end(it); + return f; +} + static void attachOrder(apf::Mesh* m) { apf::numberOverlapDimension(m, "sim_order", m->getDimension()); @@ -42,7 +66,7 @@ static void fixPyramids(apf::Mesh2* m) return; /* no pyramids exist in 2D */ if (apf::countEntitiesOfType(m, apf::Mesh::HEX)) return; /* meshadapt can't even look at hexes */ - ma::Input* in = ma::configureIdentity(m); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m)); in->shouldCleanupLayer = true; ma::adapt(in); } @@ -54,6 +78,8 @@ const char* smb_path = NULL; int should_log = 0; int should_fix_pyramids = 1; int should_attach_order = 0; +const char* extruRootPath = NULL; +int ExtruRootId =0; bool found_bad_arg = false; void getConfig(int argc, char** argv) { @@ -64,6 +90,7 @@ void getConfig(int argc, char** argv) { {"no-pyramid-fix", no_argument, &should_fix_pyramids, 0}, {"attach-order", no_argument, &should_attach_order, 1}, {"enable-log", no_argument, &should_log, 2}, + {"model-face-root", required_argument, 0, 'e'}, {"native-model", required_argument, 0, 'n'}, {0, 0, 0, 0} // terminate the option array }; @@ -74,6 +101,7 @@ void getConfig(int argc, char** argv) { " --no-pyramid-fix Disable quad-connected pyramid tetrahedronization\n" " --attach-order Attach the Simmetrix element order as a Numbering\n" " --enable-log Enable Simmetrix logging\n" + " --model-face-root=/path/to/file ASCII input file with one integer per line listing the face ids that are the roots of mesh extrusions from SimModeler\n" " --native-model=/path/to/model Load the native Parasolid or ACIS model that the GeomSim model uses\n"; int option_index = 0; @@ -85,12 +113,15 @@ void getConfig(int argc, char** argv) { case 1: // attach order flag case 2: // enable simmetrix logging break; + case 'e': + extruRootPath = optarg; + break; case 'n': gmi_native_path = optarg; break; case '?': if (!PCU_Comm_Self()) - printf ("warning: skipping unrecognized option\n"); + printf ("warning: skipping unrecognized option \'%s\'\n", argv[optind-1]); break; default: if (!PCU_Comm_Self()) @@ -108,21 +139,298 @@ void getConfig(int argc, char** argv) { gmi_path = argv[i++]; sms_path = argv[i++]; smb_path = argv[i++]; - if (!PCU_Comm_Self()) { - printf ("fix_pyramids %d attach_order %d enable_log %d\n", - should_fix_pyramids, should_attach_order, should_log); + printf ("fix_pyramids %d attach_order %d enable_log %d extruRootPath %s\n", + should_fix_pyramids, should_attach_order, should_log, extruRootPath); printf ("native-model \'%s\' model \'%s\' simmetrix mesh \'%s\' output mesh \'%s\'\n", gmi_native_path, gmi_path, sms_path, smb_path); } } +// put the extrude tagging here which 1) loops over the mesh faces classified on the model face that is the root of the extrude +// create a tag on vertices fathers +// get the list of mesh rootfaces classified on the source geometric model face +// for each srcFace in rootfaces +// get the ids of downward adjacent vertices, store that as an array of size 3 +// get the upward adjacent region srcRgn +// call Extrusion_3DRegionsAndLayerFaces(srcRgn,...) +// for each face in the returned list of faces +// get the downward adjacent vertices of face - they will be in the same order as the srcFace ids +// set the fathers tag +// assert that the x,y coordinates of each vertex matches the srcFace vertex coordinates within some relaxed +// tolerance - sanity check my assumption that face-to-vtx adjaceny is always the same order +void addFathersTag(pGModel simModel, pParMesh sim_mesh, apf::Mesh* simApfMesh, const char* extrusionFaceFile) { + if(!extrusionFaceFile) return; + // create a tag on vertices fathers + pMeshDataId myFather = MD_newMeshDataId( "fathers2D"); + + pPList listV,listVn,faces,regions; + pFace face; + pRegion region; + pVertex vrts[4]; + int dir, err; + int count2D=0; + pGFace gface; + pVertex entV; + pMesh meshP= PM_mesh (sim_mesh, 0 ); + + char coordfilename[64]; + char cnnfilename[64]; + sprintf(coordfilename, "geom.crd"); + sprintf(cnnfilename, "geom.cnn"); + FILE* fcr = fopen(coordfilename, "w"); + FILE* fcn = fopen(cnnfilename, "w"); + + FILE* fid = fopen(extrusionFaceFile, "r"); // helper file that contains all faces with extrusions + assert(fid); + double VdisTol=1e-12; + while(1 == fscanf(fid,"%d",&ExtruRootId)) { + pGFace ExtruRootFace=NULL; + fprintf(stderr,"ExtruRootId= %d \n",ExtruRootId); + //find the root face of the extrusion + GFIter gfIter=GM_faceIter(simModel); + while ( (gface=GFIter_next(gfIter))) { + int id = GEN_tag(gface); + if(id==ExtruRootId) ExtruRootFace=gface; + } + assert(ExtruRootFace != NULL); + // all of the work so far assumes translation extrusion. Rotation extrusion (sweeping extruded entiy over an arc of some angle about + // a given axis) is useful but this would require some code change. The principle is the same. Every root entity has another + // oppositeRoot entity whose position obeys a fixed angle rotation about a fixed axis. + pPList gRegions,gFaces,gEdges,gVertices; + double parFace[2]; + double normal[3]; + double pLow, pHigh; + double coordGVSelf[3]; + double coordGVOther[3]; + double xmin[3] = {1.0e8, 1.0e8, 1.0e8}; + double xmax[3] = {-1.0e8, -1.0e8, -1.0e8}; + GF_parRange ( ExtruRootFace, 0, &pLow, &pHigh); + parFace[0] = ( pLow + pHigh ) * 0.5; + GF_parRange ( ExtruRootFace, 1, &pLow, &pHigh); + parFace[1] = ( pLow + pHigh ) * 0.5; + GF_normal(ExtruRootFace,parFace,normal); + gRegions=GF_regions(ExtruRootFace); //pPList of model regions adjacent to root model Fac + pGRegion gRegion = (pGRegion) PList_item( gRegions , 0 ); // there can be only one for extrusions + gVertices = GR_vertices(gRegion); + for( int j = 0; j < PList_size( gVertices ); j++ ){ + pGVertex gVertex = (pGVertex) PList_item( gVertices , j ); + GV_point( gVertex , coordGVOther ); + for( int i = 0; i < 3; i++) { + xmin[i]=std::min(xmin[i],coordGVOther[i]); + xmax[i]=std::max(xmax[i],coordGVOther[i]); + } + } + // just in case normal is not a unit vector + double nLength=(normal[0]*normal[0]+normal[1]*normal[1]+normal[2]*normal[2]); + for( int i = 0; i < 3; i++) normal[i]=normal[i]/nLength; + + double sepVec[3]; + for( int i = 0; i < 3; i++) sepVec[i]=xmax[i]-xmin[i]; + double ExtruDistance=abs(sepVec[0]*normal[0]+sepVec[1]*normal[1]+sepVec[2]*normal[2]); + PList_delete(gRegions); + PList_delete(gVertices); + + + FIter fIter = M_classifiedFaceIter( meshP, ExtruRootFace, 0 ); // 0 says I don't want closure + while ((face = FIter_next(fIter))) { + dir=1; + listV= F_vertices(face, dir); + void *iter = 0; // Must initialize to 0 + int i=0; + while ((entV =(pVertex)PList_next(listV, &iter))) { //loop over plist of vertices + // Process each item in list + vrts[i] = (pVertex)entV; + i++; + } + int nvert=i; + PList_delete(listV); + + double coordNewPt[nvert][3]; + for(i=0; i< nvert ; i++) { + int* markedData; + if(!EN_getDataPtr((pEntity)vrts[i],myFather,(void**)&markedData)){ // not sure about marked yet + gType vClassDim; + pGEntity vConG; + int foundESTag = 0; + int foundETag = 0; + int foundEETag = 0; + double de; //dx,dy; + vClassDim=V_whatInType(vrts[i]); + vConG=V_whatIn(vrts[i]); + if(vClassDim == 0) {// classified on vert so vert->edge->vert + foundESTag = GEN_tag( (pGVertex) vConG ); // found Extrusion Start Tag + gEdges = GV_edges( (pGVertex) vConG ); // pPList of model edges adjacent to root model vertex + for(int j = 0; j < PList_size( gEdges ); j++ ){ + pGEdge gEdge = (pGEdge) PList_item( gEdges , j ); // candidate edge + pGVertex gVert0 = GE_vertex( gEdge , 0 ); + pGVertex gVert1 = GE_vertex( gEdge , 1 ); + if( gVert0 == (pGVertex) vConG) { //1 is at other end of edge + GV_point( gVert1 , coordGVOther ); + GV_point( gVert0 , coordGVSelf ); + for( int i = 0; i < 3; i++) sepVec[i]=coordGVOther[i]-coordGVSelf[i]; + de=abs(sepVec[0]*normal[0]+sepVec[1]*normal[1]+sepVec[2]*normal[2]); + if( abs(de-ExtruDistance) < VdisTol ) { + foundETag = GEN_tag( gEdge ); + foundEETag = GEN_tag( gVert1 ); + } + } else { // 0 is at the other edge + GV_point( gVert0 , coordGVOther ); + GV_point( gVert1 , coordGVSelf ); + for( int i = 0; i < 3; i++) sepVec[i]=coordGVOther[i]-coordGVSelf[i]; + de=abs(sepVec[0]*normal[0]+sepVec[1]*normal[1]+sepVec[2]*normal[2]); + if( abs(de-ExtruDistance) < VdisTol ) { + foundETag = GEN_tag(gEdge); + foundEETag = GEN_tag(gVert0); + } + } + } + PList_delete(gEdges); + } else if(vClassDim == 1) { // classified on edge so edge->face->edge + foundESTag = GEN_tag( (pGEdge) vConG ); // found Extrusion Start Tag + GE_parRange ( (pGEdge) vConG, &pLow, &pHigh); + parFace[0] = ( pLow + pHigh ) * 0.5; + GE_point( (pGEdge) vConG , parFace[0], coordGVSelf ); + gFaces = GE_faces( (pGEdge) vConG); // pPList of model faces adjacent to root model edge + for(int j = 0; j < PList_size( gFaces ); j++ ){ + pGFace gFace = (pGFace) PList_item( gFaces , j ); // candidate face + gEdges = GF_edges( gFace ); // pPList of model edges of jth adjacent face + for(int k = 0; k < PList_size( gEdges ); k++ ){ // loop over that pPlist + pGEdge gEdge = (pGEdge) PList_item( gEdges , k ); // candidate edge on candidate face + if( gEdge != (pGEdge) vConG ) { // exclude root classified edge + GE_parRange ( gEdge, &pLow, &pHigh); + parFace[0] = ( pLow + pHigh ) * 0.5; + GE_point( gEdge , parFace[0], coordGVOther ); + for( int i = 0; i < 3; i++) sepVec[i]=coordGVOther[i]-coordGVSelf[i]; + de=abs(sepVec[0]*normal[0]+sepVec[1]*normal[1]+sepVec[2]*normal[2]); + if( abs(de-ExtruDistance) < VdisTol ) { + foundETag = GEN_tag( gFace ); // found Extruded Tag + foundEETag = GEN_tag( gEdge ); // found Extrusion End Tag + } + } + } + PList_delete(gEdges); + } + PList_delete(gFaces); + } else if(vClassDim == 2) { // classified on face so face->region->face + foundESTag = GEN_tag( (pGFace) vConG ); // found Extrusion Start Tag + GF_parRange ( (pGFace) vConG, 0, &pLow, &pHigh); + parFace[0] = ( pLow + pHigh ) * 0.5; + GF_parRange ( (pGFace) vConG, 1, &pLow, &pHigh); + parFace[1] = ( pLow + pHigh ) * 0.5; + GF_point( (pGFace) vConG, parFace , coordGVSelf ); + gRegions = GF_regions( (pGFace) vConG ); //pPList of model regions adjacent to root model Fac + pGRegion gRegion = (pGRegion) PList_item( gRegions , 0 ); // there can be only one for extrusions + gFaces = GR_faces( gRegion ); + for( int j = 0; j < PList_size( gFaces ); j++ ){ + pGFace gFace = (pGFace) PList_item( gFaces , j ); + if( gFace != (pGFace) vConG ) { // exclude root classified face + GF_parRange ( gFace, 0, &pLow, &pHigh); + parFace[0] = ( pLow + pHigh ) * 0.5; + GF_parRange ( gFace, 1, &pLow, &pHigh); + parFace[1] = ( pLow + pHigh ) * 0.5; + GF_point( (pGFace) gFace , parFace , coordGVOther ); + for( int i = 0; i < 3; i++) sepVec[i]=coordGVOther[i]-coordGVSelf[i]; + de=abs(sepVec[0]*normal[0]+sepVec[1]*normal[1]+sepVec[2]*normal[2]); + if( abs(de-ExtruDistance) < VdisTol ) { + foundETag = GEN_tag( gRegion ); // found Extruded Tag + foundEETag = GEN_tag( gFace ); // found Extrusion End Tag + } + } + } + PList_delete( gFaces ); + } else { + PCU_ALWAYS_ASSERT(false); + } + PCU_ALWAYS_ASSERT(foundEETag != 0); + count2D++; + int* vtxData = new int[1]; + vtxData[0] = count2D; + EN_attachDataPtr((pEntity)vrts[i],myFather,(void*)vtxData); + V_coord(vrts[i],coordNewPt[i]); + + fprintf ( fcr, "%.15E %.15E %d %d %d %d \n", coordNewPt[i][0],coordNewPt[i][1], vClassDim, foundESTag, foundETag, foundEETag ); + } + } + + double coordFather[nvert][3]; + int fatherIds[4]; //store the ids of the fathers (vertices) on the root face + for(i=0; i< nvert ; i++) { + int* fatherIdPtr; + const int exists = EN_getDataPtr((pEntity)vrts[i],myFather,(void**)&fatherIdPtr); + assert(exists); + fatherIds[i] = fatherIdPtr[0]; + V_coord(vrts[i],coordFather[i]); + fprintf ( fcn, "%d ", fatherIds[i]); + } + fprintf ( fcn, "\n"); + + dir=0; // 1 fails + // get the upward adjacent region srcRgn + region = F_region(face, dir ); // 0 is the negative normal which I assume for a face on the boundary in is interior. + if(region==NULL) { // try other dir + dir=1; // 1 fails + region = F_region(face, dir ); // 0 is the negative normal which I assume for a face on the boundary in is interior. + } + + regions=PList_new(); + faces=PList_new(); + err = Extrusion_3DRegionsAndLayerFaces(region, regions, faces, 1); + PList_delete(regions); // not used so delete + if(err!=1 && !PCU_Comm_Self()) + fprintf(stderr, "Extrusion_3DRegionsAndLayerFaces returned %d for err \n", err); + + // for each face in the returned list of faces + iter=0; + pFace sonFace; + int iface=0; + dir=0; + while( (sonFace = (pFace)PList_next(faces, &iter)) ) { //loop over plist of vertices + if(iface !=0) { // root face is in the stack but we already took care of it above + // get the downward adjacent vertices of face - they will be in the same order as the srcFace ids + listVn= F_vertices(sonFace, dir); + void *iter2=0; // Must initialize to 0 + i=0; + int my2Dfath; + pVertex sonVtx; + double dist, dx, dy, distMin; + double coordSon[3]; + int iMin; + while( (sonVtx = (pVertex)PList_next(listVn, &iter2)) ) { //loop over plist of vertices + V_coord(sonVtx,coordSon); + distMin=1.0e7; + for(i=0; i< nvert; i++){ + dx=coordSon[0]-coordFather[i][0]; + dy=coordSon[1]-coordFather[i][1]; + dist=dx*dx+dy*dy; + if(dist < distMin) { + iMin=i; + distMin=dist; + } + } + my2Dfath=fatherIds[iMin]; + int* vtxData = new int[1]; + vtxData[0] = my2Dfath; + EN_attachDataPtr((pEntity)sonVtx,myFather,(void*)vtxData); + } + PList_delete(listVn); + } + iface++; + } + PList_delete(faces); + } //end root face iterator + } + apf::MeshSIM* cake = reinterpret_cast(simApfMesh); + cake->createIntTag("fathers2D", myFather, 1); +} + int main(int argc, char** argv) { MPI_Init(&argc, &argv); PCU_Comm_Init(); lion_set_verbosity(1); MS_init(); + SimAdvMeshing_start(); //for fancy BL/extrusion queries SimModel_start(); Sim_readLicenseFile(NULL); SimPartitionedMesh_start(&argc,&argv); @@ -143,17 +451,43 @@ int main(int argc, char** argv) Progress_setDefaultCallback(progress); gmi_model* mdl; - if( gmi_native_path ) + if( gmi_native_path ) { + if (!PCU_Comm_Self()) + fprintf(stderr, "loading native model %s\n", gmi_native_path); mdl = gmi_sim_load(gmi_native_path,gmi_path); - else + } else { mdl = gmi_load(gmi_path); + } + pGModel simModel = gmi_export_sim(mdl); +/* + pParasolidNativeModel nModel = ParasolidNM_createFromFile(gmi_native_path,0); + pGModel Amodel = GAM_createFromNativeModel(nModel,progress); +*/ +/* leaving this litle model tester in for a while + pGModel Amodel = simModel; + double coordGVOther[3]; + pGVertex gvertex; + GVIter gvIter=GM_vertexIter(Amodel); + while ( (gvertex=GVIter_next(gvIter))) { + int id = GEN_tag(gvertex); + GV_point( gvertex , coordGVOther ); + cout<< id << " tag and coords " << coordGVOther[0] << " " << coordGVOther[1]<< " " << coordGVOther[2] <shouldSnap = true; in->shouldTransferParametric = true; in->maximumIterations = 1; @@ -157,7 +157,7 @@ static void testBezier(const char* modelFile, const char* meshFile, crv::BezierCurver bc(m,order,1); bc.run(); Linear sf(m); - ma::Input* in = ma::configure(m,&sf); + ma::Input* in = ma::makeAdvanced(ma::configure(m,&sf)); in->shouldSnap = true; in->shouldTransferParametric = true; in->maximumIterations = 1; diff --git a/test/degenerateSurfs.cc b/test/degenerateSurfs.cc index 583adbf16..730e8cbf7 100644 --- a/test/degenerateSurfs.cc +++ b/test/degenerateSurfs.cc @@ -55,7 +55,7 @@ int main(int argc, char** argv) crv::BezierCurver bc(m, order, 0); bc.run(); - ma::Input* in = ma::configureUniformRefine(m, level); + ma::Input* in = ma::makeAdvanced(ma::configureUniformRefine(m, level)); if (in->shouldSnap) { PCU_ALWAYS_ASSERT(in->shouldTransferParametric); } diff --git a/test/describe.cc b/test/describe.cc index 73e79a56b..c4a141c5b 100644 --- a/test/describe.cc +++ b/test/describe.cc @@ -32,7 +32,11 @@ static double get_peak() static double get_peak() { +#if defined(__GNUG__) && defined(PUMI_HAS_MALLINFO2) + return mallinfo2().arena; +#elif defined(__GNUG__) return mallinfo().arena; +#endif } #else @@ -62,7 +66,11 @@ static void print_stats(const char* name, double value) static double get_chunks() { +#if defined(__GNUG__) && defined(PUMI_HAS_MALLINFO2) + struct mallinfo2 m = mallinfo2(); +#elif defined(__GNUG__) struct mallinfo m = mallinfo(); +#endif return m.uordblks + m.hblkhd; } @@ -101,6 +109,7 @@ int main(int argc, char** argv) gmi_register_mesh(); print_stats("malloc used before", get_chunks()); apf::Mesh2* m = apf::loadMdsMesh(argv[1],argv[2]); + m->verify(); print_stats("kernel heap", get_peak()); print_stats("malloc used", get_chunks()); Parma_PrintPtnStats(m, ""); diff --git a/test/dg_ma_test.cc b/test/dg_ma_test.cc index c46f04c8d..188592c6a 100644 --- a/test/dg_ma_test.cc +++ b/test/dg_ma_test.cc @@ -54,7 +54,7 @@ int main(int argc, char** argv) ma::Mesh* m = apf::loadMdsMesh(modelFile,meshFile); m->verify(); Linear sf(m); - ma::Input* in = ma::configure(m, &sf); + ma::Input* in = ma::makeAdvanced(ma::configure(m, &sf)); if (!PCU_Comm_Self()) printf("Matched mesh: disabling" " snapping, and shape correction,\n"); diff --git a/test/embedded_edges.cc b/test/embedded_edges.cc index 74531aae3..be0948569 100644 --- a/test/embedded_edges.cc +++ b/test/embedded_edges.cc @@ -21,7 +21,7 @@ int main(int argc, char** argv) lion_set_verbosity(1); gmi_register_mesh(); gmi_register_null(); - int* conn; + apf::Gid* conn; double* coords; int nelem; int etype; diff --git a/test/fieldReduce.cc b/test/fieldReduce.cc index ffe6095fb..b33fea701 100644 --- a/test/fieldReduce.cc +++ b/test/fieldReduce.cc @@ -53,6 +53,7 @@ apf::Field* getTestField(apf::Mesh* m, const char* fname, double addval) double val = getValue(coords, addval); apf::setScalar(f, e, 0, val); } // end while + m->end(it); } // end for return f; @@ -133,8 +134,10 @@ bool testReduce(apf::Mesh* m, int casenum) } // end if ntimes } // end while + m->end(it); } // end for + delete shr; return failflag; } diff --git a/test/fixlayer.cc b/test/fixlayer.cc index 20b26ec70..0827bc025 100644 --- a/test/fixlayer.cc +++ b/test/fixlayer.cc @@ -27,7 +27,7 @@ int main(int argc, char** argv) #endif gmi_register_mesh(); ma::Mesh* m = apf::loadMdsMesh(argv[1], argv[2]); - ma::Input* in = ma::configureIdentity(m); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m)); in->shouldCleanupLayer = true; ma::adapt(in); m->writeNative(argv[3]); diff --git a/test/fixshape.cc b/test/fixshape.cc index 543ee0df6..899e1c0e4 100644 --- a/test/fixshape.cc +++ b/test/fixshape.cc @@ -27,7 +27,7 @@ int main(int argc, char** argv) #endif gmi_register_mesh(); ma::Mesh* m = apf::loadMdsMesh(argv[1],argv[2]); - ma::Input* in = ma::configureIdentity(m); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m)); in->shouldFixShape = true; ma::adapt(in); m->writeNative(argv[3]); diff --git a/test/fusion.cc b/test/fusion.cc index 2e83bbb6b..7da4c72f2 100644 --- a/test/fusion.cc +++ b/test/fusion.cc @@ -129,11 +129,7 @@ static void testIndexing(apf::Mesh2* m) static void fusionAdapt(apf::Mesh2* m) { Vortex sf(m); - ma::Input* in = ma::configure(m, &sf); - in->maximumIterations = 9; - in->shouldRunPreZoltan = true; - in->shouldRunMidParma = true; - in->shouldRunPostParma = true; + const ma::Input* in = ma::configure(m, &sf); ma::adapt(in); m->verify(); } diff --git a/test/fusion3.cc b/test/fusion3.cc index 99dd00234..a8df775e4 100644 --- a/test/fusion3.cc +++ b/test/fusion3.cc @@ -290,8 +290,7 @@ int main(int argc, char * argv[]) apf::Mesh2* mesh=apf::loadMdsMesh(model, argv[1]); mesh->verify(); Vortex sfv(mesh, center, modelLen); - ma::Input* in = ma::configure(mesh,&sfv); - in->maximumIterations = 9; + const ma::Input* in = ma::configure(mesh,&sfv); ma::adapt(in); mesh->verify(); apf::writeVtkFiles("adapted",mesh); diff --git a/test/generate.cc b/test/generate.cc index cfff3b502..a5c94f15e 100644 --- a/test/generate.cc +++ b/test/generate.cc @@ -227,7 +227,7 @@ void fixMatches(apf::Mesh2* m) void fixPyramids(apf::Mesh2* m) { - ma::Input* in = ma::configureIdentity(m); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m)); in->shouldCleanupLayer = true; ma::adapt(in); } diff --git a/test/gmsh.cc b/test/gmsh.cc index 307fab07e..b2f3911ec 100644 --- a/test/gmsh.cc +++ b/test/gmsh.cc @@ -13,20 +13,45 @@ int main(int argc, char** argv) MPI_Init(&argc,&argv); PCU_Comm_Init(); lion_set_verbosity(1); - if ( argc != 4 ) { + if ( argc != 5 ) { if ( !PCU_Comm_Self() ) - printf("Usage: %s \n", argv[0]); + printf("Usage: %s \n" + "The input .msh and output .smb file names are required. \n" + "If 'none' is specified as the input model file name then \n" + "a output model (.dmg) will be written to the specified filename. \n" + "When a **gmsh v2** .msh is passed in, a minimal model will be created from " + "the mesh.\n" + "When a **gmsh v4** .msh is passed in, a topological model will be created " + "from the geometric model entities defined in the gmsh input file.\n", argv[0]); MPI_Finalize(); exit(EXIT_FAILURE); } gmi_register_null(); gmi_register_mesh(); - apf::Mesh2* m = apf::loadMdsFromGmsh(gmi_load(argv[1]), argv[2]); - // if input model is null derive a basic model for verify to pass. - if (std::string(argv[1]).compare(".null") == 0) - apf::deriveMdsModel(m); + + std::string model(argv[1]); + std::string gmsh(argv[2]); + std::string outMesh(argv[3]); + std::string outModel(argv[4]); + + const int gmshVersion = apf::gmshMajorVersion(gmsh.c_str()); + fprintf(stderr, "version %d\n", gmshVersion); + apf::Mesh2* m = NULL; + if (gmshVersion == 2) { + if (model.compare("none") == 0) { + m = apf::loadMdsFromGmsh(gmi_load(".null"), gmsh.c_str()); + apf::deriveMdsModel(m); + gmi_write_dmg(m->getModel(),outModel.c_str()); + } else { + m = apf::loadMdsFromGmsh(gmi_load(model.c_str()), gmsh.c_str()); + } + } else if (gmshVersion == 4) { + if (model.compare("none") == 0) { + m = apf::loadMdsDmgFromGmsh(outModel.c_str(), gmsh.c_str()); + } + } m->verify(); - m->writeNative(argv[3]); + m->writeNative(outMesh.c_str()); m->destroyNative(); apf::destroyMesh(m); PCU_Comm_Free(); diff --git a/test/highOrderSizeFields.cc b/test/highOrderSizeFields.cc new file mode 100644 index 000000000..7cbbe34fc --- /dev/null +++ b/test/highOrderSizeFields.cc @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIMMETRIX +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + + +void computeSizesFrames( + apf::Mesh2* m, + const apf::Vector3 &p, + apf::Vector3 &sz, + apf::Matrix3x3 &frm); + +void testAdapt( + const char* model, + const char* mesh, + int order); + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + PCU_Comm_Init(); + lion_set_verbosity(1); + +#ifdef HAVE_SIMMETRIX + MS_init(); + SimModel_start(); + Sim_readLicenseFile(0); + gmi_sim_start(); + gmi_register_sim(); +#endif + gmi_register_mesh(); + + if (argc != 3) { + if(0==PCU_Comm_Self()) + std::cerr <<"usage: " << argv[0] + << " \n"; + return EXIT_FAILURE; + } + + for (int order = 3; order < 5; order++) { + if(0==PCU_Comm_Self()) + lion_oprint(1, "Testing aniso adapt w/ sizefield order %d\n", order); + testAdapt(argv[1], argv[2], order); + } + + PCU_Comm_Free(); +#ifdef HAVE_SIMMETRIX + gmi_sim_stop(); + Sim_unregisterAllKeys(); + SimModel_stop(); + MS_exit(); +#endif + MPI_Finalize(); +} + +void computeSizesFrames( + apf::Mesh2* m, + const apf::Vector3 &p, + apf::Vector3 &sz, + apf::Matrix3x3 &frm) +{ + apf::Vector3 llb; + apf::Vector3 umb; + ma::getBoundingBox(m, llb, umb); + const double pi = 3.14152; + apf::Vector3 diff = umb - llb; + diff[1] = 0.0; + // total length in the direction of the wave + double length = diff.getLength(); + // normal in the direction of the wave + apf::Vector3 normal = diff / length; + apf::Vector3 tangent(-normal[2], 0., normal[0]); + + double maxSz = length / 12.; + double minSz = length / 200.; + double wavelength = length / 4.; + + + double alpha = (maxSz - minSz)/2.; + double beta = (maxSz + minSz)/2.; + + double s = normal * p; + + sz = apf::Vector3( + length/4., + length/4., + beta + alpha * sin(2*pi*s/wavelength) + ); + + frm[0] = normal; + frm[1] = apf::Vector3(0., 1., 0.); + frm[2] = tangent; +} + +void testAdapt( + const char* model, + const char* mesh, + int order) +{ + gmi_model* g = gmi_load(model); + apf::Mesh2* m = apf::loadMdsMesh(g,mesh); + m->verify(); + + const ma::Input* in = ma::configureUniformRefine(m, 1, 0); + ma::adapt(in); + + apf::FieldShape* fs = apf::getH1Shape(order); + apf::Field* sizes = apf::createField(m, "sizes", apf::VECTOR, fs); + apf::Field* frames = apf::createField(m, "frames", apf::MATRIX, fs); + + int dim = m->getDimension(); + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!fs->countNodesOn(apf::Mesh::simplexTypes[d])) + continue; + it = m->begin(d); + while( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = fs->countNodesOn(type); + apf::MeshElement* me = apf::createMeshElement(m, ent); + for (int i = 0; i < non; i++) { + apf::Vector3 xi, p, value; + fs->getNodeXi(type, i, xi); + apf::mapLocalToGlobal(me, xi, p); + apf::Vector3 sz; + apf::Matrix3x3 frm; + computeSizesFrames(m, p, sz, frm); + apf::setVector(sizes, ent, i, sz); + apf::setMatrix(frames, ent, i, frm); + } + apf::destroyMeshElement(me); + } + m->end(it); + } + + ma::Input* inAdv = ma::makeAdvanced(ma::configure(m, sizes, frames, 0, true)); + inAdv->shouldFixShape = true; + inAdv->maximumIterations = 10; + inAdv->shouldForceAdaptation = true; + + std::stringstream ss; + ss << "before_adapt_with_ho_sizefield_order_" << order; + apf::writeVtkFiles(ss.str().c_str(), m); + ss.str(""); + ma::adaptVerbose(inAdv); + ss << "after_adapt_with_ho_sizefield_order_" << order; + apf::writeVtkFiles(ss.str().c_str(), m); + + m->removeField(sizes); + m->removeField(frames); + + apf::destroyField(sizes); + apf::destroyField(frames); + + m->destroyNative(); + apf::destroyMesh(m); +} diff --git a/test/highOrderSolutionTransfer.cc b/test/highOrderSolutionTransfer.cc new file mode 100644 index 000000000..bba9616cd --- /dev/null +++ b/test/highOrderSolutionTransfer.cc @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIMMETRIX +#include +#include +#include +#include +#endif +#include +#include +#include + +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p); + +apf::Field* addH1Field( + apf::Mesh2* m, + int ndOrder, int exactOrder); + +double testH1Field( + apf::Mesh2* m, + apf::Field* f, + const apf::Vector3& testXi, + int exactOrder); + +void testCurveAdapt( + const char* modelFile, + const char* meshFile, + const int mesh_order, + const int exact_order, + const int field_order); + +int main(int argc, char** argv) +{ + const char* modelFile = argv[1]; + const char* meshFile = argv[2]; + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + lion_set_verbosity(1); +#ifdef HAVE_SIMMETRIX + MS_init(); + SimModel_start(); + Sim_readLicenseFile(0); + gmi_sim_start(); + gmi_register_sim(); +#endif + gmi_register_mesh(); + + + /* Note on choices of the orders: + * -- mesh geometry if of order p [that is x(xi) is a polynomial of order p in xi] + * -- test (exact) field is order q [that is F(x) is a polynomial of order q in x] + * -- then the finite element basis has to be of order at least p*q. This is because + * F(x) = F(x(xi)) will be a polynomial of order p*q in xi. + */ + + // linear adapt + testCurveAdapt(modelFile, meshFile, + 1 /*mesh_order*/, + 2 /*exact_order*/, + 2 /*field_order*/); + + // quadratic adapts + testCurveAdapt(modelFile, meshFile, + 2 /*mesh_order*/, + 2 /*exact_order*/, + 4 /*field_order*/); + testCurveAdapt(modelFile, meshFile, + 2 /*mesh_order*/, + 3 /*exact_order*/, + 6 /*field_order*/); + + // cubic adapt + testCurveAdapt(modelFile, meshFile, + 3 /*mesh_order*/, + 2 /*exact_order*/, + 6 /*field_order*/); + + PCU_Comm_Free(); +#ifdef HAVE_SIMMETRIX + gmi_sim_stop(); + Sim_unregisterAllKeys(); + MS_exit(); + SimModel_stop(); +#endif + MPI_Finalize(); +} + + +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p) +{ + // Polynomial coefficients for each component of exact vector field + double a[7] = { 1.0, -1.0, 2., -2., -1.0, 1.0, -1.0}; + double b[7] = {-2.0, 1.0, -2., 2., -1.0, -1.0, 1.0}; + double c[7] = { 3.0, 0.0, -1., 100., -1.0, 1.0, 0.0}; + + value[0] = 0.0; + value[1] = 0.0; + value[2] = 0.0; + for (int i = p; i >= 0; i--) { + value[0] += pow(x[0],p)*a[p]; + value[1] += pow(x[1],p)*b[p]; + value[2] += pow(x[2],p)*c[p]; + } +} + +apf::Field* addH1Field( + apf::Mesh2* m, + int ndOrder, int exactOrder) +{ + // Loop over all nodes and set scalar dofs. + int dim = m->getDimension(); + apf::Field* h1Field = apf::createField( + m, "h1_test", apf::VECTOR, apf::getH1Shape(ndOrder)); + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!h1Field->getShape()->countNodesOn(apf::Mesh::simplexTypes[d])) + continue; + + it = m->begin(d); + int count = 0; + while( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = h1Field->getShape()->countNodesOn(type); + apf::MeshElement* me = apf::createMeshElement(m, ent); + for (int i = 0; i < non; i++) + { + apf::Vector3 xi, p, value; + h1Field->getShape()->getNodeXi(type, i, xi); + apf::mapLocalToGlobal(me, xi, p); + E_exact(p, value, exactOrder); + + apf::setVector(h1Field, ent, i, value); + } + apf::destroyMeshElement(me); + count++; + } + m->end(it); + } + + return h1Field; +} + +double testH1Field( + apf::Mesh2* m, + apf::Field* f, + const apf::Vector3& testXi, + int exactOrder) +{ + + int dim = m->getDimension(); + int count; + apf::MeshIterator* it; + apf::MeshEntity* ent; + + // Verify that interpolated solution field agrees with exact field. + double L2ErrorE = 0.; + it = m->begin(dim); + count = 0; + while( (ent = m->iterate(it)) ) { + apf::MeshElement* me = apf::createMeshElement(m, ent); + apf::Vector3 x; + apf::mapLocalToGlobal(me, testXi, x); + apf::Vector3 eFieldExact; + E_exact(x, eFieldExact, exactOrder); + + // obtain interpolated value + apf::Element* el = apf::createElement(f, me); + apf::Vector3 eFieldValue; + apf::getVector(el, testXi, eFieldValue); + + double err = ((eFieldValue - eFieldExact) * (eFieldValue - eFieldExact)); + /* err /= (eFieldExact * eFieldExact); // normalization factor */ + L2ErrorE += err; + apf::destroyMeshElement(me); + apf::destroyElement(el); + count++; + } + m->end(it); + + return L2ErrorE; +} + +void testCurveAdapt( + const char* modelFile, + const char* meshFile, + const int mesh_order, + const int exact_order, + const int field_order) +{ + + apf::Mesh2* m = apf::loadMdsMesh(modelFile,meshFile); + m->verify(); + + if (mesh_order > 1) { + crv::BezierCurver bc(m, mesh_order, 0); + bc.run(); + } + + apf::Field* f = addH1Field(m, field_order, exact_order); + double l2ErrorBefore = testH1Field(m, f, apf::Vector3(1./3., 1./4., 1./5.), exact_order); + + ma::Input* in = ma::makeAdvanced(ma::configureUniformRefine(m,1)); + // Snap is off for solutions transfer testing. + in->shouldSnap = false; + in->goodQuality = 0.3*0.3*0.3; + in->shouldFixShape = false; + if (mesh_order > 1) + crv::adapt(in); + else + ma::adapt(in); + + double l2ErrorAfter = testH1Field(m, f, apf::Vector3(1./3., 1./4., 1./5.), exact_order); + + lion_oprint(1, "mesh/exact/fields orders %d/%d/%d, before/after errors %e/%e\n", + mesh_order, exact_order, field_order, l2ErrorBefore, l2ErrorAfter); + + PCU_ALWAYS_ASSERT(l2ErrorAfter < 1.e-16); + + m->destroyNative(); + apf::destroyMesh(m); +} diff --git a/test/icesheet.cc b/test/icesheet.cc index 7dd6db281..ac2decfb7 100644 --- a/test/icesheet.cc +++ b/test/icesheet.cc @@ -66,9 +66,9 @@ void readCoords(FILE* f, unsigned numvtx, double* coordinates) { } void readElements(FILE* f, unsigned numelms, int numVtxPerElm, - unsigned numVerts, int* elements) { + unsigned numVerts, apf::Gid* elements) { unsigned i; - std::map count; + std::map count; for (i = 0; i < numelms*numVtxPerElm; i++) { int vtxid; gmi_fscanf(f, 1, "%u", &vtxid); @@ -80,7 +80,7 @@ void readElements(FILE* f, unsigned numelms, int numVtxPerElm, struct MeshInfo { double* coords; - int* elements; + apf::Gid* elements; unsigned elementType; unsigned numVerts; unsigned numElms; @@ -95,7 +95,7 @@ void readMesh(const char* meshfilename, MeshInfo& mesh) { mesh.numVerts, mesh.numElms, mesh.numVtxPerElm); mesh.coords = new double[mesh.numVerts*3]; readCoords(f, mesh.numVerts, mesh.coords); - mesh.elements = new int [mesh.numElms*mesh.numVtxPerElm]; + mesh.elements = new apf::Gid [mesh.numElms*mesh.numVtxPerElm]; readElements(f, mesh.numElms, mesh.numVtxPerElm, mesh.numVerts, mesh.elements); mesh.elementType = getElmType(mesh.numVtxPerElm); fclose(f); @@ -180,7 +180,6 @@ int setModelClassification(gmi_model* model, void setFaceClassification(gmi_model* model, apf::Mesh2* mesh, apf::MeshTag* vtxType) { int numbdryfaces = 0; - int markedfaces = 0; int skippedfaces = 0; std::map faceClass; @@ -205,8 +204,6 @@ void setFaceClassification(gmi_model* model, apf::Mesh2* mesh, apf::MeshTag* vtx counttaggedvtx += vtx_type_num[i]; PCU_ALWAYS_ASSERT(counttaggedvtx==3); int isSet = setModelClassification(model, mesh, vtx_type_num, face, faceClass); - if( isSet == 1 ) - markedfaces++; if(isSet == 0) skippedfaces++; if( isSet == 0 || isSet == 1 ) diff --git a/test/inClosureOf_test.cc b/test/inClosureOf_test.cc index 2078638cc..7b673dee3 100644 --- a/test/inClosureOf_test.cc +++ b/test/inClosureOf_test.cc @@ -29,7 +29,6 @@ int main(int argc, char** argv) gmi_sim_start(); gmi_register_sim(); #endif - gmi_register_mesh(); gmi_model* model = gmi_load(modelFile); // read the model here diff --git a/test/ma_test.cc b/test/ma_test.cc index 7b83cd19c..c9e3284c3 100644 --- a/test/ma_test.cc +++ b/test/ma_test.cc @@ -54,7 +54,7 @@ int main(int argc, char** argv) ma::Mesh* m = apf::loadMdsMesh(modelFile,meshFile); m->verify(); Linear sf(m); - ma::Input* in = ma::configure(m, &sf); + ma::Input* in = ma::makeAdvanced(ma::configure(m, &sf)); in->shouldRunPreZoltan = true; in->shouldRunMidParma = true; in->shouldRunPostParma = true; diff --git a/test/ma_test_analytic_model.cc b/test/ma_test_analytic_model.cc index 8c210618a..6d5d45635 100644 --- a/test/ma_test_analytic_model.cc +++ b/test/ma_test_analytic_model.cc @@ -179,10 +179,7 @@ int main(int argc, char** argv) apf::writeVtkFiles("initial_mesh_on_analytic_model", m); - ma::Input* in = ma::configureUniformRefine(m, 2); - in->shouldSnap = true; - in->shouldTransferParametric = true; - in->shouldFixShape = true; + const ma::Input* in = ma::configureUniformRefine(m, 2); ma::adapt(in); apf::writeVtkFiles("adapted_mesh_on_analytic_model", m); diff --git a/test/makeAllCavities.cc b/test/makeAllCavities.cc new file mode 100644 index 000000000..3e11d44fb --- /dev/null +++ b/test/makeAllCavities.cc @@ -0,0 +1,938 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIMMETRIX +#include +#include +#include +#include +#endif + +// === includes for safe_mkdir === +#include +#include /*required for mode_t for mkdir on some systems*/ +#include /*using POSIX mkdir call for SMB "foo/" path*/ +#include /* for checking the error from mkdir */ + + +static void safe_mkdir( + const char* path); + +static void makeCavityMeshes( + apf::Mesh2* m, + apf::MeshEntity* e, + apf::Mesh2* &cavityMeshLinear, + apf::Mesh2* &cavityMeshCurved); + +static void makeEntMeshes( + apf::Mesh2* m, + apf::MeshEntity* e, + apf::Mesh2* &entMeshLinear, + apf::Mesh2* &entMeshCurved); + +static void writeMeshes( + apf::Mesh2* m, + const char* prefix0, + const char* prefix1, + const char* prefix2, + const char* name, + int res = 10); + +static apf::MeshTag* tagMesh( + apf::Mesh2* m, + int dim, + int model); + +static apf::MeshTag* tagMesh( + apf::Mesh2* m, + const std::vector& ids); + +int main(int argc, char** argv) +{ + + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + if (PCU_Comm_Peers() > 1) { + printf("%s should only be used for serial (single part) meshes!\n", argv[0]); + printf("use the serialize utility to get a serial mesh, and retry!\n"); + MPI_Finalize(); + exit(EXIT_FAILURE); + } + if (argc != 6) { + printf("USAGE: %s \n", argv[0]); + printf("modes are as follows \n"); + printf("aa: creates all vert, edge, face cavities\n"); + printf("ai: creates all vert, edge, face cavities classified on interior\n"); + printf("ab: creates all vert, edge, face cavities classified on boundary\n"); + printf("va: creates all vert cavities\n"); + printf("vi: creates all vert cavities classified on interior\n"); + printf("vb: creates all vert cavities classified on boundary\n"); + printf("ea: creates all edge cavities\n"); + printf("ei: creates all edge cavities classified on interior\n"); + printf("eb: creates all edge cavities classified on boundary\n"); + printf("fa: creates all face cavities\n"); + printf("fi: creates all face cavities classified on interior\n"); + printf("fb: creates all face cavities classified on boundary\n"); + printf("ls: get a list from user and creates cavities for that list\n"); + printf("tagname: creates cavities for all entities that have tagname\n"); + MPI_Finalize(); + exit(EXIT_FAILURE); + } + +#ifdef HAVE_SIMMETRIX + MS_init(); + SimModel_start(); + Sim_readLicenseFile(0); + gmi_sim_start(); + gmi_register_sim(); +#endif + + gmi_register_null(); + + const char* modelFile = argv[1]; + const char* meshFile = argv[2]; + const char* prefix = argv[3]; + int res = atoi(argv[4]); + std::string mode(argv[5]); + + apf::MeshTag* tag = 0; + + + // load the mesh and check if the tag exists on the mesh + apf::Mesh2* m = apf::loadMdsMesh(modelFile,meshFile); + + if (mode.compare(std::string("aa")) == 0) + tag = tagMesh(m, -1, 1); + else if (mode.compare(std::string("ai")) == 0) + tag = tagMesh(m, -1, 2); + else if (mode.compare(std::string("ab")) == 0) + tag = tagMesh(m, -1, 3); + else if (mode.compare(std::string("va")) == 0) + tag = tagMesh(m, 0, 1); + else if (mode.compare(std::string("vi")) == 0) + tag = tagMesh(m, 0, 2); + else if (mode.compare(std::string("vb")) == 0) + tag = tagMesh(m, 0, 3); + else if (mode.compare(std::string("ea")) == 0) + tag = tagMesh(m, 1, 1); + else if (mode.compare(std::string("ei")) == 0) + tag = tagMesh(m, 1, 2); + else if (mode.compare(std::string("eb")) == 0) + tag = tagMesh(m, 1, 3); + else if (mode.compare(std::string("fa")) == 0) + tag = tagMesh(m, 2, 1); + else if (mode.compare(std::string("fi")) == 0) + tag = tagMesh(m, 2, 2); + else if (mode.compare(std::string("fb")) == 0) + tag = tagMesh(m, 2, 3); + else if (mode.compare(std::string("ls")) == 0) { + std::cout << "provide the list of entities format \"v_i,e_i,f_i\"" << std::endl; + std::cout << "example: v120 e23 f0 represents vert 120," << std::endl; + std::cout << "edge 23 and face 0." << std::endl; + std::cout << "total #verts=" << m->count(0); + std::cout << ", #edges=" << m->count(1); + std::cout << ", #faces=" << m->count(2) << std::endl; + int cnt=0; + std::cout << "enter #of ents in the list:" << std::endl; + std::cin >> cnt; + std::vector ents; + ents.clear(); + while ((int)ents.size() < cnt) { + std::string temp; + std::cin >> temp; + ents.push_back(temp); + } + PCU_ALWAYS_ASSERT((int)ents.size() == cnt); + std::cout << "creating cavities for " << std::endl; + for (int i = 0; i < (int)ents.size(); i++) + std::cout << ents[i] << " "; + std::cout << std::endl; + + tag = tagMesh(m, ents); + } + else { + tag = m->findTag(mode.c_str()); + if (!tag) { + printf("tag with name %s was not found on the mesh. Aborting!\n", mode.c_str()); + MPI_Finalize(); + exit(EXIT_FAILURE); + } + } + + PCU_ALWAYS_ASSERT(tag); + + + // make the root directory to save the cavity info + safe_mkdir(prefix); + writeMeshes(m, prefix, "mesh", NULL, "linear", res); + // change the order of the mesh + m->changeShape(crv::getBezier(3), true); + writeMeshes(m, prefix, "mesh", NULL, "curved", res); + + + apf::MeshEntity* e; + apf::MeshIterator* it; + + + for (int d = 0; d < 3; d++) { + it = m->begin(d); + int index = 0; + + // for now cavities are defined as follows + // all the upward adjacent entities of dimension "dim" that + // are adjacent to the verts of the "e". E.g., in case of edges, + // this would give us the bi-directional edge collapse cavity. + while ( (e = m->iterate(it)) ) { + if (!m->hasTag(e, tag)) { + index++; + continue; + } + int etype = m->getType(e); + + apf::Mesh2* cavityMeshCurved = 0; + apf::Mesh2* cavityMeshLinear = 0; + makeCavityMeshes(m, e, cavityMeshLinear, cavityMeshCurved); + + apf::Mesh2* entMeshCurved = 0; + apf::Mesh2* entMeshLinear = 0; + makeEntMeshes(m, e, entMeshLinear, entMeshCurved); + + char cavityFolderName[128]; + char cavityFileNameLinear[128]; + char cavityFileNameCurved[128]; + char entityFileNameLinear[128]; + char entityFileNameCurved[128]; + sprintf(cavityFolderName, "%s_%05d", apf::Mesh::typeName[etype], index); + sprintf(cavityFileNameLinear, "%s", "cavity_linear"); + sprintf(cavityFileNameCurved, "%s", "cavity_curved"); + sprintf(entityFileNameLinear, "%s", "entity_linear"); + sprintf(entityFileNameCurved, "%s", "entity_curved"); + writeMeshes(cavityMeshLinear, prefix, "cavities", + cavityFolderName, cavityFileNameLinear, res); + writeMeshes(cavityMeshCurved, prefix, "cavities", + cavityFolderName, cavityFileNameCurved, res); + + writeMeshes(entMeshLinear, prefix, "cavities", + cavityFolderName, entityFileNameLinear, res); + writeMeshes(entMeshCurved, prefix, "cavities", + cavityFolderName, entityFileNameCurved, res); + + entMeshLinear->destroyNative(); + entMeshCurved->destroyNative(); + apf::destroyMesh(entMeshLinear); + apf::destroyMesh(entMeshCurved); + cavityMeshLinear->destroyNative(); + cavityMeshCurved->destroyNative(); + apf::destroyMesh(cavityMeshLinear); + apf::destroyMesh(cavityMeshCurved); + index++; + } + m->end(it); + } + + // rest of the clean up + m->destroyNative(); + apf::destroyMesh(m); + +#ifdef HAVE_SIMMETRIX + gmi_sim_stop(); + Sim_unregisterAllKeys(); + SimModel_stop(); + MS_exit(); +#endif + + PCU_Comm_Free(); + MPI_Finalize(); +} + +static void safe_mkdir( + const char* path) +{ + mode_t const mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + int err; + errno = 0; + err = mkdir(path, mode); + if (err != 0 && errno != EEXIST) + { + reel_fail("Err: could not create directory \"%s\"\n", path); + } +} + +static apf::Vector3 getEdgeCenter( + apf::Mesh2* m, + apf::MeshEntity* e) +{ + PCU_ALWAYS_ASSERT(m->getType(e) == apf::Mesh::EDGE); + apf::MeshEntity* dv[2]; + m->getDownward(e, 0, dv); + apf::Vector3 center(0., 0., 0.); + for (int i = 0; i < 2; i++) { + apf::Vector3 p; + m->getPoint(dv[i], 0, p); + center = center + p; + } + center = center * (0.5); + return center; +} + +static apf::Mesh2* makePoint( + apf::Mesh2* m, + apf::MeshEntity* e) +{ + PCU_ALWAYS_ASSERT(m->getType(e) == apf::Mesh::VERTEX); + apf::Mesh2* sphMesh = apf::makeEmptyMdsMesh(gmi_load(".null"), 1, false); + double xrange[2] = {1.e16, -1.e16}; + double yrange[2] = {1.e16, -1.e16}; + double zrange[2] = {1.e16, -1.e16}; + + apf::Adjacent adj; + m->getAdjacent(e, 1, adj); + + for (int i = 0; i < (int)adj.getSize(); i++) { + apf::Vector3 p = getEdgeCenter(m, adj[i]); + if (p[0] < xrange[0]) xrange[0] = p[0]; + if (p[0] > xrange[1]) xrange[1] = p[0]; + + if (p[1] < yrange[0]) yrange[0] = p[1]; + if (p[1] > yrange[1]) yrange[1] = p[1]; + + if (p[2] < zrange[0]) zrange[0] = p[2]; + if (p[2] > zrange[1]) zrange[1] = p[2]; + } + + double minsize = 1.e16; + if (xrange[1]-xrange[0] < minsize) minsize = xrange[1] - xrange[0]; + if (yrange[1]-yrange[0] < minsize) minsize = yrange[1] - yrange[0]; + if (zrange[1]-zrange[0] < minsize) minsize = zrange[1] - zrange[0]; + + double radius = minsize / 20.; + apf::Vector3 center; + m->getPoint(e, 0, center); + + // z = 0 plane + int n = 20; + const double pi = 3.141595; + const apf::Vector3 param(0., 0., 0.); + std::vector vs; + vs.clear(); + for (int i = 0; i < n; i++) { + apf::Vector3 p(0., 0., 0.); + p[0] = center[0] + radius * std::cos(2.*i*pi/n); + p[1] = center[1] + radius * std::sin(2.*i*pi/n); + p[2] = center[2]; + apf::MeshEntity* newV = sphMesh->createVertex(m->toModel(e), p, param); + vs.push_back(newV); + } + + for (int i = 0; i < n; i++) { + apf::MeshEntity* dv[2]; + dv[0] = vs[i]; + dv[1] = vs[(i+1)%20]; + sphMesh->createEntity(apf::Mesh::EDGE, m->toModel(e), dv); + } + + // y = 0 plane + vs.clear(); + for (int i = 0; i < n; i++) { + apf::Vector3 p(0., 0., 0.); + p[0] = center[0] + radius * std::cos(2.*i*pi/n); + p[1] = center[1]; + p[2] = center[2] + radius * std::sin(2.*i*pi/n); + apf::MeshEntity* newV = sphMesh->createVertex(m->toModel(e), p, param); + vs.push_back(newV); + } + + for (int i = 0; i < n; i++) { + apf::MeshEntity* dv[2]; + dv[0] = vs[i]; + dv[1] = vs[(i+1)%20]; + sphMesh->createEntity(apf::Mesh::EDGE, m->toModel(e), dv); + } + + // x = 0 plane + vs.clear(); + for (int i = 0; i < n; i++) { + apf::Vector3 p(0., 0., 0.); + p[0] = center[0]; + p[1] = center[1] + radius * std::cos(2.*i*pi/n); + p[2] = center[2] + radius * std::sin(2.*i*pi/n); + apf::MeshEntity* newV = sphMesh->createVertex(m->toModel(e), p, param); + vs.push_back(newV); + } + + for (int i = 0; i < n; i++) { + apf::MeshEntity* dv[2]; + dv[0] = vs[i]; + dv[1] = vs[(i+1)%20]; + sphMesh->createEntity(apf::Mesh::EDGE, m->toModel(e), dv); + } + sphMesh->acceptChanges(); + apf::deriveMdsModel(sphMesh); + sphMesh->verify(); + return sphMesh; +} + +static void makeEntMeshes( + apf::Mesh2* m, + apf::MeshEntity* e, + apf::Mesh2* &entMeshLinear, + apf::Mesh2* &entMeshCurved) +{ + PCU_ALWAYS_ASSERT(!entMeshLinear); + PCU_ALWAYS_ASSERT(!entMeshCurved); + + const apf::Vector3 param(0., 0., 0.); + + if (m->getType(e) == apf::Mesh::VERTEX) + { + entMeshLinear = makePoint(m, e); + entMeshCurved = makePoint(m, e); + return; + } + if (m->getType(e) == apf::Mesh::EDGE) + { + entMeshLinear = apf::makeEmptyMdsMesh(gmi_load(".null"), 1, false); + entMeshCurved = apf::makeEmptyMdsMesh(gmi_load(".null"), 1, false); + apf::MeshEntity* vs[2]; + m->getDownward(e, 0, vs); + apf::Vector3 p[2]; + m->getPoint(vs[0], 0, p[0]); + m->getPoint(vs[1], 0, p[1]); + apf::MeshEntity* newVs[2]; + newVs[0] = entMeshLinear->createVertex(0, p[0], param); + newVs[1] = entMeshLinear->createVertex(0, p[1], param); + entMeshLinear->createEntity(apf::Mesh::EDGE, 0, newVs); + + apf::MeshEntity* newVsc[2]; + newVsc[0] = entMeshCurved->createVertex(0, p[0], param); + newVsc[1] = entMeshCurved->createVertex(0, p[1], param); + apf::MeshEntity* edge = entMeshCurved->createEntity(apf::Mesh::EDGE, 0, newVsc); + + entMeshLinear->acceptChanges(); + apf::deriveMdsModel(entMeshLinear); + entMeshLinear->verify(); + + entMeshCurved->acceptChanges(); + apf::deriveMdsModel(entMeshCurved); + entMeshCurved->verify(); + + apf::FieldShape* fs = m->getShape(); + entMeshCurved->changeShape(fs, true); + if (fs->countNodesOn(apf::Mesh::EDGE)) + { + for (int i = 0; i < fs->countNodesOn(apf::Mesh::EDGE); i++) { + apf::Vector3 p; + m->getPoint(e, i, p); + entMeshCurved->setPoint(edge, i, p); + } + } + entMeshCurved->acceptChanges(); + return; + } + if (m->getType(e) == apf::Mesh::TRIANGLE) + { + entMeshLinear = apf::makeEmptyMdsMesh(gmi_load(".null"), 2, false); + entMeshCurved = apf::makeEmptyMdsMesh(gmi_load(".null"), 2, false); + apf::MeshEntity* downverts[3]; + apf::MeshEntity* downedges[3]; + m->getDownward(e, 0, downverts); + m->getDownward(e, 1, downedges); + int edge_vert[3][2]; + for (int i = 0; i < 3; i++) { + apf::MeshEntity* dv[2]; + m->getDownward(downedges[i], 0, dv); + int i0 = apf::findIn(downverts, 3, dv[0]); + int i1 = apf::findIn(downverts, 3, dv[1]); + PCU_ALWAYS_ASSERT(i0 != -1); + PCU_ALWAYS_ASSERT(i1 != -1); + edge_vert[i][0] = i0; + edge_vert[i][1] = i1; + } + + apf::MeshEntity* newvertsLinear[3]; + apf::MeshEntity* newedgesLinear[3]; + apf::MeshEntity* newvertsCurved[3]; + apf::MeshEntity* newedgesCurved[3]; + apf::Vector3 param(0.,0.,0.); + for (int i = 0; i < 3; i++) { + apf::Vector3 p; + m->getPoint(downverts[i], 0, p); + newvertsLinear[i] = entMeshLinear->createVertex(0, p, param); + newvertsCurved[i] = entMeshCurved->createVertex(0, p, param); + } + + for (int i = 0; i < 3; i++) { + apf::MeshEntity* evLinear[2] = { + newvertsLinear[edge_vert[i][0]], + newvertsLinear[edge_vert[i][1]] + }; + newedgesLinear[i] = entMeshLinear->createEntity( + apf::Mesh::EDGE, 0, evLinear); + + apf::MeshEntity* evCurved[2] = { + newvertsCurved[edge_vert[i][0]], + newvertsCurved[edge_vert[i][1]] + }; + newedgesCurved[i] = entMeshCurved->createEntity( + apf::Mesh::EDGE, 0, evCurved); + } + + + entMeshLinear->createEntity( + apf::Mesh::TRIANGLE, 0, newedgesLinear); + + apf::MeshEntity* face = + entMeshCurved->createEntity( + apf::Mesh::TRIANGLE, 0, newedgesCurved); + + + entMeshLinear->acceptChanges(); + apf::deriveMdsModel(entMeshLinear); + entMeshLinear->verify(); + + entMeshCurved->acceptChanges(); + apf::deriveMdsModel(entMeshCurved); + entMeshCurved->verify(); + + apf::FieldShape* fs = m->getShape(); + entMeshCurved->changeShape(fs, true); + + int nnodes = fs->countNodesOn(apf::Mesh::EDGE); + if (nnodes) { + for (int i = 0; i < 3; i++) { + for (int n = 0; n < nnodes; n++) { + apf::Vector3 p; + m->getPoint(downedges[i], n, p); + entMeshCurved->setPoint(newedgesCurved[i], n, p); + } + } + } + + nnodes = fs->countNodesOn(apf::Mesh::TRIANGLE); + if (nnodes) { + for (int n = 0; n < nnodes; n++) { + apf::Vector3 p; + m->getPoint(e, n, p); + entMeshCurved->setPoint(face, n, p); + } + } + + entMeshCurved->acceptChanges(); + return; + } +} + +static void makeCavityMeshes( + apf::Mesh2* m, + apf::MeshEntity* e, + apf::Mesh2* &cavityMeshLinear, + apf::Mesh2* &cavityMeshCurved) +{ + typedef std::vector Cavity; + typedef std::vector::iterator CavityIter; + PCU_ALWAYS_ASSERT(!cavityMeshLinear); + PCU_ALWAYS_ASSERT(!cavityMeshCurved); + + int dim = m->getDimension(); + + // input cavities + Cavity icavity3; // tets in the cavity + Cavity icavity2; // faces in the cavity + Cavity icavity1; // edges in the cavity + Cavity icavity0; // verts in the cavity + icavity3.clear(); + icavity2.clear(); + icavity1.clear(); + icavity0.clear(); + + + // connectivity tables + std::vector> tet_face; + std::vector> face_edge; + std::vector> edge_vert; + + apf::Downward dv; + int nv = m->getDownward(e, 0, dv); + for (int i = 0; i < nv; i++) { + apf::Adjacent adj; + m->getAdjacent(dv[i], dim, adj); + for (int j = 0; j < (int)adj.getSize(); j++) { + if (std::find(icavity3.begin(), icavity3.end(), adj[j]) == icavity3.end()) + icavity3.push_back(adj[j]); + } + } + + + // construct the unique vector of faces and tet_face connectivity table + for (int i = 0; i < (int)icavity3.size(); i++) { + apf::Downward dents; + int nents = m->getDownward(icavity3[i], 2, dents); + for (int j = 0; j < nents; j++) { + if (std::find(icavity2.begin(), icavity2.end(), dents[j]) == icavity2.end()) + icavity2.push_back(dents[j]); + } + std::vector conn; + for (int j = 0; j < nents; j++) { + CavityIter it = std::find(icavity2.begin(), icavity2.end(), dents[j]); + PCU_ALWAYS_ASSERT(it != icavity2.end()); + conn.push_back(std::distance(icavity2.begin(), it)); + } + PCU_ALWAYS_ASSERT((int)conn.size() == nents); + tet_face.push_back(conn); + } + PCU_ALWAYS_ASSERT(icavity3.size() == tet_face.size()); + + // construct the unique vector of edges and face_edge connectivity table + for (int i = 0; i < (int)icavity2.size(); i++) { + apf::Downward dents; + int nents = m->getDownward(icavity2[i], 1, dents); + for (int j = 0; j < nents; j++) { + if (std::find(icavity1.begin(), icavity1.end(), dents[j]) == icavity1.end()) + icavity1.push_back(dents[j]); + } + std::vector conn; + for (int j = 0; j < nents; j++) { + CavityIter it = std::find(icavity1.begin(), icavity1.end(), dents[j]); + PCU_ALWAYS_ASSERT(it != icavity1.end()); + conn.push_back(std::distance(icavity1.begin(), it)); + } + PCU_ALWAYS_ASSERT((int)conn.size() == nents); + face_edge.push_back(conn); + } + PCU_ALWAYS_ASSERT(icavity2.size() == face_edge.size()); + + // construct the unique vector of verts and edge_vert connectivity table + for (int i = 0; i < (int)icavity1.size(); i++) { + apf::Downward dents; + int nents = m->getDownward(icavity1[i], 0, dents); + for (int j = 0; j < nents; j++) { + if (std::find(icavity0.begin(), icavity0.end(), dents[j]) == icavity0.end()) + icavity0.push_back(dents[j]); + } + std::vector conn; + for (int j = 0; j < nents; j++) { + CavityIter it = std::find(icavity0.begin(), icavity0.end(), dents[j]); + PCU_ALWAYS_ASSERT(it != icavity0.end()); + conn.push_back(std::distance(icavity0.begin(), it)); + } + PCU_ALWAYS_ASSERT((int)conn.size() == nents); + edge_vert.push_back(conn); + } + PCU_ALWAYS_ASSERT(icavity1.size() == edge_vert.size()); + + + // output cavities + Cavity ocavity3linear; // tets in the cavity + Cavity ocavity2linear; // faces in the cavity + Cavity ocavity1linear; // edges in the cavity + Cavity ocavity0linear; // verts in the cavity + Cavity ocavity3curved; // tets in the cavity + Cavity ocavity2curved; // faces in the cavity + Cavity ocavity1curved; // edges in the cavity + Cavity ocavity0curved; // verts in the cavity + ocavity3linear.clear(); + ocavity2linear.clear(); + ocavity1linear.clear(); + ocavity0linear.clear(); + ocavity3curved.clear(); + ocavity2curved.clear(); + ocavity1curved.clear(); + ocavity0curved.clear(); + + + + // construct the cavity meshes + // we do this in a bottom up fashion, ie vets first then edges, faces and tets + /* cavityMeshLinear = apf::makeEmptyMdsMesh(m->getModel(), dim, false); */ + /* cavityMeshCurved = apf::makeEmptyMdsMesh(m->getModel(), dim, false); */ + cavityMeshLinear = apf::makeEmptyMdsMesh(gmi_load(".null"), dim, false); + cavityMeshCurved = apf::makeEmptyMdsMesh(gmi_load(".null"), dim, false); + + // verts + for (int i = 0; i < (int) icavity0.size(); i++) { + apf::MeshEntity* ent = icavity0[i]; + apf::ModelEntity* c = m->toModel(ent); + apf::Vector3 coords; + apf::Vector3 params; + m->getPoint(ent, 0, coords); + m->getParam(ent, params); + + apf::MeshEntity* newEnt = cavityMeshLinear->createVertex(c, coords, params); + ocavity0linear.push_back(newEnt); + + apf::MeshEntity* newEntc = cavityMeshCurved->createVertex(c, coords, params); + ocavity0curved.push_back(newEntc); + } + PCU_ALWAYS_ASSERT(icavity0.size() == ocavity0linear.size()); + PCU_ALWAYS_ASSERT(icavity0.size() == ocavity0curved.size()); + + // edges + for (int i = 0; i < (int) icavity1.size(); i++) { + apf::MeshEntity* ent = icavity1[i]; + apf::ModelEntity* c = m->toModel(ent); + apf::MeshEntity* downv[2]; + downv[0] = ocavity0linear[edge_vert[i][0]]; + downv[1] = ocavity0linear[edge_vert[i][1]]; + apf::MeshEntity* newEnt = cavityMeshLinear->createEntity( + apf::Mesh::EDGE, c, downv); + ocavity1linear.push_back(newEnt); + + downv[0] = ocavity0curved[edge_vert[i][0]]; + downv[1] = ocavity0curved[edge_vert[i][1]]; + apf::MeshEntity* newEntc = cavityMeshCurved->createEntity( + apf::Mesh::EDGE, c, downv); + ocavity1curved.push_back(newEntc); + } + PCU_ALWAYS_ASSERT(icavity1.size() == ocavity1linear.size()); + PCU_ALWAYS_ASSERT(icavity1.size() == ocavity1curved.size()); + + // faces + for (int i = 0; i < (int) icavity2.size(); i++) { + apf::MeshEntity* ent = icavity2[i]; + apf::ModelEntity* c = m->toModel(ent); + apf::MeshEntity* downe[3]; + downe[0] = ocavity1linear[face_edge[i][0]]; + downe[1] = ocavity1linear[face_edge[i][1]]; + downe[2] = ocavity1linear[face_edge[i][2]]; + apf::MeshEntity* newEnt = cavityMeshLinear->createEntity( + apf::Mesh::TRIANGLE, c, downe); + ocavity2linear.push_back(newEnt); + + downe[0] = ocavity1curved[face_edge[i][0]]; + downe[1] = ocavity1curved[face_edge[i][1]]; + downe[2] = ocavity1curved[face_edge[i][2]]; + apf::MeshEntity* newEntc = cavityMeshCurved->createEntity( + apf::Mesh::TRIANGLE, c, downe); + ocavity2curved.push_back(newEntc); + } + PCU_ALWAYS_ASSERT(icavity2.size() == ocavity2linear.size()); + PCU_ALWAYS_ASSERT(icavity2.size() == ocavity2curved.size()); + + + // tets + for (int i = 0; i < (int) icavity3.size(); i++) { + apf::MeshEntity* ent = icavity3[i]; + apf::ModelEntity* c = m->toModel(ent); + apf::MeshEntity* downf[4]; + downf[0] = ocavity2linear[tet_face[i][0]]; + downf[1] = ocavity2linear[tet_face[i][1]]; + downf[2] = ocavity2linear[tet_face[i][2]]; + downf[3] = ocavity2linear[tet_face[i][3]]; + apf::MeshEntity* newEnt = cavityMeshLinear->createEntity( + apf::Mesh::TET, c, downf); + ocavity3linear.push_back(newEnt); + + downf[0] = ocavity2curved[tet_face[i][0]]; + downf[1] = ocavity2curved[tet_face[i][1]]; + downf[2] = ocavity2curved[tet_face[i][2]]; + downf[3] = ocavity2curved[tet_face[i][3]]; + apf::MeshEntity* newEntc = cavityMeshCurved->createEntity( + apf::Mesh::TET, c, downf); + ocavity3curved.push_back(newEntc); + } + PCU_ALWAYS_ASSERT(icavity3.size() == ocavity3linear.size()); + PCU_ALWAYS_ASSERT(icavity3.size() == ocavity3curved.size()); + + + cavityMeshLinear->acceptChanges(); + apf::deriveMdsModel(cavityMeshLinear); + cavityMeshLinear->verify(); + + cavityMeshCurved->acceptChanges(); + apf::deriveMdsModel(cavityMeshCurved); + cavityMeshCurved->verify(); + + // curve cavityMeshCurved + // this can be done by + // a) setting the shape of cavityMeshCurved to that of m + // b) setting the node coordinates of the entities + // in ocavity{1,2,3}curved to those of entities + // in icavity{1,2,3} + cavityMeshCurved->changeShape(m->getShape(), true); + apf::FieldShape* fs = cavityMeshCurved->getShape(); + + int nnodes = fs->countNodesOn(apf::Mesh::TET); + if (nnodes) { + for (int i = 0; i < (int)icavity3.size(); i++) { + apf::MeshEntity* fromEnt = icavity3[i]; + apf::MeshEntity* toEnt = ocavity3curved[i]; + for (int j = 0; j < nnodes; j++) { + apf::Vector3 p; + m->getPoint(fromEnt, j, p); + cavityMeshCurved->setPoint(toEnt, j, p); + } + } + } + + nnodes = fs->countNodesOn(apf::Mesh::TRIANGLE); + if (nnodes) { + for (int i = 0; i < (int)icavity2.size(); i++) { + apf::MeshEntity* fromEnt = icavity2[i]; + apf::MeshEntity* toEnt = ocavity2curved[i]; + for (int j = 0; j < nnodes; j++) { + apf::Vector3 p; + m->getPoint(fromEnt, j, p); + cavityMeshCurved->setPoint(toEnt, j, p); + } + } + } + + nnodes = fs->countNodesOn(apf::Mesh::EDGE); + if (nnodes) { + for (int i = 0; i < (int)icavity1.size(); i++) { + apf::MeshEntity* fromEnt = icavity1[i]; + apf::MeshEntity* toEnt = ocavity1curved[i]; + for (int j = 0; j < nnodes; j++) { + apf::Vector3 p; + m->getPoint(fromEnt, j, p); + cavityMeshCurved->setPoint(toEnt, j, p); + } + } + } + + cavityMeshCurved->acceptChanges(); +} + +static void writeMeshes( + apf::Mesh2* m, + const char* prefix0, + const char* prefix1, + const char* prefix2, + const char* name, + int res) +{ + PCU_ALWAYS_ASSERT(prefix0); + PCU_ALWAYS_ASSERT(name); + if (!prefix1) + PCU_ALWAYS_ASSERT(!prefix2); + + int order = m->getShape()->getOrder(); + std::stringstream ss; + ss << prefix0 << "/"; + if (prefix1) { + ss << prefix1; + safe_mkdir(ss.str().c_str()); + ss << "/"; + } + if (prefix2) { + ss << prefix2; + safe_mkdir(ss.str().c_str()); + ss << "/"; + } + ss << name; + + if (order == 1) { + apf::writeVtkFiles(ss.str().c_str(), m); + } + else { + crv::writeCurvedVtuFiles(m, apf::Mesh::TRIANGLE, res, ss.str().c_str()); + crv::writeCurvedWireFrame(m, res, ss.str().c_str()); + } + ss << ".smb"; + m->writeNative(ss.str().c_str()); +} + +static apf::MeshTag* tagMesh( + apf::Mesh2* m, + int dim, + int model) +{ + // model = 1 means all + // model = 2 means only interior + // model = 3 means only boundary + // dim = -1 tags all dims + // dim = 0 tags verts only + // dim = 1 tags edges only + // dim = 2 tags faces only + apf::MeshEntity* e; + apf::MeshIterator* it; + apf::MeshTag* t = m->createIntTag("which_ent", 1); + for (int d = 0; d < 3; d++) { + if (dim == 0 && d != 0) continue; + if (dim == 1 && d != 1) continue; + if (dim == 2 && d != 2) continue; + it = m->begin(d); + while ( (e = m->iterate(it)) ) { + int mtype = m->getModelType(m->toModel(e)); + if (model == 2 && mtype !=3) continue; + if (model == 3 && mtype ==3) continue; + int val = 1; // the value does not matter + m->setIntTag(e, t, &val); + } + m->end(it); + } + return t; +} + +static void getEntIds( + const std::vector& ids, + std::vector& vids, + std::vector& eids, + std::vector& fids) +{ + vids.clear(); + eids.clear(); + fids.clear(); + + for (std::size_t i = 0; i < ids.size(); i++) { + std::string key = ids[i].substr(0,1); + int value = atoi(ids[i].substr(1).c_str()); + if (key.compare(std::string("v")) == 0) + vids.push_back(value); + if (key.compare(std::string("e")) == 0) + eids.push_back(value); + if (key.compare(std::string("f")) == 0) + fids.push_back(value); + } + +} + +static apf::MeshTag* tagMesh( + apf::Mesh2* m, + const std::vector& ids) +{ + std::vector vids; + std::vector eids; + std::vector fids; + getEntIds(ids, vids, eids, fids); + PCU_ALWAYS_ASSERT(ids.size() == vids.size()+eids.size()+fids.size()); + + std::vector::iterator vit; + apf::MeshEntity* e; + apf::MeshIterator* it; + apf::MeshTag* t = m->createIntTag("which_ent", 1); + for (int d = 0; d < 3; d++) { + int index = 0; + it = m->begin(d); + while ( (e = m->iterate(it)) ) { + bool found = false; + if (d == 0 && + std::find(vids.begin(), vids.end(), index) != vids.end()) + found = true; + if (d == 1 && + std::find(eids.begin(), eids.end(), index) != eids.end()) + found = true; + if (d == 2 && + std::find(fids.begin(), fids.end(), index) != fids.end()) + found = true; + int val = 1; // the value does not matter + if (found) + m->setIntTag(e, t, &val); + index++; + } + m->end(it); + } + return t; +} diff --git a/test/matchedNodeElmReader.cc b/test/matchedNodeElmReader.cc new file mode 100644 index 000000000..4dfdfabd0 --- /dev/null +++ b/test/matchedNodeElmReader.cc @@ -0,0 +1,832 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* from https://github.com/SCOREC/core/issues/205 +0=fully interior of the volume +1-6 =classified on face (not edge or vertex) +11-22 = classified on model edge (not end points which are model vertices) +31-38 = classified on a model vertex. +*/ + +/* tags on vertices */ +#define INTERIORTAG 0 +#define FACE 1 +#define FACE_LAST 6 +#define EDGE 11 +#define EDGE_LAST 22 +#define VERTEX 31 +#define VERTEX_LAST 38 + +/* model entity ids */ +//#define INTERIOR_REGION 0 +//int INTERIOR_REGION=0; // initialized but will be checked from read input + +//Manifold single region apf::ModelEntity* getMdlRgn(gmi_model* model) { +//Manifold single region apf::ModelEntity* rgn = reinterpret_cast( +//Manifold single region gmi_find(model, 3, INTERIOR_REGION)); +//Manifold single region PCU_ALWAYS_ASSERT(rgn); +//Manifold single region return rgn; +//Manifold single region } + + +apf::ModelEntity* getMdlRegion(apf::Mesh2* mesh, int tag) { + apf::ModelEntity* region = mesh->findModelEntity(3,tag); + PCU_ALWAYS_ASSERT(region); + return region; +} + +apf::ModelEntity* getMdlFace(apf::Mesh2* mesh, int tag) { + apf::ModelEntity* face = mesh->findModelEntity(2,tag); + PCU_ALWAYS_ASSERT(face); + return face; +} + +apf::ModelEntity* getMdlEdge(apf::Mesh2* mesh, int tag) { + apf::ModelEntity* edge = mesh->findModelEntity(1,tag); + PCU_ALWAYS_ASSERT(edge); + return edge; +} + +apf::ModelEntity* getMdlVtx(apf::Mesh2* mesh, int tag) { + apf::ModelEntity* vertex = mesh->findModelEntity(0,tag); + PCU_ALWAYS_ASSERT(vertex); + return vertex; +} + +int findRegionTag2Face(gmi_model* model, int* cAll, int nverts) { + std::vector cOrdered(nverts); + int jmax=0; + int rtag, cmax; + std::vector cLocal(nverts); + for(int i=0; in; i++) { + for (int j = 0; j < RegionsAdjMin->n; j++) { + if(RegionsAdjMax->e[i]==RegionsAdjMin->e[j]){ + rtag=gmi_tag(model,RegionsAdjMax->e[j]); + found=true; + break; + } + } + } + free(RegionsAdjMax); + free(RegionsAdjMin); + if(found) { + return rtag; + } + } + } + return -1; +} + +int findRegionTag1Face(gmi_model* model, int cmax) { + int rtag,dimE, cnd; + dimE=cmax/1000000; + cnd=cmax-dimE*1000000; + gmi_ent* maxE = gmi_find(model,dimE,cnd); + gmi_set* RegionsAdjMax = gmi_adjacent(model,maxE,3); + rtag=gmi_tag(model,RegionsAdjMax->e[0]); + free(RegionsAdjMax); + return rtag; +} + +void setVtxClassification(gmi_model* model, apf::Mesh2* mesh, apf::MeshTag* vtxClass) { + (void)model; + (void)mesh; + (void)vtxClass; + apf::MeshIterator* it = mesh->begin(0); + apf::MeshEntity* v; + int c; + while( (v = mesh->iterate(it)) ) { + mesh->getIntTag(v,vtxClass,&c); +// +// New approach: mesher will add 0 to mdlConvert's dmg tag number for vertices, +// 1000000 to mdlConvert's dmg tag number for edges, +// 2000000 to mdlConvert's dmg tag number for faces, +// 3000000 to mdlConvert's dmg tag number for regions + if (c >= 3000000 ) { + mesh->setModelEntity(v,getMdlRegion(mesh,c-3000000)); + } else if (c >= 2000000) { + mesh->setModelEntity(v,getMdlFace(mesh,c-2000000)); + } else if (c >= 1000000) { + mesh->setModelEntity(v,getMdlEdge(mesh,c-1000000)); + } else { + mesh->setModelEntity(v,getMdlVtx(mesh,c)); + } + } + mesh->end(it); +} + +void setEdgeClassification(gmi_model* model, apf::Mesh2* mesh,apf::MeshTag* vtxClass) { + (void)model; + (void)mesh; + (void)vtxClass; + apf::MeshIterator* it = mesh->begin(1); + apf::MeshEntity* e; + int c; + apf::Adjacent verts; + int k,ff; + double distFromDebug1; + apf::Vector3 xd1(0.000509276, 0, 0.0797419); + apf::Vector3 dx1; + apf::Vector3 dx2; + apf::Vector3 tmp; + apf::Vector3 Centroid; + while( (e = mesh->iterate(it)) ) { + mesh->getAdjacent(e, 0, verts); + int nverts = verts.size(); + if(1==0) { + Centroid=apf::getLinearCentroid(mesh,e); + dx1=xd1-Centroid; + distFromDebug1=dx1[0]*dx1[0] + +dx1[1]*dx1[1] + +dx1[2]*dx1[2]; + } + int cmax=-100; + int cmin=100000000; + for(int i=0; i<(int)verts.size(); i++) { + mesh->getIntTag(verts[i],vtxClass,&c); + cmax=std::max(cmax,c); + cmin=std::min(cmin,c); + } + if(1==0 && distFromDebug1 < 1e-8) { + fprintf(stderr, "%d %d %.15e %.15E %.15E \n", cmin, cmax, Centroid[0], Centroid[1], Centroid[2]); + for (int i=0; i < nverts; i++) { + mesh->getPoint(verts[i],0,tmp); + fprintf(stderr, "%d %.15e %.15E %.15E \n", i , tmp[0], tmp[1], tmp[2]); + } + } + int dimMin=cmin/1000000; + int dimMax=cmax/1000000; + int tagMin=cmin-dimMin*1000000; + int tagMax=cmax-dimMax*1000000; + if (cmax >= 3000000) { + mesh->setModelEntity(e,getMdlRegion(mesh,cmax-3000000)); + } else if (cmax >= 2000000) { //max is a face + if(cmin==cmax) { //min is same face -> correct face to cls + mesh->setModelEntity(e,getMdlFace(mesh,cmax-2000000)); + } else if (cmin >= 2000000) { // min is a DIFFERENT face -> interior + nverts=2; + int ctri[2]={cmax,cmin}; + int rtag = findRegionTag2Face(model, ctri, nverts); + assert(rtag != -1); // bad input list of ctri (e.g., not two distinct faces) + mesh->setModelEntity(e,getMdlRegion(mesh,rtag)); + } else { +//FAILS ROLL OUR OWN int res = gmi_is_in_closure_of(model,gmi_find(model,dimMin,tagMin), gmi_find(model,dimMax,tagMax)); + ff=-1; + gmi_ent* ge=gmi_find(model,dimMin,tagMin); + gmi_ent* gf =gmi_find(model,2,tagMax); // get the model face that goes with max + gmi_set* Edges = gmi_adjacent(model,gf,1); + k=0; + while(k<((Edges->n)) && ff==-1){ // check all edges until one found + if(dimMin==1) { + if(ge==Edges->e[k]) ff=k; // edges must be checked. + } else { // Verts probably can't fail but we still check + gmi_set* Verts = gmi_adjacent(model,Edges->e[k],0); + for (int j = 0; j < Verts->n; j++) + if(ge==Verts->e[j]) ff=j; + free(Verts); + } + k++; + } + free(Edges); + if( ff!=-1 ) { // is it in cls + mesh->setModelEntity(e,getMdlFace(mesh,cmax-2000000)); + } else { // edge not in closure so interior + int rtag = findRegionTag1Face(model, cmax) ; + mesh->setModelEntity(e,getMdlRegion(mesh,rtag)); + } + } + } else if (cmax >= 1000000) { // max is an edge + if (cmin == cmax) // cls on same edge + mesh->setModelEntity(e,getMdlEdge(mesh,cmax-1000000)); + else if (cmin >= 1000000) { // min is a different edge and there is a face they must be in the closure of but which face is it + gmi_set* maxFaces = gmi_adjacent(model,gmi_find(model,1,tagMax),2); + gmi_set* minFaces = gmi_adjacent(model,gmi_find(model,1,tagMin),2); + for (int i = 0; i < maxFaces->n; i++) { + for (int j = 0; j < minFaces->n; j++) { + if(minFaces->e[j]==maxFaces->e[i]){ + int fftag=gmi_tag(model,maxFaces->e[j]); + mesh->setModelEntity(e,getMdlFace(mesh,fftag)); + } + } + } + free(maxFaces); + free(minFaces); + } else mesh->setModelEntity(e,getMdlEdge(mesh,cmax-1000000)); // min is vtx thus max is correct edge to classify + + } else if (cmax < 1000000) { // two model verts so this is a 1 elm in z mesh + gmi_set* maxEdges = gmi_adjacent(model,gmi_find(model,0,tagMax),1); + gmi_set* minEdges = gmi_adjacent(model,gmi_find(model,0,tagMin),1); + for (int i = 0; i < maxEdges->n; i++) { + for (int j = 0; j < minEdges->n; j++) { + if(minEdges->e[j]==maxEdges->e[i]){ + int fftag=gmi_tag(model,maxEdges->e[i]); + mesh->setModelEntity(e,getMdlEdge(mesh,fftag)); + } + } + } + free(maxEdges); + free(minEdges); + } else { // should never get here since cmax < 10000000 is a vtx + fprintf(stderr, "edge classification of these vert failed %d %d \n", cmin, cmax); + } + } + mesh->end(it); +} + +/* if any of four vertices are classified on region -> region + * else on model face and it is impossible to have more than one face in the 4 + * vertices classification + * */ +void setFaceClassification(gmi_model* model, apf::Mesh2* mesh, apf::MeshTag* vtxClass) { + (void)model; + (void)mesh; + (void)vtxClass; + apf::MeshIterator* it = mesh->begin(2); + apf::MeshEntity* f; + int c,rtag; + apf::Adjacent verts; + double distFromDebug1; + apf::Vector3 xd1(-0.597998, 0.41004, 0.08); + apf::Vector3 dx1; + apf::Vector3 dx2; + apf::Vector3 tmp; + apf::Vector3 Centroid; + while( (f = mesh->iterate(it)) ) { + mesh->getAdjacent(f, 0, verts); + int nverts = verts.size(); + if(1==0) { + Centroid=apf::getLinearCentroid(mesh,f); + dx1=xd1-Centroid; + distFromDebug1=dx1[0]*dx1[0] + +dx1[1]*dx1[1] + +dx1[2]*dx1[2]; + } + int cmin=100000000; + int cmax=-100; + int ctri[4]; // up to 4 points on a face + for(int i=0; igetIntTag(verts[i],vtxClass,&c); + cmin=std::min(cmin,c); + cmax=std::max(cmax,c); + ctri[i]=c; + } + if(0==1 && distFromDebug1 < 1e-11) { + fprintf(stderr, "%d %d %.15e %.15E %.15E \n", cmin, cmax, Centroid[0], Centroid[1], Centroid[2]); + for (int i=0; i < nverts; i++) { + mesh->getPoint(verts[i],0,tmp); // fprintf(stderr, "%d %.15e %.15E %.15E \n", i , tmp[0], tmp[1], tmp[2]); + } + } + if (cmax >= 3000000) { // at least one vertex is interior -> cls interior + mesh->setModelEntity(f,getMdlRegion(mesh,cmax-3000000)); // mesh->setModelEntity(f,getMdlRgn(model)); //cint++; + } else if(cmin >= 2000000) { // all nodes on model face(s?) + if(cmax != cmin) { // all on faces but not all on same so classified on interior + rtag = findRegionTag2Face(model, ctri, nverts); + assert(rtag!=-1); // bad input list of ctri (e.g., not two distinct faces) + mesh->setModelEntity(f,getMdlRegion(mesh,rtag)); + } else { // all on same face so classify on that one + mesh->setModelEntity(f,getMdlFace(mesh,cmax-2000000)); + } + } else { // faces can ONLY be classified on model faces or interior but their vertices can be classified on model faces, edge, or vertices (regions caught in if). Consequently, the simplest logic is to loop over faces and check if any face has all of this mesh face's verts model classification in its closure + gmi_iter* gi=gmi_begin(model,2); // iterator over ALL the models faces + gmi_ent* gf; + gmi_ent* gt; + int i,dimi,ff,tagi,k; + int faceFound=0; + int ifaceS=0; + while ( (gf=gmi_next(model,gi)) && faceFound != nverts ) { + faceFound=0; i=0; + while(in)) && ff==-1){ // check all edges until one found + if(dimi==1) { + if(ge==Edges->e[k]) ff=k; // edges must be checked. + } else { // Verts probably can't fail but we still check + gmi_set* Verts = gmi_adjacent(model,Edges->e[k],0); + for (int j = 0; j < Verts->n; j++) + if(ge==Verts->e[j]) ff=j; + free(Verts); + } + k++; + } + free(Edges); + } + if( ff!=-1 ) faceFound++; + i++; + } + if(faceFound==nverts ) { + int fftag=gmi_tag(model,gf); + mesh->setModelEntity(f,getMdlFace(mesh,fftag)); + } + ifaceS++; + } + gmi_end(model,gi); + if(faceFound != nverts ) { // none of the model face's closure held all verts classificaton so interior wedges can sit in corner with a face with 1 vertex on each of two faces and other one on the edge that intersects the two faces (or a model vertex) so the above fails in this LITERAL conner case) + rtag = findRegionTag2Face(model, ctri, nverts); + assert(rtag!=-1); //bad input list of ctri (e.g., not two distinct faces) POSSIBLE yet unseen/unhandled case + mesh->setModelEntity(f,getMdlRegion(mesh,rtag)); + } + } + } + mesh->end(it); +} + +/** \brief set the mesh region classification + \details hacked to set the classification to the same geometric model region +*/ +//manifold void setRgnClassification(gmi_model* model, apf::Mesh2* mesh) { +void setRgnClassification(gmi_model* model, apf::Mesh2* mesh, apf::MeshTag* vtxClass) { + (void)model; + (void)mesh; + (void)vtxClass; + int c; + apf::Adjacent verts; + apf::MeshIterator* it = mesh->begin(3); + apf::MeshEntity* rgn; + while( (rgn = mesh->iterate(it)) ) { + mesh->getAdjacent(rgn, 0, verts); + int nverts = verts.size(); + int cmax=-100; + std::vector cAll(nverts); + for(int i=0; igetIntTag(verts[i],vtxClass,&c); + cmax=std::max(cmax,c); + cAll[i]=c; + } + if(cmax >= 3000000) { + mesh->setModelEntity(rgn,getMdlRegion(mesh,cmax-3000000)); + } else { + int rtag = findRegionTag2Face(model, cAll.data(), nverts); + assert(rtag!=-1); // bad input list of ctri (e.g., not two distinct faces) POSSIBLE yet unseen/unhandled case + mesh->setModelEntity(rgn,getMdlRegion(mesh,rtag)); + } + } + mesh->end(it); +} + +void setClassification(gmi_model* model, apf::Mesh2* mesh, apf::MeshTag* t) { + setVtxClassification(model,mesh,t); + setEdgeClassification(model,mesh,t); + setFaceClassification(model,mesh,t); + setRgnClassification(model,mesh,t); + mesh->acceptChanges(); +} + + + +void getLocalRange(apf::Gid total, int& local, + apf::Gid& first, apf::Gid& last) { + const int self = PCU_Comm_Self(); + const int peers = PCU_Comm_Peers(); + local = total/peers; + if( self == peers-1 ) { //last rank + apf::Gid lp=local*peers; + if( lp < total ){ + apf::Gid lpd; + lpd= total - lp; + local += lpd; + } + } + first = PCU_Exscan_Long(local); + last = first+local; +} + +void printElmTypeError(int dim, int numVtxPerElm) { + fprintf(stderr, "unknown element type for" + "dim %d and numVtxPerElm %d in %s\n", + dim, numVtxPerElm, __func__); +} + +unsigned getElmType(int dim, int numVtxPerElm) { + if (dim == 2) { + if (numVtxPerElm == 3) + return apf::Mesh::TRIANGLE; + if (numVtxPerElm == 4) + return apf::Mesh::QUAD; + else { + printElmTypeError(dim, numVtxPerElm); + exit(EXIT_FAILURE); + } + } else if (dim == 3) { + if (numVtxPerElm == 4) + return apf::Mesh::TET; + else if (numVtxPerElm == 6) + return apf::Mesh::PRISM; + else if (numVtxPerElm == 8) + return apf::Mesh::HEX; + else { + printElmTypeError(dim, numVtxPerElm); + exit(EXIT_FAILURE); + } + } else { + printElmTypeError(dim, numVtxPerElm); + exit(EXIT_FAILURE); + } +} + +bool skipLine(char* line) { + // lines that start with either a '#' or a single white space + // are skipped + return (line[0] == '#' || line[0] == ' ' ); +} + +void getNumVerts(FILE* f, apf::Gid& verts) { + rewind(f); + gmi_fscanf(f, 1, "%ld", &verts); +} + +void readClassification(FILE* f, int localNumVtx, int** classification) { + *classification = new int[localNumVtx]; + rewind(f); + int mdlId; + for(int i=0; i= 1 && matchedVtx <= numvtx )); + if( matchedVtx != -1 ) + --matchedVtx; + (*matches)[i] = matchedVtx; + } +// I think the above will perform better than the code commented out below +// int vidx = 0; +// while( 1 == fscanf(f, "%ld", &matchedVtx) ) { +// PCU_ALWAYS_ASSERT( matchedVtx == -1 || +// ( matchedVtx >= 1 && matchedVtx <= numvtx )); +// if( matchedVtx != -1 ) +// --matchedVtx; +// (*matches)[vidx] = matchedVtx; +// vidx++; +// } +} + +//static int starts_with(char const* with, char const* s) { +// int lw; +// int ls; +// lw = strlen(with); +// ls = strlen(s); +// if (ls < lw) +// return 0; +// return strncmp(with, s, lw) == 0; +//} + +bool seekPart(std::ifstream& f, const std::string& marker) { + std::stringstream ss; + ss << "^\\s+" << marker << "$"; + std::regex partId(ss.str()); + std::string line; + while (std::getline(f, line)) { + if (std::regex_match(line,partId)) { + return true; + } + } + return false; +} + +struct BlockInfo { + long numElms; + int vtxPerElm; +}; + +std::vector readTopoBlockInfo(std::ifstream& f) { + std::vector blocks; + long blockSize; + int vtxPerElement; + + std::string line; + while (std::getline(f, line)) { + std::istringstream iss(line); + if (!(iss >> blockSize >> vtxPerElement)) { break; } // error + blocks.push_back({blockSize,vtxPerElement}); + } + return blocks; +} + +void rewindStream(std::ifstream& f) { + f.clear(); + f.seekg(0); +} + +/** +fh = header file (there is only one for all processes), containing: + Part0 + + < NodesInElementTopo2> + ... for as many topos as are in Part 0 + Repeat the above bock for each part. +**/ +std::vector readHeader(std::ifstream& fh) { + rewindStream(fh); + const int self = PCU_Comm_Self();; + bool ret = seekPart(fh, std::to_string(self)); + PCU_ALWAYS_ASSERT(ret); + auto blockInfo = readTopoBlockInfo(fh); + assert(blockInfo.size()>0); + for(auto b : blockInfo) { + std::cout << self << " " << b.numElms << " " << b.vtxPerElm << "\n"; + } + return blockInfo; +} + +/** +- f = part file, each part gets a file that contains a rectangular array, one for each topology + present on that part, that provides element to vertexGlobalId connectivity in + the order listed in the section of the header file for that part +**/ +void readElements(std::ifstream& f, apf::Gid numElms, + unsigned numVtxPerElm, apf::Gid* elements, bool rewind) { + if(rewind) rewindStream(f); + unsigned elmIdx = 0; + apf::Gid* elmVtx = new apf::Gid[numVtxPerElm]; + for (int i = 0; i < numElms; i++) { + for (unsigned j = 0; j < numVtxPerElm; j++) + f >> elmVtx[j]; + for (unsigned j = 0; j < numVtxPerElm; j++) { + const unsigned elmVtxIdx = elmIdx*numVtxPerElm+j; + elements[elmVtxIdx] = --(elmVtx[j]); //export from matlab using 1-based indices + } + elmIdx++; + } + PCU_ALWAYS_ASSERT(numElms==elmIdx); + delete [] elmVtx; +} + +struct MeshInfo { + double* coords; + double* solution; + std::vector elements; + apf::Gid* matches; + int* classification; + int* fathers2D; + unsigned dim; + std::vector elementType; + apf::Gid numVerts; + int localNumVerts; + std::vector numElms; + std::vector numVtxPerElm; +}; + +void readMesh(const char* meshfilename, + const char* coordfilename, + const char* matchfilename, + const char* classfilename, + const char* fathers2Dfilename, + const char* solutionfilename, + const char* connHeadfilename, + MeshInfo& mesh) { + + mesh.dim = 3; //FIXME + + int self = PCU_Comm_Self(); + + char filename[1024]; + sprintf(filename, "%s.%d",coordfilename,self); + + FILE* fc = fopen(filename , "r"); + PCU_ALWAYS_ASSERT(fc); + getNumVerts(fc,mesh.numVerts); + mesh.localNumVerts=mesh.numVerts; + mesh.numVerts=PCU_Add_Long(mesh.numVerts); + + if(!PCU_Comm_Self()) + fprintf(stderr, "numVerts %ld\n", mesh.numVerts); + readCoords(fc, mesh.localNumVerts, &(mesh.coords)); + fclose(fc); + + if(0==1) { + sprintf(filename, "%s.%d",solutionfilename,self); + FILE* fs = fopen(filename, "r"); + PCU_ALWAYS_ASSERT(fs); + readSolution(fs, mesh.localNumVerts, &(mesh.solution)); + fclose(fs); + } + + sprintf(filename, "%s.%d",classfilename,self); + FILE* ff = fopen(filename, "r"); + PCU_ALWAYS_ASSERT(ff); + readClassification(ff, mesh.localNumVerts, &(mesh.classification)); + fclose(ff); + + if( strcmp(fathers2Dfilename, "NULL") ) { + //add an argument to readMesh for the fathers2D + sprintf(filename, "%s.%d",fathers2Dfilename,self); + FILE* fff = fopen(filename, "r"); + PCU_ALWAYS_ASSERT(fff); + readFathers(fff, mesh.localNumVerts, &(mesh.fathers2D)); + fclose(fff); + } + + if( strcmp(matchfilename, "NULL") ) { + sprintf(filename, "%s.%d",matchfilename,self); + FILE* fm = fopen(filename, "r"); + PCU_ALWAYS_ASSERT(fm); + readMatches(fm, mesh.numVerts, mesh.localNumVerts, &(mesh.matches)); + fclose(fm); + } + + std::stringstream ss; + ss << meshfilename << "." << self; + std::ifstream meshConnStream(ss.str()); + PCU_ALWAYS_ASSERT(meshConnStream.is_open()); + std::ifstream connHeadStream(connHeadfilename, std::ios::in); + PCU_ALWAYS_ASSERT(connHeadStream.is_open()); + auto blockInfo = readHeader(connHeadStream); + connHeadStream.close(); + bool rewind = true; + for(auto b : blockInfo) { + mesh.numElms.push_back(b.numElms); + mesh.numVtxPerElm.push_back(b.vtxPerElm); + apf::Gid* elements = new apf::Gid[b.numElms*b.vtxPerElm]; + readElements(meshConnStream, b.numElms, b.vtxPerElm, elements,rewind); + rewind=false; + mesh.elementType.push_back(getElmType(mesh.dim, b.vtxPerElm)); + mesh.elements.push_back(elements); + } + meshConnStream.close(); +} + + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + lion_set_verbosity(1); + int noVerify=0; // maintain default of verifying if not explicitly requesting it off + if( argc < 11 ) { + if( !PCU_Comm_Self() ) { + printf("Usage: %s " + " " + " " + " " + " " + " " + " " + " " + " " + "turn off verify mesh if equal 1 (on if you give nothing)\n", + argv[0]); + } + return 0; + } + + gmi_register_mesh(); + gmi_register_null(); + + if( argc == 11 ) noVerify=atoi(argv[10]); + + double t0 = PCU_Time(); + MeshInfo m; + readMesh(argv[2],argv[3],argv[4],argv[5],argv[6],argv[7],argv[8],m); + + bool isMatched = true; + if( !strcmp(argv[3], "NULL") ) + isMatched = false; + + if(!PCU_Comm_Self()) + fprintf(stderr, "isMatched %d\n", isMatched); + + gmi_model* model = gmi_load(argv[1]); + apf::Mesh2* mesh = apf::makeEmptyMdsMesh(model, m.dim, isMatched); + apf::GlobalToVert outMap; + for( size_t i=0; i< m.elements.size(); i++) { + apf::assemble(mesh, m.elements[i], m.numElms[i], m.elementType[i], outMap); + delete [] m.elements[i]; + } + apf::finalise(mesh, outMap); + apf::alignMdsRemotes(mesh); + apf::deriveMdsModel(mesh); + apf::setCoords(mesh, m.coords, m.localNumVerts, outMap); + delete [] m.coords; + if( isMatched ) { + apf::setMatches(mesh, m.matches, m.localNumVerts, outMap); + mesh->acceptChanges(); + delete [] m.matches; + } + apf::MeshTag* tc = setMappedTag(mesh, "classification", m.classification, 1, + m.localNumVerts, outMap); + delete [] m.classification; + setClassification(model,mesh,tc); + apf::removeTagFromDimension(mesh, tc, 0); + mesh->destroyTag(tc); + + if( strcmp(argv[6], "NULL") ) { + setMappedTag(mesh, "fathers2D", m.fathers2D, 1, m.localNumVerts, outMap); + delete [] m.fathers2D; + } else if(!PCU_Comm_Self()) + fprintf(stderr, "fathers2D not requested \n"); + + if(0==1) { + apf::MeshTag* ts = setMappedTag(mesh, "solution", m.solution, 5, + m.localNumVerts, outMap); + (void) ts; + } + + if(!PCU_Comm_Self()) + fprintf(stderr, "seconds to create mesh %.3f\n", PCU_Time()-t0); + if(noVerify != 1) mesh->verify(); + + outMap.clear(); + apf::writeVtkFiles("rendered",mesh); + mesh->writeNative(argv[10]); + if(noVerify != 1) mesh->verify(); + + mesh->destroyNative(); + apf::destroyMesh(mesh); + PCU_Comm_Free(); + MPI_Finalize(); +} diff --git a/test/nedelecShapes.cc b/test/nedelecShapes.cc new file mode 100644 index 000000000..9575e948e --- /dev/null +++ b/test/nedelecShapes.cc @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; + +// User defined vector functions E(x,y,z) of order up to 6 +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p); + +void testNedelec( + apf::Mesh2* m, + const apf::Vector3& testXi, + int ndOrder, int exactOrder); + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + + lion_set_verbosity(0); + + if (argc != 3) { + if(0==PCU_Comm_Self()) + std::cerr << "usage: " << argv[0] + << " \n"; + return EXIT_FAILURE; + } + + gmi_register_mesh(); + gmi_register_null(); + + gmi_model* g = gmi_load(argv[1]); + apf::Mesh2* m = apf::loadMdsMesh(g,argv[2]); + m->verify(); + + + // constant fields have to be interpolated exactly by any order Nedelec shape + for (int order = 1; order <= 6; order++) { + testNedelec( + m, /* mesh */ + apf::Vector3(1./4., 1./5., 1./6.), /* test point */ + order, /* order of Nedelec field */ + 0); /* constant field */ + } + + // Fields of order p are interpolated exactly by Nedelec shapes of order p+1 + for (int i = 1; i <= 6; i++) { + testNedelec( + m, /* mesh */ + apf::Vector3(1./4., 1./5., 1./6.), /* test point */ + i, /* order of Nedelec field */ + i-1); /* order of test field */ + } + + apf::destroyMesh(m); + PCU_Comm_Free(); + MPI_Finalize(); +} + +void E_exact(const apf::Vector3& x, apf::Vector3& value, int p) +{ + // Polynomial coefficients for each component of exact vector field + double a[6] = { 1.0, -1.0, 2., -2., -1.0, 1.0}; + double b[6] = {-2.0, 1.0, -2., 2., -1.0, -1.0}; + double c[6] = { 3.0, 0.0, -1., 0., -1.0, 1.0}; + + value[0] = 0.0; + value[1] = 0.0; + value[2] = 0.0; + for (int i = p; i >= 0; i--) { + value[0] += pow(x[0],p)*a[p]; + value[1] += pow(x[1],p)*b[p]; + value[2] += pow(x[2],p)*c[p]; + } +} + + + +void testNedelec( + apf::Mesh2* m, + const apf::Vector3& testXi, + int ndOrder, int exactOrder) +{ + apf::Field* ndField = apf::createField( + m, "nedelec_test", apf::SCALAR, apf::getNedelec(ndOrder)); + + // Loop over all nodes and set scalar dofs. + int dim = m->getDimension(); + apf::MeshEntity* ent; + apf::MeshIterator* it; + + for (int d = 0; d <= dim; d++) { + if (!ndField->getShape()->countNodesOn(apf::Mesh::simplexTypes[d])) { + lion_oprint(1, "no nodes in dimension %d\n", d); + continue; + } + else + lion_oprint(1, "computing dofs for dimension %d\n", d); + it = m->begin(d); + while( (ent = m->iterate(it)) ) { + int type = m->getType(ent); + int non = ndField->getShape()->countNodesOn(type); + apf::MeshElement* me = apf::createMeshElement(m, ent); + for (int i = 0; i < non; i++) + { + apf::Vector3 xi, p, value; + ndField->getShape()->getNodeXi(type, i, xi); + apf::mapLocalToGlobal(me, xi, p); + E_exact(p, value, exactOrder); + + apf::Matrix3x3 J; + apf::getJacobian( me, xi, J); + + apf::Vector3 t; + ndField->getShape()->getNodeTangent(type, i, t); + + + // dof is t . J^T value + // note getJacobian returns the transpose of J, so no need to transpose + // J again. + apf::Vector3 temp = J * value; + double dof = temp * t; + apf::setScalar(ndField, ent, i, dof); + } + apf::destroyMeshElement(me); + } + m->end(it); + } + + + // Verify that interpolated solution field agrees with exact field. + double L2ErrorE = 0.; + double L2ErrorCurlE = 0.; + it = m->begin(3); + while( (ent = m->iterate(it)) ) { + apf::MeshElement* me = apf::createMeshElement(m, ent); + apf::Vector3 x; + apf::mapLocalToGlobal(me, testXi, x); + apf::Vector3 eFieldExact; + E_exact(x, eFieldExact, exactOrder); + + // obtain interpolated value + apf::Element* el = apf::createElement(ndField, me); + apf::Vector3 eFieldValue; + apf::getVector(el, testXi, eFieldValue); + // obtain curl field values + apf::Vector3 eCurlValue; + apf::getCurl(el, testXi, eCurlValue); + + L2ErrorE += ((eFieldValue - eFieldExact) * (eFieldValue - eFieldExact)) + / (eFieldExact * eFieldExact); // normalization factor + L2ErrorCurlE += eCurlValue * eCurlValue; + apf::destroyMeshElement(me); + apf::destroyElement(el); + } + m->end(it); + + // check for field interpolation + PCU_ALWAYS_ASSERT_VERBOSE(L2ErrorE < 1.e-16, + "Fields were not interpolated correctly!"); + // check for curl (only applies for constant field) + if (exactOrder == 0) + PCU_ALWAYS_ASSERT_VERBOSE(L2ErrorCurlE < 1.e-16, + "Curls are expected to be zero!"); + + apf::destroyField(ndField); +} diff --git a/test/ph_adapt.cc b/test/ph_adapt.cc index d8f0b7d1d..927ca1aca 100644 --- a/test/ph_adapt.cc +++ b/test/ph_adapt.cc @@ -75,7 +75,7 @@ int main(int argc, char** argv) sam::multiplySF(m, szFld, 1.0); apf::writeVtkFiles("before",m); /* mesh adaptation */ - ma::Input* ma_in = ma::configure(m, szFld); + ma::Input* ma_in = ma::makeAdvanced(ma::configure(m, szFld)); ma_in->shouldRunPreZoltan = true; ma_in->shouldRunMidParma = true; ma_in->shouldRunPostParma = true; diff --git a/test/print_pumipic_partition.cc b/test/print_pumipic_partition.cc new file mode 100644 index 000000000..3a6be4bc8 --- /dev/null +++ b/test/print_pumipic_partition.cc @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SIMMETRIX +#include +#include +#include +#include +#endif +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + lion_set_verbosity(1); + if ( argc != 5 && argc != 6) { + if ( !PCU_Comm_Self() ) + printf("Usage: %s \n", argv[0]); + MPI_Finalize(); + exit(EXIT_FAILURE); + } + if (PCU_Comm_Peers() > 1) { + if ( !PCU_Comm_Self() ) + printf("This tool must be run in serial.\n"); + MPI_Finalize(); + exit(EXIT_FAILURE); + } +#ifdef HAVE_SIMMETRIX + MS_init(); + SimModel_start(); + Sim_readLicenseFile(NULL); + gmi_sim_start(); + gmi_register_sim(); +#endif + gmi_register_mesh(); + + + apf::Mesh2* m = apf::loadMdsMesh(argv[1],argv[2]); + + int num_ranks = atoi(argv[3]); + //Partition the mesh (Taken from zsplit.cc) + apf::Splitter* splitter = apf::makeZoltanSplitter( + m, apf::GRAPH, apf::PARTITION, false); + apf::MeshTag* weights = Parma_WeighByMemory(m); + apf::Migration* plan = splitter->split(weights, 1.05, num_ranks); + apf::removeTagFromDimension(m, weights, m->getDimension()); + m->destroyTag(weights); + delete splitter; + + //Write the partition out + char filename[256]; + sprintf(filename , "%s_%d.ptn", argv[4], num_ranks); + printf("Writing partition to %s\n", filename); + std::ofstream out(filename); + apf::MeshIterator* mitr = m->begin(m->getDimension()); + while (apf::MeshEntity* ent = m->iterate(mitr)) { + if (plan->has(ent)) + out << plan->sending(ent) << '\n'; + else + out << 0 << '\n'; + } + m->end(mitr); + + delete plan; + + m->destroyNative(); + apf::destroyMesh(m); +#ifdef HAVE_SIMMETRIX + gmi_sim_stop(); + Sim_unregisterAllKeys(); + SimModel_stop(); + MS_exit(); +#endif + PCU_Comm_Free(); + MPI_Finalize(); +} diff --git a/test/pumi.cc b/test/pumi.cc index 249cadce8..bda9ed4a8 100644 --- a/test/pumi.cc +++ b/test/pumi.cc @@ -191,6 +191,7 @@ int main(int argc, char** argv) TEST_MESH(m); // re-load partitioned mesh via file i/o + pumi_geom_delete(g); pumi_mesh_delete(m); g = pumi_geom_load(modelFile); @@ -341,7 +342,7 @@ void TEST_GENT_SETGET_TAG (pGeom g, pGeomEnt ent) // pumi_gent_set/getPtrTag pumi_gent_setPtrTag (ent, pointer_tag, (void*)(data)); - void* void_data = (void*)calloc(strlen(data), sizeof(char)); + void* void_data; // pumi_gent_getPtrTag will point void_data at the stored address pumi_gent_getPtrTag (ent, pointer_tag, &void_data); PCU_ALWAYS_ASSERT(!strcmp((char*)void_data, data)); diff --git a/test/pumiLoadMesh.cc b/test/pumiLoadMesh.cc new file mode 100644 index 000000000..c5b5d0d01 --- /dev/null +++ b/test/pumiLoadMesh.cc @@ -0,0 +1,15 @@ +#include "mpi.h" +#include "pumi.h" + +int main(int argc, char** argv) +{ + MPI_Init(&argc,&argv); + pumi_start(); + pGeom g = pumi_geom_load(argv[1], "mesh"); + pMesh m = pumi_mesh_load(g, argv[2], 1); + pumi_mesh_delete(m); + pumi_geom_delete(g); + pumi_finalize(); + MPI_Finalize(); +} + diff --git a/test/refine2x.cc b/test/refine2x.cc index 6413ad24f..d09c090d5 100644 --- a/test/refine2x.cc +++ b/test/refine2x.cc @@ -103,9 +103,12 @@ int main(int argc, char** argv) gmi_register_mesh(); ma::Mesh* m = apf::loadMdsMesh(argv[1],argv[2]); AnisotropicX* ansx = new AnisotropicX(m, atoi(argv[3])); - ma::Input* in = ma::configure(m, ansx); + ma::Input* in = ma::makeAdvanced(ma::configure(m, ansx)); +#ifdef PUMI_HAS_ZOLTAN in->shouldRunPreZoltanRib = true; +#else in->shouldRunPreParma = true; +#endif in->shouldRunMidParma = true; in->shouldRunPostParma = true; in->maximumIterations = 10; diff --git a/test/residualErrorEstimation_test.cc b/test/residualErrorEstimation_test.cc new file mode 100644 index 000000000..085966460 --- /dev/null +++ b/test/residualErrorEstimation_test.cc @@ -0,0 +1,163 @@ +#include "ma.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIMMETRIX +#include +#include +#include +#include +#endif +#include + +void E_exact(const apf::Vector3 &x, apf::Vector3& E); +double computeElementExactError(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f); + +double freq = 1.0, kappa; +int dim = 3; + +int main(int argc, char** argv) +{ + PCU_ALWAYS_ASSERT(argc==3); + const char* modelFile = argv[1]; + const char* meshFile = argv[2]; + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + lion_set_verbosity(1); +#ifdef HAVE_SIMMETRIX + MS_init(); + SimModel_start(); + Sim_readLicenseFile(0); + gmi_sim_start(); + gmi_register_sim(); +#endif + gmi_register_mesh(); + ma::Mesh* m = apf::loadMdsMesh(modelFile,meshFile); + m->verify(); + + kappa = freq * M_PI; + + // get electric field + apf::Field* electric_field = m->getField(0); + PCU_ALWAYS_ASSERT(electric_field); + + // compute residual error + apf::Field* residual_error_field = ree::estimateError(electric_field); + + // compute exact error + apf::Field* exact_error_field = apf::createIPField( + m, "exact_error_field", apf::SCALAR, 1); + apf::MeshEntity* ent; + apf::MeshIterator* itr = m->begin(3); + while ((ent = m->iterate(itr))) + { + double exact_element_error = computeElementExactError( + m, ent, electric_field); + apf::setScalar(exact_error_field, ent, 0, exact_element_error); + } + m->end(itr); + + // get max and avg computed and exact errors + double max_exact_error = 0., max_computed_error = 0.; + double avg_exact_error = 0., avg_computed_error = 0.; + itr = m->begin(3); + while ((ent = m->iterate(itr))) + { + double exact_error = apf::getScalar(exact_error_field, ent, 0); + if (exact_error > max_exact_error) max_exact_error = exact_error; + avg_exact_error += exact_error; + + double computed_error = apf::getScalar(residual_error_field, ent, 0); + if (computed_error > max_computed_error) max_computed_error = computed_error; + avg_computed_error += computed_error; + } + m->end(itr); + avg_exact_error /= m->count(3); + avg_computed_error /= m->count(3); + + lion_oprint(1, "Max Exact Error: %e\n", max_exact_error); + lion_oprint(1, "Average Exact Error: %e\n", avg_exact_error); + lion_oprint(1, "Max Computed Error: %e\n", max_computed_error); + lion_oprint(1, "Average Computed Error: %e\n", avg_computed_error); + + apf::destroyField(residual_error_field); + apf::destroyField(exact_error_field); + + m->destroyNative(); + apf::destroyMesh(m); +#ifdef HAVE_SIMMETRIX + gmi_sim_stop(); + Sim_unregisterAllKeys(); + SimModel_stop(); + MS_exit(); +#endif + PCU_Comm_Free(); + MPI_Finalize(); +} + +void E_exact(const apf::Vector3 &x, apf::Vector3& E) +{ + if (dim == 3) + { + E[0] = sin(kappa * x[1]); + E[1] = sin(kappa * x[2]); + E[2] = sin(kappa * x[0]); + } + else + { + E[0] = sin(kappa * x[1]); + E[1] = sin(kappa * x[0]); + E[2] = 0.0; + } +} + +double computeElementExactError(apf::Mesh* mesh, apf::MeshEntity* e, + apf::Field* f) +{ + double error = 0.0; + + apf::FieldShape* fs = f->getShape(); + int type = mesh->getType(e); + PCU_ALWAYS_ASSERT(type == apf::Mesh::TET); + int dim = apf::getDimension(mesh, e); + double w; + + apf::MeshElement* me = apf::createMeshElement(mesh, e); + apf::Element* el = apf::createElement(f, me); + int order = 2*fs->getOrder() + 1; + int np = apf::countIntPoints(me, order); + + apf::Vector3 femsol, exsol; + + apf::Vector3 p; + for (int i = 0; i < np; i++) { + apf::getIntPoint(me, order, i, p); + double weight = apf::getIntWeight(me, order, i); + apf::Matrix3x3 J; + apf::getJacobian(me, p, J); + double jdet = apf::getJacobianDeterminant(J, dim); + w = weight * jdet; + + apf::getVector(el, p, femsol); + + apf::Vector3 global; + apf::mapLocalToGlobal(me, p, global); + E_exact(global, exsol); + apf::Vector3 diff = exsol - femsol; + + error += w * (diff * diff); + } + if (error < 0.0) + error = -error; + + apf::destroyElement(el); + apf::destroyMeshElement(me); + + return sqrt(error); +} + diff --git a/test/rm_extrusion.cc b/test/rm_extrusion.cc index 4e36da2c4..a21133b8b 100644 --- a/test/rm_extrusion.cc +++ b/test/rm_extrusion.cc @@ -135,6 +135,7 @@ int main(int argc, char** argv) M_release(sim_mesh); Progress_delete(progress); + gmi_destroy(mdl); gmi_sim_stop(); SimPartitionedMesh_stop(); Sim_unregisterAllKeys(); diff --git a/test/runSimxAnisoAdapt.cc b/test/runSimxAnisoAdapt.cc index a0c0389d8..bfb399a38 100644 --- a/test/runSimxAnisoAdapt.cc +++ b/test/runSimxAnisoAdapt.cc @@ -14,10 +14,6 @@ #include "MeshSimAdapt.h" #include "SimDiscrete.h" #include "SimMessages.h" -#include "SimError.h" -#include "SimErrorCodes.h" -#include "SimMeshingErrorCodes.h" -#include "SimDiscreteErrorCodes.h" #include #include @@ -34,7 +30,7 @@ typedef vector mat; void printModelStats(pGModel model); void makeSimxModelAndMesh( double* coords, int nverts, - int* conn, int nelem, + apf::Gid* conn, int nelem, pMesh& mesh, pDiscreteModel& model, pVertex* vReturn, pEntity* eReturn); bool checkVertexOrder( @@ -151,7 +147,7 @@ int main(int argc, char** argv) m->verify(); // extract the coordinates and connectivities - int* conn; + apf::Gid* conn; double* coords; int nelem; int etype; @@ -262,7 +258,7 @@ void printModelStats(pGModel model) void makeSimxModelAndMesh( double* coords, int nverts, - int* conn, int nelem, + apf::Gid* connGid, int nelem, pMesh& mesh, pDiscreteModel& model, pVertex* vReturn, pEntity* eReturn) { @@ -273,6 +269,12 @@ void makeSimxModelAndMesh( Sim_setMessageHandler(0); + const int connSize = 4*nelem; + int* conn = new int[connSize]; + for(int i=0; i(connGid[i]); + } + mesh = M_new(0,0); if(M_importFromData(mesh,nverts,coords,nelem, elementType,conn,vReturn,eReturn,0)) { //check for error @@ -304,6 +306,7 @@ void makeSimxModelAndMesh( GM_release(model); return; } + delete [] conn; } bool checkVertexOrder( @@ -504,7 +507,7 @@ static void getSizeAndFramesFromArray( void destructSimxMesh( pMesh mesh, double*& adaptedCoords, - int*& adaptedConns, + apf::Gid*& adaptedConns, int& nverts, int& nelem, vector& adaptedSizes, vector& adaptedFrames) @@ -513,7 +516,7 @@ void destructSimxMesh( nelem = countRegions(mesh); adaptedCoords = new double[3*nverts]; - adaptedConns = new int[4*nelem]; + adaptedConns = new apf::Gid[4*nelem]; VIter vertices; RIter regions; @@ -544,10 +547,10 @@ void destructSimxMesh( i=0; while( (region = RIter_next(regions)) ){ regionVerts = R_vertices(region,0); - vector ids; + vector ids; for(j=0; j < 4; j++){ vertex = (pVertex)PList_item(regionVerts,j); - int id = EN_id((pEntity)vertex); + apf::Gid id = EN_id((pEntity)vertex); ids.push_back(id); } // simmetrix's local ordering is different from pumi's @@ -585,7 +588,7 @@ apf::Mesh2* convertToPumi( const char* frameName) { double* adaptedCoords; - int* adaptedConns; + apf::Gid* adaptedConns; int adaptedNumVerts, adaptedNumElems; vector adaptedSizes; vector adaptedFrames; diff --git a/test/serialize.cc b/test/serialize.cc index 87f1b7be1..f233770b4 100644 --- a/test/serialize.cc +++ b/test/serialize.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #ifdef HAVE_SIMMETRIX #include @@ -42,12 +43,15 @@ int main( int argc, char* argv[]) gmi_register_sim(); #endif gmi_register_mesh(); + gmi_register_null(); crv::getBezier(2);//hack to make sure curved meshes can be serialized! GroupCode code; code.mesh = apf::loadMdsMesh(argv[1], argv[2]); code.meshFile = argv[3]; apf::Unmodulo outMap(PCU_Comm_Self(), PCU_Comm_Peers()); Parma_ShrinkPartition(code.mesh, atoi(argv[4]), code); + code.mesh->destroyNative(); + apf::destroyMesh(code.mesh); #ifdef HAVE_SIMMETRIX gmi_sim_stop(); Sim_unregisterAllKeys(); diff --git a/test/simTranslate.cc b/test/simTranslate.cc index f2425a404..535ded1d1 100644 --- a/test/simTranslate.cc +++ b/test/simTranslate.cc @@ -11,8 +11,6 @@ #include "SimAdvModel.h" #include "SimUtil.h" #include "SimMessages.h" -#include "SimError.h" -#include "SimErrorCodes.h" #include "MeshSim.h" #include "SimAttribute.h" #include "AttributeTypes.h" @@ -20,10 +18,23 @@ #include #include +/* hack to get SIMMODSUITE_MAJOR_VERSION and SIMMODSUITE_MINOR_VERSION */ +#include "../apf_sim/apf_simConfig.h" /* cheap hackish way to get SIM_PARASOLID and SIM_ACIS */ #include "gmi_sim_config.h" #include +#if SIMMODSUITE_MAJOR_VERSION >= 18 + #include "SimInfo.h" + #include "SimInfoCodes.h" + #define SIM_ERROR(suffix,err) SimInfo_##suffix(err) + typedef pSimInfo pSimError; +#else + #include "SimError.h" + #include "SimErrorCodes.h" + #define SIM_ERROR(suffix,err) SimError_##suffix(err) +#endif + #ifdef SIM_PARASOLID #include "SimParasolidKrnl.h" #endif @@ -44,14 +55,14 @@ static std::string acisExt = ".sat"; static std::string paraExt = ".xmt_txt"; static std::string paraExtshort = ".x_t"; -void printSimError(pSimError err) -{ +void printSimError(pSimError err) { printf("Simmetrix error caught:\n"); - printf(" Error code: %d\n",SimError_code(err)); - printf(" Error string: %s\n",SimError_toString(err)); - SimError_delete(err); + printf(" Error code: %d\n",SIM_ERROR(code,err)); + printf(" Error string: %s\n",SIM_ERROR(toString,err)); + SIM_ERROR(delete,err); } + void translateModel(std::string mdlName, pGModel* simmodel, pProgress& progress) { (void)progress; @@ -96,7 +107,12 @@ void translateModel(std::string mdlName, pGModel* simmodel, pProgress& progress) PList_delete(modelErrors); // translate the model +#if SIMMODSUITE_MAJOR_VERSION >= 15 && SIMMODSUITE_MINOR_VERSION >= 200714 + const int keepAnalyticSurfaces = 1; + *simmodel = GM_translateModel(model, NULL, keepAnalyticSurfaces); +#else *simmodel = GM_translateModel(model, NULL); +#endif GM_release(model); } diff --git a/test/smokeTesting.cmake b/test/smokeTesting.cmake new file mode 100644 index 000000000..49a460dec --- /dev/null +++ b/test/smokeTesting.cmake @@ -0,0 +1,27 @@ +function(smoke_test TESTNAME PROCS EXE) + set(tname smoke_test_${TESTNAME}) + add_test( + NAME ${tname} + COMMAND ${MPIRUN} ${MPIRUN_PROCFLAG} ${PROCS} ${VALGRIND} ${VALGRIND_ARGS} ${EXE} ${ARGN}) + SET_TESTS_PROPERTIES(${tname} PROPERTIES LABELS "SMOKE_TEST" ) +endfunction(smoke_test) + +set(MDIR ${MESHES}/pipe) +smoke_test(uniform_serial 1 + ./uniform + "${MDIR}/pipe.dmg" + "${MDIR}/pipe.smb" + "pipe_unif.smb") + +smoke_test(split_2 2 + ./split + "${MDIR}/pipe.dmg" + "${MDIR}/pipe.smb" + "pipe_2_.smb" + 2) + +include(GNUInstallDirs) +# install the test input files for use in spack post-install tests +install(FILES "${MDIR}/pipe.dmg" "${MDIR}/pipe0.smb" + DESTINATION ${CMAKE_INSTALL_DATADIR}/testdata) + diff --git a/test/snap.cc b/test/snap.cc index c5b63379a..6134d245d 100644 --- a/test/snap.cc +++ b/test/snap.cc @@ -21,9 +21,7 @@ int main(int argc, char** argv) gmi_sim_start(); gmi_register_sim(); ma::Mesh* m = apf::loadMdsMesh(argv[1],argv[2]); - ma::Input* in = ma::configureIdentity(m); - in->shouldSnap = true; - in->shouldTransferParametric = true; + const ma::Input* in = ma::configureIdentity(m); ma::adapt(in); m->writeNative(argv[3]); m->destroyNative(); diff --git a/test/swapDoubles.cc b/test/swapDoubles.cc new file mode 100644 index 000000000..beacc27d2 --- /dev/null +++ b/test/swapDoubles.cc @@ -0,0 +1,29 @@ +#include +#include //pcu_swap_doubles +#include //PCU_ALWAYS_ASSERT +#include //iota +#include //cerr + +int main(int argc, char** argv) { + MPI_Init(&argc,&argv); + PCU_Comm_Init(); + const size_t n = 2; + double *d_orig = new double[n]; + std::iota(d_orig,d_orig+n,0); + double *d = new double[n]; + std::iota(d,d+n,0); + pcu_swap_doubles(d, n); + pcu_swap_doubles(d, n); + for(size_t i=0; icount(i) == countInt->getCount()); } + delete countInt; mesh->destroyNative(); apf::destroyMesh(mesh); PCU_Comm_Free(); diff --git a/test/test_matrix_grad.cc b/test/test_matrix_grad.cc index aae3ac861..71eb13729 100644 --- a/test/test_matrix_grad.cc +++ b/test/test_matrix_grad.cc @@ -27,6 +27,7 @@ void setMatField(apf::Mesh* mesh, apf::Field* nodal_fld) apf::Matrix3x3 F(1+5*coords[0], 0, 2*coords[1], 0, 1+coords[1], 0, 0, 0, 1+3*coords[2]); apf::setMatrix(nodal_fld, ent, 0, F); } + mesh->end(it); } class MatrixDerivIntegrator : public apf::Integrator @@ -107,10 +108,14 @@ int main(int argc, char* argv[]) std::cout<<"Setting matrix derivatives"<process(mesh); + delete set_matrix_deriv; std::cout<<"Checking values"<process(mesh); + delete check_matrix_deriv; std::cout<<"Done"<destroyNative(); + apf::destroyMesh(mesh); PCU_Comm_Free(); MPI_Finalize(); return 0; diff --git a/test/testing.cmake b/test/testing.cmake index 96c419933..6c4bda03e 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -8,16 +8,6 @@ function(mpi_test TESTNAME PROCS EXE) ) endfunction(mpi_test) -function(smoke_test TESTNAME PROCS EXE) - set(tname smoke_test_${TESTNAME}) - add_test( - NAME ${tname} - COMMAND ${MPIRUN} ${MPIRUN_PROCFLAG} ${PROCS} ${VALGRIND} ${VALGRIND_ARGS} ${EXE} ${ARGN} - CONFIGURATIONS SMOKE_TEST_CONFIG - WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin) - SET_TESTS_PROPERTIES(${tname} PROPERTIES LABELS "SMOKE_TEST" ) -endfunction(smoke_test) - mpi_test(shapefun 1 ./shapefun) mpi_test(shapefun2 1 ./shapefun2) mpi_test(bezierElevation 1 ./bezierElevation) @@ -28,10 +18,21 @@ mpi_test(bezierSubdivision 1 ./bezierSubdivision) mpi_test(bezierValidity 1 ./bezierValidity) mpi_test(ma_analytic 1 ./ma_test_analytic_model) +if(ENABLE_ZOLTAN) +mpi_test(print_pumipic_partion 1 + ./print_pumipic_partition + ${MESHES}/cube/cube.dmg + ${MESHES}/cube/pumi11/cube.smb + 4 + pumipic_cube + ) +endif() + mpi_test(align 1 ./align) mpi_test(eigen_test 1 ./eigen_test) mpi_test(integrate 1 ./integrate) mpi_test(qr_test 1 ./qr) +mpi_test(swapDoubles 1 ./swapDoubles) mpi_test(base64 1 ./base64) mpi_test(tensor_test 1 ./tensor) mpi_test(verify_convert 1 ./verify_convert) @@ -56,6 +57,10 @@ if(ENABLE_SIMMETRIX) mpi_test(modelInfo_smd 1 ./modelInfo "${MESHES}/cube/cube.smd") + mpi_test(highorder_sizefield 1 + ./highOrderSizeFields + "${MESHES}/cube/cube.smd" + "${MESHES}/cube/pumi11/cube.smb") endif(ENABLE_SIMMETRIX) if(ENABLE_SIMMETRIX) @@ -73,6 +78,18 @@ if(ENABLE_SIMMETRIX AND SIM_PARASOLID AND SIMMODSUITE_SimAdvMeshing_FOUND) add_test(NAME chef-BL_query-diff COMMAND diff -r run_case/4-procs_case/ good_case/4-procs_case WORKING_DIRECTORY ${MDIR}) + set(MDIR ${MESHES}/simExtrusionInfo) + mpi_test(convertExtrudedRoots 1 ${CMAKE_CURRENT_BINARY_DIR}/convert + --model-face-root=${MDIR}/ExtruRootID.txt + --native-model=${MDIR}/geom.xmt_txt + ${MDIR}/geom.smd ${MDIR}/geom.sms ${MDIR}/mdsMesh.smb + WORKING_DIRECTORY ${MDIR}) + add_test(NAME convertExtrudedRoots_diff_cnn + COMMAND diff -r geom.cnn geom_expected.cnn + WORKING_DIRECTORY ${MDIR}) + add_test(NAME convertExtrudedRoots_diff_crd + COMMAND diff -r geom.crd geom_expected.crd + WORKING_DIRECTORY ${MDIR}) endif() if(ENABLE_SIMMETRIX AND SIM_PARASOLID AND SIMMODSUITE_SimAdvMeshing_FOUND) @@ -122,7 +139,7 @@ if(ENABLE_SIMMETRIX AND SIM_PARASOLID AND SIMMODSUITE_SimAdvMeshing_FOUND) endif(ENABLE_SIMMETRIX AND SIM_PARASOLID AND SIMMODSUITE_SimAdvMeshing_FOUND) set(MDIR ${MESHES}/phasta/loopDriver) -if(ENABLE_SIMMETRIX AND PCU_COMPRESS AND SIM_PARASOLID +if(ENABLE_ZOLTAN AND ENABLE_SIMMETRIX AND PCU_COMPRESS AND SIM_PARASOLID AND SIMMODSUITE_SimAdvMeshing_FOUND) mpi_test(ph_adapt 1 ${CMAKE_CURRENT_BINARY_DIR}/ph_adapt @@ -131,6 +148,7 @@ if(ENABLE_SIMMETRIX AND PCU_COMPRESS AND SIM_PARASOLID WORKING_DIRECTORY ${MDIR}) endif() + if(ENABLE_ZOLTAN) mpi_test(pumi3d-1p 4 ./test_pumi @@ -184,6 +202,50 @@ mpi_test(create_misSquare 1 ${MESHES}/square/square.smb mis_test) +set(MDIR ${MESHES}/matchedNodeElementReader) +mpi_test(matchedNodeElementReader_p1 1 + ./matchedNodeElmReader + "${MDIR}/model.dmg" + "${MDIR}/1part/geom3D.cnndt" + "${MDIR}/1part/geom3D.coord" + "${MDIR}/1part/geom3D.match" + "${MDIR}/1part/geom3D.class" + "${MDIR}/1part/geom3D.fathr" + "NULL" + "${MDIR}/1part/geom3DHead.cnn" + "geom.dmg" "geom.smb") + +mpi_test(matchedNodeElementReader_p4 4 + ./matchedNodeElmReader + "${MDIR}/model.dmg" + "${MDIR}/4part/geom3D.cnndt" + "${MDIR}/4part/geom3D.coord" + "${MDIR}/4part/geom3D.match" + "${MDIR}/4part/geom3D.class" + "${MDIR}/4part/geom3D.fathr" + "NULL" + "${MDIR}/4part/geom3DHead.cnn" + "geom.dmg" "geom.smb") + +set(MDIR ${MESHES}/gmsh) +mpi_test(gmshv2TwoQuads 1 + ./from_gmsh + "none" + "${MDIR}/twoQuads.msh" + "${MDIR}/twoQuads.smb" + "${MDIR}/twoQuads.dmg") + +set(MDIR ${MESHES}/gmsh/v4) +mpi_test(gmshV4AirFoil 1 + ./from_gmsh + "none" + "${MDIR}/AirfoilDemo.msh" + "${MDIR}/AirfoilDemo.smb" + "${MDIR}/AirfoilDemo.dmg") +add_test(NAME gmshV4AirFoil_dmgDiff + COMMAND diff -r ${MDIR}/AirfoilDemo.dmg AirfoilDemo_gold.dmg + WORKING_DIRECTORY ${MDIR}) + set(MDIR ${MESHES}/ugrid) mpi_test(naca_ugrid 2 ./from_ugrid @@ -246,11 +308,6 @@ mpi_test(uniform_serial 1 "pipe.smb" "pipe_unif.smb") mpi_test(classifyThenAdapt 1 ./classifyThenAdapt) -smoke_test(uniform_serial 1 - ./uniform - "${MDIR}/pipe.${GXT}" - "${MDIR}/pipe.smb" - "pipe_unif.smb") if(ENABLE_SIMMETRIX) mpi_test(snap_serial 1 ./snap @@ -258,21 +315,21 @@ if(ENABLE_SIMMETRIX) "pipe_unif.smb" "pipe.smb") endif() -mpi_test(ma_serial 1 - ./ma_test - "${MDIR}/pipe.${GXT}" - "pipe.smb") -mpi_test(aniso_ma_serial 1 - ./aniso_ma_test - "${MESHES}/cube/cube.dmg" - "${MESHES}/cube/pumi670/cube.smb" - "0") -mpi_test(aniso_ma_serial_log_interpolation 1 - ./aniso_ma_test - "${MESHES}/cube/cube.dmg" - "${MESHES}/cube/pumi670/cube.smb" - "1") if(ENABLE_ZOLTAN) + mpi_test(ma_serial 1 + ./ma_test + "${MDIR}/pipe.${GXT}" + "pipe.smb") + mpi_test(aniso_ma_serial 1 + ./aniso_ma_test + "${MESHES}/cube/cube.dmg" + "${MESHES}/cube/pumi670/cube.smb" + "0") + mpi_test(aniso_ma_serial_log_interpolation 1 + ./aniso_ma_test + "${MESHES}/cube/cube.dmg" + "${MESHES}/cube/pumi670/cube.smb" + "1") mpi_test(torus_ma_parallel 4 ./torus_ma_test "${MESHES}/torus/torus.dmg" @@ -283,6 +340,12 @@ mpi_test(tet_serial 1 "${MDIR}/pipe.${GXT}" "pipe.smb" "tet.smb") +if(ENABLE_SIMMETRIX AND SIM_PARASOLID) + mpi_test(test_residual_error_estimate 1 + ./residualErrorEstimation_test + "${MESHES}/electromagnetic/fichera.x_t" + "${MESHES}/electromagnetic/fichera_1k.smb") +endif() if(PCU_COMPRESS) set(MESHFILE "bz2:pipe_2_.smb") else() @@ -294,12 +357,6 @@ mpi_test(split_2 2 "pipe.smb" ${MESHFILE} 2) -smoke_test(split_2 2 - ./split - "${MDIR}/pipe.${GXT}" - "${MDIR}/pipe.smb" - ${MESHFILE} - 2) mpi_test(collapse_2 2 ./collapse "${MDIR}/pipe.${GXT}" @@ -451,7 +508,7 @@ if(ENABLE_ZOLTAN) ) endif() -if(ENABLE_CGNS) +if(ENABLE_CGNS AND ENABLE_ZOLTAN) # # sort of an arbitrary choice set(numProcs 4) @@ -536,7 +593,7 @@ mpi_test(cgns_bcs_3 ${numProcs} bcs3.smb additional) -endif(ENABLE_CGNS) +endif(ENABLE_CGNS AND ENABLE_ZOLTAN) mpi_test(construct 4 ./construct @@ -574,7 +631,7 @@ mpi_test(mixedNumbering 4 out) set(MDIR ${MESHES}/square) mpi_test(hierarchic_2p_2D 1 - ./hierarchic + ./hierarchic "${MDIR}/square.dmg" "${MDIR}/square.smb" 2) @@ -589,6 +646,33 @@ mpi_test(hierarchic_2p_3D 1 "${MDIR}/cube.dmg" "${MDIR}/cube.smb" 2) +set(MDIR ${MESHES}/cube/pumi24) +mpi_test(nedelec 1 + ./nedelecShapes + "${MDIR}/cube.dmg" + "${MDIR}/cube.smb") +set(MDIR ${MESHES}/cube/pumi670) +mpi_test(l2_shape_tet_serial 1 + ./L2Shapes + "${MDIR}/../cube.dmg" + "${MDIR}/cube.smb") +mpi_test(l2_shape_tet_parallel 4 + ./L2Shapes + "${MDIR}/../cube.dmg" + "${MDIR}/4/cube.smb") +mpi_test(h1_shape_serial 1 + ./H1Shapes + "${MDIR}/../cube.dmg" + "${MDIR}/cube.smb") +mpi_test(h1_shape_parallel 4 + ./H1Shapes + "${MDIR}/../cube.dmg" + "${MDIR}/4/cube.smb") +set(MDIR ${MESHES}/cube/pumi24) +mpi_test(pumiLoadMesh-1p 1 + ./pumiLoadMesh + ${MDIR}/cube.dmg + ${MDIR}/cube.smb) set(MDIR ${MESHES}/cube) mpi_test(test_verify 4 ./test_verify @@ -663,11 +747,13 @@ if(ENABLE_SIMMETRIX) "${MDIR}/upright.smd" "67k") endif() - # adapt_meshgen uses the output of parallel_meshgen - mpi_test(adapt_meshgen 4 - ./ma_test - "${MDIR}/upright.smd" - "67k/") + if(ENABLE_ZOLTAN) + # adapt_meshgen uses the output of parallel_meshgen + mpi_test(adapt_meshgen 4 + ./ma_test + "${MDIR}/upright.smd" + "67k/") + endif() endif() if(SIM_PARASOLID) mpi_test(convert_para 1 @@ -690,6 +776,10 @@ if(ENABLE_SIMMETRIX) ./curvetest "${MDIR}/sphere1.xmt_txt" "${MDIR}/sphere1_4.smb") + mpi_test(highOrderSolutionTransfer 1 + ./highOrderSolutionTransfer + "${MDIR}/sphere1.xmt_txt" + "${MDIR}/sphere1_4.smb") mpi_test(curvedKova 1 ./curvetest "${MDIR}/Kova.xmt_txt" @@ -758,7 +848,7 @@ if (PCU_COMPRESS) add_test(NAME chef8 COMMAND diff -r out_mesh/ good_mesh/ WORKING_DIRECTORY ${MDIR}) - if(ENABLE_SIMMETRIX) + if(ENABLE_ZOLTAN AND ENABLE_SIMMETRIX) mpi_test(chef9 2 ${CMAKE_CURRENT_BINARY_DIR}/chef WORKING_DIRECTORY ${MESHES}/phasta/simModelAndAttributes) endif() diff --git a/test/tetrahedronize.cc b/test/tetrahedronize.cc index a6192cc4c..edd035d3f 100644 --- a/test/tetrahedronize.cc +++ b/test/tetrahedronize.cc @@ -27,7 +27,7 @@ int main(int argc, char** argv) #endif gmi_register_mesh(); ma::Mesh* m = apf::loadMdsMesh(argv[1], argv[2]); - ma::Input* in = ma::configureIdentity(m); + ma::Input* in = ma::makeAdvanced(ma::configureIdentity(m)); in->shouldTurnLayerToTets = true; ma::adapt(in); m->writeNative(argv[3]); diff --git a/test/torus_ma_test.cc b/test/torus_ma_test.cc index fe04b6beb..d7d6bcc06 100644 --- a/test/torus_ma_test.cc +++ b/test/torus_ma_test.cc @@ -55,11 +55,15 @@ int main(int argc, char** argv) m->verify(); apf::writeVtkFiles("torus_before",m); CylindricalShock sf(m); - ma::Input* in = ma::configure(m, &sf); + ma::Input* in = ma::makeAdvanced(ma::configure(m, &sf)); + // Note that the optimal number of iterations will be set + // inside the call to ma::configure above. However for this + // test we override this value to 3 to reduce the run time of + // test. + in->maximumIterations = 3; in->shouldRunPreZoltan = true; in->shouldRunMidParma = true; in->shouldRunPostParma = true; - in->shouldRefineLayer = true; ma::adapt(in); m->verify(); apf::writeVtkFiles("torus_after",m); diff --git a/test/uniform.cc b/test/uniform.cc index dc43109e5..427598a06 100644 --- a/test/uniform.cc +++ b/test/uniform.cc @@ -46,7 +46,7 @@ int main(int argc, char** argv) gmi_register_mesh(); getConfig(argc,argv); ma::Mesh* m = apf::loadMdsMesh(modelFile,meshFile); - ma::Input* in = ma::configureUniformRefine(m, 1); + ma::Input* in = ma::makeAdvanced(ma::configureUniformRefine(m, 1)); if (in->shouldSnap) { in->shouldSnap = false; PCU_ALWAYS_ASSERT(in->shouldTransferParametric); diff --git a/test/verify_2nd_order_shapes.cc b/test/verify_2nd_order_shapes.cc index cedb5f713..bc26005eb 100644 --- a/test/verify_2nd_order_shapes.cc +++ b/test/verify_2nd_order_shapes.cc @@ -100,6 +100,8 @@ int main(int argc, char** argv) { abort(); } + apf::destroyElement(elem); + m->destroyNative(); apf::destroyMesh(m); #ifdef HAVE_SIMMETRIX diff --git a/test/visualizeAnisoSizes.cc b/test/visualizeAnisoSizes.cc index 4f9c75e02..00a1c2178 100644 --- a/test/visualizeAnisoSizes.cc +++ b/test/visualizeAnisoSizes.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -25,27 +26,10 @@ #include /* for checking the error from mkdir */ // =============================== -static double PI = 3.14159265359; - void safe_mkdir(const char* path); double getLargetsSize( apf::Mesh2* m, apf::Field* sizes); -apf::Vector3 getPointOnEllipsoid( - apf::Vector3 center, - apf::Vector3 abc, - apf::Matrix3x3 rotation, - double scaleFactor, - double u, - double v); -void makeEllipsoid( - apf::Mesh2* msf, - apf::Mesh2* m, - apf::Field* sizes, - apf::Field* frames, - apf::MeshEntity* vert, - double scaleFactor, - int sampleSize[2]); void visualizeSizeField( const char* modelFile, const char* meshFile, @@ -140,94 +124,6 @@ double getLargetsSize( return maxSize; } -apf::Vector3 getPointOnEllipsoid( - apf::Vector3 center, - apf::Vector3 abc, - apf::Matrix3x3 rotation, - double scaleFactor, - double u, - double v) -{ - apf::Vector3 result; - result[0] = abc[0] * cos(u) * cos(v); - result[1] = abc[1] * cos(u) * sin(v); - result[2] = abc[2] * sin(u); - - result = result * scaleFactor; - - result = rotation * result + center; - return result; -} - - -void makeEllipsoid( - apf::Mesh2* msf, - apf::Mesh2* mesh, - apf::Field* sizes, - apf::Field* frames, - apf::MeshEntity* vert, - double scaleFactor, - int sampleSize[2]) -{ - - apf::Vector3 center; - mesh->getPoint(vert, 0, center); - - apf::Vector3 abc; - apf::getVector(sizes, vert, 0, abc); - - apf::Matrix3x3 rotation; - apf::getMatrix(frames, vert, 0, rotation); - - - double U0 = 0.0; - double U1 = 2 * PI; - double V0 = -PI/2.; - double V1 = PI/2.; - int n = sampleSize[0]; - int m = sampleSize[1]; - double dU = (U1 - U0) / (n-1); - double dV = (V1 - V0) / (m-1); - - // make the array of vertex coordinates in the physical space - std::vector ps; - for (int j = 0; j < m; j++) { - for (int i = 0; i < n; i++) { - double u = U0 + i * dU; - double v = V0 + j * dV; - apf::Vector3 pt = getPointOnEllipsoid(center, abc, rotation, scaleFactor, u, v); - ps.push_back(pt); - } - } - // make the vertexes and set the coordinates using the array - std::vector vs; - for (size_t i = 0; i < ps.size(); i++) { - apf::MeshEntity* newVert = msf->createVert(0); - msf->setPoint(newVert, 0, ps[i]); - vs.push_back(newVert); - } - - PCU_ALWAYS_ASSERT(vs.size() == ps.size()); - - apf::MeshEntity* v[3]; - // make the lower/upper t elems - for (int i = 0; i < n-1; i++) { - for (int j = 0; j < m-1; j++) { - // upper triangle - v[0] = vs[(i + 0) + n * (j + 0)]; - v[1] = vs[(i + 0) + n * (j + 1)]; - v[2] = vs[(i + 1) + n * (j + 0)]; - apf::buildElement(msf, 0, apf::Mesh::TRIANGLE, v); - // upper triangle - v[0] = vs[(i + 0) + n * (j + 1)]; - v[1] = vs[(i + 1) + n * (j + 1)]; - v[2] = vs[(i + 1) + n * (j + 0)]; - apf::buildElement(msf, 0, apf::Mesh::TRIANGLE, v); - } - } -} - - void visualizeSizeField( const char* modelFile, const char* meshFile, @@ -290,23 +186,12 @@ void visualizeSizeField( m->verify(); - // create the size-field visualization mesh - apf::Mesh2* msf = apf::makeEmptyMdsMesh(gmi_load(".null"), 2, false); - - apf::MeshEntity* vert; - apf::MeshIterator* it = m->begin(0); - while ( (vert = m->iterate(it)) ) - if (m->isOwned(vert)) - makeEllipsoid(msf, m, sizes, frames, vert, userScale , sampleSize); - m->end(it); - std::stringstream ss; ss << outputPrefix << "/size_field_vis"; - apf::writeVtkFiles(ss.str().c_str(), msf); + ma_dbg::visualizeSizeField( + m, sizes, frames, sampleSize, userScale, ss.str().c_str()); ss.str(""); - msf->destroyNative(); - apf::destroyMesh(msf); m->destroyNative(); apf::destroyMesh(m); } diff --git a/test/xgc_split.cc b/test/xgc_split.cc index deaaddc75..2417362c2 100644 --- a/test/xgc_split.cc +++ b/test/xgc_split.cc @@ -76,7 +76,7 @@ int main(int argc, char** argv) char without_extension[256]; snprintf(without_extension,strlen(argv[3])-3,"%s",argv[3]); - char vtk_fname[32]; + char vtk_fname[512]; sprintf(vtk_fname,"%s",without_extension); pumi_mesh_write(m, vtk_fname, "vtk"); diff --git a/zoltan/CMakeLists.txt b/zoltan/CMakeLists.txt index 5450ee52a..0901f9df5 100644 --- a/zoltan/CMakeLists.txt +++ b/zoltan/CMakeLists.txt @@ -63,6 +63,7 @@ if(ENABLE_ZOLTAN) ${ZOLTAN_LIBRARIES} ${PARMETIS_LIBRARIES} ) + target_compile_definitions(apf_zoltan PUBLIC PUMI_HAS_ZOLTAN) endif() scorec_export_library(apf_zoltan)